Forráskód Böngészése

Merge branch 'release/1.5.5'

tianyunperfect 4 éve
szülő
commit
b24b516848
60 módosított fájl, 1834 hozzáadás és 1103 törlés
  1. 4 0
      CHANGELOG.md
  2. 2 56
      README.md
  3. 4 2
      bin/push.sh
  4. 38 6
      config/config.ts
  5. 3 1
      config/plugin.config.ts
  6. 1 0
      package.json
  7. 1 1
      public/CNAME
  8. 1 0
      public/back/CNAME
  9. BIN
      public/back/favicon.png
  10. BIN
      public/back/home_bg.png
  11. BIN
      public/back/icons/icon-128x128.png
  12. BIN
      public/back/icons/icon-192x192.png
  13. BIN
      public/back/icons/icon-512x512.png
  14. 0 0
      public/back/pro_icon.svg
  15. BIN
      public/favicon.png
  16. BIN
      public/home_bg.png
  17. BIN
      public/icons/icon-128x128.png
  18. BIN
      public/icons/icon-192x192.png
  19. BIN
      public/icons/icon-512x512.png
  20. 0 0
      public/pro_icon.svg
  21. 0 0
      src/assets/logo.back.svg
  22. 0 0
      src/assets/logo.svg
  23. 1 12
      src/layouts/BasicLayout.tsx
  24. 13 1
      src/layouts/UserLayout.tsx
  25. 2 0
      src/locales/zh-CN/menu.ts
  26. 0 25
      src/pages/ListTableList/components/CreateForm.tsx
  27. 0 214
      src/pages/ListTableList/components/UpdateForm.tsx
  28. 0 232
      src/pages/ListTableList/index.tsx
  29. 0 38
      src/pages/ListTableList/service.ts
  30. 4 4
      src/pages/Memory/components/Quill.tsx
  31. 161 113
      src/pages/Memory/components/UpdateForm.tsx
  32. 0 7
      src/pages/Memory/data.d.ts
  33. 10 1
      src/pages/Memory/index.less
  34. 212 169
      src/pages/Memory/index.tsx
  35. 1 0
      src/pages/Memory/service.ts
  36. 0 6
      src/pages/MemoryList/_mock.ts
  37. 14 16
      src/pages/MemoryList/data.d.ts
  38. 0 0
      src/pages/MemoryList/index.less
  39. 198 0
      src/pages/MemoryList/index.tsx
  40. 7 0
      src/pages/MemoryList/service.ts
  41. 0 151
      src/pages/ServiceList/index.tsx
  42. 0 6
      src/pages/ServiceList/service.ts
  43. 103 14
      src/pages/Setting/index.tsx
  44. 2 0
      src/pages/Setting/service.ts
  45. 56 0
      src/pages/UserList/_mock.ts
  46. 7 6
      src/pages/UserList/data.d.ts
  47. 6 0
      src/pages/UserList/index.less
  48. 298 0
      src/pages/UserList/index.tsx
  49. 10 0
      src/pages/UserList/service.ts
  50. 7 11
      src/pages/document.ejs
  51. 8 0
      src/pages/user/Register/_mock.ts
  52. 274 0
      src/pages/user/Register/index.tsx
  53. 78 0
      src/pages/user/Register/locales/en-US.ts
  54. 74 0
      src/pages/user/Register/locales/zh-CN.ts
  55. 74 0
      src/pages/user/Register/locales/zh-TW.ts
  56. 48 0
      src/pages/user/Register/model.ts
  57. 9 0
      src/pages/user/Register/service.ts
  58. 60 0
      src/pages/user/Register/style.less
  59. 9 7
      src/pages/user/login/index.tsx
  60. 34 4
      src/utils/utils.ts

+ 4 - 0
CHANGELOG.md

@@ -0,0 +1,4 @@
+### 1.5.5
+
+- 支持注册
+- 支持用户的管理

+ 2 - 56
README.md

@@ -1,59 +1,5 @@
 # Ant Design Pro
 
