tianyunperfect %!s(int64=5) %!d(string=hai) anos
pai
achega
dd415972bd

+ 6 - 0
config/config.ts

@@ -92,6 +92,12 @@ export default defineConfig({
               path: '/memory',
               component: './Memory',
             },
+            {
+              name: 'list.memoryList',
+              icon: 'smile',
+              path: '/memoryList',
+              component: './MemoryList',
+            },
             {
               name: 'list.setting',
               icon: 'smile',

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

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

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

@@ -3,14 +3,14 @@ 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'],
+    [{'header': [1, 2, false]}, 'bold', 'italic', 'blockquote', 'code-block', 'link'],
     [{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}],
   ],
 };

+ 73 - 117
src/pages/Memory/components/UpdateForm.tsx

@@ -1,143 +1,99 @@
-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 from 'react';
+import {Button, message, Modal} from 'antd';
+import {TableListItem} from '../../MemoryList/data.d';
+import Quill from "@/pages/Memory/components/Quill";
+import service from "@/pages/Memory/service";
+import {useSingleState} from "nice-hooks";
 
 // 表单特殊字段
-export interface FormValueType extends Partial<TableListItem> {}
+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 formLayout = {
-  labelCol: { span: 7 },
-  wrapperCol: { span: 13 },
-};
 
 const UpdateForm: React.FC<UpdateFormProps> = (props) => {
-  const [formVals, setFormVals] = useState<FormValueType>(props.values);
 
-  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,
+    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 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.`);
-      }
-    },
-  };
-
-  const renderFooter = () => {
-    return (
-      <>
-        <Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
-        <Button type="primary" onClick={() => submit()}>
-          提交
-        </Button>
-      </>
-    );
-  };
 
   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) => {
+            setFormValues({front: value});
+          }}
+          value={formValues.front}/>
 
-        <FormItem
-          name="modelType"
-          label="模型类型"
-          rules={[{ required: true, message: '请选择模型类型!' }]}
+        <p/>
+        <h4>反面</h4>
+        <Quill
+          readonly={false}
+          onChange={(value) => {
+            setFormValues({back: value});
+          }}
+          value={formValues.back}
+        />
+        <p/>
+        <Button
+          type="primary"
+          onClick={async () => {
+            let res;
+            if (formValues.id != undefined) {
+              res = await service.update(formValues);
+            } else {
+              res = await service.insert(formValues);
+            }
+            if (res.success) {
+              message.info('操作成功');
+              if (formValues.id != undefined) {
+                hideModal(true);
+              } else {
+                setFormValues({front:'',back:''})
+              }
+            } else {
+              message.info(`操作失败: ${res.message}`);
+            }
+          }}
         >
-          <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>
+        <span> </span>
+        <Button
+          type="primary"
+          onClick={() => {
+            hideModal(false)
+          }}
+        >关闭</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;
 
-}

+ 20 - 59
src/pages/Memory/index.tsx

@@ -3,8 +3,9 @@ import React, {useEffect} 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 {TableListItem} from '../MemoryList/data.d';
 import service from './service';
+import UpdateForm from "@/pages/Memory/components/UpdateForm";
 
 enum Step {
   MAIN = 0,
@@ -25,12 +26,15 @@ const TableList: React.FC<{}> = () => {
     showBack: false,
   });
 
-  const [memory, setMemory] = useSingleState<Memory>({
-    id: null,
-    userId: null,
-    front: '',
-    back: '',
-    tag: '',
+  const [memory, setMemory] = useSingleState<TableListItem>({
+    back: "",
+    front: "",
+    id: undefined,
+    period: 0,
+    remindTime: new Date(),
+    tag: "",
+    updateTime: "",
+    userId: 0
   });
 
 
@@ -57,12 +61,13 @@ const TableList: React.FC<{}> = () => {
       countRemind();
     },1000*60*60)  // 每隔一个小时查询一次
   }, []);
+
   /**
    * 清空memory
    */
   const clearMemory = () => {
     setMemory({
-      id: null,
+      id: undefined,
       front: '',
       back: '',
     });
@@ -130,8 +135,6 @@ const TableList: React.FC<{}> = () => {
     setState({step: Step.ADD});
   };
 
-
-
   return (
     <PageHeaderWrapper title={false}>
       {state.step === Step.MAIN ? (
@@ -255,56 +258,14 @@ const TableList: React.FC<{}> = () => {
         </div>
       ) : null}
 
-      {state.step === Step.ADD ? (
-        <div>
-          <h4>正面</h4>
-          <Quill
-            readonly={false}
-            onChange={(value) => {
-              setMemory({front: value});
-            }}
-            value={memory.front}/>
+      <UpdateForm
+        onCancel={()=>{
+          changStepMain();
+        }}
+        modalVisible={state.step === Step.ADD}
+        values={memory}>
 
-          <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>
-      ) : null}
+      </UpdateForm>
     </PageHeaderWrapper>
   );
 };

+ 60 - 0
src/pages/MemoryList/_mock.ts

@@ -0,0 +1,60 @@
+// 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({
+      msg: "为学者日益", success: "成功",
+      id: index,
+      serviceIp: `${index}name`,
+      modelName: `${index}name`,
+      modelVersion: `${new Date()}`,
+      currentVersion: `${new Date()}`
+    });
+  }
+  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,
+};

+ 33 - 0
src/pages/MemoryList/data.d.ts

@@ -0,0 +1,33 @@
+// 表单
+export interface TableListItem {
+  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;
+  id?: number;
+  pageSize?: number;
+  currentPage?: number;
+}
+

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

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

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

@@ -0,0 +1,168 @@
+import {Divider, message, 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";
+
+
+/**
+ *  删除节点
+ * @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 getTextFromHtml = (html: string) => {
+  let reg = /<\/?.+?\/?>/g;
+  return html.replace(reg, '').replace(/&nbsp;/g,'')
+}
+
+const TableList: React.FC<{}> = () => {
+  const [sorter, setSorter] = useState<string>('');
+  const [modelVisible, setModalVisible] = useState<boolean>(false);
+  const [formValues, setFormValues] = useState({});
+  const actionRef = useRef<ActionType>();
+
+  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: 200,
+      hideInSearch: true,
+      hideInForm: true,
+      render: (text, row) => {
+        return (<>
+          <span>{text + "分钟"}</span>
+        </>);
+      }
+    },
+    {
+      title: '提醒时间',
+      dataIndex: 'remindTime',
+      valueType: 'dateTime',
+      width: 200,
+      hideInSearch: true,
+      hideInForm: true
+    },
+    {
+      title: '操作',
+      dataIndex: 'option',
+      width: 100,
+      valueType: 'option',
+      render: (_, record, index, action) => (
+        <>
+          <a
+            onClick={() => {
+              setFormValues(record);
+              setModalVisible(true);
+            }}
+          >
+            修改
+          </a>
+          <Divider type="vertical"/>
+          <a
+            onClick={async () => {
+              await handleRemove(record);
+              if (actionRef.current) {
+                actionRef.current.reload();
+              }
+            }}
+          >
+            删除
+          </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'),
+};

+ 53 - 0
src/pages/ModelList/_mock.ts

@@ -0,0 +1,53 @@
+// 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({
+      id: index,
+      modelName: `${index}name`,
+      modelFile: `${index}name`,
+      vocabFile: `${index}name`,
+      labelFile: `${index}name`,
+      mustTag: ``,
+      mustNotTag: ``,
+      onlyWord: `true`,
+      modelType: 0,
+    });
+  }
+  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;
+
+  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);
+}
+
+export default {
+  'GET /api/model': getModels,
+};

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

@@ -0,0 +1,25 @@
+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;

+ 89 - 0
src/pages/ModelList/components/QueryForm.tsx

@@ -0,0 +1,89 @@
+import React, { useState } from 'react';
+import { Button, Form, Input, Modal } from 'antd';
+import service from '../service';
+
+const { TextArea } = Input;
+
+// 表单特殊字段
+export interface FormValueType {
+  modelName?: string;
+  queryStr?: string;
+}
+
+export interface FormProps {
+  onCancel: (flag?: boolean) => void;
+  // onSubmit: (values: FormValueType) => void;
+  modalVisible: boolean;
+  values: FormValueType;
+}
+
+const FormItem = Form.Item;
+
+const formLayout = {
+  labelCol: { span: 7 },
+  wrapperCol: { span: 13 },
+};
+
+const QueryForm: React.FC<FormProps> = (props) => {
+  const [resultStr, setResultStr] = useState('');
+
+  const [form] = Form.useForm();
+
+  const query = async () => {
+    const fields = await form.validateFields();
+    const result = await service.query(fields);
+    setResultStr(result);
+  };
+
+  const {
+    // onSubmit: handleUpdate,
+    onCancel: cancelModalVisible,
+    modalVisible,
+    values,
+  } = props;
+
+  const renderFooter = () => {
+    return (
+      <>
+        <Button onClick={() => cancelModalVisible()}>取消</Button>
+        <Button type="primary" onClick={() => query()}>
+          查询
+        </Button>
+      </>
+    );
+  };
+
+  return (
+    <Modal
+      width={640}
+      bodyStyle={{ padding: '32px 40px 48px' }}
+      destroyOnClose
+      title="相似度查询"
+      visible={modalVisible}
+      footer={renderFooter()}
+      onCancel={() => cancelModalVisible()}
+    >
+      <Form {...formLayout} form={form} initialValues={values}>
+        <FormItem
+          name="modelName"
+          label="模型名称"
+          rules={[{ required: true, message: '请输入规则名称!' }]}
+        >
+          <Input placeholder="请输入" readOnly />
+        </FormItem>
+        <FormItem
+          name="queryStr"
+          label="查询文字"
+          rules={[{ required: true, message: '请输入规则名称!' }]}
+        >
+          <Input placeholder="请输入" />
+        </FormItem>
+        <FormItem label="查询结果">
+          <TextArea value={resultStr} rows={4} readOnly />
+        </FormItem>
+      </Form>
+    </Modal>
+  );
+};
+
+export default QueryForm;

+ 145 - 0
src/pages/ModelList/components/UpdateForm.tsx

@@ -0,0 +1,145 @@
+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';
+
+// 表单特殊字段
+export interface FormValueType extends Partial<TableListItem> {}
+
+export interface UpdateFormProps {
+  onCancel: (flag?: boolean, formVals?: FormValueType) => void;
+  onSubmit: (values: FormValueType) => void;
+  updateModalVisible: boolean;
+  values: Partial<TableListItem>;
+}
+
+const FormItem = Form.Item;
+
+const formLayout = {
+  labelCol: { span: 7 },
+  wrapperCol: { span: 13 },
+};
+
+const UpdateForm: React.FC<UpdateFormProps> = (props) => {
+  const [formVals, setFormVals] = useState<FormValueType>(props.values);
+
+  const [form] = Form.useForm();
+
+  const {
+    onSubmit: handleUpdate,
+    onCancel: handleUpdateModalVisible,
+    updateModalVisible,
+    values,
+  } = 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 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.`);
+      }
+    },
+  };
+
+  const renderFooter = () => {
+    return (
+      <>
+        <Button onClick={() => handleUpdateModalVisible(false, values)}>取消</Button>
+        <Button type="primary" onClick={() => submit()}>
+          提交
+        </Button>
+      </>
+    );
+  };
+
+  return (
+    <Modal
+      width={640}
+      bodyStyle={{ padding: '32px 40px 48px' }}
+      destroyOnClose
+      title="配置模型"
+      visible={updateModalVisible}
+      footer={renderFooter()}
+      onCancel={() => handleUpdateModalVisible()}
+    >
+      <Form {...formLayout} form={form} initialValues={formVals}>
+        <FormItem
+          name="modelName"
+          label="模型名称"
+          rules={[{ required: true, message: '请输入规则名称!' }]}
+        >
+          <Input placeholder="请输入" />
+        </FormItem>
+
+        <FormItem
+          name="modelType"
+          label="模型类型"
+          rules={[{ required: true, message: '请选择模型类型!' }]}
+        >
+          <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>
+    </Modal>
+  );
+};
+
+export default UpdateForm;

+ 33 - 0
src/pages/ModelList/data.d.ts

@@ -0,0 +1,33 @@
+// 表单
+export interface TableListItem {
+  id?: number;
+  modelName: string;
+  modelFile: string;
+  vocabFile: string;
+  labelFile: string;
+  mustTag: string;
+  mustNotTag: string;
+  onlyWord: string;
+  modelType: number;
+}
+// 分页参数
+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;
+  id?: number;
+  pageSize?: number;
+  currentPage?: number;
+}

+ 3 - 0
src/pages/ModelList/index.less

@@ -0,0 +1,3 @@
+.upload{
+  display: inline;
+}

+ 280 - 0
src/pages/ModelList/index.tsx

@@ -0,0 +1,280 @@
+import { PlusOutlined } from '@ant-design/icons';
+import { Button, Divider, 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 { TableListItem } from './data.d';
+import service from './service';
+import QueryForm from '@/pages/ModelList/components/QueryForm';
+
+/**
+ * 添加节点
+ * @param fields
+ */
+const handleAdd = async (fields: TableListItem) => {
+  const hide = message.loading('正在添加');
+  try {
+    await service.addModel({ ...fields });
+    hide();
+    message.success('添加成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ * @param fields
+ */
+const handleUpdate = async (fields: TableListItem) => {
+  const hide = message.loading('正在配置');
+  try {
+    await service.updateModel(fields);
+    hide();
+    message.success('配置成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ *  删除节点
+ * @param fields
+ */
+const handleRemove = async (fields: TableListItem) => {
+  const hide = message.loading('正在删除');
+  try {
+    await service.removeModel(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 [queryVisible, setQueryVisible] = useState<boolean>(false);
+
+  const columns: ProColumns<TableListItem>[] = [
+    {
+      title: '模型类型',
+      dataIndex: 'modelType',
+      hideInSearch: true,
+      rules: [
+        {
+          required: true,
+          message: '模型类型为必填项',
+        },
+      ],
+      renderFormItem: () => {
+        return (
+          <Radio.Group>
+            <Radio value={0}>albert</Radio>
+            <Radio value={1}>Blstm</Radio>
+          </Radio.Group>
+        );
+      },
+      valueEnum: {
+        0: { text: 'albert' },
+        1: { text: 'Blstm' },
+      },
+    },
+    {
+      title: '模型名称',
+      dataIndex: 'modelName',
+      rules: [
+        {
+          required: true,
+          message: '模型名称为必填项',
+        },
+      ],
+    },
+    {
+      title: '模型文件',
+      dataIndex: 'modelFile',
+      hideInSearch: true,
+      rules: [
+        {
+          required: true,
+          message: '模型名称为必填项',
+        },
+      ],
+    },
+    {
+      title: '向量文件',
+      dataIndex: 'vocabFile',
+      hideInSearch: true,
+      rules: [
+        {
+          required: true,
+          message: '词向量文件为必填项',
+        },
+      ],
+    },
+    {
+      title: '标签文件',
+      dataIndex: 'labelFile',
+      hideInSearch: true,
+      rules: [
+        {
+          required: true,
+          message: '标签文件为必填项',
+        },
+      ],
+    },
+    {
+      title: '必包含',
+      dataIndex: 'mustTag',
+      hideInSearch: true,
+    },
+    {
+      title: '必不包含',
+      dataIndex: 'mustNotTag',
+      hideInSearch: true,
+    },
+    {
+      title: '纯文本',
+      dataIndex: 'onlyWord',
+      hideInSearch: true,
+      renderFormItem: () => {
+        return (
+          <Radio.Group>
+            <Radio value="true">是</Radio>
+            <Radio value="false">否</Radio>
+          </Radio.Group>
+        );
+      },
+      valueEnum: {
+        true: { text: '是' },
+        false: { text: '否' },
+      },
+    },
+
+    {
+      title: '操作',
+      dataIndex: 'option',
+      valueType: 'option',
+      render: (_, record) => (
+        <>
+          <a
+            onClick={() => {
+              setModalVisible(true);
+              setFormValues(record);
+              console.log(record);
+            }}
+          >
+            配置
+          </a>
+          <Divider type="vertical" />
+          <a
+            onClick={() => {
+              handleRemove(record);
+              console.log(record);
+            }}
+          >
+            删除
+          </a>
+          <Divider type="vertical" />
+          <a
+            onClick={() => {
+              setQueryVisible(true);
+              setFormValues(record);
+              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,
+        }}
+        /* eslint-disable */
+        toolBarRender={(action, { selectedRows }) => [
+          <Button
+            type="primary"
+            onClick={() => {
+              setModalVisible(true);
+              setFormValues({});
+            }}
+          >
+            <PlusOutlined /> 新建
+          </Button>,
+        ]}
+        request={(params) => service.queryModel(params)}
+        columns={columns}
+        rowSelection={{}}
+      />
+      <Modal
+        destroyOnClose
+        title={'id' in formValues ? '编辑' : '新建'}
+        visible={modelVisible}
+        onCancel={() => setModalVisible(false)}
+        footer={null}
+      >
+        <ProTable<TableListItem, TableListItem>
+          onSubmit={async (value) => {
+            let success = false;
+            if (value.id) {
+              success = await handleAdd(value);
+            } else {
+              success = await handleUpdate(value);
+            }
+
+            if (success) {
+              setModalVisible(false);
+              if (actionRef.current) {
+                actionRef.current.reload();
+              }
+            }
+          }}
+          rowKey="id"
+          type="form"
+          columns={columns}
+          rowSelection={{}}
+          form={{ initialValues: formValues }}
+        />
+      </Modal>
+
+      <QueryForm
+        onCancel={() => setQueryVisible(false)}
+        modalVisible={queryVisible}
+        values={formValues}
+      />
+    </PageHeaderWrapper>
+  );
+};
+
+export default TableList;

+ 9 - 0
src/pages/ModelList/service.ts

@@ -0,0 +1,9 @@
+import api from '@/utils/api';
+
+export default {
+  queryModel: api.get('/api/model'),
+  removeModel: api.post('/api/rule'),
+  addModel: api.post('/api/rule'),
+  updateModel: api.post('/api/rule'),
+  query: api.post('/api/rule'),
+};