Pārlūkot izejas kodu

feat: 教师评审管理

NeeDaye 6 mēneši atpakaļ
vecāks
revīzija
8e35626ac6

+ 16 - 4
.umirc.ts

@@ -9,13 +9,25 @@ export default defineConfig({
   layout: false,
   routes: [
     {
+      path: '/login',
+      name: '登录',
+      component: './Login',
+      layout: false,
+    },
+    {
       path: '/',
-      redirect: '/table',
+      redirect: '/record',
+      custom: [{
+        label: '课堂录播', key: '/record'
+      }]
     },
     {
-      name: ' CRUD 示例',
-      path: '/table',
-      component: './Table',
+      name: '课堂录播',
+      path: '/record',
+      component: './Record',
+      custom: [{
+        label: '课堂录播', key: '/record'
+      }]
     },
   ],
   npmClient: 'pnpm',

+ 2 - 0
package.json

@@ -13,6 +13,8 @@
   "dependencies": {
     "@ant-design/icons": "^5.0.1",
     "@ant-design/pro-components": "^2.4.4",
+    "@ant-design/pro-list": "^2.6.7",
+    "@ant-design/pro-table": "^3.19.0",
     "@umijs/max": "^4.4.11",
     "antd": "^5.4.0"
   },

+ 12 - 6
pnpm-lock.yaml

@@ -11,6 +11,12 @@ dependencies:
   '@ant-design/pro-components':
     specifier: ^2.4.4
     version: 2.8.7(antd@5.25.2)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1)
+  '@ant-design/pro-list':
+    specifier: ^2.6.7
+    version: 2.6.7(antd@5.25.2)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1)
+  '@ant-design/pro-table':
+    specifier: ^3.19.0
+    version: 3.19.0(antd@5.25.2)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1)
   '@umijs/max':
     specifier: ^4.4.11
     version: 4.4.11(@babel/core@7.27.1)(@types/react-dom@18.3.7)(@types/react@18.3.22)(dva@2.5.0-beta.2)(prettier@2.8.8)(rc-field-form@2.7.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.8.3)(webpack@5.99.9)
@@ -2039,7 +2045,7 @@ packages:
     peerDependencies:
       react: '>=16.3.0'
     dependencies:
-      '@babel/runtime': 7.23.6
+      '@babel/runtime': 7.27.1
       hoist-non-react-statics: 3.3.2
       react: 18.3.1
       react-is: 16.13.1
@@ -2693,7 +2699,7 @@ packages:
     resolution: {integrity: sha512-hy8b7Y1J8OGe6LbAjj3xniQrj3v6lsivCcrmf4TzSgPzLkhIeKgc5IZnT7ReIqmEuodjfO8EYAuoFvIrHi/+jQ==}
     deprecated: This is a stub types definition. history provides its own type definitions, so you do not need this installed.
     dependencies:
-      history: 4.10.1
+      history: 5.3.0
     dev: false
 
   /@types/hoist-non-react-statics@3.3.6:
@@ -2784,7 +2790,7 @@ packages:
       '@types/history': 4.7.11
       '@types/react': 18.3.22
       '@types/react-router': 5.1.20
-      redux: 3.7.2
+      redux: 4.2.1
     dev: false
 
   /@types/react-router@5.1.20:
@@ -3223,7 +3229,7 @@ packages:
   /@umijs/history@5.3.1:
     resolution: {integrity: sha512-/e0cEGrR2bIWQD7pRl3dl9dcyRGeC9hoW0OCvUTT/hjY0EfUrkd6G8ZanVghPMpDuY5usxq9GVcvrT8KNXLWvA==}
     dependencies:
-      '@babel/runtime': 7.23.6
+      '@babel/runtime': 7.27.1
       query-string: 6.14.1
     dev: false
 
@@ -6731,7 +6737,7 @@ packages:
   /history@5.3.0:
     resolution: {integrity: sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==}
     dependencies:
-      '@babel/runtime': 7.23.6
+      '@babel/runtime': 7.27.1
     dev: false
 
   /hmac-drbg@1.0.1:
@@ -10406,7 +10412,7 @@ packages:
       react: ^16.6.0 || ^17.0.0 || ^18.0.0
       react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0
     dependencies:
-      '@babel/runtime': 7.23.6
+      '@babel/runtime': 7.27.1
       invariant: 2.2.4
       prop-types: 15.8.1
       react: 18.3.1

+ 69 - 23
src/layouts/SideMenu.tsx

@@ -1,17 +1,12 @@
-import React, {useState} from 'react';
-import type { MenuProps } from 'antd';
-import { Menu } from 'antd';
+import React, { useState } from 'react';
+import { Button, Input, Space } from 'antd';
 import { ReactComponent as SvgLogo } from '@/icons/logo.svg';
-import { history } from '@umijs/max';
 
 
 import './styles/side-menu.less';
-import {useLocation} from "@@/exports";
+import { SearchOutlined } from '@ant-design/icons';
+import ProList from '@ant-design/pro-list';
 
-type MenuItem = Required<MenuProps>['items'][number];
-
-const items: MenuItem[] = [
-];
 
 interface SideMenuProps {
     onCollaped: (isExpanded: boolean)=> void,
@@ -21,25 +16,76 @@ interface SideMenuProps {
 function SideMenu  ({
     initWidth = 200,
 }: SideMenuProps) {
-  const location = useLocation();
-  const [selectedKeys, setSelectedKeys] = useState([location.pathname]);
+  const [activeKey, setActiveKey] = useState<string | number>('');
+  const data = [
+    {
+      teacherName: '张三',
+      schoolName: '清华大学',
+    },
+    {
+      teacherName: '李四',
+      schoolName: '北京大学',
+    },
+    {
+      teacherName: '王五',
+      schoolName: '南京大学',
+    },
+    {
+      teacherName: '麻六',
+      schoolName: '上海大学',
+    }
+  ].map((item) => ({
+    title: item.teacherName,
+    description: item.schoolName,
+    actions: [],
+    avatar: false,
+  }));
 
   return (
     <div style={{ width: initWidth, height: '100vh' }}>
-      <div className='side-header'>
-        <SvgLogo style={{fill: 'currentColor'}} className='header-logo'/>
-        <h3 className='header-title-text'>评审管理系统</h3>
+      <div className="side-header">
+        <SvgLogo style={{ fill: 'currentColor' }} className="header-logo" />
+        <h3 className="header-title-text">评审管理系统</h3>
+      </div>
+      <div className="side-search">
+        <div
+          style={{
+            color: 'black',
+            fontSize: 16,
+            fontWeight: 'bold',
+            margin: '20px 0 20px 10px',
+            textAlign: 'left',
+          }}
+        >
+          参评教师
+        </div>
+        <Space direction="horizontal" style={{ padding: '0 10px' }}>
+          <Input placeholder="输入教师姓名搜索" />
+          <Button icon={<SearchOutlined />} type="primary" />
+        </Space>
       </div>
-      <div className='menu-area'>
-        <Menu
-          defaultSelectedKeys={selectedKeys}
-          mode="inline"
-          theme="light"
-          items={items}
-          onClick={(e) => {
-            setSelectedKeys([e.key]);
-            history.push(e.key);
+      <div className="menu-area">
+        <ProList<any>
+          pagination={false}
+          showActions="hover"
+          onItem={(record) => {
+            return {
+              onClick: () => {
+                setActiveKey(record.title);
+              },
+            };
+          }}
+          rowClassName={(record: { title: string }) =>
+            record.title === activeKey ? 'active' : ''
+          }
+          metas={{
+            title: {},
+            description: {
+              render: (text) => <div style={{ textAlign: 'left' }}>{text}</div>,
+            },
+            avatar: {},
           }}
+          dataSource={data}
         />
       </div>
     </div>

+ 11 - 0
src/layouts/styles/index.less

@@ -27,4 +27,15 @@
   flex: 1;
   overflow: hidden;
   padding: 10px 20px 20px;
+  .ant-pro-card-body {
+    margin: 16px;
+    .ant-list-items {
+      display: flex;
+      flex-wrap: wrap;
+      gap: 30px;
+      > div {
+        flex: 0 0 calc(25% - 23px);
+      }
+    }
+  }
 }

+ 9 - 0
src/layouts/styles/side-menu.less

@@ -33,8 +33,17 @@
     height: 20px !important;
 }
 .menu-area {
+    margin-top: 10px;
     height: calc(100vh - 65px);
     overflow-y: auto;
+    .ant-list-item {
+        cursor: pointer;
+    }
+    .active {
+        .ant-pro-list-row-title, .ant-list-item-meta-description {
+            color: #3C82F6 !important;
+        }
+    }
 }
 .ant-menu-item-selected::before {
     content: ' ';

+ 25 - 0
src/pages/Login/index.tsx

@@ -0,0 +1,25 @@
+import { useNavigate } from "@umijs/max";
+import React from "react";
+import * as LoginController from '@/services/login/LoginController';
+import { Button } from 'antd';
+
+
+const LoginPage: React.FC = () => {
+  const navigate = useNavigate();
+
+  const handleLogin = async () => {
+    if (localStorage.getItem('token')) {
+      navigate('/');
+    } else {
+      const data = await LoginController.login({ username: 'mxkj_xy', password: 'Aa@@1144' });
+      if (data.code === 200) {
+        localStorage.setItem('token', data.data.accessToken as string)
+        navigate('/');
+      }
+    }
+  };
+
+  return <Button onClick={handleLogin}>登录</Button>;
+}
+
+export default LoginPage;

+ 116 - 0
src/pages/Record/index.tsx

@@ -0,0 +1,116 @@
+import {
+  PageContainer,
+} from '@ant-design/pro-components';
+import React from 'react';
+import ProList from '@ant-design/pro-list';
+
+const TableList: React.FC<unknown> = () => {
+  const data = [
+    {
+      image: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
+      courseName: '这是课程名称',
+      gradeSubjectName: '高中数学',
+      schoolName: '杭州市滨江区市一中',
+      teacherName: '张老师',
+      teacherTime: '2025-05-28 12:00:00',
+      classRoom: '高一4班'
+    },
+    {
+      image: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
+      courseName: '这是课程名称',
+      gradeSubjectName: '高中数学',
+      schoolName: '杭州市滨江区市一中',
+      teacherName: '张老师',
+      teacherTime: '2025-05-28 12:00:00',
+      classRoom: '高一4班'
+    },
+    {
+      image: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
+      courseName: '这是课程名称',
+      gradeSubjectName: '高中数学',
+      schoolName: '杭州市滨江区市一中',
+      teacherName: '张老师',
+      teacherTime: '2025-05-28 12:00:00',
+      classRoom: '高一4班'
+    },
+    {
+      image: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
+      courseName: '这是课程名称',
+      gradeSubjectName: '高中数学',
+      schoolName: '杭州市滨江区市一中',
+      teacherName: '张老师',
+      teacherTime: '2025-05-28 12:00:00',
+      classRoom: '高一4班'
+    },
+    {
+      image: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
+      courseName: '这是课程名称',
+      gradeSubjectName: '高中数学',
+      schoolName: '杭州市滨江区市一中',
+      teacherName: '张老师',
+      teacherTime: '2025-05-28 12:00:00',
+      classRoom: '高一4班'
+    },
+    {
+      image: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
+      courseName: '这是课程名称',
+      gradeSubjectName: '高中数学',
+      schoolName: '杭州市滨江区市一中',
+      teacherName: '张老师',
+      teacherTime: '2025-05-28 12:00:00',
+      classRoom: '高一4班'
+    },
+    {
+      image: 'https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg',
+      courseName: '这是课程名称',
+      gradeSubjectName: '高中数学',
+      schoolName: '杭州市滨江区市一中',
+      teacherName: '张老师',
+      teacherTime: '2025-05-28 12:00:00',
+      classRoom: '高一4班'
+    }
+  ];
+
+  return (
+    <PageContainer>
+      <ProList<any>
+        search={{}}
+        className="table-box"
+        pagination={{
+          defaultPageSize: 20,
+          showSizeChanger: false,
+        }}
+        showActions="hover"
+        onItem={(record: any) => {
+          return {
+            onClick: () => {
+              console.log(record);
+            },
+          };
+        }}
+        renderItem={(item) => (
+          <div style={{ backgroundColor: '#F8FAFC' }}>
+            <img
+              width={'100%'}
+              src={item.image}
+            />
+            <div>
+              <div>
+                <div>{item.courseName}</div>
+                <div>{item.gradeSubjectName}</div>
+              </div>
+              <div></div>
+              <div></div>
+            </div>
+          </div>
+        )}
+        metas={{
+          actions: {},
+        }}
+        dataSource={data}
+      />
+    </PageContainer>
+  );
+};
+
+export default TableList;

+ 0 - 26
src/pages/Table/components/CreateForm.tsx

@@ -1,26 +0,0 @@
-import { Modal } from 'antd';
-import React, { PropsWithChildren } from 'react';
-
-interface CreateFormProps {
-  modalVisible: boolean;
-  onCancel: () => void;
-}
-
-const CreateForm: React.FC<PropsWithChildren<CreateFormProps>> = (props) => {
-  const { modalVisible, onCancel } = props;
-
-  return (
-    <Modal
-      destroyOnClose
-      title="新建"
-      width={420}
-      open={modalVisible}
-      onCancel={() => onCancel()}
-      footer={null}
-    >
-      {props.children}
-    </Modal>
-  );
-};
-
-export default CreateForm;

+ 0 - 138
src/pages/Table/components/UpdateForm.tsx

@@ -1,138 +0,0 @@
-import {
-  ProFormDateTimePicker,
-  ProFormRadio,
-  ProFormSelect,
-  ProFormText,
-  ProFormTextArea,
-  StepsForm,
-} from '@ant-design/pro-components';
-import { Modal } from 'antd';
-import React from 'react';
-
-export interface FormValueType extends Partial<API.UserInfo> {
-  target?: string;
-  template?: string;
-  type?: string;
-  time?: string;
-  frequency?: string;
-}
-
-export interface UpdateFormProps {
-  onCancel: (flag?: boolean, formVals?: FormValueType) => void;
-  onSubmit: (values: FormValueType) => Promise<void>;
-  updateModalVisible: boolean;
-  values: Partial<API.UserInfo>;
-}
-
-const UpdateForm: React.FC<UpdateFormProps> = (props) => (
-  <StepsForm
-    stepsProps={{
-      size: 'small',
-    }}
-    stepsFormRender={(dom, submitter) => {
-      return (
-        <Modal
-          width={640}
-          bodyStyle={{ padding: '32px 40px 48px' }}
-          destroyOnClose
-          title="规则配置"
-          open={props.updateModalVisible}
-          footer={submitter}
-          onCancel={() => props.onCancel()}
-        >
-          {dom}
-        </Modal>
-      );
-    }}
-    onFinish={props.onSubmit}
-  >
-    <StepsForm.StepForm
-      initialValues={{
-        name: props.values.name,
-        nickName: props.values.nickName,
-      }}
-      title="基本信息"
-    >
-      <ProFormText
-        width="md"
-        name="name"
-        label="规则名称"
-        rules={[{ required: true, message: '请输入规则名称!' }]}
-      />
-      <ProFormTextArea
-        name="desc"
-        width="md"
-        label="规则描述"
-        placeholder="请输入至少五个字符"
-        rules={[
-          { required: true, message: '请输入至少五个字符的规则描述!', min: 5 },
-        ]}
-      />
-    </StepsForm.StepForm>
-    <StepsForm.StepForm
-      initialValues={{
-        target: '0',
-        template: '0',
-      }}
-      title="配置规则属性"
-    >
-      <ProFormSelect
-        width="md"
-        name="target"
-        label="监控对象"
-        valueEnum={{
-          0: '表一',
-          1: '表二',
-        }}
-      />
-      <ProFormSelect
-        width="md"
-        name="template"
-        label="规则模板"
-        valueEnum={{
-          0: '规则模板一',
-          1: '规则模板二',
-        }}
-      />
-      <ProFormRadio.Group
-        name="type"
-        width="md"
-        label="规则类型"
-        options={[
-          {
-            value: '0',
-            label: '强',
-          },
-          {
-            value: '1',
-            label: '弱',
-          },
-        ]}
-      />
-    </StepsForm.StepForm>
-    <StepsForm.StepForm
-      initialValues={{
-        type: '1',
-        frequency: 'month',
-      }}
-      title="设定调度周期"
-    >
-      <ProFormDateTimePicker
-        name="time"
-        label="开始时间"
-        rules={[{ required: true, message: '请选择开始时间!' }]}
-      />
-      <ProFormSelect
-        name="frequency"
-        label="监控对象"
-        width="xs"
-        valueEnum={{
-          month: '月',
-          week: '周',
-        }}
-      />
-    </StepsForm.StepForm>
-  </StepsForm>
-);
-
-export default UpdateForm;

+ 0 - 270
src/pages/Table/index.tsx

@@ -1,270 +0,0 @@
-import services from '@/services/demo';
-import {
-  ActionType,
-  FooterToolbar,
-  PageContainer,
-  ProDescriptions,
-  ProDescriptionsItemProps,
-  ProTable,
-} from '@ant-design/pro-components';
-import { Button, Divider, Drawer, message } from 'antd';
-import React, { useRef, useState } from 'react';
-import CreateForm from './components/CreateForm';
-import UpdateForm, { FormValueType } from './components/UpdateForm';
-
-const { addUser, queryUserList, deleteUser, modifyUser } =
-  services.UserController;
-
-/**
- * 添加节点
- * @param fields
- */
-const handleAdd = async (fields: API.UserInfo) => {
-  const hide = message.loading('正在添加');
-  try {
-    await addUser({ ...fields });
-    hide();
-    message.success('添加成功');
-    return true;
-  } catch (error) {
-    hide();
-    message.error('添加失败请重试!');
-    return false;
-  }
-};
-
-/**
- * 更新节点
- * @param fields
- */
-const handleUpdate = async (fields: FormValueType) => {
-  const hide = message.loading('正在配置');
-  try {
-    await modifyUser(
-      {
-        userId: fields.id || '',
-      },
-      {
-        name: fields.name || '',
-        nickName: fields.nickName || '',
-        email: fields.email || '',
-      },
-    );
-    hide();
-
-    message.success('配置成功');
-    return true;
-  } catch (error) {
-    hide();
-    message.error('配置失败请重试!');
-    return false;
-  }
-};
-
-/**
- *  删除节点
- * @param selectedRows
- */
-const handleRemove = async (selectedRows: API.UserInfo[]) => {
-  const hide = message.loading('正在删除');
-  if (!selectedRows) return true;
-  try {
-    await deleteUser({
-      userId: selectedRows.find((row) => row.id)?.id || '',
-    });
-    hide();
-    message.success('删除成功,即将刷新');
-    return true;
-  } catch (error) {
-    hide();
-    message.error('删除失败,请重试');
-    return false;
-  }
-};
-
-const TableList: React.FC<unknown> = () => {
-  const [createModalVisible, handleModalVisible] = useState<boolean>(false);
-  const [updateModalVisible, handleUpdateModalVisible] =
-    useState<boolean>(false);
-  const [stepFormValues, setStepFormValues] = useState({});
-  const actionRef = useRef<ActionType>();
-  const [row, setRow] = useState<API.UserInfo>();
-  const [selectedRowsState, setSelectedRows] = useState<API.UserInfo[]>([]);
-  const columns: ProDescriptionsItemProps<API.UserInfo>[] = [
-    {
-      title: '名称',
-      dataIndex: 'name',
-      tip: '名称是唯一的 key',
-      formItemProps: {
-        rules: [
-          {
-            required: true,
-            message: '名称为必填项',
-          },
-        ],
-      },
-    },
-    {
-      title: '昵称',
-      dataIndex: 'nickName',
-      valueType: 'text',
-    },
-    {
-      title: '性别',
-      dataIndex: 'gender',
-      hideInForm: true,
-      valueEnum: {
-        0: { text: '男', status: 'MALE' },
-        1: { text: '女', status: 'FEMALE' },
-      },
-    },
-    {
-      title: '操作',
-      dataIndex: 'option',
-      valueType: 'option',
-      render: (_, record) => (
-        <>
-          <a
-            onClick={() => {
-              handleUpdateModalVisible(true);
-              setStepFormValues(record);
-            }}
-          >
-            配置
-          </a>
-          <Divider type="vertical" />
-          <a href="">订阅警报</a>
-        </>
-      ),
-    },
-  ];
-
-  return (
-    <PageContainer
-      header={{
-        title: 'CRUD 示例',
-      }}
-    >
-      <ProTable<API.UserInfo>
-        headerTitle="查询表格"
-        actionRef={actionRef}
-        rowKey="id"
-        search={{
-          labelWidth: 120,
-        }}
-        toolBarRender={() => [
-          <Button
-            key="1"
-            type="primary"
-            onClick={() => handleModalVisible(true)}
-          >
-            新建
-          </Button>,
-        ]}
-        request={async (params, sorter, filter) => {
-          const { data, success } = await queryUserList({
-            ...params,
-            // FIXME: remove @ts-ignore
-            // @ts-ignore
-            sorter,
-            filter,
-          });
-          return {
-            data: data?.list || [],
-            success,
-          };
-        }}
-        columns={columns}
-        rowSelection={{
-          onChange: (_, selectedRows) => setSelectedRows(selectedRows),
-        }}
-      />
-      {selectedRowsState?.length > 0 && (
-        <FooterToolbar
-          extra={
-            <div>
-              已选择{' '}
-              <a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}
-              项&nbsp;&nbsp;
-            </div>
-          }
-        >
-          <Button
-            onClick={async () => {
-              await handleRemove(selectedRowsState);
-              setSelectedRows([]);
-              actionRef.current?.reloadAndRest?.();
-            }}
-          >
-            批量删除
-          </Button>
-          <Button type="primary">批量审批</Button>
-        </FooterToolbar>
-      )}
-      <CreateForm
-        onCancel={() => handleModalVisible(false)}
-        modalVisible={createModalVisible}
-      >
-        <ProTable<API.UserInfo, API.UserInfo>
-          onSubmit={async (value) => {
-            const success = await handleAdd(value);
-            if (success) {
-              handleModalVisible(false);
-              if (actionRef.current) {
-                actionRef.current.reload();
-              }
-            }
-          }}
-          rowKey="id"
-          type="form"
-          columns={columns}
-        />
-      </CreateForm>
-      {stepFormValues && Object.keys(stepFormValues).length ? (
-        <UpdateForm
-          onSubmit={async (value) => {
-            const success = await handleUpdate(value);
-            if (success) {
-              handleUpdateModalVisible(false);
-              setStepFormValues({});
-              if (actionRef.current) {
-                actionRef.current.reload();
-              }
-            }
-          }}
-          onCancel={() => {
-            handleUpdateModalVisible(false);
-            setStepFormValues({});
-          }}
-          updateModalVisible={updateModalVisible}
-          values={stepFormValues}
-        />
-      ) : null}
-
-      <Drawer
-        width={600}
-        open={!!row}
-        onClose={() => {
-          setRow(undefined);
-        }}
-        closable={false}
-      >
-        {row?.name && (
-          <ProDescriptions<API.UserInfo>
-            column={2}
-            title={row?.name}
-            request={async () => ({
-              data: row || {},
-            })}
-            params={{
-              id: row?.name,
-            }}
-            columns={columns}
-          />
-        )}
-      </Drawer>
-    </PageContainer>
-  );
-};
-
-export default TableList;

+ 22 - 0
src/services/login/LoginController.ts

@@ -0,0 +1,22 @@
+
+import { request } from '@umijs/max';
+
+/**
+ * login
+ * @returns
+ * @param body
+ * @param options
+ */
+export async function login(
+  body?: API.LoginParams,
+  options?: { [key: string]: any },
+) {
+  return request<API.LoginRes>('/api/auth/login', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+    },
+    data: body,
+    ...(options || {}),
+  });
+}

+ 5 - 0
src/services/login/index.ts

@@ -0,0 +1,5 @@
+
+import * as LoginController from './LoginController';
+export default {
+  LoginController,
+};

+ 15 - 0
src/services/login/typings.d.ts

@@ -0,0 +1,15 @@
+declare namespace API {
+  // 参数接口
+  interface LoginParams {
+    password?: string;
+    username?: string;
+  }
+
+  // 响应接口
+  interface LoginRes {
+    code: number;
+    data: Record<string, unknown>;
+    message: string;
+    timestamp: number;
+  }
+}