Explorar o código

feat: 协议管理

sun-chaochao %!s(int64=3) %!d(string=hai) anos
pai
achega
c5db052fb7

+ 3 - 1
src/components/BaseCrud/save/index.tsx

@@ -30,6 +30,7 @@ import { CurdModel } from '@/components/BaseCrud/model';
 import type { ISchemaFieldProps } from '@formily/react/lib/types';
 import type { ModalProps } from 'antd/lib/modal/Modal';
 import FUpload from '@/components/Upload';
+import FileUpload from '@/pages/link/Protocol/FileUpload';
 import FMonacoEditor from '@/components/FMonacoEditor';
 import type { Form as Form1 } from '@formily/core';
 import FBraftEditor from '@/components/FBraftEditor';
@@ -83,6 +84,7 @@ const Save = <T extends Record<string, any>>(props: Props<T>) => {
       Editable,
       NumberPicker,
       FUpload,
+      FileUpload,
       FMonacoEditor,
       ArrayItems,
       Space,
@@ -133,7 +135,7 @@ const Save = <T extends Record<string, any>>(props: Props<T>) => {
     >
       <Spin spinning={modelConfig?.loading || false}>
         <PreviewText.Placeholder value="-">
-          <Form form={customForm || form} labelCol={4} wrapperCol={18}>
+          <Form form={customForm || form} layout={'vertical'}>
             <SchemaField schema={schema} {...schemaConfig} />
           </Form>
         </PreviewText.Placeholder>

+ 4 - 0
src/global.less

@@ -55,3 +55,7 @@ ol {
     min-height: 100vh;
   }
 }
+
+// .ant-formily-item-colon {
+//   display: none;
+// }

+ 59 - 0
src/pages/link/Protocol/FileUpload/index.tsx

@@ -0,0 +1,59 @@
+import SystemConst from '@/utils/const';
+import Token from '@/utils/token';
+import { useState } from 'react';
+import { connect } from '@formily/react';
+import { Button, Input, Upload } from 'antd';
+import type { UploadChangeParam } from 'antd/lib/upload/interface';
+
+interface Props {
+  value: string;
+  onChange: (value: string) => void;
+  accept?: string;
+}
+
+const FileUpload = connect((props: Props) => {
+  const [url, setUrl] = useState<string>(props?.value);
+  // const [loading, setLoading] = useState<boolean>(false);
+
+  const handleChange = (info: UploadChangeParam) => {
+    // setLoading(true)
+    if (info.file.status === 'uploading') {
+      // setLoading(false);
+    }
+    if (info.file.status === 'done') {
+      info.file.url = info.file.response?.result;
+      // setLoading(false);
+      setUrl(info.file.response?.result);
+      props.onChange(info.file.response?.result);
+    }
+  };
+
+  return (
+    <Upload
+      accept={props?.accept || '*'}
+      listType={'text'}
+      action={`/${SystemConst.API_BASE}/file/static`}
+      headers={{
+        'X-Access-Token': Token.get(),
+      }}
+      onChange={handleChange}
+      showUploadList={false}
+    >
+      <Input.Group compact>
+        <Input
+          style={{ width: 'calc(100% - 100px)' }}
+          value={url}
+          readOnly
+          onClick={(e) => {
+            e.preventDefault();
+            e.stopPropagation();
+          }}
+        />
+        <Button shape="round" style={{ width: '100px', textAlign: 'center' }} type="primary">
+          上传jar包
+        </Button>
+      </Input.Group>
+    </Upload>
+  );
+});
+export default FileUpload;

+ 89 - 139
src/pages/link/Protocol/index.tsx

@@ -1,14 +1,12 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import type { ProtocolItem } from '@/pages/link/Protocol/typings';
-import { useRef, useState } from 'react';
+import { useRef } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import { message, Popconfirm, Tag, Tooltip } from 'antd';
 import {
-  BugOutlined,
-  CloseOutlined,
   CloudSyncOutlined,
+  DeleteOutlined,
   EditOutlined,
-  MinusOutlined,
   PlayCircleOutlined,
 } from '@ant-design/icons';
 import BaseCrud from '@/components/BaseCrud';
@@ -16,14 +14,11 @@ import { useIntl } from '@@/plugin-locale/localeExports';
 import type { ISchema } from '@formily/json-schema';
 import { CurdModel } from '@/components/BaseCrud/model';
 import Service from '@/pages/link/Protocol/service';
-import Debug from '@/pages/link/Protocol/Debug';
 
 export const service = new Service('protocol');
 const Protocol = () => {
   const intl = useIntl();
   const actionRef = useRef<ActionType>();
-  const [visible, setVisible] = useState<boolean>(false);
-  const [current, setCurrent] = useState<Partial<ProtocolItem>>({});
 
   const modifyState = async (id: string, type: 'deploy' | 'un-deploy') => {
     const resp = await service.modifyState(id, type);
@@ -55,21 +50,18 @@ const Protocol = () => {
       }),
     },
     {
+      dataIndex: 'type',
+      title: '类型',
+    },
+    {
       dataIndex: 'state',
       title: '状态',
       renderText: (text) =>
         text === 1 ? <Tag color="#108ee9">正常</Tag> : <Tag color="#F50">禁用</Tag>,
     },
     {
-      dataIndex: 'type',
-      title: '类型',
-    },
-    {
-      dataIndex: 'provider',
-      title: intl.formatMessage({
-        id: 'pages.table.provider',
-        defaultMessage: '服务商',
-      }),
+      dataIndex: 'description',
+      title: '说明',
     },
     {
       title: intl.formatMessage({
@@ -114,31 +106,6 @@ const Protocol = () => {
             </Popconfirm>
           </a>
         ),
-        record.state === 1 && (
-          <a key="unDeploy">
-            <Popconfirm onConfirm={() => modifyState(record.id, 'un-deploy')} title="发布?">
-              <Tooltip title="取消发布">
-                <CloseOutlined />
-              </Tooltip>
-            </Popconfirm>
-          </a>
-        ),
-        <a
-          key="debug"
-          onClick={() => {
-            setVisible(true);
-            setCurrent(record);
-          }}
-        >
-          <Tooltip
-            title={intl.formatMessage({
-              id: 'pages.notice.option.debug',
-              defaultMessage: '调试',
-            })}
-          >
-            <BugOutlined />
-          </Tooltip>
-        </a>,
         record.state !== 1 && (
           <a key="delete">
             <Popconfirm
@@ -163,7 +130,7 @@ const Protocol = () => {
                   defaultMessage: '删除',
                 })}
               >
-                <MinusOutlined />
+                <DeleteOutlined />
               </Tooltip>
             </Popconfirm>
           </a>
@@ -179,141 +146,126 @@ const Protocol = () => {
         type: 'void',
         'x-component': 'FormGrid',
         'x-component-props': {
-          maxColumns: 2,
-          minColumns: 2,
+          maxColumns: 1,
+          minColumns: 1,
         },
         properties: {
           id: {
             title: 'ID',
             'x-component': 'Input',
             'x-decorator': 'FormItem',
-            required: true,
             'x-decorator-props': {
               gridSpan: 1,
             },
+            'x-validator': [
+              {
+                required: true,
+                message: '请输入ID',
+              },
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+            ],
           },
           name: {
             title: '名称',
-            required: true,
             'x-component': 'Input',
             'x-decorator': 'FormItem',
             'x-decorator-props': {
               gridSpan: 1,
             },
+            'x-validator': [
+              {
+                required: true,
+                message: '请输入名称',
+              },
+              {
+                max: 64,
+                message: '最多可输入64个字符',
+              },
+            ],
           },
           type: {
             title: '类型',
             'x-component': 'Select',
             'x-decorator': 'FormItem',
-            required: true,
+            'x-decorator-props': {
+              tooltip: <div>jar:上传协议jar包,文件格式支持.jar或.zip</div>,
+            },
+            'x-validator': [
+              {
+                required: true,
+                message: '请选择类型',
+              },
+            ],
             enum: [
               { label: 'jar', value: 'jar' },
               { label: 'local', value: 'local' },
-              { label: 'script', value: 'script' },
+              // { label: 'script', value: 'script' },
             ],
           },
           configuration: {
             type: 'object',
             properties: {
-              provider: {
-                title: '类名',
-                'x-component': 'Input',
-                'x-decorator': 'FormItem',
-                'x-visible': false,
-                'x-reactions': {
-                  dependencies: ['..type'],
-                  fulfill: {
-                    state: {
-                      visible: '{{["jar","local"].includes($deps[0])}}',
-                    },
-                  },
-                },
-              },
-              '{url:location}': {
+              location: {
                 title: '文件地址',
-                'x-component': 'FUpload',
                 'x-decorator': 'FormItem',
-                'x-component-props': {
-                  type: 'file',
-                },
                 'x-visible': false,
-                'x-reactions': {
-                  dependencies: ['..type'],
-                  when: '{{$deps[0]==="script"}}',
-                  fulfill: {
-                    state: {
-                      visible: false,
-                    },
-                  },
-                  otherwise: {
-                    state: {
-                      visible: '{{["jar","local"].includes($deps[0])}}',
-                      componentType: '{{$deps[0]==="jar"?"FUpload":"Input"}}',
-                      componentProps: '{{$deps[0]==="jar"?{type:"file"}:{}}}',
-                    },
-                  },
-                },
-              },
-              protocol: {
-                title: '协议标识',
-                'x-component': 'Input',
-                'x-decorator': 'FormItem',
-              },
-              transport: {
-                title: '链接协议',
-                'x-component': 'Select',
-                'x-decorator': 'FormItem',
-                enum: [
-                  { label: 'MQTT', value: 'MQTT' },
-                  { label: 'UDP', value: 'UDP' },
-                  { label: 'CoAP', value: 'CoAP' },
-                  { label: 'TCP', value: 'TCP' },
-                  { label: 'HTTP', value: 'HTTP' },
-                  { label: 'HTTPS', value: 'HTTPS' },
-                ],
-              },
-              script: {
-                title: '脚本',
-                'x-component': 'FMonacoEditor',
-                'x-decorator': 'FormItem',
                 'x-decorator-props': {
-                  gridSpan: 2,
-                  labelCol: 2,
-                  wrapperCol: 22,
+                  tooltip: (
+                    <div>
+                      local:填写本地协议编译目录绝对地址,如:d:/workspace/protocol/target/classes
+                    </div>
+                  ),
                 },
-                default: `//解码,收到设备上行消息时
-codec.decoder(function (context) {
-  var message = context.getMessage();
-  return {
-    messageType:"REPORT_PROPERTY"//消息类型
-  };
-});
-
-//编码读取设备属性消息
-codec.encoder("READ_PROPERTY",function(context){
-  var message = context.getMessage();
-  var properties = message.properties;
-})`,
-                'x-component-props': {
-                  height: 200,
-                  theme: 'dark',
-                  language: 'javascript',
-                  editorDidMount: (editor1: any) => {
-                    editor1.onDidContentSizeChange?.(() => {
-                      editor1.getAction('editor.action.formatDocument').run();
-                    });
+                'x-validator': [
+                  {
+                    required: true,
+                    message: '请输入文件地址',
                   },
-                },
-                'x-visible': false,
+                ],
                 'x-reactions': {
                   dependencies: ['..type'],
                   fulfill: {
                     state: {
-                      visible: '{{$deps[0]==="script"}}',
+                      visible: '{{["jar","local"].includes($deps[0])}}',
+                      componentType: '{{$deps[0]==="jar"?"FileUpload":"Input"}}',
+                      componentProps: '{{$deps[0]==="jar"?{type:"file", accept: ".jar, .zip"}:{}}}',
                     },
                   },
                 },
               },
+              // provider: {
+              //   title: '类名',
+              //   'x-component': 'Input',
+              //   'x-decorator': 'FormItem',
+              //   'x-visible': false,
+              //   'x-validator': [
+              //     {
+              //       required: true,
+              //       message: '请选择类名',
+              //     },
+              //   ],
+              //   'x-reactions': {
+              //     dependencies: ['..type'],
+              //     fulfill: {
+              //       state: {
+              //         visible: '{{["jar","local"].includes($deps[0])}}',
+              //       },
+              //     },
+              //   },
+              // },
+            },
+          },
+          description: {
+            title: '说明',
+            'x-component': 'Input.TextArea',
+            'x-decorator': 'FormItem',
+            'x-component-props': {
+              rows: 3,
+              showCount: true,
+              maxLength: 200,
             },
           },
         },
@@ -326,15 +278,13 @@ codec.encoder("READ_PROPERTY",function(context){
       <BaseCrud
         columns={columns}
         service={service}
-        title={intl.formatMessage({
-          id: 'pages.link.protocol',
-          defaultMessage: '协议管理',
-        })}
-        modelConfig={{ width: '50vw' }}
+        title={'插件管理'}
+        search={false}
+        modelConfig={{ width: '550px' }}
         schema={schema}
         actionRef={actionRef}
       />
-      {visible && <Debug data={current} close={() => setVisible(!visible)} />}
+      {/* {visible && <Debug data={current} close={() => setVisible(!visible)} />} */}
     </PageContainer>
   );
 };

+ 112 - 25
src/pages/link/Type/index.tsx

@@ -1,17 +1,27 @@
-import BaseService from '@/utils/BaseService';
 import { useRef, useState } from 'react';
 import type { ActionType, ProColumns } from '@jetlinks/pro-table';
 import ProTable from '@jetlinks/pro-table';
-import { Button, Tooltip } from 'antd';
-import { BugOutlined, EditOutlined, MinusOutlined, PlusOutlined } from '@ant-design/icons';
+import { Badge, Button, message, Popconfirm, Tooltip } from 'antd';
+import {
+  CheckCircleOutlined,
+  DeleteOutlined,
+  EditOutlined,
+  PlusOutlined,
+  StopOutlined,
+} from '@ant-design/icons';
 import { PageContainer } from '@ant-design/pro-layout';
 import type { NetworkItem } from '@/pages/link/Type/typings';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import SearchComponent from '@/components/SearchComponent';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 import { history } from 'umi';
+import Service from '@/pages/link/Type/service';
 
-export const service = new BaseService<NetworkItem>('network/config');
+export const service = new Service('network/config');
+
+const statusMap = new Map();
+statusMap.set('enabled', 'success');
+statusMap.set('disabled', 'error');
 
 const Network = () => {
   const intl = useIntl();
@@ -25,6 +35,7 @@ const Network = () => {
     },
     {
       dataIndex: 'name',
+      align: 'center',
       title: intl.formatMessage({
         id: 'pages.table.name',
         defaultMessage: '名称',
@@ -32,25 +43,52 @@ const Network = () => {
     },
     {
       dataIndex: 'type',
+      align: 'center',
       title: intl.formatMessage({
         id: 'pages.link.type',
         defaultMessage: '类型',
       }),
     },
     {
+      dataIndex: 'shareCluster',
+      title: '集群',
+      align: 'center',
+      renderText: (text) => (text ? '共享配置' : '独立配置'),
+    },
+    {
+      dataIndex: 'detail',
+      title: '详情',
+      align: 'center',
+      render: (text, record: any) => {
+        if (record.shareCluster) {
+          return (
+            <div>
+              公网: {record?.configuration?.host}:{record?.configuration?.port}
+            </div>
+          );
+        } else {
+          return (record?.cluster || []).slice(0, 3).map((i: any) => (
+            <div key={i?.serverId}>
+              公网: {i?.configuration?.host}:{i?.configuration?.port}
+            </div>
+          ));
+        }
+      },
+    },
+    {
+      dataIndex: 'description',
+      title: '说明',
+      align: 'center',
+    },
+    {
       dataIndex: 'state',
+      align: 'center',
       title: intl.formatMessage({
         id: 'pages.searchTable.titleStatus',
         defaultMessage: '状态',
       }),
-      render: (text, record) => record.state.value,
-    },
-    {
-      dataIndex: 'provider',
-      title: intl.formatMessage({
-        id: 'pages.table.provider',
-        defaultMessage: '服务商',
-      }),
+      renderText: (record) =>
+        record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
     },
     {
       title: intl.formatMessage({
@@ -61,7 +99,7 @@ const Network = () => {
       align: 'center',
       width: 200,
       render: (text, record) => [
-        <a onClick={() => console.log(record)}>
+        <a key="edit" onClick={() => console.log(record)}>
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.data.option.edit',
@@ -71,25 +109,70 @@ const Network = () => {
             <EditOutlined />
           </Tooltip>
         </a>,
-        <a>
-          <Tooltip
+        <a key="debug">
+          <Popconfirm
             title={intl.formatMessage({
-              id: 'pages.data.option.remove',
-              defaultMessage: '删除',
+              id: `pages.data.option.${
+                record.state.value === 'enabled' ? 'disabled' : 'enabled'
+              }.tips`,
+              defaultMessage: record.state.value === 'enabled' ? '禁用' : '启用',
             })}
+            onConfirm={async () => {
+              let resp = undefined;
+              if (record.state.value !== 'enabled') {
+                resp = await service._start(record.id);
+              } else {
+                resp = await service._shutdown(record.id);
+              }
+              if (resp.status === 200) {
+                message.success(
+                  intl.formatMessage({
+                    id: 'pages.data.option.success',
+                    defaultMessage: '操作成功!',
+                  }),
+                );
+                actionRef.current?.reload();
+              }
+            }}
           >
-            <MinusOutlined />
-          </Tooltip>
+            <Tooltip
+              title={intl.formatMessage({
+                id: `pages.data.option.${
+                  record.state.value === 'enabled' ? 'disabled' : 'enabled'
+                }`,
+                defaultMessage: record.state.value === 'enabled' ? '禁用' : '启用',
+              })}
+            >
+              {record.state.value === 'enabled' ? <StopOutlined /> : <CheckCircleOutlined />}
+            </Tooltip>
+          </Popconfirm>
         </a>,
-        <a>
-          <Tooltip
+        <a key="remove">
+          <Popconfirm
             title={intl.formatMessage({
-              id: 'pages.notice.option.debug',
-              defaultMessage: '调试',
+              id: 'pages.data.option.remove.tips',
+              defaultMessage: '确认删除?',
             })}
+            onConfirm={async () => {
+              await service.remove(record.id);
+              message.success(
+                intl.formatMessage({
+                  id: 'pages.data.option.success',
+                  defaultMessage: '操作成功!',
+                }),
+              );
+              actionRef.current?.reload();
+            }}
           >
-            <BugOutlined />
-          </Tooltip>
+            <Tooltip
+              title={intl.formatMessage({
+                id: 'pages.data.option.remove',
+                defaultMessage: '删除',
+              })}
+            >
+              <DeleteOutlined />
+            </Tooltip>
+          </Popconfirm>
         </a>,
       ],
     },
@@ -114,6 +197,10 @@ const Network = () => {
           actionRef.current?.reset?.();
           setParam(data);
         }}
+        onReset={() => {
+          actionRef.current?.reset?.();
+          setParam({});
+        }}
       />
       <ProTable<NetworkItem>
         actionRef={actionRef}

+ 18 - 0
src/pages/link/Type/service.ts

@@ -0,0 +1,18 @@
+import type { NetworkItem } from '@/pages/link/Type/typings';
+import { request } from 'umi';
+import BaseService from '@/utils/BaseService';
+import SystemConst from '@/utils/const';
+
+class Service extends BaseService<NetworkItem> {
+  public _start = (id: string) =>
+    request(`/${SystemConst.API_BASE}/network/config/${id}/_start`, {
+      method: 'POST',
+    });
+
+  public _shutdown = (id: string) =>
+    request(`/${SystemConst.API_BASE}/network/config/${id}/_shutdown`, {
+      method: 'POST',
+    });
+}
+
+export default Service;

+ 36 - 11
src/pages/system/Role/Edit/UserManage/BindUser.tsx

@@ -4,8 +4,8 @@ import { message, Modal } from 'antd';
 import { useRef, useState } from 'react';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { service } from '@/pages/system/User/index';
-import encodeQuery from '@/utils/encodeQuery';
 import Service from '@/pages/system/Role/service';
+import SearchComponent from '@/components/SearchComponent';
 interface Props {
   visible: boolean;
   data: any;
@@ -17,7 +17,9 @@ const BindUser = (props: Props) => {
   const intl = useIntl();
   const actionRef = useRef<any>();
   const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
-  const columns: ProColumns<RoleItem>[] = [
+  const [param, setParam] = useState({ terms: [] });
+
+  const columns: ProColumns<UserItem>[] = [
     {
       dataIndex: 'index',
       valueType: 'indexBorder',
@@ -75,8 +77,25 @@ const BindUser = (props: Props) => {
         props.cancel();
       }}
     >
+      <SearchComponent<UserItem>
+        field={columns}
+        target="user"
+        pattern={'simple'}
+        onSearch={(data) => {
+          console.log(data);
+          // 重置分页数据
+          actionRef.current?.reset?.();
+          setParam(data);
+        }}
+        onReset={() => {
+          // 重置分页及搜索参数
+          actionRef.current?.reset?.();
+          setParam({});
+        }}
+      />
       <ProTable
         actionRef={actionRef}
+        search={false}
         rowSelection={{
           selectedRowKeys: selectedRowKeys,
           onChange: (key) => {
@@ -86,16 +105,22 @@ const BindUser = (props: Props) => {
         pagination={{
           pageSize: 10,
         }}
-        request={async (param: any) => {
-          const response = await service.query(
-            encodeQuery({
-              pageSize: param.pageSize,
-              pageIndex: param.current,
-              terms: {
-                'id$in-dimension$role$not': props.data.id,
+        request={async (params: any) => {
+          const response = await service.query({
+            pageSize: params.pageSize,
+            pageIndex: params.current,
+            terms: [
+              ...(param?.terms || []),
+              {
+                terms: [
+                  {
+                    column: 'id$in-dimension$role$not',
+                    value: props.data.id,
+                  },
+                ],
               },
-            }),
-          );
+            ],
+          });
           return {
             result: { data: response.result.data },
             success: true,

+ 61 - 33
src/pages/system/Role/Edit/UserManage/index.tsx

@@ -1,14 +1,15 @@
-import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
+import { DisconnectOutlined, PlusOutlined } from '@ant-design/icons';
 import type { ProColumns, ActionType } from '@jetlinks/pro-table';
-import { Button, Card, message, Popconfirm, Space, Tooltip } from 'antd';
+import { Badge, Button, Card, message, Popconfirm, Space, Tooltip } from 'antd';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { useRef, useState } from 'react';
 import ProTable from '@jetlinks/pro-table';
 import BindUser from './BindUser';
 import { service } from '@/pages/system/User/index';
-import encodeQuery from '@/utils/encodeQuery';
 import { useParams } from 'umi';
 import Service from '@/pages/system/Role/service';
+import moment from 'moment';
+import SearchComponent from '@/components/SearchComponent';
 
 const UserManage = () => {
   const roleService = new Service('role');
@@ -17,6 +18,8 @@ const UserManage = () => {
   const actionRef = useRef<ActionType>();
   const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);
   const [bindUserVisible, setBindUserVisible] = useState<boolean>(false);
+  const [param, setParam] = useState({ terms: [] });
+
   const unBindUser = (id: string, ids: string[]) => {
     roleService.unbindUser(id, ids).subscribe((resp) => {
       if (resp.status === 200) {
@@ -25,41 +28,45 @@ const UserManage = () => {
       }
     });
   };
-  const columns: ProColumns<RoleItem>[] = [
+  const columns: ProColumns<UserItem>[] = [
     {
       dataIndex: 'index',
       valueType: 'indexBorder',
       width: 48,
     },
     {
-      title: intl.formatMessage({
-        id: 'pages.table.name',
-        defaultMessage: '名称',
-      }),
+      title: '姓名',
       dataIndex: 'name',
-      // copyable: true,
       ellipsis: true,
-      tip: intl.formatMessage({
-        id: 'pages.system.userName.tips',
-        defaultMessage: '用户名过长会自动收缩',
-      }),
-      formItemProps: {
-        rules: [
-          {
-            required: true,
-            message: '此项为必填项',
-          },
-        ],
-      },
+      align: 'center',
     },
     {
       title: intl.formatMessage({
         id: 'pages.system.username',
         defaultMessage: '用户名',
       }),
+      align: 'center',
       dataIndex: 'username',
-      filters: true,
-      onFilter: true,
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'createTime',
+      ellipsis: true,
+      width: '200px',
+      align: 'center',
+      render: (text: any) => moment(text).format('YYYY-MM-DD HH:mm:ss'),
+    },
+    {
+      title: '状态',
+      dataIndex: 'status',
+      ellipsis: true,
+      align: 'center',
+      render: (text, record) => (
+        <Badge
+          status={record?.status === 1 ? 'success' : 'error'}
+          text={record?.status === 1 ? '正常' : '禁用'}
+        />
+      ),
     },
     {
       title: intl.formatMessage({
@@ -78,7 +85,7 @@ const UserManage = () => {
             }}
           >
             <Tooltip title={'解绑'}>
-              <MinusOutlined />
+              <DisconnectOutlined />
             </Tooltip>
           </Popconfirm>
         </a>,
@@ -87,8 +94,23 @@ const UserManage = () => {
   ];
   return (
     <Card>
+      <SearchComponent<UserItem>
+        field={columns}
+        target="user"
+        onSearch={(data) => {
+          // 重置分页数据
+          actionRef.current?.reset?.();
+          setParam(data);
+        }}
+        onReset={() => {
+          // 重置分页及搜索参数
+          actionRef.current?.reset?.();
+          setParam({});
+        }}
+      />
       <ProTable
         actionRef={actionRef}
+        search={false}
         tableAlertOptionRender={() => (
           <Space size={16}>
             <a
@@ -122,16 +144,22 @@ const UserManage = () => {
         pagination={{
           pageSize: 10,
         }}
-        request={async (param: any) => {
-          const response = await service.query(
-            encodeQuery({
-              pageSize: param.pageSize,
-              pageIndex: param.current,
-              terms: {
-                'id$in-dimension$role': params.id,
+        request={async (data: any) => {
+          const response = await service.query({
+            pageSize: data.pageSize,
+            pageIndex: data.current,
+            terms: [
+              {
+                terms: [
+                  {
+                    column: 'id$in-dimension$role',
+                    value: params.id,
+                  },
+                ],
               },
-            }),
-          );
+              ...(param?.terms || []),
+            ],
+          });
           return {
             result: { data: response.result.data },
             success: true,