-更新模型列表,master 是否已修改
-
-This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
-
-## Environment Prepare
-
-Install `node_modules`:
-
-```bash
-npm install
-```
-
-or
-
-```bash
-yarn
+```text
+npm run pre
 ```
-
-## Provided Scripts
-
-Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
-
-Scripts provided in `package.json`. It's safe to modify or add additional script:
-
-### Start project
-
-```bash
-npm start
-```
-
-### Build project
-
-```bash
-npm run build
-```
-
-### Check code style
-
-```bash
-npm run lint
-```
-
-You can also use script to auto fix some lint error:
-
-```bash
-npm run lint:fix
-```
-
-### Test code
-
-```bash
-npm test
-```
-
-## More
-
-You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).

+ 4 - 2
bin/push.sh

@@ -1,6 +1,8 @@
-remote=worker@10.20.222.191
+umi build
+
+remote=root@www.tianyunperfect.cn
 
 # 在本地执行的代码,比如上传文件到服务器 scp 本地文件 user@ip:远程目录
-scp -r dist/* ${remote}:/apps/bainuo/bainuo-model2/static
+scp -r dist/* ${remote}:/app/memory/static
 
 echo ok

+ 38 - 6
config/config.ts

@@ -8,7 +8,10 @@ const { winPath } = utils; // preview.pro.ant.design only do not use in your pro
 
 const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION, REACT_APP_ENV, GA_KEY } = process.env;
 export default defineConfig({
-  history: { type: 'hash' }, // 默认是 browser
+  history: {
+    type: 'hash',
+  },
+  // 默认是 browser
   hash: true,
   antd: {},
   analytics: GA_KEY
@@ -43,6 +46,12 @@ export default defineConfig({
           path: '/user/login',
           component: './user/login',
         },
+        {
+          name: '注册页',
+          icon: 'smile',
+          path: '/user/register',
+          component: './user/Register',
+        },
       ],
     },
     {
@@ -52,13 +61,12 @@ export default defineConfig({
         {
           path: '/',
           component: '../layouts/BasicLayout',
-          authority: ['admin', 'user'],
+          // authority: ['admin', 'user'],
           routes: [
             {
               path: '/',
               redirect: '/memory',
-            },
-            // {
+            }, // {
             //   path: '/welcome',
             //   name: 'welcome',
             //   icon: 'smile',
@@ -92,12 +100,25 @@ export default defineConfig({
               path: '/memory',
               component: './Memory',
             },
+            {
+              name: 'list.memoryList',
+              icon: 'smile',
+              path: '/memoryList',
+              component: './MemoryList',
+            },
             {
               name: 'list.setting',
               icon: 'smile',
               path: '/setting',
               component: './Setting',
             },
+            {
+              name: 'list.userList',
+              icon: 'smile',
+              path: '/userList',
+              authority: ['1'],
+              component: './UserList',
+            },
             {
               component: './404',
             },
@@ -133,7 +154,7 @@ export default defineConfig({
           resourcePath: string;
         },
         _: string,
-        localName: string,
+        localName: string
       ) => {
         if (
           context.resourcePath.includes('node_modules') ||
@@ -162,5 +183,16 @@ export default defineConfig({
     basePath: '/',
   },
   proxy: proxy[REACT_APP_ENV || 'dev'],
-  chainWebpack: webpackPlugin,
+  chainWebpack: webpackPlugin, // scripts: [
+  //   'https://unpkg.com/react@16.8.6/umd/react.production.min.js',
+  //   'https://unpkg.com/react-dom@16.8.6/umd/react-dom.production.min.js',
+  //   'https://unpkg.com/bizcharts@3.5.5/umd/BizCharts.min.js',
+  //   'https://cdn.bootcss.com/antd/4.0.0/antd.min.js',
+  // ],
+  // externals: {
+  //   react: 'React',
+  //   antd: 'antd',
+  //   'react-dom': 'ReactDOM',
+  //   bizcharts: 'BizCharts',
+  // },
 });

+ 3 - 1
config/plugin.config.ts

@@ -30,7 +30,9 @@ const webpackPlugin = (config: IWebpackChainConfig) => {
       chunks: 'async',
       name: 'vendors',
       maxInitialRequests: Infinity,
-      minSize: 0,
+      minSize: 30000,
+      minChunks: 3,
+      automaticNameDelimiter: '.',
       cacheGroups: {
         vendors: {
           test: (module: { context: string }) => {

+ 1 - 0
package.json

@@ -60,6 +60,7 @@
     "lodash": "^4.17.19",
     "moment": "^2.24.0",
     "nice-hooks": "^1.2.1",
+    "nzh": "^1.0.4",
     "omit.js": "^1.0.2",
     "path-to-regexp": "2.4.0",
     "qs": "^6.9.0",

+ 1 - 1
public/CNAME

@@ -1 +1 @@
-preview.pro.ant.design
+tianyunperfect.cn

+ 1 - 0
public/back/CNAME

@@ -0,0 +1 @@
+tianyunperfect.cn

BIN
public/back/favicon.png


BIN
public/back/home_bg.png


BIN
public/back/icons/icon-128x128.png


BIN
public/back/icons/icon-192x192.png


BIN
public/back/icons/icon-512x512.png


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
public/back/pro_icon.svg


BIN
public/favicon.png


BIN
public/home_bg.png


BIN
public/icons/icon-128x128.png


BIN
public/icons/icon-192x192.png


BIN
public/icons/icon-512x512.png


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
public/pro_icon.svg


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
src/assets/logo.back.svg


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
src/assets/logo.svg


+ 1 - 12
src/layouts/BasicLayout.tsx

@@ -9,7 +9,7 @@ import ProLayout, {
   Settings,
   DefaultFooter,
 } from '@ant-design/pro-layout';
-import React, { useEffect } from 'react';
+import React from 'react';
 import { Link, useIntl, connect, Dispatch } from 'umi';
 // import { GithubOutlined } from '@ant-design/icons';
 import { Result, Button } from 'antd';
@@ -81,17 +81,6 @@ const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
       pathname: '/',
     },
   } = props;
-  /**
-   * constructor
-   */
-
-  useEffect(() => {
-    if (dispatch) {
-      dispatch({
-        type: 'user/fetchCurrent',
-      });
-    }
-  }, []);
   /**
    * init variables
    */

+ 13 - 1
src/layouts/UserLayout.tsx

@@ -57,7 +57,19 @@ const UserLayout: React.FC<UserLayoutProps> = (props) => {
           </div>
           {children}
         </div>
-        <DefaultFooter />
+        <DefaultFooter
+          copyright="tianyunperfect"
+          links={
+            [
+              // {
+              //   key: '云知声-智享未来',
+              //   title: '云知声-智享未来',
+              //   href: 'www.unisound.com',
+              //   blankTarget: true,
+              // }
+            ]
+          }
+        />
       </div>
     </HelmetProvider>
   );

+ 2 - 0
src/locales/zh-CN/menu.ts

@@ -23,7 +23,9 @@ export default {
   'menu.form.advanced-form': '高级表单',
   'menu.list': '列表页',
   'menu.list.memory': '记忆',
+  'menu.list.memoryList': '查询',
   'menu.list.setting': '设置',
+  'menu.list.userList': '用户',
   'menu.list.service-list': '服务列表',
   'menu.list.basic-list': '标准列表',
   'menu.list.card-list': '卡片列表',

+ 0 - 25
src/pages/ListTableList/components/CreateForm.tsx

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

+ 0 - 214
src/pages/ListTableList/components/UpdateForm.tsx

@@ -1,214 +0,0 @@
-import React, { useState} from 'react';
-import { Form, Button, DatePicker, Input, Modal, Radio, Select, Steps } from 'antd';
-
-import { TableListItem } from '../data.d';
-
-export interface FormValueType extends Partial<TableListItem> {
-  target?: string;
-  template?: string;
-  type?: string;
-  time?: string;
-  frequency?: string;
-}
-
-export interface UpdateFormProps {
-  onCancel: (flag?: boolean, formVals?: FormValueType) => void;
-  onSubmit: (values: FormValueType) => void;
-  updateModalVisible: boolean;
-  values: Partial<TableListItem>;
-}
-const FormItem = Form.Item;
-const { Step } = Steps;
-const { TextArea } = Input;
-const { Option } = Select;
-const RadioGroup = Radio.Group;
-
-export interface UpdateFormState {
-  formVals: FormValueType;
-  currentStep: number;
-}
-
-const formLayout = {
-  labelCol: { span: 7 },
-  wrapperCol: { span: 13 },
-};
-
-const UpdateForm: React.FC<UpdateFormProps> = (props) => {
-  const [formVals, setFormVals] = useState<FormValueType>({
-    name: props.values.name,
-    desc: props.values.desc,
-    key: props.values.key,
-    target: '0',
-    template: '0',
-    type: '1',
-    time: '',
-    frequency: 'month',
-  });
-
-  const [currentStep, setCurrentStep] = useState<number>(0);
-
-  const [form] = Form.useForm();
-
-  const {
-    onSubmit: handleUpdate,
-    onCancel: handleUpdateModalVisible,
-    updateModalVisible,
-    values,
-  } = props;
-
-  const forward = () => setCurrentStep(currentStep + 1);
-
-  const backward = () => setCurrentStep(currentStep - 1);
-
-  const handleNext = async () => {
-    const fieldsValue = await form.validateFields();
-
-    setFormVals({ ...formVals, ...fieldsValue });
-
-    if (currentStep < 2) {
-      forward();
-    } else {
-      handleUpdate(formVals);
-    }
-  };
-
-  const renderContent = () => {
-    if (currentStep === 1) {
-      return (
-        <>
-          <FormItem name="target" label="监控对象">
-            <Select style={{ width: '100%' }}>
-              <Option value="0">表一</Option>
-              <Option value="1">表二</Option>
-            </Select>
-          </FormItem>
-          <FormItem name="template" label="规则模板">
-            <Select style={{ width: '100%' }}>
-              <Option value="0">规则模板一</Option>
-              <Option value="1">规则模板二</Option>
-            </Select>
-          </FormItem>
-          <FormItem name="type" label="规则类型">
-            <RadioGroup>
-              <Radio value="0">强</Radio>
-              <Radio value="1">弱</Radio>
-            </RadioGroup>
-          </FormItem>
-        </>
-      );
-    }
-    if (currentStep === 2) {
-      return (
-        <>
-          <FormItem
-            name="time"
-            label="开始时间"
-            rules={[{ required: true, message: '请选择开始时间!' }]}
-          >
-            <DatePicker
-              style={{ width: '100%' }}
-              showTime
-              format="YYYY-MM-DD HH:mm:ss"
-              placeholder="选择开始时间"
-            />
-          </FormItem>
-          <FormItem name="frequency" label="调度周期">
-            <Select style={{ width: '100%' }}>
-              <Option value="month">月</Option>
-              <Option value="week">周</Option>
-            </Select>
-          </FormItem>
-        </>
-      );
-    }
-    return (
-      <>
-        <FormItem
-          name="name"
-          label="规则名称"
-          rules={[{ required: true, message: '请输入规则名称!' }]}
-        >
-          <Input placeholder="请输入" />
-        </FormItem>
-        <FormItem
-          name="desc"
-          label="规则描述"
-          rules={[{ required: true, message: '请输入至少五个字符的规则描述!', min: 5 }]}
-        >
-          <TextArea rows={4} placeholder="请输入至少五个字符" />
-        </FormItem>
-      </>
-    );
-  };
-
-  const renderFooter = () => {
-    if (currentStep === 1) {
-      return (
-        <>
-          <Button style={{ float: 'left' }} onClick={backward}>
-            上一步
-          </Button>
-          <Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
-          <Button type="primary" onClick={() => handleNext()}>
-            下一步
-          </Button>
-        </>
-      );
-    }
-    if (currentStep === 2) {
-      return (
-        <>
-          <Button style={{ float: 'left' }} onClick={backward}>
-            上一步
-          </Button>
-          <Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
-          <Button type="primary" onClick={() => handleNext()}>
-            完成
-          </Button>
-        </>
-      );
-    }
-    return (
-      <>
-        <Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
-        <Button type="primary" onClick={() => handleNext()}>
-          下一步
-        </Button>
-      </>
-    );
-  };
-
-  return (
-    <Modal
-      width={640}
-      bodyStyle={{ padding: '32px 40px 48px' }}
-      destroyOnClose
-      title="规则配置"
-      visible={updateModalVisible}
-      footer={renderFooter()}
-      onCancel={() => handleUpdateModalVisible()}
-    >
-      <Steps style={{ marginBottom: 28 }} size="small" current={currentStep}>
-        <Step title="基本信息" />
-        <Step title="配置规则属性" />
-        <Step title="设定调度周期" />
-      </Steps>
-      <Form
-        {...formLayout}
-        form={form}
-        initialValues={{
-          target: formVals.target,
-          template: formVals.template,
-          type: formVals.type,
-          frequency: formVals.frequency,
-          name: formVals.name,
-          desc: formVals.desc,
-        }}
-      >
-        {renderContent()}
-      </Form>
-    </Modal>
-  );
-};
-
-export default UpdateForm;

+ 0 - 232
src/pages/ListTableList/index.tsx

@@ -1,232 +0,0 @@
-import { DownOutlined, PlusOutlined } from '@ant-design/icons';
-import { Button, Divider, Dropdown, Menu, message } from 'antd';
-import React, { useState, useRef } from 'react';
-import { PageHeaderWrapper } from '@ant-design/pro-layout';
-import ProTable, { ProColumns, ActionType } from '@ant-design/pro-table';
-import { SorterResult } from 'antd/es/table/interface';
-
-import CreateForm from './components/CreateForm';
-import UpdateForm, { FormValueType } from './components/UpdateForm';
-import { TableListItem } from './data.d';
-import { queryRule, updateRule, addRule, removeRule } from './service';
-
-/**
- * 添加节点
- * @param fields
- */
-const handleAdd = async (fields: TableListItem) => {
-  const hide = message.loading('正在添加');
-  try {
-    await addRule({ ...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 updateRule({
-      name: fields.name,
-      desc: fields.desc,
-      key: fields.key,
-    });
-    hide();
-
-    message.success('配置成功');
-    return true;
-  } catch (error) {
-    hide();
-    message.error('配置失败请重试!');
-    return false;
-  }
-};
-
-/**
- *  删除节点
- * @param selectedRows
- */
-const handleRemove = async (selectedRows: TableListItem[]) => {
-  const hide = message.loading('正在删除');
-  if (!selectedRows) return true;
-  try {
-    await removeRule({
-      key: selectedRows.map((row) => row.key),
-    });
-    hide();
-    message.success('删除成功,即将刷新');
-    return true;
-  } catch (error) {
-    hide();
-    message.error('删除失败,请重试');
-    return false;
-  }
-};
-
-const TableList: React.FC<{}> = () => {
-  const [sorter, setSorter] = useState<string>('');
-  const [createModalVisible, handleModalVisible] = useState<boolean>(false);
-  const [updateModalVisible, handleUpdateModalVisible] = useState<boolean>(false);
-  const [stepFormValues, setStepFormValues] = useState({});
-  const actionRef = useRef<ActionType>();
-  const columns: ProColumns<TableListItem>[] = [
-    {
-      title: '规则名称',
-      dataIndex: 'name',
-      rules: [
-        {
-          required: true,
-          message: '规则名称为必填项',
-        },
-      ],
-    },
-    {
-      title: '描述',
-      dataIndex: 'desc',
-      valueType: 'textarea',
-    },
-    {
-      title: '服务调用次数',
-      dataIndex: 'callNo',
-      sorter: true,
-      hideInForm: true,
-      renderText: (val: string) => `${val} 万`,
-    },
-    {
-      title: '状态',
-      dataIndex: 'status',
-      hideInForm: true,
-      valueEnum: {
-        0: { text: '关闭', status: 'Default' },
-        1: { text: '运行中', status: 'Processing' },
-        2: { text: '已上线', status: 'Success' },
-        3: { text: '异常', status: 'Error' },
-      },
-    },
-    {
-      title: '上次调度时间',
-      dataIndex: 'updatedAt',
-      sorter: true,
-      valueType: 'dateTime',
-      hideInForm: true,
-    },
-    {
-      title: '操作',
-      dataIndex: 'option',
-      valueType: 'option',
-      render: (_, record) => (
-        <>
-          <a
-            onClick={() => {
-              handleUpdateModalVisible(true);
-              setStepFormValues(record);
-            }}
-          >
-            配置
-          </a>
-          <Divider type="vertical" />
-          <a href="">订阅警报</a>
-        </>
-      ),
-    },
-  ];
-
-  return (
-    <PageHeaderWrapper>
-      <ProTable<TableListItem>
-        headerTitle="查询表格"
-        actionRef={actionRef}
-        rowKey="key"
-        onChange={(_, _filter, _sorter) => {
-          const sorterResult = _sorter as SorterResult<TableListItem>;
-          if (sorterResult.field) {
-            setSorter(`${sorterResult.field}_${sorterResult.order}`);
-          }
-        }}
-        params={{
-          sorter,
-        }}
-        toolBarRender={(action, { selectedRows }) => [
-          <Button type="primary" onClick={() => handleModalVisible(true)}>
-            <PlusOutlined /> 新建
-          </Button>,
-          selectedRows && selectedRows.length > 0 && (
-            <Dropdown
-              overlay={
-                <Menu
-                  onClick={async (e) => {
-                    if (e.key === 'remove') {
-                      await handleRemove(selectedRows);
-                      action.reload();
-                    }
-                  }}
-                  selectedKeys={[]}
-                >
-                  <Menu.Item key="remove">批量删除</Menu.Item>
-                  <Menu.Item key="approval">批量审批</Menu.Item>
-                </Menu>
-              }
-            >
-              <Button>
-                批量操作 <DownOutlined />
-              </Button>
-            </Dropdown>
-          ),
-        ]}
-
-        request={(params) => queryRule(params)}
-        columns={columns}
-        rowSelection={{}}
-      />
-      <CreateForm onCancel={() => handleModalVisible(false)} modalVisible={createModalVisible}>
-        <ProTable<TableListItem, TableListItem>
-          onSubmit={async (value) => {
-            const success = await handleAdd(value);
-            if (success) {
-              handleModalVisible(false);
-              if (actionRef.current) {
-                actionRef.current.reload();
-              }
-            }
-          }}
-          rowKey="key"
-          type="form"
-          columns={columns}
-          rowSelection={{}}
-        />
-
-      </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}
-    </PageHeaderWrapper>
-  );
-};
-
-export default TableList;

+ 0 - 38
src/pages/ListTableList/service.ts

@@ -1,38 +0,0 @@
-import request from '@/utils/request';
-import { TableListParams, TableListItem } from './data.d';
-
-export async function queryRule(params?: TableListParams) {
-  return request('/api/rule', {
-    params,
-  });
-}
-
-export async function removeRule(params: { key: number[] }) {
-  return request('/api/rule', {
-    method: 'POST',
-    data: {
-      ...params,
-      method: 'delete',
-    },
-  });
-}
-
-export async function addRule(params: TableListItem) {
-  return request('/api/rule', {
-    method: 'POST',
-    data: {
-      ...params,
-      method: 'post',
-    },
-  });
-}
-
-export async function updateRule(params: TableListParams) {
-  return request('/api/rule', {
-    method: 'POST',
-    data: {
-      ...params,
-      method: 'update',
-    },
-  });
-}

+ 4 - 4
src/pages/Memory/components/Quill.tsx

@@ -3,15 +3,15 @@ import ReactQuill from "react-quill";
 import 'react-quill/dist/quill.snow.css';
 
 export interface Props {
-  onChange: (value?:any) => void;
+  onChange: (value?: any) => void;
   value: any;
   readonly: boolean;
 }
 
 const modules = {
   toolbar: [
-    [{'header': [1, 2, false]},'bold', 'italic', 'blockquote', 'code-block', 'link'],
-    [{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}],
+    [{'header': [1, 2, false]}, 'bold', 'italic', 'blockquote', 'code-block', 'link'],
+    [{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'},'image'],
   ],
 };
 
@@ -19,7 +19,7 @@ const formats = [
   'header',
   'bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block',
   'list', 'bullet', 'indent',
-  'link',
+  'link','image'
 ];
 
 const Quill: React.FC<Props> = (props) => {

+ 161 - 113
src/pages/Memory/components/UpdateForm.tsx

@@ -1,143 +1,191 @@
-import React, { useState } from 'react';
-import { Button, Form, Input, message, Modal, Radio, Upload } from 'antd';
-import { TableListItem } from '../data.d';
-import { UploadOutlined } from '@ant-design/icons';
+import React, { useEffect, useState } from 'react';
+import { Button, message, Modal } from 'antd';
+import Quill from '@/pages/Memory/components/Quill';
+import service from '@/pages/Memory/service';
+import { useSingleState } from 'nice-hooks';
+import { getTextFromHtml } from '@/utils/utils';
+import { TableListItem } from '../../MemoryList/data.d';
 
 // 表单特殊字段
 export interface FormValueType extends Partial<TableListItem> {}
 
 export interface UpdateFormProps {
-  onCancel: (flag?: boolean, formVals?: FormValueType) => void;
-  onSubmit: (values: FormValueType) => void;
-  updateModalVisible: boolean;
+  onCancel: (fresh?: boolean) => void;
+  modalVisible: boolean;
   values: Partial<TableListItem>;
 }
 
-const FormItem = Form.Item;
+const dealImage = (base64: string, targetWidth: number, targetSize: number, callback: Function) => {
+  const newImage = new Image();
+  let quality = 0.8;
+  newImage.src = base64;
+  // url为外域时需要
+  newImage.setAttribute('crossOrigin', 'Anonymous');
+  let imgWidth;
+  let imgHeight;
+  newImage.onload = function () {
+    imgWidth = newImage.width;
+    imgHeight = newImage.height;
+    const canvas = document.createElement('canvas');
+    const ctx = canvas.getContext('2d');
+    if (Math.max(imgWidth, imgHeight) > targetWidth) {
+      if (imgWidth > imgHeight) {
+        canvas.width = targetWidth;
+        canvas.height = (targetWidth * imgHeight) / imgWidth;
+      } else {
+        canvas.height = targetWidth;
+        canvas.width = (targetWidth * imgWidth) / imgHeight;
+      }
+    } else {
+      canvas.width = imgWidth;
+      canvas.height = imgHeight;
+    }
+    // @ts-ignore
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    // @ts-ignore
+    ctx.drawImage(newImage, 0, 0, canvas.width, canvas.height);
+    // 如想确保图片压缩到自己想要的尺寸,如要求在50-150kb之间,请加以下语句,quality初始值根据情况自定
+    while (base64.length / 1024 > targetSize) {
+      quality -= 0.01;
+      // eslint-disable-next-line no-param-reassign
+      base64 = canvas.toDataURL('image/jpeg', quality);
+    }
+    // 防止最后一次压缩低于最低尺寸,只要quality递减合理,无需考虑
+    // while (base64.length / 1024 < 50) {
+    // 	quality += 0.001;
+    // 	base64 = canvas.toDataURL("image/jpeg", quality);
+    // }
+    callback(base64); // 必须通过回调函数返回,否则无法及时拿到该值
+  };
+};
 
-const formLayout = {
-  labelCol: { span: 7 },
-  wrapperCol: { span: 13 },
+/**
+ * 压缩html中的base64
+ */
+const dealHtml = (html: string, callBack: Function) => {
+  callBack(html);
+  const htmlDoc = new DOMParser().parseFromString(html, 'text/html');
+  const imgs = htmlDoc.querySelectorAll('img');
+  for (let i = 0; i < imgs.length; i += 1) {
+    const { src } = imgs[i];
+    if (src.startsWith('data')) {
+      dealImage(src, 500, 150, (base64: string) => {
+        imgs[i].src = base64;
+        callBack(htmlDoc.body.innerHTML);
+        console.log(`压缩图片${i}`);
+      });
+    }
+  }
 };
 
 const UpdateForm: React.FC<UpdateFormProps> = (props) => {
-  const [formVals, setFormVals] = useState<FormValueType>(props.values);
+  const [btnDisable, setBtnDisable] = useState(false);
 
-  const [form] = Form.useForm();
+  const [formValues, setFormValues] = useSingleState<TableListItem>({
+    back: '',
+    front: '',
+    id: undefined,
+    period: 0,
+    remindTime: new Date(),
+    tag: '',
+    updateTime: '',
+    userId: 0,
+    ...props.values,
+  });
 
-  const {
-    onSubmit: handleUpdate,
-    onCancel: handleUpdateModalVisible,
-    updateModalVisible,
-    values,
-  } = props;
+  const { onCancel: hideModal, modalVisible } = props;
 
-  const submit = async () => {
-    const fieldsValue = await form.validateFields();
-    const newValue = { ...formVals, ...fieldsValue };
-    setFormVals(newValue);
-    handleUpdate(newValue);
-  };
-
-  const modelFileProps = {
-    action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
-    // @ts-ignore
-    onChange(info) {
-      if (info.file.status === 'done') {
-        setFormVals({ ...formVals, ...{ modelFile: info.file.response.url } });
-        message.success(`file uploaded successfully`);
-      } else if (info.file.status === 'error') {
-        message.error(`file upload failed.`);
-      }
-    },
-  };
-  const vocabFileProps = {
-    action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
-    // @ts-ignore
-    onChange(info) {
-      if (info.file.status === 'done') {
-        setFormVals({ ...formVals, ...{ vocabFile: info.file.response.url } });
-        message.success(`file uploaded successfully`);
-      } else if (info.file.status === 'error') {
-        message.error(`file upload failed.`);
+  const addHandle = async () => {
+    /**
+     * 判断
+     */
+    console.log(formValues);
+    if (getTextFromHtml(formValues.front + formValues.back).length <= 1) {
+      message.info('你是不是没有输入文字?');
+      return;
+    }
+    if (formValues.front.length >= 300000 || formValues.back.length >= 300000) {
+      message.info('文字太长了,减少一些吧!');
+      return;
+    }
+    setBtnDisable(true); // 禁用按钮
+    let res;
+    if (formValues.id !== undefined) {
+      res = await service.update(formValues);
+    } else {
+      res = await service.insert(formValues);
+    }
+    setBtnDisable(false); // 释放按钮
+    if (res.success) {
+      message.info('操作成功');
+      if (formValues.id !== undefined) {
+        hideModal(true);
+      } else {
+        setFormValues({ front: '', back: '' });
       }
-    },
-  };
-  const labelFileProps = {
-    action: 'https://www.mocky.io/v2/5cc8019d300000980a055e76',
-    // @ts-ignore
-    onChange(info) {
-      if (info.file.status === 'done') {
-        setFormVals({ ...formVals, ...{ labelFile: info.file.response.url } });
-        message.success(`file uploaded successfully`);
-      } else if (info.file.status === 'error') {
-        message.error(`file upload failed.`);
-      }
-    },
+    } else {
+      message.info(`操作失败: ${res.message}`);
+    }
   };
 
-  const renderFooter = () => {
-    return (
-      <>
-        <Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
-        <Button type="primary" onClick={() => submit()}>
-          提交
-        </Button>
-      </>
-    );
-  };
+  useEffect(() => {
+    console.log(props);
+    document.onkeydown = (ev) => {
+      if (ev.ctrlKey && ev.key.toLowerCase() === 'enter') {
+        addHandle();
+      } else if (ev.key.toLowerCase() === 'escape') {
+        hideModal(false);
+      }
+    };
+  }, []);
 
   return (
     <Modal
       width={640}
-      bodyStyle={{ padding: '32px 40px 48px' }}
+      bodyStyle={{ padding: '15px 15px 15px' }}
       destroyOnClose
       title="配置模型"
-      visible={updateModalVisible}
-      footer={renderFooter()}
-      onCancel={() => handleUpdateModalVisible()}
+      visible={modalVisible}
+      onCancel={() => hideModal(false)}
+      footer={null}
     >
-      <Form {...formLayout} form={form} initialValues={formVals}>
-        <FormItem
-          name="modelName"
-          label="模型名称"
-          rules={[{ required: true, message: '请输入规则名称!' }]}
-        >
-          <Input placeholder="请输入" />
-        </FormItem>
+      <div>
+        <h4>正面</h4>
+        <Quill
+          readonly={false}
+          onChange={(value) => {
+            dealHtml(value, (html: string) => {
+              setFormValues({ front: html });
+            });
+          }}
+          value={formValues.front}
+        />
 
-        <FormItem
-          name="modelType"
-          label="模型类型"
-          rules={[{ required: true, message: '请选择模型类型!' }]}
+        <p />
+        <h4>反面</h4>
+        <Quill
+          readonly={false}
+          onChange={(value) => {
+            dealHtml(value, (html: string) => {
+              setFormValues({ back: html });
+            });
+          }}
+          value={formValues.back}
+        />
+        <p />
+        <Button type="primary" disabled={btnDisable} onClick={addHandle}>
+          确定
+        </Button>
+        <span> </span>
+        <Button
+          type="primary"
+          onClick={() => {
+            hideModal(false);
+          }}
         >
-          <Radio.Group>
-            <Radio.Button value="0">aubert</Radio.Button>
-            <Radio.Button value="1">blstm</Radio.Button>
-          </Radio.Group>
-        </FormItem>
-        <FormItem label="模型文件">
-          <Upload {...modelFileProps}>
-            <Button>
-              <UploadOutlined /> Upload
-            </Button>
-          </Upload>
-        </FormItem>
-        <FormItem label="词向量文件">
-          <Upload {...vocabFileProps}>
-            <Button>
-              <UploadOutlined /> Upload
-            </Button>
-          </Upload>
-        </FormItem>
-        <FormItem label="标签文件">
-          <Upload {...labelFileProps}>
-            <Button>
-              <UploadOutlined /> Upload
-            </Button>
-          </Upload>
-        </FormItem>
-      </Form>
+          关闭
+        </Button>
+      </div>
     </Modal>
   );
 };

+ 0 - 7
src/pages/Memory/data.d.ts

@@ -41,11 +41,4 @@ export interface TableListParams {
   currentPage?: number;
 }
 
-export interface Memory {
-  id?: number | null;
-  userId?: number | null;
-  front: string;
-  back: string;
-  tag: string;
 
-}

+ 10 - 1
src/pages/Memory/index.less

@@ -2,6 +2,15 @@
   display: inline;
 }
 
-.btn-right{
+
+.btnRight{
   margin-right: 5px;
 }
+
+.divBottom{
+  margin-bottom: 5px;
+}
+
+.minHeight{
+  min-height: 100px;
+}

+ 212 - 169
src/pages/Memory/index.tsx

@@ -1,10 +1,13 @@
-import {Button, message} from 'antd';
-import React, {useEffect} from 'react';
+import {Button, message, Modal} from 'antd';
+import React, {useEffect, useState} from 'react';
 import {PageHeaderWrapper} from '@ant-design/pro-layout';
 import {useSingleState} from 'nice-hooks';
-import Quill from "@/pages/Memory/components/Quill";
-import {Memory} from './data.d';
+import Quill from '@/pages/Memory/components/Quill';
+import UpdateForm from '@/pages/Memory/components/UpdateForm';
+import {TableListItem} from '../MemoryList/data.d';
 import service from './service';
+import styles from './index.less';
+import {ExclamationCircleOutlined} from '@ant-design/icons/lib';
 
 enum Step {
   MAIN = 0,
@@ -19,26 +22,39 @@ interface State {
 }
 
 const TableList: React.FC<{}> = () => {
+  const [btnDisable, setBtnDisable] = useState(false);
+  let counting = false;
+
+  const {confirm} = Modal;
+
   const [state, setState] = useSingleState<State>({
     remindCount: 0,
     step: Step.MAIN, // 0 主页,1 复习,2 添加
     showBack: false,
   });
 
-  const [memory, setMemory] = useSingleState<Memory>({
-    id: null,
-    userId: null,
-    front: '',
+  const [memory, setMemory] = useSingleState<TableListItem>({
     back: '',
+    front: '',
+    id: undefined,
+    period: 0,
+    remindTime: new Date(),
     tag: '',
+    updateTime: '',
+    userId: 0,
   });
 
-
   /**
    * 查询用户待提醒数量
    * @param fields
    */
   const countRemind = async () => {
+    // 如果上一个查询还没有结束,则直接返回
+    console.log(counting);
+    if (counting) {
+      return;
+    }
+    counting = true;
     const hide = message.loading('正在查询');
     try {
       const res = await service.countRemind({});
@@ -46,91 +62,121 @@ const TableList: React.FC<{}> = () => {
       if (res.success) {
         setState({remindCount: parseInt(res.data, 10)});
       }
+      counting = false;
     } catch (error) {
       hide();
       message.error('查询异常!');
+      counting = false;
     }
   };
-  useEffect(() => {
-    countRemind();
-    setTimeout( ()=> {
-      countRemind();
-    },1000*60*60)  // 每隔一个小时查询一次
-  }, []);
   /**
    * 清空memory
    */
   const clearMemory = () => {
     setMemory({
-      id: null,
+      id: undefined,
       front: '',
       back: '',
     });
   };
-  /**
-   * 每次修改step,都关闭背面
-   */
-  useEffect(() => {
-    setState({showBack: false});
-    clearMemory();
-  }, [state.step]);
 
-  useEffect(() => {
-    // console.log(state)
-    // console.log(memory)
-  });
+
   /**
-   * 进入统计页面
+   * 处理返回值
+   * @param res
    */
-  const changStepMain = () => {
-    setState({step: Step.MAIN});
-    countRemind();
-  };
-
-  const findNextLine = async () => {
-    const res = await service.findNext({});
+  const memoryRes = (res: any) => {
+    // 只要有返回值,就使按钮可见
+    setBtnDisable(false);
     if (res.success) {
       setMemory({...memory, ...res.data});
     } else {
-      changStepMain();
+      setState({step: Step.MAIN});
     }
   };
 
-  /**
-   * 初次进入复习页面
-   */
-  const changStepMemory = async () => {
-    if (state.remindCount <= 0) {
-      message.info('已经复习完了哟!');
-      return;
-    }
-    setState({step: Step.MEMORY});
-    await findNextLine();
+  const memorySuccess = async (factorInt: number) => {
+    setBtnDisable(true);
+    memoryRes(await service.memorySuccess({id: memory.id, factor: factorInt}));
   };
-  /**
-   * 处理返回值
-   * @param res
-   */
-  const memoryRes = (res:any)=>{
+
+
+  const findNextLine = async () => {
+    const res = await service.findNext({});
     if (res.success) {
-      setState({showBack: false});
       setMemory({...memory, ...res.data});
     } else {
-      changStepMain();
+      setState({step: Step.MAIN});
     }
+  };
 
+  // 绑定快捷键
+  const bindShortKey = () => {
+    document.onkeydown = (ev) => {
+      // 空格显示背面和默认选择
+      if (ev.key === ' ' && state.step === Step.MEMORY) {
+        ev.preventDefault(); // 关闭浏览器快捷键
+        if (state.showBack === false) {
+          setState({showBack: true});
+        } else {
+          memorySuccess(2);
+        }
+      } else if (ev.key === ' ' && state.step === Step.MAIN) {
+        // 空格刷新
+        // 如果待复习大于0,则直接进去
+        if (state.remindCount > 0) {
+          setState({step: Step.MEMORY});
+        } else {
+          // 刷新一下,再判断是否应该去复习
+          countRemind();
+          if (state.remindCount > 0) {
+            setState({step: Step.MEMORY});
+          }
+        }
+      } else if (ev.key.toLowerCase() === 'e') {
+        // 快捷 E,进入编辑、新建页面
+        setState({step: Step.ADD});
+      } else if (ev.key.toLowerCase() === 'escape') {
+        // esc键回到主页
+        setState({step: Step.MAIN});
+      }
+    };
   }
-  const memorySuccess = async (factorInt: number) => {
-    memoryRes(await service.memorySuccess({id: memory.id, factor: factorInt}));
 
-  };
+  useEffect(() => {
+    countRemind();
+    setTimeout(() => {
+      countRemind();
+    }, 1000 * 60 * 60); // 每隔一个小时查询一次
+    bindShortKey()
 
+  }, []);
 
-  const changStepAdd = () => {
-    setState({step: Step.ADD});
-  };
+  useEffect(() => {
+    // 关闭背面,清空 memory
+    setState({showBack: false});
+    clearMemory();
 
+    if (state.step === Step.MAIN) {
+      // 进入统计页面
+      countRemind();
+    } else if (state.step === Step.MEMORY) {
+      findNextLine();
+    }
+  }, [state.step])
 
+  useEffect(() => {
+    // 判断是否为主页面刷新
+    if (memory.front.length === 0) {
+      return;
+    }
+    // 进入复习页面后
+    if (memory.back.length > 0) {
+      setState({showBack: false});
+    } else {
+      setState({showBack: true});
+    }
+  }, [memory.front]);
 
   return (
     <PageHeaderWrapper title={false}>
@@ -138,11 +184,17 @@ const TableList: React.FC<{}> = () => {
         <div>
           <div>待复习: {state.remindCount}</div>
           <p/>
-          <Button type="primary" size="large" onClick={changStepMemory}>
+          <Button type="primary" size="large" onClick={() => {
+            if (state.remindCount <= 0) {
+              message.info('已经复习完了哟!');
+              return;
+            }
+            setState({step: Step.MEMORY})
+          }}>
             复习
           </Button>
           <p/>
-          <Button type="primary" size="large" onClick={changStepAdd}>
+          <Button type="primary" size="large" onClick={() => setState({step: Step.ADD})}>
             添加
           </Button>
         </div>
@@ -151,93 +203,124 @@ const TableList: React.FC<{}> = () => {
       {state.step === Step.MEMORY ? (
         <div>
           问题:
-          <p />
-          <Quill
-            readonly
-            onChange={()=>{}}
-            value={memory.front}/>
-            <p />
+          <p/>
+          <Quill readonly onChange={() => {
+          }} value={memory.front}/>
+          <p/>
           {state.showBack ? (
             <div>
-              <Quill
-                readonly
-                onChange={()=>{}}
-                value={memory.back}/>
-                <p />
-              <Button
-                type="primary"
-                onClick={() => {
-                  memorySuccess(0.5);
-                }}
-              >
-                生疏
-              </Button>
-              <span> </span>
-              <Button
-                type="primary"
-                onClick={() => {
-                  memorySuccess(2);
-                }}
-              >
-                一般
-              </Button>
-              <span> </span>
-              <Button
-                type="primary"
-                onClick={() => {
-                  memorySuccess(3);
-                }}
-              >
-                简单
-              </Button>
+              <Quill readonly onChange={() => {
+              }} value={memory.back}/>
               <p/>
-              <div>
+              <div className={styles.divBottom}>
                 <Button
                   type="primary"
-                  onClick={async () => {
-                    memoryRes(await service.restart(memory));
+                  disabled={btnDisable}
+                  onClick={() => {
+                    memorySuccess(0.5);
                   }}
                 >
-                  重新开始复习
+                  生疏
                 </Button>
                 <span> </span>
                 <Button
                   type="primary"
-                  onClick={async () => {
-                    memoryRes(await service.noMemory(memory));
+                  disabled={btnDisable}
+                  onClick={() => {
+                    memorySuccess(2);
                   }}
                 >
-                  不在复习
+                  一般
                 </Button>
                 <span> </span>
                 <Button
                   type="primary"
-                  onClick={async () => {
-                    memoryRes(await service.delete(memory));
-                  }}
-                >
-                  删除
-                </Button>
-              </div>
-              <p/>
-              <div>
-                <Button
-                  type="primary"
+                  disabled={btnDisable}
                   onClick={() => {
-                    changStepAdd();
+                    memorySuccess(3);
                   }}
                 >
-                  编辑
+                  简单
                 </Button>
                 <span> </span>
                 <Button
                   type="primary"
-                  onClick={() => {
-                    changStepMain();
+                  disabled={btnDisable}
+                  onClick={async () => {
+                    setBtnDisable(true);
+                    memoryRes(await service.delay(memory));
                   }}
                 >
-                  关闭
+                  搁置
                 </Button>
+                <p/>
+                <div>
+                  <Button
+                    type="primary"
+                    disabled={btnDisable}
+                    onClick={async () => {
+                      setBtnDisable(true);
+                      memoryRes(await service.restart(memory));
+                    }}
+                  >
+                    重新开始复习
+                  </Button>
+                  <span> </span>
+                  <Button
+                    type="primary"
+                    disabled={btnDisable}
+                    onClick={async () => {
+                      setBtnDisable(true);
+                      memoryRes(await service.noMemory(memory));
+                    }}
+                  >
+                    不在复习
+                  </Button>
+                  <span> </span>
+                  <Button
+                    type="primary"
+                    disabled={btnDisable}
+                    onClick={async () => {
+                      confirm({
+                        title: '确定删除吗?',
+                        icon: <ExclamationCircleOutlined/>,
+                        content: '删除后无法恢复',
+                        okText: '确定',
+                        okType: 'danger',
+                        cancelText: '取消',
+                        async onOk() {
+                          setBtnDisable(true);
+                          memoryRes(await service.delete(memory));
+                        },
+                        onCancel() {
+                          console.log('Cancel');
+                        },
+                      });
+                    }}
+                  >
+                    删除
+                  </Button>
+                </div>
+                <p/>
+                <div>
+                  <Button
+                    type="primary"
+                    onClick={() => {
+                      setState({step: Step.ADD});
+                    }}
+                  >
+                    编辑
+                  </Button>
+                  <span> </span>
+                  <Button
+                    type="primary"
+                    onClick={() => {
+                      setState({step: Step.MAIN});
+                    }}
+                  >
+                    关闭
+                  </Button>
+                </div>
               </div>
             </div>
           ) : (
@@ -256,54 +339,14 @@ const TableList: React.FC<{}> = () => {
       ) : null}
 
       {state.step === Step.ADD ? (
-        <div>
-          <h4>正面</h4>
-          <Quill
-            readonly={false}
-            onChange={(value) => {
-              setMemory({front: value});
-            }}
-            value={memory.front}/>
-
-          <p/>
-          <h4>反面</h4>
-          <Quill
-            readonly={false}
-            onChange={(value) => {
-              setMemory({back: value});
-            }}
-            value={memory.back}
-          />
-          <p/>
-          <Button
-            type="primary"
-            onClick={async () => {
-              let res;
-              if (memory != null && memory.id != null) {
-                res = await service.update(memory);
-              } else {
-                res = await service.insert(memory);
-              }
-              if (res.success) {
-                message.info('操作成功');
-                clearMemory();
-              } else {
-                message.info(`操作失败: ${res.message}`);
-              }
-            }}
-          >
-            确定
-          </Button>
-          <span> </span>
-          <Button
-            type="primary"
-            onClick={() => {
-              changStepMain();
-            }}
-          >
-            关闭
-          </Button>
-        </div>
+        <UpdateForm
+          onCancel={() => {
+            bindShortKey();
+            setState({step: Step.MAIN});
+          }}
+          modalVisible={state.step === Step.ADD}
+          values={memory}
+        />
       ) : null}
     </PageHeaderWrapper>
   );

+ 1 - 0
src/pages/Memory/service.ts

@@ -15,4 +15,5 @@ export default {
   restart: api.post("/api/memory/restart"),
   noMemory: api.post("/api/memory/noMemory"),
   delete: api.post("/api/memory/delete"),
+  delay: api.post("/api/memory/delay"),
 };

+ 0 - 6
src/pages/ServiceList/_mock.ts → src/pages/MemoryList/_mock.ts

@@ -10,12 +10,6 @@ const genList = (current: number, pageSize: number) => {
   for (let i = 0; i < pageSize; i += 1) {
     const index = (current - 1) * 10 + i;
     tableListDataSource.push({
-      msg: "为学者日益", success: "成功",
-      id: index,
-      serviceIp: `${index}name`,
-      modelName: `${index}name`,
-      modelVersion: `${new Date()}`,
-      currentVersion: `${new Date()}`
     });
   }
   tableListDataSource.reverse();

+ 14 - 16
src/pages/ListTableList/data.d.ts → src/pages/MemoryList/data.d.ts

@@ -1,35 +1,33 @@
+// 表单
 export interface TableListItem {
-  key: number;
-  disabled?: boolean;
-  href: string;
-  avatar: string;
-  name: string;
-  owner: string;
-  desc: string;
-  callNo: number;
-  status: number;
-  updatedAt: Date;
-  createdAt: Date;
-  progress: number;
+  id?: number;
+  userId: number;
+  front: string;
+  back: string;
+  tag: string;
+  period: number;
+  remindTime: Date;
+  updateTime:string;
 }
-
+// 分页参数
 export interface TableListPagination {
   total: number;
   pageSize: number;
   current: number;
 }
-
+// 返回的分页数据
 export interface TableListData {
   list: TableListItem[];
   pagination: Partial<TableListPagination>;
 }
-
+// 查询参数
 export interface TableListParams {
   sorter?: string;
   status?: string;
   name?: string;
   desc?: string;
-  key?: number;
+  id?: number;
   pageSize?: number;
   currentPage?: number;
 }
+

+ 0 - 0
src/pages/ServiceList/index.less → src/pages/MemoryList/index.less


+ 198 - 0
src/pages/MemoryList/index.tsx

@@ -0,0 +1,198 @@
+import {Divider, message, Modal, Tooltip} from 'antd';
+import React, {useRef, useState} from 'react';
+import {PageHeaderWrapper} from '@ant-design/pro-layout';
+import ProTable, {ActionType, ProColumns} from '@ant-design/pro-table';
+import {SorterResult} from 'antd/es/table/interface';
+import {TableListItem} from './data.d';
+import service from './service';
+import UpdateForm from "@/pages/Memory/components/UpdateForm";
+import {getTextFromHtml, minuteToDay} from "@/utils/utils";
+import {ExclamationCircleOutlined} from "@ant-design/icons/lib";
+
+
+/**
+ *  删除节点
+ * @param fields
+ */
+const handleRemove = async (fields: TableListItem) => {
+  const hide = message.loading('正在删除');
+  try {
+    await service.delete(fields);
+    hide();
+    message.success('删除成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+
+
+const TableList: React.FC<{}> = () => {
+  const [sorter, setSorter] = useState<string>('');
+  const [modelVisible, setModalVisible] = useState<boolean>(false);
+  const [formValues, setFormValues] = useState({});
+  const actionRef = useRef<ActionType>();
+
+  const { confirm } = Modal;
+
+  const deleteMemory = async (record:any) => {
+    await handleRemove(record);
+    if (actionRef.current) {
+      actionRef.current.reload();
+    }
+
+  };
+
+  const columns: ProColumns<TableListItem>[] = [
+    {
+      title: '正面',
+      dataIndex: 'front',
+      width: 150,
+      hideInSearch: true,
+      render: (text, row) => {
+        const show = getTextFromHtml(row.front);
+        return (<>
+          <Tooltip title={show}>
+            <span>{show.substring(0, 10)}</span>
+          </Tooltip>
+        </>);
+      }
+    },
+    {
+      title: '背面',
+      dataIndex: 'back',
+      width: 150,
+      hideInSearch: true,
+      render: (text, row) => {
+        const show = getTextFromHtml(row.back);
+        return (<>
+          <Tooltip title={show}>
+            <span>{show.substring(0, 10)}</span>
+          </Tooltip>
+        </>);
+      }
+    },
+    {
+      title: '间隔时间',
+      dataIndex: 'period',
+      width: 100,
+      hideInSearch: true,
+      hideInForm: true,
+      render: (text, row) => {
+        return (<>
+          <span>{minuteToDay(row.period)}</span>
+        </>);
+      }
+    },
+    {
+      title: '提醒时间',
+      dataIndex: 'remindTime',
+      valueType: 'dateTime',
+      width: 150,
+      hideInSearch: true,
+      hideInForm: true,
+      sorter:true
+    },
+    {
+      title: '更新时间',
+      dataIndex: 'updateTime',
+      valueType: 'dateTime',
+      width: 150,
+      hideInSearch: true,
+      hideInForm: true,
+      sorter:true
+    },
+    {
+      title: '操作',
+      dataIndex: 'option',
+      width: 100,
+      valueType: 'option',
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      render: (_, record, index, action) => (
+        <>
+          <a
+            onClick={() => {
+              setFormValues(record);
+              setModalVisible(true);
+            }}
+          >
+            修改
+          </a>
+          <Divider type="vertical"/>
+          <a
+            onClick={async () => {
+              confirm({
+                title: '确定删除吗?',
+                icon: <ExclamationCircleOutlined />,
+                content: '删除后无法恢复',
+                okText: '确定',
+                okType: 'danger',
+                cancelText: '取消',
+                onOk() {
+                  deleteMemory(record);
+                },
+                onCancel() {
+                  console.log('Cancel');
+                },
+              });
+            }}
+          >
+            删除
+          </a>
+        </>
+      ),
+    },
+  ];
+
+  return (
+    <PageHeaderWrapper title={false}>
+      <ProTable<TableListItem>
+        search={false}
+        headerTitle="查询表格"
+        actionRef={actionRef}
+        rowKey="id"
+        onChange={(_, _filter, _sorter) => {
+          const sorterResult = _sorter as SorterResult<TableListItem>;
+          if (sorterResult.field) {
+            setSorter(`${sorterResult.field}_${sorterResult.order}`);
+          }
+        }}
+        pagination={{
+          defaultPageSize: 10,
+        }}
+        params={{
+          sorter,
+        }}
+        request={async (params = {}) => {
+          const res = await service.queryList(params);
+          return {
+            data: res.data.list,
+            page: params.current,
+            success: true,
+            total: res.data.total,
+          };
+        }}
+        columns={columns}
+        rowSelection={{}}
+      />
+      {modelVisible ? (
+        <UpdateForm
+          onCancel={(refresh) => {
+            setModalVisible(false);
+            if (refresh && actionRef.current) {
+              actionRef.current.reload();
+            }
+          }}
+          modalVisible={modelVisible}
+          values={formValues}>
+        </UpdateForm>
+      ) : null}
+
+    </PageHeaderWrapper>
+  );
+};
+
+export default TableList;

+ 7 - 0
src/pages/MemoryList/service.ts

@@ -0,0 +1,7 @@
+import api from '@/utils/api';
+
+export default {
+  queryList: api.get('/api/memory/queryList'),
+  update: api.post('/api/memory/update'),
+  delete: api.post('/api/memory/delete'),
+};

+ 0 - 151
src/pages/ServiceList/index.tsx

@@ -1,151 +0,0 @@
-import { message, Tooltip, Radio } from 'antd';
-import React, { useRef, useState } from 'react';
-import { PageHeaderWrapper } from '@ant-design/pro-layout';
-import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table';
-import { SorterResult } from 'antd/es/table/interface';
-import { TableListItem } from './data.d';
-import service from './service';
-import styles from './index.less'
-
-/**
- * 更新节点
- * @param fields
- */
-const handleUpdate = async (fields: TableListItem) => {
-  const hide = message.loading('正在配置');
-  try {
-    const data = await service.forceUpdateModel(fields);
-    hide();
-    message.success('配置成功');
-    return data.success;
-  } catch (error) {
-    hide();
-    message.error('配置失败请重试!');
-    return false;
-  }
-};
-
-const TableList: React.FC<{}> = () => {
-  const [sorter, setSorter] = useState<string>('');
-  const actionRef = useRef<ActionType>();
-
-  const columns: ProColumns<TableListItem>[] = [
-    {
-      title: '服务IP',
-      dataIndex: 'serviceIp',
-      width: 150,
-      copyable: true,
-    },
-    {
-      title: '模型名称',
-      dataIndex: 'modelName',
-      width: 150,
-      copyable: true,
-    },
-    {
-      title: '模型版本',
-      dataIndex: 'modelVersion',
-      valueType: 'dateTime',
-      width: 200,
-      hideInSearch: true,
-    },
-    {
-      title: '当前版本',
-      dataIndex: 'currentVersion',
-      valueType: 'dateTime',
-      width: 220,
-      hideInSearch: true,
-      render: (text, row)=>{
-        if (row.currentVersion < row.modelVersion) {
-           return (<div className={styles.red}>{text} :待更新</div>)
-        }
-        return (<div>{text}</div>)
-
-      }
-    },
-    {
-      title: '启动状态',
-      dataIndex: 'success',
-      copyable: true,
-      width: 100,
-      renderFormItem: () => {
-        return (
-          <Radio.Group>
-            <Radio value="成功">成功</Radio>
-            <Radio value="失败">失败</Radio>
-          </Radio.Group>
-        );
-      },
-      valueEnum: {
-        "成功": { text: "成功", status: 'Success' },
-        "失败": { text: "失败", status: 'Error' }
-      },
-    },
-    {
-      title: '启动信息',
-      dataIndex: 'msg',
-      hideInSearch: true,
-      valueType: 'text',
-      width: 200,
-      render: (text, row) => {
-        if (!text) {
-          row.msg = '';
-        }
-
-        return (<>
-          <Tooltip title={row.msg}>
-            <span>{row.msg.substring(0, 10)}</span>
-          </Tooltip>
-        </>);
-      },
-    },
-    {
-      title: '操作',
-      dataIndex: 'option',
-      width: 100,
-      valueType: 'option',
-      render: (_, record,index,action) => (
-        <>
-          <a
-            onClick={async () => {
-              const success = await handleUpdate(record);
-              if (success) {
-                await action.reload();
-              }
-              console.log(record);
-            }}
-          >
-            强制更新
-          </a>
-        </>
-      ),
-    },
-  ];
-
-  return (
-    <PageHeaderWrapper>
-      <ProTable<TableListItem>
-        headerTitle="查询表格"
-        actionRef={actionRef}
-        rowKey="id"
-        onChange={(_, _filter, _sorter) => {
-          const sorterResult = _sorter as SorterResult<TableListItem>;
-          if (sorterResult.field) {
-            setSorter(`${sorterResult.field}_${sorterResult.order}`);
-          }
-        }}
-        pagination={{
-          defaultPageSize: 10,
-        }}
-        params={{
-          sorter,
-        }}
-        request={(params) => service.queryList(params)}
-        columns={columns}
-        rowSelection={{}}
-      />
-    </PageHeaderWrapper>
-  );
-};
-
-export default TableList;

+ 0 - 6
src/pages/ServiceList/service.ts

@@ -1,6 +0,0 @@
-import api from '@/utils/api';
-
-export default {
-  queryList: api.get('/service/queryList'),
-  forceUpdateModel: api.get('/service/forceUpdate'),
-};

+ 103 - 14
src/pages/Setting/index.tsx

@@ -1,23 +1,28 @@
 import {Button, Card, Form, Input, message} from 'antd';
-import React from 'react';
+import React, {useState} from 'react';
 import {PageHeaderWrapper} from '@ant-design/pro-layout';
 import service from './service';
 
-interface PasswordChange{
+interface PasswordChange {
   newPassword: string;
   oldPassword: string;
 }
 
+enum InputState {
+  default = '',
+  success = 'success',
+  error = 'error'
+}
+
 const layout = {
-  labelCol: { span: 4 },
-  wrapperCol: { span: 18 },
+  labelCol: {span: 4},
+  wrapperCol: {span: 18},
 };
 const tailLayout = {
-  wrapperCol: { offset: 4, span: 18 },
+  wrapperCol: {offset: 4, span: 18},
 };
 
 
-
 /**
  * 更新节点
  * @param fields
@@ -32,15 +37,54 @@ const handleUpdate = async (fields: PasswordChange) => {
     } else {
       message.error('修改失败!');
     }
-  }catch (e) {
+  } catch (e) {
     console.log(e);
   }
 };
 
+
 const TableList: React.FC<{}> = () => {
+  const [success, setSuccess] = useState<InputState>(InputState.default);
+
+  const [form2] = Form.useForm();
+
+  const nameExist = async () => {
+    const userName = form2.getFieldValue("userName");
+    if (userName == null || userName.length <= 6) {
+      message.error("密码长度不够6位");
+      return false;
+    }
+    const res = await service.nameExist({userName: form2.getFieldValue("userName")});
+    if (res.success) {
+      setSuccess(InputState.success);
+      return true;
+    }
+    setSuccess(InputState.error);
+
+    return false;
+  }
+
+
+  /**
+   * 密码更新
+   * @param values
+   */
   const onFinish = (values: any) => {
     handleUpdate(values);
   };
+  /**
+   * 用户名更新
+   * @param value
+   */
+  const onFinishUserName = async (value: any) => {
+    const res = await service.updateAccountName(value);
+    if (res.success) {
+      message.success('修改成功');
+      window.history.go(0);
+    } else {
+      message.error('修改失败!');
+    }
+  }
 
   // const onFinishFailed = errorInfo => {
   //   console.log('Failed:', errorInfo);
@@ -48,28 +92,60 @@ const TableList: React.FC<{}> = () => {
 
   return (
     <PageHeaderWrapper title={false}>
-      <Card title="重置密码"  style={{ width: '100%' }}>
+
+      <Card title="修改用户名" style={{width: '100%'}}>
+        <Form
+          {...layout}
+          name="basic"
+          form={form2}
+          initialValues={{remember: true}}
+          onFinish={onFinishUserName}
+          // onFinishFailed={onFinishFailed}
+        >
+          <Form.Item
+            label="新用户名"
+            name="userName"
+            hasFeedback
+            validateStatus={success}
+            rules={[{required: true, message: '请输入用户名!'}]}
+          >
+            <Input/>
+          </Form.Item>
+
+          <Form.Item {...tailLayout}>
+            <Button type="primary" htmlType="button" onClick={nameExist}>
+              校验
+            </Button>
+            <span> </span>
+            <Button type="primary" htmlType="submit">
+              提交
+            </Button>
+          </Form.Item>
+        </Form>
+      </Card>
+
+      <Card title="重置密码" style={{width: '100%'}}>
         <Form
           {...layout}
           name="basic"
-          initialValues={{ remember: true }}
+          initialValues={{remember: true}}
           onFinish={onFinish}
           // onFinishFailed={onFinishFailed}
         >
           <Form.Item
             label="旧密码"
             name="oldPassword"
-            rules={[{ required: true, message: '请输入旧密码!' }]}
+            rules={[{required: true, message: '请输入旧密码!'}]}
           >
-            <Input.Password />
+            <Input.Password/>
           </Form.Item>
 
           <Form.Item
             label="新密码"
             name="newPassword"
-            rules={[{ required: true, message: '请输入旧密码' }]}
+            rules={[{required: true, message: '请输入旧密码'}]}
           >
-            <Input.Password />
+            <Input.Password/>
           </Form.Item>
 
           <Form.Item {...tailLayout}>
@@ -79,7 +155,20 @@ const TableList: React.FC<{}> = () => {
           </Form.Item>
         </Form>
       </Card>
-
+      <Card title="刷新页面" style={{width: '100%'}}>
+        <Form
+          {...layout}
+          name="basic"
+        >
+          <Form.Item {...tailLayout}>
+            <Button type="primary" htmlType="button" onClick={()=>{
+              window.location.reload();
+            }}>
+              刷新页面
+            </Button>
+          </Form.Item>
+        </Form>
+      </Card>
     </PageHeaderWrapper>
   );
 };

+ 2 - 0
src/pages/Setting/service.ts

@@ -4,4 +4,6 @@ export default {
   queryList: api.get('/service/queryList'),
   forceUpdateModel: api.post('/service/forceUpdate'),
   updatePassword: api.get('/api/user/resetPassword'),
+  nameExist: api.get('/api/user/nameExist'),
+  updateAccountName: api.get('/api/user/updateAccountName'),
 };

+ 56 - 0
src/pages/UserList/_mock.ts

@@ -0,0 +1,56 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Request, Response } from 'express';
+import { parse } from 'url';
+import { TableListItem, TableListParams } from './data.d';
+
+// mock tableListDataSource
+const genList = (current: number, pageSize: number) => {
+  const tableListDataSource: TableListItem[] = [];
+
+  for (let i = 0; i < pageSize; i += 1) {
+    const index = (current - 1) * 10 + i;
+    tableListDataSource.push({
+    });
+  }
+  tableListDataSource.reverse();
+  return tableListDataSource;
+};
+
+const tableListDataSource = genList(1, 100);
+
+function getModels(req: Request, res: Response, u: string) {
+  let realUrl = u;
+  if (!realUrl || Object.prototype.toString.call(realUrl) !== '[object String]') {
+    realUrl = req.url;
+  }
+  const { current = 1, pageSize = 10 } = req.query;
+  const params = (parse(realUrl, true).query as unknown) as TableListParams;
+
+  // @ts-ignore
+  const dataSource = [...tableListDataSource].slice((current - 1) * pageSize, current * pageSize);
+
+  const result = {
+    data: dataSource,
+    total: tableListDataSource.length,
+    success: true,
+    pageSize,
+    current: parseInt(`${params.currentPage}`, 10) || 1,
+  };
+
+  return res.json(result);
+}
+
+function success(req: Request, res: Response, u: string) {
+  const result = {
+    data: "ok",
+    success: true,
+  };
+  return res.json(result);
+}
+
+export default {
+  'GET /service/queryList': getModels,
+  'POST /service/forceUpdate': success,
+  '/api/user/delete': success,
+  '/api/user/forceUpdatePassword': success,
+};

+ 7 - 6
src/pages/ServiceList/data.d.ts → src/pages/UserList/data.d.ts

@@ -1,12 +1,12 @@
 // 表单
 export interface TableListItem {
   id?: number;
-  serviceIp: string;
-  modelName: string;
-  modelVersion: Date;
-  currentVersion: Date;
-  success:string;
-  msg:string;
+  userName: string;
+  gender: string;
+  age: number;
+  phone: string;
+  status: string;
+  roleId: number;
 }
 // 分页参数
 export interface TableListPagination {
@@ -29,3 +29,4 @@ export interface TableListParams {
   pageSize?: number;
   currentPage?: number;
 }
+

+ 6 - 0
src/pages/UserList/index.less

@@ -0,0 +1,6 @@
+.upload {
+  display: inline;
+}
+.red{
+  color: red;
+}

+ 298 - 0
src/pages/UserList/index.tsx

@@ -0,0 +1,298 @@
+import {Button, Divider, Input, message, Modal, Radio} from 'antd';
+import React, {useRef, useState} from 'react';
+import {PageHeaderWrapper} from '@ant-design/pro-layout';
+import ProTable, {ActionType, ProColumns} from '@ant-design/pro-table';
+import {SorterResult} from 'antd/es/table/interface';
+import {ExclamationCircleOutlined, PlusOutlined} from "@ant-design/icons/lib";
+import {TableListItem} from './data.d';
+import service from './service';
+
+
+/**
+ *  删除节点
+ * @param fields
+ */
+const handleRemove = async (fields: TableListItem) => {
+  const hide = message.loading('正在删除');
+  try {
+    const res = await service.delete(fields);
+    if (res.success) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error('删除失败,请重试');
+    }
+    hide();
+    return res.success;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+/**
+ * 添加节点
+ * @param fields
+ */
+const handleAdd = async (fields: TableListItem) => {
+  const hide = message.loading('正在添加');
+  try {
+    const res = await service.register({...fields});
+    hide();
+    if (res.success) {
+      message.success('添加成功');
+      return true;
+    }
+    message.error('添加失败请重试!');
+    return false;
+
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ * @param fields
+ */
+const handleUpdate = async (fields: TableListItem) => {
+  const hide = message.loading('正在配置');
+  try {
+    const res = await service.update(fields);
+    hide();
+    if (res.success) {
+      message.success('配置成功');
+      return true;
+    }
+    message.error('配置失败请重试!');
+    return false;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+
+const TableList: React.FC<{}> = () => {
+  const [sorter, setSorter] = useState<string>('');
+  const [modelVisible, setModalVisible] = useState<boolean>(false);
+
+  const [formValues, setFormValues] = useState({});
+  const actionRef = useRef<ActionType>();
+
+  // 重置表单
+  const [resetVisible, setResetVisible] = useState<boolean>(false);
+  const [resetLoading, setResetLoading] = useState<boolean>(false);
+  const [newPassword, setNewPassword] = useState<string>('');
+
+  const {confirm} = Modal;
+
+  const deleteMemory = async (record: any) => {
+    await handleRemove(record);
+    if (actionRef.current) {
+      actionRef.current.reload();
+    }
+
+  };
+
+  const columns: ProColumns<TableListItem>[] = [
+    {
+      title: '用户名',
+      dataIndex: 'userName',
+      width: 100,
+      hideInSearch: false,
+    },
+    {
+      title: '性别',
+      dataIndex: 'gender',
+      width: 50,
+      hideInSearch: true,
+      renderFormItem: () => {
+        return (
+          <Radio.Group>
+            <Radio value="0">女</Radio>
+            <Radio value='1'>男</Radio>
+          </Radio.Group>
+        );
+      },
+      valueEnum: {
+        '0': {text: '女'},
+        '1': {text: '男'}
+      },
+    },
+    {
+      title: '年龄',
+      dataIndex: 'age',
+      width: 50,
+      hideInSearch: true,
+    },
+    {
+      title: '手机号',
+      dataIndex: 'phone',
+      width: 80,
+      hideInSearch: true,
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      width: 80,
+    },
+    {
+      title: '角色',
+      dataIndex: 'roleId',
+      width: 80,
+      renderFormItem: () => {
+        return (
+          <Radio.Group>
+            <Radio value={1}>管理员</Radio>
+            <Radio value={2}>普通用户</Radio>
+          </Radio.Group>
+        );
+      },
+      valueEnum: {
+        1: {text: '管理员'},
+        2: {text: '普通用户'}
+      },
+    },
+    {
+      title: '操作',
+      dataIndex: 'option',
+      width: 150,
+      valueType: 'option',
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      render: (_, record, index, action) => (
+        <>
+          <a
+            onClick={() => {
+              setFormValues(record);
+              setModalVisible(true);
+            }}
+          >
+            修改
+          </a>
+          <Divider type="vertical"/>
+          <a
+            onClick={async () => {
+              confirm({
+                title: '确定删除吗?',
+                icon: <ExclamationCircleOutlined/>,
+                content: '删除后无法恢复',
+                okText: '确定',
+                okType: 'danger',
+                cancelText: '取消',
+                onOk() {
+                  deleteMemory(record);
+                },
+                onCancel() {
+                  console.log('Cancel');
+                },
+              });
+            }}
+          >
+            删除
+          </a>
+          <Divider type="vertical"/>
+          <a
+            onClick={() => {
+              setFormValues(record);
+              setResetVisible(true);
+            }}
+          >
+            重置密码
+          </a>
+        </>
+      ),
+    },
+  ];
+
+  return (
+    <PageHeaderWrapper title={false}>
+      <ProTable<TableListItem>
+        // search={false}
+        headerTitle="查询表格"
+        actionRef={actionRef}
+        rowKey="id"
+        onChange={(_, _filter, _sorter) => {
+          const sorterResult = _sorter as SorterResult<TableListItem>;
+          if (sorterResult.field) {
+            setSorter(`${sorterResult.field}_${sorterResult.order}`);
+          }
+        }}
+
+        pagination={{
+          defaultPageSize: 10,
+        }}
+        params={{
+          sorter,
+        }}
+        request={async (params = {}) => {
+          const res = await service.queryList(params);
+          return {
+            data: res.data.list,
+            page: params.current,
+            success: true,
+            total: res.data.total,
+          };
+        }}
+        columns={columns}
+        rowSelection={{}}
+      />
+
+      <Modal
+        title="重置密码"
+        visible={resetVisible}
+        onOk={ async ()=>{
+          setResetLoading(true);
+          let res = await service.forceUpdatePassword({user_Id: formValues['id'], newPassword: newPassword});
+          if (res.success) {
+            message.success("修改成功");
+            setResetVisible(false);
+          } else {
+            message.error("修改失败");
+          }
+          setResetLoading(false);
+        }}
+        confirmLoading={resetLoading}
+        onCancel={()=>{setResetVisible(false)}}
+      >
+        <Input placeholder="新密码" value={newPassword} onChange={(e)=>setNewPassword(e.target.value)} />
+      </Modal>
+
+      <Modal
+        destroyOnClose
+        title={'id' in formValues ? '编辑' : '新建'}
+        visible={modelVisible}
+        onCancel={() => setModalVisible(false)}
+        footer={null}
+      >
+        <ProTable<TableListItem, TableListItem>
+          onSubmit={async (value) => {
+            let success = false;
+            if ('id' in formValues) {
+              value['id'] = formValues['id']
+              success = await handleUpdate(value);
+            } else {
+              success = await handleAdd(value);
+            }
+            if (success) {
+              setModalVisible(false);
+              if (actionRef.current) {
+                actionRef.current.reload();
+              }
+            }
+          }}
+          rowKey="id"
+          type="form"
+          columns={columns}
+          rowSelection={{}}
+          form={{initialValues: formValues}}
+        />
+      </Modal>
+
+    </PageHeaderWrapper>
+  );
+};
+
+export default TableList;

+ 10 - 0
src/pages/UserList/service.ts

@@ -0,0 +1,10 @@
+import api from '@/utils/api';
+
+export default {
+  queryList: api.get('/api/user/queryList'),
+  update: api.post('/api/user/update'),
+  delete: api.post('/api/user/delete'),
+  register: api.post('/api/user/register'),
+  forceUpdatePassword: api.get('/api/user/forceUpdatePassword'),
+  nameExist: api.get('/api/user/nameExist'),
+};

+ 7 - 11
src/pages/document.ejs

@@ -1,27 +1,23 @@
 <!DOCTYPE html>
 <html lang="en">
   <head>
+    <base target="_blank" />
     <meta charset="UTF-8" />
     <meta http-equiv="X-UA-Compatible" content="IE=edge" />
     <meta
       name="keywords"
       content="antd,umi,umijs,ant design,脚手架,布局, Ant Design,项目,Pro,admin,控制台,主页,开箱即用,中后台,解决方案,组件库"
     />
-    <meta
-      name="description"
-      content="
-    An out-of-box UI solution for enterprise applications as a React boilerplate."
-    />
-    <meta
-      name="description"
-      content="
-      开箱即用的中台前端/设计解决方案。"
-    />
+
+    <meta http-equiv="pragma" content="no-cache">
+    <meta http-equiv="Cache-Control" content="no-cache, must-revalidate">
+    <meta http-equiv="expires" content="0">
+
     <meta
       name="viewport"
       content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
     />
-    <title>Ant Design Pro</title>
+    <title>念念不忘</title>
     <link rel="icon" href="/favicon.png" type="image/x-icon" />
   </head>
   <body>

+ 8 - 0
src/pages/user/Register/_mock.ts

@@ -0,0 +1,8 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { Request, Response } from 'express';
+
+export default {
+  'POST  /api/register': (_: Request, res: Response) => {
+    res.send({ status: 'ok', currentAuthority: 'user' });
+  },
+};

+ 274 - 0
src/pages/user/Register/index.tsx

@@ -0,0 +1,274 @@
+import {Button, Form, Input, message, Popover, Progress, Select} from 'antd';
+import React, {FC, useEffect, useState} from 'react';
+import {connect, Dispatch, formatMessage, FormattedMessage, history, Link} from 'umi';
+
+import {StateType} from './model';
+import styles from './style.less';
+
+const FormItem = Form.Item;
+const { Option } = Select;
+const InputGroup = Input.Group;
+
+const passwordStatusMap = {
+  ok: (
+    <div className={styles.success}>
+      <FormattedMessage id="userandregister.strength.strong" />
+    </div>
+  ),
+  pass: (
+    <div className={styles.warning}>
+      <FormattedMessage id="userandregister.strength.medium" />
+    </div>
+  ),
+  poor: (
+    <div className={styles.error}>
+      <FormattedMessage id="userandregister.strength.short" />
+    </div>
+  ),
+};
+
+const passwordProgressMap: {
+  ok: 'success';
+  pass: 'normal';
+  poor: 'exception';
+} = {
+  ok: 'success',
+  pass: 'normal',
+  poor: 'exception',
+};
+
+interface RegisterProps {
+  dispatch: Dispatch<any>;
+  userAndRegister: StateType;
+  submitting: boolean;
+}
+
+export interface UserRegisterParams {
+  userName: string;
+  password: string;
+  confirm: string;
+  mobile: string;
+  captcha: string;
+  prefix: string;
+}
+
+const Register: FC<RegisterProps> = ({
+  submitting,
+  dispatch,
+  userAndRegister,
+}) => {
+  const [visible, setvisible]: [boolean, any] = useState(false);
+  const [popover, setpopover]: [boolean, any] = useState(false);
+  const confirmDirty = false;
+  let interval: number | undefined;
+  const [form] = Form.useForm();
+  useEffect(() => {
+    if (!userAndRegister) {
+      return;
+    }
+    const account = form.getFieldValue('userName');
+    if (userAndRegister.status) {
+      message.success('注册成功!');
+
+      history.push({
+        pathname: '/user/login',
+        state: {
+          account,
+        },
+      });
+    } else {
+      message.info("请输入正确信息");
+    }
+  }, [userAndRegister]);
+  useEffect(
+    () => () => {
+      clearInterval(interval);
+    },
+    [],
+  );
+
+  const getPasswordStatus = () => {
+    const value = form.getFieldValue('password');
+    if (value && value.length > 9) {
+      return 'ok';
+    }
+    if (value && value.length > 5) {
+      return 'pass';
+    }
+    return 'poor';
+  };
+  const onFinish = (values: { [key: string]: any }) => {
+    dispatch({
+      type: 'userAndRegister/submit',
+      payload: {
+        ...values,
+      },
+    });
+  };
+  const checkConfirm = (_: any, value: string) => {
+    const promise = Promise;
+    if (value && value !== form.getFieldValue('password')) {
+      return promise.reject(formatMessage({ id: 'userandregister.password.twice' }));
+    }
+    return promise.resolve();
+  };
+  const checkPassword = (_: any, value: string) => {
+    const promise = Promise;
+    // 没有值的情况
+    if (!value) {
+      setvisible(!!value);
+      return promise.reject(formatMessage({ id: 'userandregister.password.required' }));
+    }
+    // 有值的情况
+    if (!visible) {
+      setvisible(!!value);
+    }
+    setpopover(!popover);
+    if (value.length < 6) {
+      return promise.reject('');
+    }
+    if (value && confirmDirty) {
+      form.validateFields(['confirm']);
+    }
+    return promise.resolve();
+  };
+
+  const renderPasswordProgress = () => {
+    const value = form.getFieldValue('password');
+    const passwordStatus = getPasswordStatus();
+    return value && value.length ? (
+      <div className={styles[`progress-${passwordStatus}`]}>
+        <Progress
+          status={passwordProgressMap[passwordStatus]}
+          className={styles.progress}
+          strokeWidth={6}
+          percent={value.length * 10 > 100 ? 100 : value.length * 10}
+          showInfo={false}
+        />
+      </div>
+    ) : null;
+  };
+
+  return (
+    <div className={styles.main}>
+      <h3>
+        <FormattedMessage id="userandregister.register.register" />
+      </h3>
+      <Form form={form} name="UserRegister" onFinish={onFinish}>
+        <FormItem
+          name="userName"
+          rules={[
+            {
+              required: true,
+              message: "用户名不能为空",
+            },
+          ]}
+        >
+          <Input size="large" placeholder="用户名" />
+        </FormItem>
+        <FormItem
+          name="registerCode"
+          rules={[
+            {
+              required: true,
+              message: "邀请码不能为空",
+            },
+          ]}
+        >
+          <Input size="large" placeholder="邀请码" />
+        </FormItem>
+        <Popover
+          getPopupContainer={(node) => {
+            if (node && node.parentNode) {
+              return node.parentNode as HTMLElement;
+            }
+            return node;
+          }}
+          content={
+            visible && (
+              <div style={{ padding: '4px 0' }}>
+                {passwordStatusMap[getPasswordStatus()]}
+                {renderPasswordProgress()}
+                <div style={{ marginTop: 10 }}>
+                  <FormattedMessage id="userandregister.strength.msg" />
+                </div>
+              </div>
+            )
+          }
+          overlayStyle={{ width: 240 }}
+          placement="right"
+          visible={visible}
+        >
+          <FormItem
+            name="password"
+            className={
+              form.getFieldValue('password') &&
+              form.getFieldValue('password').length > 0 &&
+              styles.password
+            }
+            rules={[
+              {
+                validator: checkPassword,
+              },
+            ]}
+          >
+            <Input
+              size="large"
+              type="password"
+              placeholder={formatMessage({ id: 'userandregister.password.placeholder' })}
+            />
+          </FormItem>
+        </Popover>
+        <FormItem
+          name="confirm"
+          rules={[
+            {
+              required: true,
+              message: formatMessage({ id: 'userandregister.confirm-password.required' }),
+            },
+            {
+              validator: checkConfirm,
+            },
+          ]}
+        >
+          <Input
+            size="large"
+            type="password"
+            placeholder={formatMessage({ id: 'userandregister.confirm-password.placeholder' })}
+          />
+        </FormItem>
+
+        <FormItem>
+          <Button
+            size="large"
+            loading={submitting}
+            className={styles.submit}
+            type="primary"
+            htmlType="submit"
+          >
+            <FormattedMessage id="userandregister.register.register" />
+          </Button>
+          <Link className={styles.login} to="/user/login">
+            <FormattedMessage id="userandregister.register.sign-in" />
+          </Link>
+        </FormItem>
+      </Form>
+    </div>
+  );
+};
+export default connect(
+  ({
+    userAndRegister,
+    loading,
+  }: {
+    userAndRegister: StateType;
+    loading: {
+      effects: {
+        [key: string]: boolean;
+      };
+    };
+  }) => ({
+    userAndRegister,
+    submitting: loading.effects['userAndRegister/submit'],
+  }),
+)(Register);

+ 78 - 0
src/pages/user/Register/locales/en-US.ts

@@ -0,0 +1,78 @@
+export default {
+  'userandregister.login.userName': 'userName',
+  'userandregister.login.password': 'password',
+  'userandregister.login.message-invalid-credentials':
+    'Invalid username or password(admin/ant.design)',
+  'userandregister.login.message-invalid-verification-code': 'Invalid verification code',
+  'userandregister.login.tab-login-credentials': 'Credentials',
+  'userandregister.login.tab-login-mobile': 'Mobile number',
+  'userandregister.login.remember-me': 'Remember me',
+  'userandregister.login.forgot-password': 'Forgot your password?',
+  'userandregister.login.sign-in-with': 'Sign in with',
+  'userandregister.login.signup': 'Sign up',
+  'userandregister.login.login': 'Login',
+  'userandregister.register.register': 'Register',
+  'userandregister.register.get-verification-code': 'Get code',
+  'userandregister.register.sign-in': 'Already have an account?',
+  'userandregister.register-result.msg': 'Account:registered at {email}',
+  'userandregister.register-result.activation-email':
+    'The activation email has been sent to your email address and is valid for 24 hours. Please log in to the email in time and click on the link in the email to activate the account.',
+  'userandregister.register-result.back-home': 'Back to home',
+  'userandregister.register-result.view-mailbox': 'View mailbox',
+  'userandregister.email.required': 'Please enter your email!',
+  'userandregister.email.wrong-format': 'The email address is in the wrong format!',
+  'userandregister.userName.required': 'Please enter your userName!',
+  'userandregister.password.required': 'Please enter your password!',
+  'userandregister.password.twice': 'The passwords entered twice do not match!',
+  'userandregister.strength.msg':
+    "Please enter at least 6 characters and don't use passwords that are easy to guess.",
+  'userandregister.strength.strong': 'Strength: strong',
+  'userandregister.strength.medium': 'Strength: medium',
+  'userandregister.strength.short': 'Strength: too short',
+  'userandregister.confirm-password.required': 'Please confirm your password!',
+  'userandregister.phone-number.required': 'Please enter your phone number!',
+  'userandregister.phone-number.wrong-format': 'Malformed phone number!',
+  'userandregister.verification-code.required': 'Please enter the verification code!',
+  'userandregister.title.required': 'Please enter a title',
+  'userandregister.date.required': 'Please select the start and end date',
+  'userandregister.goal.required': 'Please enter a description of the goal',
+  'userandregister.standard.required': 'Please enter a metric',
+  'userandregister.form.get-captcha': 'Get Captcha',
+  'userandregister.captcha.second': 'sec',
+  'userandregister.form.optional': ' (optional) ',
+  'userandregister.form.submit': 'Submit',
+  'userandregister.form.save': 'Save',
+  'userandregister.email.placeholder': 'Email',
+  'userandregister.password.placeholder': 'Password',
+  'userandregister.confirm-password.placeholder': 'Confirm password',
+  'userandregister.phone-number.placeholder': 'Phone number',
+  'userandregister.verification-code.placeholder': 'Verification code',
+  'userandregister.title.label': 'Title',
+  'userandregister.title.placeholder': 'Give the target a name',
+  'userandregister.date.label': 'Start and end date',
+  'userandregister.placeholder.start': 'Start date',
+  'userandregister.placeholder.end': 'End date',
+  'userandregister.goal.label': 'Goal description',
+  'userandregister.goal.placeholder': 'Please enter your work goals',
+  'userandregister.standard.label': 'Metrics',
+  'userandregister.standard.placeholder': 'Please enter a metric',
+  'userandregister.client.label': 'Client',
+  'userandregister.label.tooltip': 'Target service object',
+  'userandregister.client.placeholder':
+    'Please describe your customer service, internal customers directly @ Name / job number',
+  'userandregister.invites.label': 'Inviting critics',
+  'userandregister.invites.placeholder':
+    'Please direct @ Name / job number, you can invite up to 5 people',
+  'userandregister.weight.label': 'Weight',
+  'userandregister.weight.placeholder': 'Please enter weight',
+  'userandregister.public.label': 'Target disclosure',
+  'userandregister.label.help': 'Customers and invitees are shared by default',
+  'userandregister.radio.public': 'Public',
+  'userandregister.radio.partially-public': 'Partially public',
+  'userandregister.radio.private': 'Private',
+  'userandregister.publicUsers.placeholder': 'Open to',
+  'userandregister.option.A': 'Colleague A',
+  'userandregister.option.B': 'Colleague B',
+  'userandregister.option.C': 'Colleague C',
+  'userandregister.navBar.lang': 'Languages',
+};

+ 74 - 0
src/pages/user/Register/locales/zh-CN.ts

@@ -0,0 +1,74 @@
+export default {
+  'userandregister.login.userName': '用户名',
+  'userandregister.login.password': '密码',
+  'userandregister.login.message-invalid-credentials': '账户或密码错误(admin/ant.design)',
+  'userandregister.login.message-invalid-verification-code': '验证码错误',
+  'userandregister.login.tab-login-credentials': '账户密码登录',
+  'userandregister.login.tab-login-mobile': '手机号登录',
+  'userandregister.login.remember-me': '自动登录',
+  'userandregister.login.forgot-password': '忘记密码',
+  'userandregister.login.sign-in-with': '其他登录方式',
+  'userandregister.login.signup': '注册账户',
+  'userandregister.login.login': '登录',
+  'userandregister.register.register': '注册',
+  'userandregister.register.get-verification-code': '获取验证码',
+  'userandregister.register.sign-in': '使用已有账户登录',
+  'userandregister.register-result.msg': '你的账户:{email} 注册成功',
+  'userandregister.register-result.activation-email':
+    '激活邮件已发送到你的邮箱中,邮件有效期为24小时。请及时登录邮箱,点击邮件中的链接激活帐户。',
+  'userandregister.register-result.back-home': '返回首页',
+  'userandregister.register-result.view-mailbox': '查看邮箱',
+  'userandregister.email.required': '请输入邮箱地址!',
+  'userandregister.email.wrong-format': '邮箱地址格式错误!',
+  'userandregister.userName.required': '请输入用户名!',
+  'userandregister.password.required': '请输入密码!',
+  'userandregister.password.twice': '两次输入的密码不匹配!',
+  'userandregister.strength.msg': '请至少输入 6 个字符。请不要使用容易被猜到的密码。',
+  'userandregister.strength.strong': '强度:强',
+  'userandregister.strength.medium': '强度:中',
+  'userandregister.strength.short': '强度:太短',
+  'userandregister.confirm-password.required': '请确认密码!',
+  'userandregister.phone-number.required': '请输入手机号!',
+  'userandregister.phone-number.wrong-format': '手机号格式错误!',
+  'userandregister.verification-code.required': '请输入验证码!',
+  'userandregister.title.required': '请输入标题',
+  'userandregister.date.required': '请选择起止日期',
+  'userandregister.goal.required': '请输入目标描述',
+  'userandregister.standard.required': '请输入衡量标准',
+  'userandregister.form.get-captcha': '获取验证码',
+  'userandregister.captcha.second': '秒',
+  'userandregister.form.optional': '(选填)',
+  'userandregister.form.submit': '提交',
+  'userandregister.form.save': '保存',
+  'userandregister.email.placeholder': '邮箱',
+  'userandregister.password.placeholder': '至少6位密码,区分大小写',
+  'userandregister.confirm-password.placeholder': '确认密码',
+  'userandregister.phone-number.placeholder': '手机号',
+  'userandregister.verification-code.placeholder': '验证码',
+  'userandregister.title.label': '标题',
+  'userandregister.title.placeholder': '给目标起个名字',
+  'userandregister.date.label': '起止日期',
+  'userandregister.placeholder.start': '开始日期',
+  'userandregister.placeholder.end': '结束日期',
+  'userandregister.goal.label': '目标描述',
+  'userandregister.goal.placeholder': '请输入你的阶段性工作目标',
+  'userandregister.standard.label': '衡量标准',
+  'userandregister.standard.placeholder': '请输入衡量标准',
+  'userandregister.client.label': '客户',
+  'userandregister.label.tooltip': '目标的服务对象',
+  'userandregister.client.placeholder': '请描述你服务的客户,内部客户直接 @姓名/工号',
+  'userandregister.invites.label': '邀评人',
+  'userandregister.invites.placeholder': '请直接 @姓名/工号,最多可邀请 5 人',
+  'userandregister.weight.label': '权重',
+  'userandregister.weight.placeholder': '请输入',
+  'userandregister.public.label': '目标公开',
+  'userandregister.label.help': '客户、邀评人默认被分享',
+  'userandregister.radio.public': '公开',
+  'userandregister.radio.partially-public': '部分公开',
+  'userandregister.radio.private': '不公开',
+  'userandregister.publicUsers.placeholder': '公开给',
+  'userandregister.option.A': '同事甲',
+  'userandregister.option.B': '同事乙',
+  'userandregister.option.C': '同事丙',
+  'userandregister.navBar.lang': '语言',
+};

+ 74 - 0
src/pages/user/Register/locales/zh-TW.ts

@@ -0,0 +1,74 @@
+export default {
+  'userandregister.login.userName': '賬戶',
+  'userandregister.login.password': '密碼',
+  'userandregister.login.message-invalid-credentials': '賬戶或密碼錯誤(admin/ant.design)',
+  'userandregister.login.message-invalid-verification-code': '驗證碼錯誤',
+  'userandregister.login.tab-login-credentials': '賬戶密碼登錄',
+  'userandregister.login.tab-login-mobile': '手機號登錄',
+  'userandregister.login.remember-me': '自動登錄',
+  'userandregister.login.forgot-password': '忘記密碼',
+  'userandregister.login.sign-in-with': '其他登錄方式',
+  'userandregister.login.signup': '註冊賬戶',
+  'userandregister.login.login': '登錄',
+  'userandregister.register.register': '註冊',
+  'userandregister.register.get-verification-code': '獲取驗證碼',
+  'userandregister.register.sign-in': '使用已有賬戶登錄',
+  'userandregister.register-result.msg': '妳的賬戶:{email} 註冊成功',
+  'userandregister.register-result.activation-email':
+    '激活郵件已發送到妳的郵箱中,郵件有效期為24小時。請及時登錄郵箱,點擊郵件中的鏈接激活帳戶。',
+  'userandregister.register-result.back-home': '返回首頁',
+  'userandregister.register-result.view-mailbox': '查看郵箱',
+  'userandregister.email.required': '請輸入郵箱地址!',
+  'userandregister.email.wrong-format': '郵箱地址格式錯誤!',
+  'userandregister.userName.required': '請輸入賬戶!',
+  'userandregister.password.required': '請輸入密碼!',
+  'userandregister.password.twice': '兩次輸入的密碼不匹配!',
+  'userandregister.strength.msg': '請至少輸入 6 個字符。請不要使用容易被猜到的密碼。',
+  'userandregister.strength.strong': '強度:強',
+  'userandregister.strength.medium': '強度:中',
+  'userandregister.strength.short': '強度:太短',
+  'userandregister.confirm-password.required': '請確認密碼!',
+  'userandregister.phone-number.required': '請輸入手機號!',
+  'userandregister.phone-number.wrong-format': '手機號格式錯誤!',
+  'userandregister.verification-code.required': '請輸入驗證碼!',
+  'userandregister.title.required': '請輸入標題',
+  'userandregister.date.required': '請選擇起止日期',
+  'userandregister.goal.required': '請輸入目標描述',
+  'userandregister.standard.required': '請輸入衡量標淮',
+  'userandregister.form.get-captcha': '獲取驗證碼',
+  'userandregister.captcha.second': '秒',
+  'userandregister.form.optional': '(選填)',
+  'userandregister.form.submit': '提交',
+  'userandregister.form.save': '保存',
+  'userandregister.email.placeholder': '郵箱',
+  'userandregister.password.placeholder': '至少6位密碼,區分大小寫',
+  'userandregister.confirm-password.placeholder': '確認密碼',
+  'userandregister.phone-number.placeholder': '手機號',
+  'userandregister.verification-code.placeholder': '驗證碼',
+  'userandregister.title.label': '標題',
+  'userandregister.title.placeholder': '給目標起個名字',
+  'userandregister.date.label': '起止日期',
+  'userandregister.placeholder.start': '開始日期',
+  'userandregister.placeholder.end': '結束日期',
+  'userandregister.goal.label': '目標描述',
+  'userandregister.goal.placeholder': '請輸入妳的階段性工作目標',
+  'userandregister.standard.label': '衡量標淮',
+  'userandregister.standard.placeholder': '請輸入衡量標淮',
+  'userandregister.client.label': '客戶',
+  'userandregister.label.tooltip': '目標的服務對象',
+  'userandregister.client.placeholder': '請描述妳服務的客戶,內部客戶直接 @姓名/工號',
+  'userandregister.invites.label': '邀評人',
+  'userandregister.invites.placeholder': '請直接 @姓名/工號,最多可邀請 5 人',
+  'userandregister.weight.label': '權重',
+  'userandregister.weight.placeholder': '請輸入',
+  'userandregister.public.label': '目標公開',
+  'userandregister.label.help': '客戶、邀評人默認被分享',
+  'userandregister.radio.public': '公開',
+  'userandregister.radio.partially-public': '部分公開',
+  'userandregister.radio.private': '不公開',
+  'userandregister.publicUsers.placeholder': '公開給',
+  'userandregister.option.A': '同事甲',
+  'userandregister.option.B': '同事乙',
+  'userandregister.option.C': '同事丙',
+  'userandregister.navBar.lang': '語言',
+};

+ 48 - 0
src/pages/user/Register/model.ts

@@ -0,0 +1,48 @@
+import { Effect, Reducer } from 'umi';
+
+import { register } from './service';
+
+export interface StateType {
+  status?: true | false;
+  currentAuthority?: '1' | '2' | '3';
+}
+
+export interface ModelType {
+  namespace: string;
+  state: StateType;
+  effects: {
+    submit: Effect;
+  };
+  reducers: {
+    registerHandle: Reducer<StateType>;
+  };
+}
+
+const Model: ModelType = {
+  namespace: 'userAndRegister',
+
+  state: {
+    status: undefined,
+  },
+
+  effects: {
+    *submit({ payload }, { call, put }) {
+      const response = yield call(register, payload);
+      yield put({
+        type: 'registerHandle',
+        payload: response,
+      });
+    },
+  },
+
+  reducers: {
+    registerHandle(state, { payload }) {
+      return {
+        ...state,
+        status: payload.success,
+      };
+    },
+  },
+};
+
+export default Model;

+ 9 - 0
src/pages/user/Register/service.ts

@@ -0,0 +1,9 @@
+import request from 'umi-request';
+import { UserRegisterParams } from './index';
+
+export async function register(params: UserRegisterParams) {
+  return request('/api/user/register', {
+    method: 'POST',
+    data: params,
+  });
+}

+ 60 - 0
src/pages/user/Register/style.less

@@ -0,0 +1,60 @@
+@import '~antd/es/style/themes/default.less';
+
+.main {
+  width: 368px;
+  margin: 0 auto;
+
+  h3 {
+    margin-bottom: 20px;
+    font-size: 16px;
+  }
+
+  .password {
+    margin-bottom: 24px;
+    :global {
+      .ant-form-item-explain {
+        display: none;
+      }
+    }
+  }
+
+  .getCaptcha {
+    display: block;
+    width: 100%;
+  }
+
+  .submit {
+    width: 50%;
+  }
+
+  .login {
+    float: right;
+    line-height: @btn-height-lg;
+  }
+}
+
+.success,
+.warning,
+.error {
+  transition: color 0.3s;
+}
+
+.success {
+  color: @success-color;
+}
+
+.warning {
+  color: @warning-color;
+}
+
+.error {
+  color: @error-color;
+}
+
+.progress-pass > .progress {
+  :global {
+    .ant-progress-bg {
+      background-color: @warning-color;
+    }
+  }
+}

+ 9 - 7
src/pages/user/login/index.tsx

@@ -1,6 +1,6 @@
 import {Alert} from 'antd';
 import React, {useState} from 'react';
-import {connect, Dispatch} from 'umi';
+import {connect, Dispatch, Link} from 'umi';
 import {StateType} from '@/models/login';
 import {LoginParamsType} from '@/services/login';
 import {ConnectState} from '@/models/connect';
@@ -8,6 +8,7 @@ import LoginFrom from './components/Login';
 
 import styles from './style.less';
 
+// @ts-ignore
 const {Tab, UserName, Password, Mobile, Captcha, Submit} = LoginFrom;
 
 interface LoginProps {
@@ -30,6 +31,7 @@ const LoginMessage: React.FC<{
 );
 
 const Login: React.FC<LoginProps> = (props) => {
+  // @ts-ignore
   const {userLogin = {}, submitting} = props;
   // const {status, type: loginType} = userLogin;
   // const [autoLogin, setAutoLogin] = useState(true);
@@ -121,17 +123,17 @@ const Login: React.FC<LoginProps> = (props) => {
 
 
         <Submit loading={submitting}>登录</Submit>
-        {/*
+
         <div className={styles.other}>
-          其他登录方式
-          <AlipayCircleOutlined className={styles.icon}/>
-          <TaobaoCircleOutlined className={styles.icon}/>
-          <WeiboCircleOutlined className={styles.icon}/>
+          {/*其他登录方式*/}
+          {/*<AlipayCircleOutlined className={styles.icon}/>*/}
+          {/*<TaobaoCircleOutlined className={styles.icon}/>*/}
+          {/*<WeiboCircleOutlined className={styles.icon}/>*/}
           <Link className={styles.register} to="/user/register">
             注册账户
           </Link>
         </div>
-        */}
+
 
       </LoginFrom>
     </div>

+ 34 - 4
src/utils/utils.ts

@@ -1,6 +1,6 @@
-import { parse } from 'querystring';
+import {parse} from 'querystring';
 import pathRegexp from 'path-to-regexp';
-import { Route } from '@/models/connect';
+import {Route} from '@/models/connect';
 
 /* eslint no-useless-escape:0 import/prefer-default-export:0 */
 const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
@@ -16,7 +16,7 @@ export const isAntDesignPro = (): boolean => {
 
 // 给官方演示站点用,用于关闭真实开发环境不需要使用的特性
 export const isAntDesignProOrDev = (): boolean => {
-  const { NODE_ENV } = process.env;
+  const {NODE_ENV} = process.env;
   if (NODE_ENV === 'development') {
     return true;
   }
@@ -35,7 +35,7 @@ export const getAuthorityFromRouter = <T extends Route>(
   pathname: string,
 ): T | undefined => {
   const authority = router.find(
-    ({ routes, path = '/' }) =>
+    ({routes, path = '/'}) =>
       (path && pathRegexp(path).exec(pathname)) ||
       (routes && getAuthorityFromRouter(routes, pathname)),
   );
@@ -63,3 +63,33 @@ export const getRouteAuthority = (path: string, routeData: Route[]) => {
   });
   return authorities;
 };
+
+/**
+ * change minute to day hour
+ * @param minutes
+ */
+export const minuteToDay = (minutes: number) => {
+  const day = parseInt(String(minutes / 60 / 24),10);
+  const hour = parseInt(String(minutes / 60 % 24),10);
+  const min = parseInt(String(minutes % 60),10);
+  let StatusMinuteStr = "";
+  if (day > 0) {
+    StatusMinuteStr = `${day  }天`;
+  }
+  if (hour > 0) {
+    StatusMinuteStr += `${hour  }小时`;
+  }
+  if (min > 0) {
+    StatusMinuteStr += `${parseFloat(String(min))  }分钟`;
+  }
+  return StatusMinuteStr;
+}
+
+/**
+ * 去除html标签
+ * @param html
+ */
+export const getTextFromHtml = (html: string) => {
+  const regExp = /<\/?.+?\/?>/g;
+  return html.replace(regExp, '').replace(/&nbsp;/g,'')
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott