feat: 初始化项目并完成基础功能开发

- 完成项目初始化
- 实现用户注册、登录功能
- 完成用户管理与权限管理模块
- 开发后端 Tracker 服务器项目管理接口
- 实现日志管理接口
Change-Id: Ia4bde1c9ff600352a7ff0caca0cc50b02cad1af7
diff --git a/react-ui/src/pages/404.tsx b/react-ui/src/pages/404.tsx
new file mode 100644
index 0000000..0263687
--- /dev/null
+++ b/react-ui/src/pages/404.tsx
@@ -0,0 +1,18 @@
+import { history } from '@umijs/max';
+import { Button, Result } from 'antd';
+import React from 'react';
+
+const NoFoundPage: React.FC = () => (
+  <Result
+    status="404"
+    title="404"
+    subTitle="Sorry, the page you visited does not exist."
+    extra={
+      <Button type="primary" onClick={() => history.push('/')}>
+        Back Home
+      </Button>
+    }
+  />
+);
+
+export default NoFoundPage;
diff --git a/react-ui/src/pages/Monitor/Cache/List.tsx b/react-ui/src/pages/Monitor/Cache/List.tsx
new file mode 100644
index 0000000..af6147a
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Cache/List.tsx
@@ -0,0 +1,240 @@
+import React, { useEffect, useState, useRef } from 'react';
+import { clearCacheAll, clearCacheKey, clearCacheName, getCacheValue, listCacheKey, listCacheName } from '@/services/monitor/cachelist';
+import { Button, Card, Col, Form, FormInstance, Input, message, Row, Table } from 'antd';
+import styles from './index.less';
+import { FormattedMessage } from '@umijs/max';
+import { ReloadOutlined } from '@ant-design/icons';
+import { ProForm } from '@ant-design/pro-components';
+
+const { TextArea } = Input;
+
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2022/06/27
+ *
+ * */
+
+
+
+const CacheList: React.FC = () => {
+  const [cacheNames, setCacheNames] = useState<any>([]);
+  const [currentCacheName, setCurrentCacheName] = useState<any>([]);
+  const [cacheKeys, setCacheKeys] = useState<any>([]);
+  const [form] = Form.useForm();
+
+  const getCacheNames = () => {
+    listCacheName().then((res) => {
+      if (res.code === 200) {
+        setCacheNames(res.data);
+      }
+    });
+  }
+
+  useEffect(() => {
+    getCacheNames();
+  }, []);
+
+  const getCacheKeys = (cacheName: string) => {
+    listCacheKey(cacheName).then(res => {
+      if (res.code === 200) {
+        let index = 0;
+        const keysData = res.data.map((item: any) => {
+          return {
+            index: index++,
+            cacheKey: item
+          }
+        })
+        setCacheKeys(keysData);
+      }
+    });
+  };
+
+  const onClearAll = async () => {
+    clearCacheAll().then(res => {
+      if(res.code === 200) {
+        message.success("清理全部缓存成功");
+      }
+    });
+  };
+
+  const onClearAllFailed = (errorInfo: any) => {
+    message.error('Failed:', errorInfo);
+  };
+
+  const refreshCacheNames = () => {
+    getCacheNames();
+    message.success("刷新缓存列表成功");
+  };
+
+  const refreshCacheKeys = () => {
+    getCacheKeys(currentCacheName);
+    message.success("刷新键名列表成功");
+  };
+
+  const columns = [
+    {
+      title: '缓存名称',
+      dataIndex: 'cacheName',
+      key: 'cacheName',
+      render: (_: any, record: any) => {
+        return record.cacheName.replace(":", "");
+      }
+    },
+    {
+      title: '备注',
+      dataIndex: 'remark',
+      key: 'remark',
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '40px',
+      valueType: 'option',
+      render: (_: any, record: API.Monitor.CacheContent) => [
+        <Button
+          type="link"
+          size="small"
+          key="remove"
+          danger
+          onClick={() => {
+            clearCacheName(record.cacheName).then(res => {
+              if(res.code === 200) {
+                message.success("清理缓存名称[" + record.cacheName + "]成功");
+                getCacheKeys(currentCacheName);
+              }
+            });
+          }}
+        >
+          <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+        </Button>,
+      ]
+    }
+  ];
+
+  const cacheKeysColumns = [
+    {
+      title: '序号',
+      dataIndex: 'index',
+      key: 'index'
+    },
+    {
+      title: '缓存键名',
+      dataIndex: 'cacheKey',
+      key: 'cacheKey',
+      render: (_: any, record: any) => {
+        return record.cacheKey.replace(currentCacheName, "");
+      }
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '40px',
+      valueType: 'option',
+      render: (_: any, record: API.Monitor.CacheContent) => [
+        <Button
+          type="link"
+          size="small"
+          key="remove"
+          danger
+          onClick={() => {
+            console.log(record)
+            clearCacheKey(record.cacheKey).then(res => {
+              if(res.code === 200) {
+                message.success("清理缓存键名[" + record.cacheKey + "]成功");
+                getCacheKeys(currentCacheName);
+              }
+            });
+          }}
+        >
+          <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+        </Button>,
+      ]
+    }
+  ];
+
+  return (
+    <div>
+      <Row gutter={[24, 24]}>
+        <Col span={8}>
+          <Card title="缓存列表" extra={<Button icon={<ReloadOutlined />} onClick={()=>{ refreshCacheNames()}} type="link" />} className={styles.card}>
+            <Table
+              rowKey="cacheName"
+              dataSource={cacheNames}
+              columns={columns}
+              onRow={(record: API.Monitor.CacheContent) => {
+                return {
+                  onClick: () => {
+                    setCurrentCacheName(record.cacheName);
+                    getCacheKeys(record.cacheName);
+                  },
+                };
+              }}
+            />
+          </Card>
+        </Col>
+        <Col span={8}>
+          <Card title="键名列表" extra={<Button icon={<ReloadOutlined />} onClick={()=>{ refreshCacheKeys()}} type="link" />} className={styles.card}>
+            <Table
+              rowKey="index"
+              dataSource={cacheKeys}
+              columns={cacheKeysColumns}
+              onRow={(record: any) => {
+                return {
+                  onClick: () => {
+                    getCacheValue(currentCacheName, record.cacheKey).then(res => {
+                      if (res.code === 200) {
+                        form.resetFields();
+                        form.setFieldsValue({
+                          cacheName: res.data.cacheName,
+                          cacheKey: res.data.cacheKey,
+                          cacheValue: res.data.cacheValue,
+                          remark: res.data.remark,
+                        });
+                      }
+                    });
+                  },
+                };
+              }}
+            />
+          </Card>
+        </Col>
+        <Col span={8}>
+          <Card title="缓存内容" extra={<Button icon={<ReloadOutlined />} onClick={()=>{ onClearAll()}} type="link" >清理全部</Button>} className={styles.card}>
+            <ProForm
+              name="basic"
+              form={form}
+              labelCol={{ span: 8 }}
+              wrapperCol={{ span: 16 }}
+              onFinish={onClearAll}
+              onFinishFailed={onClearAllFailed}
+              autoComplete="off"
+            >
+              <Form.Item
+                label="缓存名称"
+                name="cacheName"
+              >
+                <Input />
+              </Form.Item>
+              <Form.Item
+                label="缓存键名"
+                name="cacheKey"
+              >
+                <Input />
+              </Form.Item>
+              <Form.Item
+                label="缓存内容"
+                name="cacheValue"
+              >
+                <TextArea autoSize={{ minRows: 2 }} />
+              </Form.Item>
+            </ProForm>
+          </Card>
+        </Col>
+      </Row>
+    </div>
+  );
+};
+
+export default CacheList;
diff --git a/react-ui/src/pages/Monitor/Cache/List/index.less b/react-ui/src/pages/Monitor/Cache/List/index.less
new file mode 100644
index 0000000..0c5aace
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Cache/List/index.less
@@ -0,0 +1,10 @@
+
+
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2021/09/16
+ *
+ * */
+
diff --git a/react-ui/src/pages/Monitor/Cache/index.less b/react-ui/src/pages/Monitor/Cache/index.less
new file mode 100644
index 0000000..7112c50
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Cache/index.less
@@ -0,0 +1,33 @@
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2021/09/16
+ * 
+ * */
+
+ 
+.card {
+  margin-bottom: 12px;
+}
+
+
+.miniChart {
+  position: relative;
+  width: 100%;
+  .chartContent {
+    position: absolute;
+    bottom: -28px;
+    width: 100%;
+    > div {
+      margin: 0 -5px;
+      overflow: hidden;
+    }
+  }
+  .chartLoading {
+    position: absolute;
+    top: 16px;
+    left: 50%;
+    margin-left: -7px;
+  }
+}
diff --git a/react-ui/src/pages/Monitor/Cache/index.tsx b/react-ui/src/pages/Monitor/Cache/index.tsx
new file mode 100644
index 0000000..4c247ee
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Cache/index.tsx
@@ -0,0 +1,201 @@
+import React, { useEffect, useState } from 'react';
+import { Card, Col, Row, Table } from 'antd';
+import { DataItem } from '@antv/g2plot/esm/interface/config';
+import { Gauge, Pie } from '@ant-design/plots';
+import styles from './index.less';
+import { getCacheInfo } from '@/services/monitor/cache';
+
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2021/09/16
+ *
+ * */
+
+
+const columns = [
+  {
+    title: 'col1',
+    dataIndex: 'col1',
+    key: 'col1',
+  },
+  {
+    title: 'col2',
+    dataIndex: 'col2',
+    key: 'col2',
+  },
+  {
+    title: 'col3',
+    dataIndex: 'col3',
+    key: 'col3',
+  },
+  {
+    title: 'col4',
+    dataIndex: 'col4',
+    key: 'col4',
+  },
+  {
+    title: 'col5',
+    dataIndex: 'col5',
+    key: 'col5',
+  },
+  {
+    title: 'col6',
+    dataIndex: 'col6',
+    key: 'col6',
+  },
+  {
+    title: 'col7',
+    dataIndex: 'col7',
+    key: 'col7',
+  },
+  {
+    title: 'col8',
+    dataIndex: 'col8',
+    key: 'col8',
+  },
+];
+
+const usageFormatter = (val: string): string => {
+  switch (val) {
+    case '10':
+      return '100%';
+    case '8':
+      return '80%';
+    case '6':
+      return '60%';
+    case '4':
+      return '40%';
+    case '2':
+      return '20%';
+    case '0':
+      return '0%';
+    default:
+      return '';
+  }
+};
+
+const CacheInfo: React.FC = () => {
+  const [baseInfoData, setBaseInfoData] = useState<any>([]);
+  const [memUsage, setMemUsage] = useState<Number>(0);
+  const [memUsageTitle, setMemUsageTitle] = useState<any>([]);
+  const [cmdInfoData, setCmdInfoData] = useState<DataItem[]>([]);
+  useEffect(() => {
+    getCacheInfo().then((res) => {
+      if (res.code === 200) {
+        const baseinfo = [];
+        baseinfo.push({
+          col1: 'Redis版本',
+          col2: res.data.info.redis_version,
+          col3: '运行模式',
+          col4: res.data.info.redis_mode === 'standalone' ? '单机' : '集群',
+          col5: '端口',
+          col6: res.data.info.tcp_port,
+          col7: '客户端数',
+          col8: res.data.info.connected_clients,
+        });
+        baseinfo.push({
+          col1: '运行时间(天)',
+          col2: res.data.info.uptime_in_days,
+          col3: '使用内存',
+          col4: res.data.info.used_memory_human,
+          col5: '使用CPU',
+          col6: `${res.data.info.used_cpu_user_children}%`,
+          col7: '内存配置',
+          col8: res.data.info.maxmemory_human,
+        });
+        baseinfo.push({
+          col1: 'AOF是否开启',
+          col2: res.data.info.aof_enabled === '0' ? '否' : '是',
+          col3: 'RDB是否成功',
+          col4: res.data.info.rdb_last_bgsave_status,
+          col5: 'Key数量',
+          col6: res.data.dbSize,
+          col7: '网络入口/出口',
+          col8: `${res.data.info.instantaneous_input_kbps}/${res.data.info.instantaneous_output_kbps}kps`,
+        });
+        setBaseInfoData(baseinfo);
+
+        const data: VisitDataType[] = res.data.commandStats.map((item) => {
+          return {
+            x: item.name,
+            y: Number(item.value),
+          };
+        });
+
+        setCmdInfoData(data);
+        setMemUsageTitle(res.data.info.used_memory_human);
+        setMemUsage(res.data.info.used_memory / res.data.info.total_system_memory);
+      }
+    });
+  }, []);
+
+  return (
+    <div>
+      <Row gutter={[24, 24]}>
+        <Col span={24}>
+          <Card title="基本信息" className={styles.card}>
+            <Table
+              rowKey="col1"
+              pagination={false}
+              showHeader={false}
+              dataSource={baseInfoData}
+              columns={columns}
+            />
+          </Card>
+        </Col>
+      </Row>
+      <Row gutter={[24, 24]}>
+        <Col span={12}>
+          <Card title="命令统计" className={styles.card}>
+            <Pie
+              height={320}
+              radius={0.8}
+              innerRadius={0.5}
+              angleField="y"
+              colorField="x"
+              data={cmdInfoData as any}
+              legend={false}
+              label={{
+                position: 'spider',
+                text: (item: { x: number; y: number }) => {
+                  return `${item.x}: ${item.y}`;
+                },
+              }}
+            />
+          </Card>
+        </Col>
+        <Col span={12}>
+          <Card title="内存信息" className={styles.card}>
+            <Gauge
+              title={memUsageTitle}
+              height={320}
+              percent={memUsage}
+              formatter={usageFormatter}
+              padding={-16}
+              style={{
+                textContent: () => { return memUsage.toFixed(2).toString() + '%'},
+              }}
+              data={
+                {
+                  target: memUsage,
+                  total: 100,
+                  name: 'score',
+                  thresholds: [20, 40, 60, 80, 100],
+                } as any
+              }
+              meta={{
+                color: {
+                  range: ['#C3F71F', '#B5E61D', '#FFC90E', '#FF7F27', '#FF2518'],
+                },
+              }}
+            />
+          </Card>
+        </Col>
+      </Row>
+    </div>
+  );
+};
+
+export default CacheInfo;
diff --git a/react-ui/src/pages/Monitor/Druid/index.tsx b/react-ui/src/pages/Monitor/Druid/index.tsx
new file mode 100644
index 0000000..55692d2
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Druid/index.tsx
@@ -0,0 +1,31 @@
+import React, { useEffect } from 'react'; 
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/07
+ * 
+ * */
+
+const DruidInfo: React.FC = () => {
+  useEffect(() => {
+    const frame = document.getElementById('bdIframe');
+    if (frame) {
+      const deviceWidth = document.documentElement.clientWidth;
+      const deviceHeight = document.documentElement.clientHeight;
+      frame.style.width = `${Number(deviceWidth) - 220}px`;
+      frame.style.height = `${Number(deviceHeight) - 120}px`;
+    }
+  });
+
+  return (
+    <iframe
+      style={{ width: '100%', border: '0px', height: '100%' }}
+      src={`/api/druid/login.html`}
+      id="bdIframe"
+     />
+    // </WrapContent>
+  );
+};
+
+export default DruidInfo;
diff --git a/react-ui/src/pages/Monitor/Job/detail.tsx b/react-ui/src/pages/Monitor/Job/detail.tsx
new file mode 100644
index 0000000..e27bfb7
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Job/detail.tsx
@@ -0,0 +1,131 @@
+import React, { useEffect } from 'react';
+import { Modal, Descriptions, Button } from 'antd';
+import { FormattedMessage, useIntl } from '@umijs/max';
+import { getValueEnumLabel } from '@/utils/options';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/07
+ * 
+ * */
+
+export type OperlogFormValueType = Record<string, unknown> & Partial<API.Monitor.Job>;
+
+export type OperlogFormProps = {
+  onCancel: (flag?: boolean, formVals?: OperlogFormValueType) => void;
+  open: boolean;
+  values: Partial<API.Monitor.Job>;
+  statusOptions: DictValueEnumObj;
+};
+
+const OperlogForm: React.FC<OperlogFormProps> = (props) => {
+  const { values, statusOptions } = props;
+
+  useEffect(() => {}, [props]);
+
+  const intl = useIntl();
+
+  const misfirePolicy: any = {
+    '0': '默认策略',
+    '1': '立即执行',
+    '2': '执行一次',
+    '3': '放弃执行',
+  };
+
+  const handleCancel = () => {
+    props.onCancel();
+  };
+
+  return (
+    <Modal
+      width={800}
+      title={intl.formatMessage({
+        id: 'monitor.job.detail',
+        defaultMessage: '操作日志详细信息',
+      })}
+      open={props.open}
+      destroyOnClose
+      onCancel={handleCancel}
+      footer={[
+        <Button key="back" onClick={handleCancel}>
+          关闭
+        </Button>,
+      ]}
+    >
+      <Descriptions column={24}>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.job_id" defaultMessage="任务编号" />}
+        >
+          {values.jobId}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.job_name" defaultMessage="任务名称" />}
+        >
+          {values.jobName}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.job_group" defaultMessage="任务组名" />}
+        >
+          {values.jobGroup}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.concurrent" defaultMessage="是否并发执行" />}
+        >
+          {values.concurrent === '1' ? '禁止' : '允许'}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={
+            <FormattedMessage id="monitor.job.misfire_policy" defaultMessage="计划执行错误策略" />
+          }
+        >
+          {misfirePolicy[values.misfirePolicy ? values.misfirePolicy : '0']}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.create_time" defaultMessage="创建时间" />}
+        >
+          {values.createTime?.toString()}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.status" defaultMessage="状态" />}
+        >
+          {getValueEnumLabel(statusOptions, values.status, '未知')}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={
+            <FormattedMessage id="monitor.job.next_valid_time" defaultMessage="下次执行时间" />
+          }
+        >
+          {values.nextValidTime}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={
+            <FormattedMessage id="monitor.job.cron_expression" defaultMessage="cron执行表达式" />
+          }
+        >
+          {values.cronExpression}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={
+            <FormattedMessage id="monitor.job.invoke_target" defaultMessage="调用目标字符串" />
+          }
+        >
+          {values.invokeTarget}
+        </Descriptions.Item>
+      </Descriptions>
+    </Modal>
+  );
+};
+
+export default OperlogForm;
diff --git a/react-ui/src/pages/Monitor/Job/edit.tsx b/react-ui/src/pages/Monitor/Job/edit.tsx
new file mode 100644
index 0000000..e4bfc74
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Job/edit.tsx
@@ -0,0 +1,232 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+  ProFormRadio,
+  ProFormSelect,
+  ProFormCaptcha,
+} from '@ant-design/pro-components';
+import { Form, Modal } from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictOptionType, DictValueEnumObj } from '@/components/DictTag';
+
+/**
+ * 定时任务调度 Edit Form
+ * 
+ * @author whiteshader
+ * @date 2023-02-07
+ */
+
+export type JobFormData = Record<string, unknown> & Partial<API.Monitor.Job>;
+
+export type JobFormProps = {
+  onCancel: (flag?: boolean, formVals?: JobFormData) => void;
+  onSubmit: (values: JobFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.Monitor.Job>;
+  jobGroupOptions: DictOptionType[];
+  statusOptions: DictValueEnumObj;
+};
+
+const JobForm: React.FC<JobFormProps> = (props) => {
+  const [form] = Form.useForm();
+  const { jobGroupOptions, statusOptions } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+      jobId: props.values.jobId,
+      jobName: props.values.jobName,
+      jobGroup: props.values.jobGroup,
+      invokeTarget: props.values.invokeTarget,
+      cronExpression: props.values.cronExpression,
+      misfirePolicy: props.values.misfirePolicy,
+      concurrent: props.values.concurrent,
+      status: props.values.status,
+      createBy: props.values.createBy,
+      createTime: props.values.createTime,
+      updateBy: props.values.updateBy,
+      updateTime: props.values.updateTime,
+      remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+    form.resetFields();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as JobFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'monitor.job.title',
+        defaultMessage: '编辑定时任务调度',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProForm
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal"
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="jobId"
+          label={intl.formatMessage({
+            id: 'monitor.job.job_id',
+            defaultMessage: '任务编号',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入任务编号"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入任务编号!" defaultMessage="请输入任务编号!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="jobName"
+          label={intl.formatMessage({
+            id: 'monitor.job.job_name',
+            defaultMessage: '任务名称',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入任务名称"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入任务名称!" defaultMessage="请输入任务名称!" />,
+            },
+          ]}
+        />
+        <ProFormSelect
+          name="jobGroup"
+          options={jobGroupOptions}
+          label={intl.formatMessage({
+            id: 'monitor.job.job_group',
+            defaultMessage: '任务组名',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入任务组名"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入任务组名!" defaultMessage="请输入任务组名!" />,
+            },
+          ]}
+        />
+        <ProFormTextArea
+          name="invokeTarget"
+          label={intl.formatMessage({
+            id: 'monitor.job.invoke_target',
+            defaultMessage: '调用目标字符串',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入调用目标字符串"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入调用目标字符串!" defaultMessage="请输入调用目标字符串!" />,
+            },
+          ]}
+        />
+        <ProFormCaptcha
+          name="cronExpression"
+          label={intl.formatMessage({
+            id: 'monitor.job.cron_expression',
+            defaultMessage: 'cron执行表达式',
+          })}
+          captchaTextRender={() => "生成表达式"}
+          onGetCaptcha={() => {
+            // form.setFieldValue('cronExpression', '0/20 * * * * ?');
+            return new Promise((resolve, reject) => {
+              reject();
+            });
+          }}
+        />
+        <ProFormRadio.Group
+          name="misfirePolicy"
+          label={intl.formatMessage({
+            id: 'monitor.job.misfire_policy',
+            defaultMessage: '计划执行错误策略',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入计划执行错误策略"
+          valueEnum={{
+            0: '立即执行',
+            1: '执行一次',
+            3: '放弃执行'
+          }}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入计划执行错误策略!" defaultMessage="请输入计划执行错误策略!" />,
+            },
+          ]}
+          fieldProps={{
+            optionType: "button",
+            buttonStyle: "solid"
+          }}
+        />
+        <ProFormRadio.Group
+          name="concurrent"
+          label={intl.formatMessage({
+            id: 'monitor.job.concurrent',
+            defaultMessage: '是否并发执行',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入是否并发执行"
+          valueEnum={{
+            0: '允许',
+            1: '禁止',
+          }}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入是否并发执行!" defaultMessage="请输入是否并发执行!" />,
+            },
+          ]}
+          fieldProps={{
+            optionType: "button",
+            buttonStyle: "solid"
+          }}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'monitor.job.status',
+            defaultMessage: '状态',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入状态"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入状态!" defaultMessage="请输入状态!" />,
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default JobForm;
diff --git a/react-ui/src/pages/Monitor/Job/index.tsx b/react-ui/src/pages/Monitor/Job/index.tsx
new file mode 100644
index 0000000..43030d9
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Job/index.tsx
@@ -0,0 +1,460 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess, history } from '@umijs/max';
+import { Dropdown, FormInstance, Space } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, DownOutlined, EditOutlined } from '@ant-design/icons';
+import { getJobList, removeJob, addJob, updateJob, exportJob, runJob } from '@/services/monitor/job';
+import { getDictSelectOption, getDictValueEnum } from '@/services/system/dict';
+import UpdateForm from './edit';
+import DetailForm from './detail';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 定时任务调度 List Page
+ *
+ * @author whiteshader
+ * @date 2023-02-07
+ */
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.Monitor.Job) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addJob({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.Monitor.Job) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateJob(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.Monitor.Job[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeJob(selectedRows.map((row) => row.jobId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.Monitor.Job) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.jobId];
+    const resp = await removeJob(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportJob();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const JobTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+  const [detailModalVisible, setDetailModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.Monitor.Job>();
+  const [selectedRows, setSelectedRows] = useState<API.Monitor.Job[]>([]);
+
+  const [jobGroupOptions, setJobGroupOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictSelectOption('sys_job_group').then((data) => {
+      setJobGroupOptions(data);
+    });
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.Monitor.Job>[] = [
+    {
+      title: <FormattedMessage id="monitor.job.job_id" defaultMessage="任务编号" />,
+      dataIndex: 'jobId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.job.job_name" defaultMessage="任务名称" />,
+      dataIndex: 'jobName',
+      valueType: 'text',
+      render: (dom, record) => {
+        return (
+          <a
+            onClick={() => {
+              setDetailModalVisible(true);
+              setCurrentRow(record);
+            }}
+          >
+            {dom}
+          </a>
+        );
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.job.job_group" defaultMessage="任务组名" />,
+      dataIndex: 'jobGroup',
+      valueType: 'text',
+      valueEnum: jobGroupOptions,
+      render: (_, record) => {
+        return (<DictTag options={jobGroupOptions} value={record.jobGroup} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.job.invoke_target" defaultMessage="调用目标字符串" />,
+      dataIndex: 'invokeTarget',
+      valueType: 'textarea',
+    },
+    {
+      title: <FormattedMessage id="monitor.job.cron_expression" defaultMessage="cron执行表达式" />,
+      dataIndex: 'cronExpression',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.job.status" defaultMessage="状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          icon = <EditOutlined />
+          hidden={!access.hasPerms('monitor:job:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          icon = <DeleteOutlined />
+          hidden={!access.hasPerms('monitor:job:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+        <Dropdown
+          key="more"
+          menu={{
+            items: [
+              {
+                label: '执行一次',
+                key: 'runOnce',
+              },
+              {
+                label: '详细',
+                key: 'detail',
+              },
+              {
+                label: '历史',
+                key: 'log',
+              },
+            ],
+            onClick: ({ key }) => {
+              if (key === 'runOnce') {
+                Modal.confirm({
+                  title: '警告',
+                  content: '确认要立即执行一次?',
+                  okText: '确认',
+                  cancelText: '取消',
+                  onOk: async () => {
+                    const success = await runJob(record.jobId, record.jobGroup);
+                    if (success) {
+                      message.success('执行成功');
+                    }
+                  },
+                });
+              }
+              else if (key === 'detail') {
+                setDetailModalVisible(true);
+                setCurrentRow(record);
+              }
+              else if( key === 'log') {
+                history.push(`/monitor/job-log/index/${record.jobId}`);
+              }
+            }
+          }}
+        >
+          <a className="ant-dropdown-link" onClick={(e) => e.preventDefault()}>
+            <Space>
+              <DownOutlined />
+              更多
+            </Space>
+          </a>
+        </Dropdown>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.Monitor.Job>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="jobId"
+          key="jobList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('monitor:job:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:job:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('monitor:job:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getJobList({ ...params } as API.Monitor.JobListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('monitor:job:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.jobId) {
+            success = await handleUpdate({ ...values } as API.Monitor.Job);
+          } else {
+            success = await handleAdd({ ...values } as API.Monitor.Job);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        jobGroupOptions={jobGroupOptions||{}}
+        statusOptions={statusOptions}
+      />
+      <DetailForm
+        onCancel={() => {
+          setDetailModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={detailModalVisible}
+        values={currentRow || {}}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default JobTableList;
diff --git a/react-ui/src/pages/Monitor/JobLog/detail.tsx b/react-ui/src/pages/Monitor/JobLog/detail.tsx
new file mode 100644
index 0000000..560889c
--- /dev/null
+++ b/react-ui/src/pages/Monitor/JobLog/detail.tsx
@@ -0,0 +1,105 @@
+import { getValueEnumLabel } from '@/utils/options';
+import { FormattedMessage, useIntl } from '@umijs/max';
+import { Descriptions, Modal } from 'antd';
+import React, { useEffect } from 'react';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2021/09/16
+ * 
+ * */
+
+export type JobLogFormValueType = Record<string, unknown> & Partial<API.Monitor.JobLog>;
+
+export type JobLogFormProps = {
+  onCancel: (flag?: boolean, formVals?: JobLogFormValueType) => void;
+  open: boolean;
+  values: Partial<API.Monitor.JobLog>;
+  statusOptions: DictValueEnumObj;
+  jobGroupOptions: DictValueEnumObj;
+};
+
+const JobLogDetailForm: React.FC<JobLogFormProps> = (props) => {
+
+  const { values, statusOptions, jobGroupOptions } = props;
+
+  useEffect(() => {
+  }, []);
+
+  const intl = useIntl();
+  const handleOk = () => {
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'monitor.job.log.title',
+        defaultMessage: '定时任务调度日志',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <Descriptions column={24}>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.job_id" defaultMessage="任务编号" />}
+        >
+          {values.jobLogId}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.create_time" defaultMessage="执行时间" />}
+        >
+          {values.createTime?.toString()}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.job_name" defaultMessage="任务名称" />}
+        >
+          {values.jobName}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.job_group" defaultMessage="任务组名" />}
+        >
+          {getValueEnumLabel(jobGroupOptions, values.jobGroup, '无')}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.job.invoke_target" defaultMessage="调用目标" />}
+        >
+          {values.invokeTarget}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.job.log.job_message" defaultMessage="日志信息" />}
+        >
+          {values.jobMessage}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.job.log.exception_info" defaultMessage="异常信息" />}
+        >
+          {values.exceptionInfo}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.job.status" defaultMessage="执行状态" />}
+        >
+          {getValueEnumLabel(statusOptions, values.status, '未知')}
+        </Descriptions.Item>
+      </Descriptions>
+    </Modal>
+  );
+};
+
+export default JobLogDetailForm;
diff --git a/react-ui/src/pages/Monitor/JobLog/index.tsx b/react-ui/src/pages/Monitor/JobLog/index.tsx
new file mode 100644
index 0000000..46e3313
--- /dev/null
+++ b/react-ui/src/pages/Monitor/JobLog/index.tsx
@@ -0,0 +1,343 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess, useParams, history } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getJobLogList, removeJobLog, exportJobLog } from '@/services/monitor/jobLog';
+import DetailForm from './detail';
+import { getDictValueEnum } from '@/services/system/dict';
+import { getJob } from '@/services/monitor/job';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 定时任务调度日志 List Page
+ *
+ * @author whiteshader
+ * @date 2023-02-07
+ */
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.Monitor.JobLog[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeJobLog(selectedRows.map((row) => row.jobLogId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.Monitor.JobLog) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.jobLogId];
+    const resp = await removeJobLog(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 清空日志数据
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportJobLog();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const JobLogTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalOpen, setModalOpen] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.Monitor.JobLog>();
+  const [selectedRows, setSelectedRows] = useState<API.Monitor.JobLog[]>([]);
+
+  const [jobGroupOptions, setJobGroupOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const [queryParams, setQueryParams] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  const params = useParams();
+  if (params.id === undefined) {
+    history.push('/monitor/job');
+  }
+  const jobId = params.id || 0;
+  useEffect(() => {
+    if (jobId !== undefined && jobId !== 0) {
+      getJob(Number(jobId)).then(response => {
+        setQueryParams({
+          jobName: response.data.jobName,
+          jobGroup: response.data.jobGroup
+        });
+      });
+    }
+    getDictValueEnum('sys_job_status').then((data) => {
+      setStatusOptions(data);
+    });
+    getDictValueEnum('sys_job_group').then((data) => {
+      setJobGroupOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.Monitor.JobLog>[] = [
+    {
+      title: <FormattedMessage id="monitor.job.log.job_log_id" defaultMessage="任务日志编号" />,
+      dataIndex: 'jobLogId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.job.log.job_name" defaultMessage="任务名称" />,
+      dataIndex: 'jobName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.job.log.job_group" defaultMessage="任务组名" />,
+      dataIndex: 'jobGroup',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.job.log.invoke_target" defaultMessage="调用目标字符串" />,
+      dataIndex: 'invokeTarget',
+      valueType: 'textarea',
+    },
+    {
+      title: <FormattedMessage id="monitor.job.log.job_message" defaultMessage="日志信息" />,
+      dataIndex: 'jobMessage',
+      valueType: 'textarea',
+    },
+    {
+      title: <FormattedMessage id="monitor.job.log.status" defaultMessage="执行状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.job.log.create_time" defaultMessage="异常信息" />,
+      dataIndex: 'createTime',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '120px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('monitor:job-log:edit')}
+          onClick={() => {
+            setModalOpen(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('monitor:job-log:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.Monitor.JobLog>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="jobLogId"
+          key="job-logList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('monitor:job-log:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalOpen(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:job-log:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('monitor:job-log:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          params={queryParams}
+          request={(params) =>
+            getJobLogList({ ...params } as API.Monitor.JobLogListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('monitor:job-log:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <DetailForm
+        onCancel={() => {
+          setModalOpen(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalOpen}
+        values={currentRow || {}}
+        statusOptions={statusOptions}
+        jobGroupOptions={jobGroupOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default JobLogTableList;
diff --git a/react-ui/src/pages/Monitor/Logininfor/edit.tsx b/react-ui/src/pages/Monitor/Logininfor/edit.tsx
new file mode 100644
index 0000000..5243191
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Logininfor/edit.tsx
@@ -0,0 +1,216 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormRadio,
+  ProFormTimePicker,
+  } from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type LogininforFormData = Record<string, unknown> & Partial<API.Monitor.Logininfor>;
+
+export type LogininforFormProps = {
+  onCancel: (flag?: boolean, formVals?: LogininforFormData) => void;
+  onSubmit: (values: LogininforFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.Monitor.Logininfor>;
+  statusOptions: DictValueEnumObj;
+};
+
+const LogininforForm: React.FC<LogininforFormProps> = (props) => {
+  const [form] = Form.useForm();
+  
+  const { statusOptions, } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+			infoId: props.values.infoId,
+			userName: props.values.userName,
+			ipaddr: props.values.ipaddr,
+			loginLocation: props.values.loginLocation,
+			browser: props.values.browser,
+			os: props.values.os,
+			status: props.values.status,
+			msg: props.values.msg,
+			loginTime: props.values.loginTime,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+    form.resetFields();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as LogininforFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.logininfor.title',
+        defaultMessage: '编辑系统访问记录',
+      })}
+      open={props.open}
+      destroyOnClose
+      forceRender
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+		  <ProForm 
+        form={form}
+        grid={true}
+        layout="horizontal" 
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="infoId"
+          label={intl.formatMessage({
+            id: 'system.logininfor.info_id',
+            defaultMessage: '访问编号',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入访问编号"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入访问编号!" defaultMessage="请输入访问编号!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="userName"
+          label={intl.formatMessage({
+            id: 'system.logininfor.user_name',
+            defaultMessage: '用户账号',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入用户账号"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入用户账号!" defaultMessage="请输入用户账号!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="ipaddr"
+          label={intl.formatMessage({
+            id: 'system.logininfor.ipaddr',
+            defaultMessage: '登录IP地址',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入登录IP地址"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入登录IP地址!" defaultMessage="请输入登录IP地址!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="loginLocation"
+          label={intl.formatMessage({
+            id: 'system.logininfor.login_location',
+            defaultMessage: '登录地点',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入登录地点"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入登录地点!" defaultMessage="请输入登录地点!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="browser"
+          label={intl.formatMessage({
+            id: 'system.logininfor.browser',
+            defaultMessage: '浏览器类型',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入浏览器类型"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入浏览器类型!" defaultMessage="请输入浏览器类型!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="os"
+          label={intl.formatMessage({
+            id: 'system.logininfor.os',
+            defaultMessage: '操作系统',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入操作系统"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入操作系统!" defaultMessage="请输入操作系统!" />,                  
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.logininfor.status',
+            defaultMessage: '登录状态',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入登录状态"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入登录状态!" defaultMessage="请输入登录状态!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="msg"
+          label={intl.formatMessage({
+            id: 'system.logininfor.msg',
+            defaultMessage: '提示消息',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入提示消息"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入提示消息!" defaultMessage="请输入提示消息!" />,                  
+            },
+          ]}
+        />
+        <ProFormTimePicker
+          name="loginTime"
+          label={intl.formatMessage({
+            id: 'system.logininfor.login_time',
+            defaultMessage: '访问时间',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入访问时间"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入访问时间!" defaultMessage="请输入访问时间!" />,                  
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default LogininforForm;
diff --git a/react-ui/src/pages/Monitor/Logininfor/index.tsx b/react-ui/src/pages/Monitor/Logininfor/index.tsx
new file mode 100644
index 0000000..5a39281
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Logininfor/index.tsx
@@ -0,0 +1,335 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, UnlockOutlined } from '@ant-design/icons';
+import { getLogininforList, removeLogininfor, exportLogininfor, unlockLogininfor, cleanLogininfor } from '@/services/monitor/logininfor';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.Monitor.Logininfor[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeLogininfor(selectedRows.map((row) => row.infoId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleClean = async () => {
+  const hide = message.loading('请稍候');
+  try {
+    const resp = await cleanLogininfor();
+    hide();
+    if (resp.code === 200) {
+      message.success('清空成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('请求失败,请重试');
+    return false;
+  }
+};
+
+const handleUnlock = async (userName: string) => {
+  const hide = message.loading('正在解锁');
+  try {
+    const resp = await unlockLogininfor(userName);
+    hide();
+    if (resp.code === 200) {
+      message.success('解锁成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('解锁失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ * @param id
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportLogininfor();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const LogininforTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const actionRef = useRef<ActionType>();
+  const [selectedRows, setSelectedRows] = useState<API.Monitor.Logininfor[]>([]);
+
+  const access = useAccess();
+
+  const statusOptions = {
+    0: {
+      label: '成功',
+      key: '0',
+      value: '0',
+      text: '成功',
+      status: 'success',
+      listClass: 'success'
+    },
+    1: {
+      label: '失败',
+      key: '1',
+      value: '1',
+      text: '失败',
+      status: 'error',
+      listClass: 'danger'
+    },
+  };
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+
+  }, []);
+
+  const columns: ProColumns<API.Monitor.Logininfor>[] = [
+    {
+      title: <FormattedMessage id="monitor.logininfor.info_id" defaultMessage="访问编号" />,
+      dataIndex: 'infoId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.user_name" defaultMessage="用户账号" />,
+      dataIndex: 'userName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.ipaddr" defaultMessage="登录IP地址" />,
+      dataIndex: 'ipaddr',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.login_location" defaultMessage="登录地点" />,
+      dataIndex: 'loginLocation',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.browser" defaultMessage="浏览器类型" />,
+      dataIndex: 'browser',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.os" defaultMessage="操作系统" />,
+      dataIndex: 'os',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.status" defaultMessage="登录状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.msg" defaultMessage="提示消息" />,
+      dataIndex: 'msg',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.login_time" defaultMessage="访问时间" />,
+      dataIndex: 'loginTime',
+      valueType: 'dateTime',
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.Monitor.Logininfor>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="infoId"
+          key="logininforList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:logininfor:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="clean"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:logininfor:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认清空所有数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleClean();
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.cleanAll" defaultMessage="清空" />
+            </Button>,
+            <Button
+              type="primary"
+              key="unlock"
+              hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:logininfor:unlock')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认解锁该用户的数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleUnlock(selectedRows[0].userName);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <UnlockOutlined />
+              <FormattedMessage id="monitor.logininfor.unlock" defaultMessage="解锁" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('monitor:logininfor:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getLogininforList({ ...params } as API.Monitor.LogininforListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('monitor:logininfor:remove')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+    </PageContainer>
+  );
+};
+
+export default LogininforTableList;
diff --git a/react-ui/src/pages/Monitor/Online/index.tsx b/react-ui/src/pages/Monitor/Online/index.tsx
new file mode 100644
index 0000000..2f81e3b
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Online/index.tsx
@@ -0,0 +1,162 @@
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import React, { useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import { getOnlineUserList, forceLogout } from '@/services/monitor/online';
+import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components';
+import { DeleteOutlined } from '@ant-design/icons';
+ 
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/07
+ * 
+ * */
+
+
+const handleForceLogout = async (selectedRow: API.Monitor.OnlineUserType) => {
+  const hide = message.loading('正在强制下线');
+  try {
+    await forceLogout(selectedRow.tokenId);
+    hide();
+    message.success('强制下线成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('强制下线失败,请重试');
+    return false;
+  }
+};
+
+const OnlineUserTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+  const actionRef = useRef<ActionType>();
+  const access = useAccess();
+  const intl = useIntl();
+
+  useEffect(() => {}, []);
+
+  const columns: ProColumns<API.Monitor.OnlineUserType>[] = [
+    {
+      title: <FormattedMessage id="monitor.online.user.token_id" defaultMessage="会话编号" />,
+      dataIndex: 'tokenId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.online.user.user_name" defaultMessage="用户账号" />,
+      dataIndex: 'userName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.online.user.dept_name" defaultMessage="部门名称" />,
+      dataIndex: 'deptName',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.online.user.ipaddr" defaultMessage="登录IP地址" />,
+      dataIndex: 'ipaddr',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.online.user.login_location" defaultMessage="登录地点" />,
+      dataIndex: 'loginLocation',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.online.user.browser" defaultMessage="浏览器类型" />,
+      dataIndex: 'browser',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.online.user.os" defaultMessage="操作系统" />,
+      dataIndex: 'os',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.online.user.login_time" defaultMessage="登录时间" />,
+      dataIndex: 'loginTime',
+      valueType: 'dateRange',
+      render: (_, record) => <span>{record.loginTime}</span>,
+      hideInSearch: true,
+      search: {
+        transform: (value) => {
+          return {
+            'params[beginTime]': value[0],
+            'params[endTime]': value[1],
+          };
+        },
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '60px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          icon=<DeleteOutlined />
+          hidden={!access.hasPerms('monitor:online:forceLogout')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '强踢',
+              content: '确定强制将该用户踢下线吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleForceLogout(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          强退
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.Monitor.OnlineUserType>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="tokenId"
+          key="logininforList"
+          search={{
+            labelWidth: 120,
+          }}
+          request={(params) =>
+            getOnlineUserList({ ...params } as API.Monitor.OnlineUserListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+        />
+      </div>
+  );
+};
+
+export default OnlineUserTableList;
diff --git a/react-ui/src/pages/Monitor/Operlog/detail.tsx b/react-ui/src/pages/Monitor/Operlog/detail.tsx
new file mode 100644
index 0000000..f247522
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Operlog/detail.tsx
@@ -0,0 +1,113 @@
+import React from 'react';
+import { Descriptions, Modal } from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+import { getValueEnumLabel } from '@/utils/options';
+
+export type OperlogFormData = Record<string, unknown> & Partial<API.Monitor.Operlog>;
+
+export type OperlogFormProps = {
+  onCancel: (flag?: boolean, formVals?: OperlogFormData) => void;
+  onSubmit: (values: OperlogFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.Monitor.Operlog>;
+  businessTypeOptions: DictValueEnumObj;
+  operatorTypeOptions: DictValueEnumObj;
+  statusOptions: DictValueEnumObj;
+};
+
+const OperlogDetailForm: React.FC<OperlogFormProps> = (props) => {
+
+  const { values, businessTypeOptions, operatorTypeOptions, statusOptions, } = props;
+
+  const intl = useIntl();
+  const handleOk = () => {};
+  const handleCancel = () => {
+    props.onCancel();
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'monitor.operlog.title',
+        defaultMessage: '编辑操作日志记录',
+      })}
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <Descriptions column={24}>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.module" defaultMessage="操作模块" />}
+        >
+          {`${values.title}/${getValueEnumLabel(businessTypeOptions, values.businessType)}`}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.request_method" defaultMessage="请求方式" />}
+        >
+          {values.requestMethod}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.oper_name" defaultMessage="操作人员" />}
+        >
+          {`${values.operName}/${values.operIp}`}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.operator_type" defaultMessage="操作类别" />}
+        >
+          {getValueEnumLabel(operatorTypeOptions, values.operatorType)}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.method" defaultMessage="方法名称" />}
+        >
+          {values.method}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.oper_url" defaultMessage="请求URL" />}
+        >
+          {values.operUrl}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.oper_param" defaultMessage="请求参数" />}
+        >
+          {values.operParam}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.json_result" defaultMessage="返回参数" />}
+        >
+          {values.jsonResult}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.error_msg" defaultMessage="错误消息" />}
+        >
+          {values.errorMsg}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.status" defaultMessage="操作状态" />}
+        >
+          {getValueEnumLabel(statusOptions, values.status)}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.oper_time" defaultMessage="操作时间" />}
+        >
+          {values.operTime?.toString()}
+        </Descriptions.Item>
+      </Descriptions>
+    </Modal>
+  );
+};
+
+export default OperlogDetailForm;
diff --git a/react-ui/src/pages/Monitor/Operlog/index.tsx b/react-ui/src/pages/Monitor/Operlog/index.tsx
new file mode 100644
index 0000000..9440bc8
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Operlog/index.tsx
@@ -0,0 +1,365 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getOperlogList, removeOperlog, addOperlog, updateOperlog, exportOperlog } from '@/services/monitor/operlog';
+import UpdateForm from './detail';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.Monitor.Operlog) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addOperlog({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.Monitor.Operlog) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateOperlog(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.Monitor.Operlog[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeOperlog(selectedRows.map((row) => row.operId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportOperlog();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const OperlogTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.Monitor.Operlog>();
+  const [selectedRows, setSelectedRows] = useState<API.Monitor.Operlog[]>([]);
+
+  const [businessTypeOptions, setBusinessTypeOptions] = useState<any>([]);
+  const [operatorTypeOptions, setOperatorTypeOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_oper_type', true).then((data) => {
+      setBusinessTypeOptions(data);
+    });
+    getDictValueEnum('sys_oper_type', true).then((data) => {
+      setOperatorTypeOptions(data);
+    });
+    getDictValueEnum('sys_common_status', true).then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.Monitor.Operlog>[] = [
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_id" defaultMessage="日志主键" />,
+      dataIndex: 'operId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.title" defaultMessage="操作模块" />,
+      dataIndex: 'title',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.business_type" defaultMessage="业务类型" />,
+      dataIndex: 'businessType',
+      valueType: 'select',
+      valueEnum: businessTypeOptions,
+      render: (_, record) => {
+        return (<DictTag enums={businessTypeOptions} value={record.businessType} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.request_method" defaultMessage="请求方式" />,
+      dataIndex: 'requestMethod',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.operator_type" defaultMessage="操作类别" />,
+      dataIndex: 'operatorType',
+      valueType: 'select',
+      valueEnum: operatorTypeOptions,
+      render: (_, record) => {
+        return (<DictTag enums={operatorTypeOptions} value={record.operatorType} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_name" defaultMessage="操作人员" />,
+      dataIndex: 'operName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_ip" defaultMessage="主机地址" />,
+      dataIndex: 'operIp',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_location" defaultMessage="操作地点" />,
+      dataIndex: 'operLocation',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.status" defaultMessage="操作状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag key="status" enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_time" defaultMessage="操作时间" />,
+      dataIndex: 'operTime',
+      valueType: 'dateTime',
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '120px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:operlog:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          详细
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.Monitor.Operlog>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="operId"
+          key="operlogList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:operlog:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:operlog:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('system:operlog:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getOperlogList({ ...params } as API.Monitor.OperlogListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:operlog:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.operId) {
+            success = await handleUpdate({ ...values } as API.Monitor.Operlog);
+          } else {
+            success = await handleAdd({ ...values } as API.Monitor.Operlog);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        businessTypeOptions={businessTypeOptions}
+        operatorTypeOptions={operatorTypeOptions}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default OperlogTableList;
diff --git a/react-ui/src/pages/Monitor/Server/index.tsx b/react-ui/src/pages/Monitor/Server/index.tsx
new file mode 100644
index 0000000..9ea3969
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Server/index.tsx
@@ -0,0 +1,267 @@
+import React, { useEffect, useState } from 'react';
+import { getServerInfo } from '@/services/monitor/server';
+import { Card, Col, Row, Table } from 'antd';
+import { FormattedMessage } from '@umijs/max';
+import styles from './style.less';
+
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/07
+ * 
+ * */
+
+
+const columns = [
+	{
+		title: '属性',
+		dataIndex: 'name',
+		key: 'name',
+	},
+	{
+		title: '值',
+		dataIndex: 'value',
+		key: 'value',
+	},
+];
+
+const memColumns = [
+	{
+		title: '属性',
+		dataIndex: 'name',
+		key: 'name',
+	},
+	{
+		title: '内存',
+		dataIndex: 'mem',
+		key: 'mem',
+	},
+	{
+		title: 'JVM',
+		dataIndex: 'jvm',
+		key: 'jvm',
+	},
+];
+
+const hostColumns = [
+	{
+		title: 'col1',
+		dataIndex: 'col1',
+		key: 'col1',
+	},
+	{
+		title: 'col2',
+		dataIndex: 'col2',
+		key: 'col2',
+	},
+	{
+		title: 'col3',
+		dataIndex: 'col3',
+		key: 'col3',
+	},
+	{
+		title: 'col4',
+		dataIndex: 'col4',
+		key: 'col4',
+	},
+];
+
+const diskColumns = [
+	{
+		title: <FormattedMessage id="monitor.server.disk.dirName" defaultMessage="盘符路径" />,
+		dataIndex: 'dirName',
+		key: 'dirName',
+	},
+	{
+		title: <FormattedMessage id="monitor.server.disk.sysTypeName" defaultMessage="文件系统" />,
+		dataIndex: 'sysTypeName',
+		key: 'sysTypeName',
+	},
+	{
+		title: <FormattedMessage id="monitor.server.disk.typeName" defaultMessage="盘符类型" />,
+		dataIndex: 'typeName',
+		key: 'typeName',
+	},
+	{
+		title: <FormattedMessage id="monitor.server.disk.total" defaultMessage="总大小" />,
+		dataIndex: 'total',
+		key: 'total',
+	},
+	{
+		title: <FormattedMessage id="monitor.server.disk.free" defaultMessage="可用大小" />,
+		dataIndex: 'free',
+		key: 'free',
+	},
+	{
+		title: <FormattedMessage id="monitor.server.disk.used" defaultMessage="已用大小" />,
+		dataIndex: 'used',
+		key: 'used',
+	},
+	{
+		title: <FormattedMessage id="monitor.server.disk.usage" defaultMessage="已用百分比" />,
+		dataIndex: 'usage',
+		key: 'usage',
+	},
+];
+
+const ServerInfo: React.FC = () => {
+	const [cpuData, setCpuData] = useState<API.Monitor.CpuRowType[]>([]);
+	const [memData, setMemData] = useState<API.Monitor.MemRowType[]>([]);
+	const [hostData, setHostData] = useState<any>([]);
+	const [jvmData, setJvmData] = useState<any>([]);
+	const [diskData, setDiskData] = useState<any>([]);
+
+	useEffect(() => {
+		getServerInfo().then((res: API.Monitor.ServerInfoResponseType) => {
+			if (res.code === 200) {
+				// const cpuinfo: CpuRowType[] = [];
+				// Object.keys(res.data.cpu).forEach((item: any) => {
+				//   cpuinfo.push({
+				//     name: item,
+				//     value: res.data.cpu[item],
+				//   });
+				// });
+				// setCpuData(cpuinfo);
+
+				const cpuinfo: API.Monitor.CpuRowType[] = [];
+				cpuinfo.push({ name: '核心数', value: res.data.cpu.cpuNum });
+				cpuinfo.push({ name: '用户使用率', value: `${res.data.cpu.used}%` });
+				cpuinfo.push({ name: '系统使用率', value: `${res.data.cpu.sys}%` });
+				cpuinfo.push({ name: '当前空闲率', value: `${res.data.cpu.free}%` });
+
+				setCpuData(cpuinfo);
+
+				const memDatas: API.Monitor.MemRowType[] = [];
+				memDatas.push({
+					name: '总内存',
+					mem: `${res.data.mem.total}G`,
+					jvm: `${res.data.jvm.total}M`,
+				});
+				memDatas.push({
+					name: '已用内存',
+					mem: `${res.data.mem.used}G`,
+					jvm: `${res.data.jvm.used}M`,
+				});
+				memDatas.push({
+					name: '剩余内存',
+					mem: `${res.data.mem.free}G`,
+					jvm: `${res.data.jvm.free}M`,
+				});
+				memDatas.push({
+					name: '使用率',
+					mem: `${res.data.mem.usage}%`,
+					jvm: `${res.data.jvm.usage}%`,
+				});
+				setMemData(memDatas);
+
+				const hostinfo = [];
+				hostinfo.push({
+					col1: '服务器名称',
+					col2: res.data.sys.computerName,
+					col3: '操作系统',
+					col4: res.data.sys.osName,
+				});
+				hostinfo.push({
+					col1: '服务器IP',
+					col2: res.data.sys.computerIp,
+					col3: '系统架构',
+					col4: res.data.sys.osArch,
+				});
+				setHostData(hostinfo);
+
+				const jvminfo = [];
+				jvminfo.push({
+					col1: 'Java名称',
+					col2: res.data.jvm.name,
+					col3: 'Java版本',
+					col4: res.data.jvm.version,
+				});
+				jvminfo.push({
+					col1: '启动时间',
+					col2: res.data.jvm.startTime,
+					col3: '运行时长',
+					col4: res.data.jvm.runTime,
+				});
+				jvminfo.push({
+					col1: '安装路径',
+					col2: res.data.jvm.home,
+					col3: '项目路径',
+					col4: res.data.sys.userDir,
+				});
+				setJvmData(jvminfo);
+
+				const diskinfo = res.data.sysFiles.map((item: API.Monitor.DiskInfoType) => {
+					return {
+						dirName: item.dirName,
+						sysTypeName: item.sysTypeName,
+						typeName: item.typeName,
+						total: item.total,
+						free: item.free,
+						used: item.used,
+						usage: `${item.usage}%`,
+					};
+				});
+				setDiskData(diskinfo);
+			}
+		});
+	}, []);
+
+	return (
+		<div>
+			<Row gutter={[24, 24]}>
+				<Col span={12}>
+					<Card title="CPU" className={styles.card}>
+						<Table rowKey="name" pagination={false} showHeader={false} dataSource={cpuData} columns={columns} />
+					</Card>
+				</Col>
+				<Col span={12}>
+					<Card title="内存" className={styles.card}>
+						<Table
+							rowKey="name"
+							pagination={false}
+							showHeader={false}
+							dataSource={memData}
+							columns={memColumns}
+						/>
+					</Card>
+				</Col>
+			</Row>
+			<Row gutter={[16, 16]}>
+				<Col span={24}>
+					<Card title="服务器信息" className={styles.card}>
+						<Table
+							rowKey="col1"
+							pagination={false}
+							showHeader={false}
+							dataSource={hostData}
+							columns={hostColumns}
+						/>
+					</Card>
+				</Col>
+			</Row>
+			<Row gutter={[16, 16]}>
+				<Col span={24}>
+					<Card title="Java虚拟机信息" className={styles.card}>
+						<Table
+							rowKey="col1"
+							pagination={false}
+							showHeader={false}
+							dataSource={jvmData}
+							columns={hostColumns}
+						/>
+					</Card>
+				</Col>
+			</Row>
+			<Row gutter={[16, 16]}>
+				<Col span={24}>
+					<Card title="磁盘状态" className={styles.card}>
+						<Table rowKey="dirName" pagination={false} dataSource={diskData} columns={diskColumns} />
+					</Card>
+				</Col>
+			</Row>
+		</div>
+	);
+};
+
+export default ServerInfo;
diff --git a/react-ui/src/pages/Monitor/Server/style.less b/react-ui/src/pages/Monitor/Server/style.less
new file mode 100644
index 0000000..d244a53
--- /dev/null
+++ b/react-ui/src/pages/Monitor/Server/style.less
@@ -0,0 +1,11 @@
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2021/09/16
+ * 
+ * */
+
+.card {
+  margin-bottom: 12px;
+}
diff --git a/react-ui/src/pages/System/Config/edit.tsx b/react-ui/src/pages/System/Config/edit.tsx
new file mode 100644
index 0000000..6cb3942
--- /dev/null
+++ b/react-ui/src/pages/System/Config/edit.tsx
@@ -0,0 +1,172 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormTextArea,
+  ProFormRadio,
+  } from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type ConfigFormData = Record<string, unknown> & Partial<API.System.Config>;
+
+export type ConfigFormProps = {
+  onCancel: (flag?: boolean, formVals?: ConfigFormData) => void;
+  onSubmit: (values: ConfigFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.Config>;
+  configTypeOptions: DictValueEnumObj;
+};
+
+const ConfigForm: React.FC<ConfigFormProps> = (props) => {
+  const [form] = Form.useForm();
+  
+  const { configTypeOptions } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+			configId: props.values.configId,
+			configName: props.values.configName,
+			configKey: props.values.configKey,
+			configValue: props.values.configValue,
+			configType: props.values.configType,
+			createBy: props.values.createBy,
+			createTime: props.values.createTime,
+			updateBy: props.values.updateBy,
+			updateTime: props.values.updateTime,
+			remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as ConfigFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.config.title',
+        defaultMessage: '编辑参数配置',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+		  <ProForm 
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal" 
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="configId"
+          label={intl.formatMessage({
+            id: 'system.config.config_id',
+            defaultMessage: '参数主键',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入参数主键"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入参数主键!" defaultMessage="请输入参数主键!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="configName"
+          label={intl.formatMessage({
+            id: 'system.config.config_name',
+            defaultMessage: '参数名称',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入参数名称"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入参数名称!" defaultMessage="请输入参数名称!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="configKey"
+          label={intl.formatMessage({
+            id: 'system.config.config_key',
+            defaultMessage: '参数键名',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入参数键名"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入参数键名!" defaultMessage="请输入参数键名!" />,                  
+            },
+          ]}
+        />
+        <ProFormTextArea
+          name="configValue"
+          label={intl.formatMessage({
+            id: 'system.config.config_value',
+            defaultMessage: '参数键值',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入参数键值"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入参数键值!" defaultMessage="请输入参数键值!" />,                  
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={configTypeOptions}
+          name="configType"
+          label={intl.formatMessage({
+            id: 'system.config.config_type',
+            defaultMessage: '系统内置',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入系统内置"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入系统内置!" defaultMessage="请输入系统内置!" />,                  
+            },
+          ]}
+        />
+        <ProFormTextArea
+          name="remark"
+          label={intl.formatMessage({
+            id: 'system.config.remark',
+            defaultMessage: '备注',
+          })}
+          colProps={{ md: 24 }}
+          placeholder="请输入备注"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入备注!" defaultMessage="请输入备注!" />,                  
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default ConfigForm;
diff --git a/react-ui/src/pages/System/Config/index.tsx b/react-ui/src/pages/System/Config/index.tsx
new file mode 100644
index 0000000..1428891
--- /dev/null
+++ b/react-ui/src/pages/System/Config/index.tsx
@@ -0,0 +1,397 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, ReloadOutlined, DownloadOutlined } from '@ant-design/icons';
+import { getConfigList, removeConfig, addConfig, updateConfig, exportConfig, refreshConfigCache } from '@/services/system/config';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.Config) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addConfig({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.Config) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateConfig(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.Config[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeConfig(selectedRows.map((row) => row.configId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.Config) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.configId];
+    const resp = await removeConfig(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportConfig();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+const handleRefreshCache = async () => {
+  const hide = message.loading('正在刷新');
+  try {
+    await refreshConfigCache();
+    hide();
+    message.success('刷新成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('刷新失败,请重试');
+    return false;
+  }
+};
+
+const ConfigTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.Config>();
+  const [selectedRows, setSelectedRows] = useState<API.System.Config[]>([]);
+
+  const [configTypeOptions, setConfigTypeOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_yes_no').then((data) => {
+      setConfigTypeOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.System.Config>[] = [
+    {
+      title: <FormattedMessage id="system.config.config_id" defaultMessage="参数主键" />,
+      dataIndex: 'configId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.config.config_name" defaultMessage="参数名称" />,
+      dataIndex: 'configName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.config.config_key" defaultMessage="参数键名" />,
+      dataIndex: 'configKey',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.config.config_value" defaultMessage="参数键值" />,
+      dataIndex: 'configValue',
+      valueType: 'textarea',
+    },
+    {
+      title: <FormattedMessage id="system.config.config_type" defaultMessage="系统内置" />,
+      dataIndex: 'configType',
+      valueType: 'select',
+      valueEnum: configTypeOptions,
+      render: (_, record) => {
+        return (<DictTag enums={configTypeOptions} value={record.configType} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="system.config.remark" defaultMessage="备注" />,
+      dataIndex: 'remark',
+      valueType: 'textarea',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '120px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:config:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('system:config:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.Config>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="configId"
+          key="configList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:config:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:config:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('system:config:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <DownloadOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+            <Button
+              type="primary"
+              key="refresh"
+              danger
+              hidden={!access.hasPerms('system:config:remove')}
+              onClick={async () => {
+                handleRefreshCache();
+              }}
+            >
+              <ReloadOutlined />
+              <FormattedMessage id="system.config.refreshCache" defaultMessage="刷新缓存" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getConfigList({ ...params } as API.System.ConfigListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:config:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.configId) {
+            success = await handleUpdate({ ...values } as API.System.Config);
+          } else {
+            success = await handleAdd({ ...values } as API.System.Config);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        configTypeOptions={configTypeOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default ConfigTableList;
diff --git a/react-ui/src/pages/System/Dept/edit.tsx b/react-ui/src/pages/System/Dept/edit.tsx
new file mode 100644
index 0000000..cb64813
--- /dev/null
+++ b/react-ui/src/pages/System/Dept/edit.tsx
@@ -0,0 +1,212 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormRadio,
+  ProFormTreeSelect,
+} from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DataNode } from 'antd/es/tree';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type DeptFormData = Record<string, unknown> & Partial<API.System.Dept>;
+
+export type DeptFormProps = {
+  onCancel: (flag?: boolean, formVals?: DeptFormData) => void;
+  onSubmit: (values: DeptFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.Dept>;
+  deptTree: DataNode[];
+  statusOptions: DictValueEnumObj;
+};
+
+const DeptForm: React.FC<DeptFormProps> = (props) => {
+  const [form] = Form.useForm();
+
+  const { statusOptions, deptTree } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+      deptId: props.values.deptId,
+      parentId: props.values.parentId,
+      ancestors: props.values.ancestors,
+      deptName: props.values.deptName,
+      orderNum: props.values.orderNum,
+      leader: props.values.leader,
+      phone: props.values.phone,
+      email: props.values.email,
+      status: props.values.status,
+      delFlag: props.values.delFlag,
+      createBy: props.values.createBy,
+      createTime: props.values.createTime,
+      updateBy: props.values.updateBy,
+      updateTime: props.values.updateTime,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as DeptFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.dept.title',
+        defaultMessage: '编辑部门',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProForm
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal"
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="deptId"
+          label={intl.formatMessage({
+            id: 'system.dept.dept_id',
+            defaultMessage: '部门id',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入部门id"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入部门id!" defaultMessage="请输入部门id!" />,
+            },
+          ]}
+        />
+        <ProFormTreeSelect
+          name="parentId"
+          label={intl.formatMessage({
+            id: 'system.dept.parent_dept',
+            defaultMessage: '上级部门:',
+          })}
+          request={async () => {
+            return deptTree;
+          }}
+          placeholder="请选择上级部门"
+          rules={[
+            {
+              required: true,
+              message: (
+                <FormattedMessage id="请输入用户昵称!" defaultMessage="请选择上级部门!" />
+              ),
+            },
+          ]}
+        />
+        <ProFormText
+          name="deptName"
+          label={intl.formatMessage({
+            id: 'system.dept.dept_name',
+            defaultMessage: '部门名称',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入部门名称"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入部门名称!" defaultMessage="请输入部门名称!" />,
+            },
+          ]}
+        />
+        <ProFormDigit
+          name="orderNum"
+          label={intl.formatMessage({
+            id: 'system.dept.order_num',
+            defaultMessage: '显示顺序',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入显示顺序"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入显示顺序!" defaultMessage="请输入显示顺序!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="leader"
+          label={intl.formatMessage({
+            id: 'system.dept.leader',
+            defaultMessage: '负责人',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入负责人"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入负责人!" defaultMessage="请输入负责人!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="phone"
+          label={intl.formatMessage({
+            id: 'system.dept.phone',
+            defaultMessage: '联系电话',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入联系电话"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入联系电话!" defaultMessage="请输入联系电话!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="email"
+          label={intl.formatMessage({
+            id: 'system.dept.email',
+            defaultMessage: '邮箱',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入邮箱"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入邮箱!" defaultMessage="请输入邮箱!" />,
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.dept.status',
+            defaultMessage: '部门状态',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入部门状态"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入部门状态!" defaultMessage="请输入部门状态!" />,
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default DeptForm;
diff --git a/react-ui/src/pages/System/Dept/index.tsx b/react-ui/src/pages/System/Dept/index.tsx
new file mode 100644
index 0000000..3a1c5de
--- /dev/null
+++ b/react-ui/src/pages/System/Dept/index.tsx
@@ -0,0 +1,346 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getDeptList, removeDept, addDept, updateDept, getDeptListExcludeChild } from '@/services/system/dept';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import { buildTreeData } from '@/utils/tree';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.Dept) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addDept({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.Dept) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateDept(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.Dept[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeDept(selectedRows.map((row) => row.deptId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.Dept) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.deptId];
+    const resp = await removeDept(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+
+const DeptTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.Dept>();
+  const [selectedRows, setSelectedRows] = useState<API.System.Dept[]>([]);
+
+  const [deptTree, setDeptTree] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.System.Dept>[] = [
+    {
+      title: <FormattedMessage id="system.dept.dept_name" defaultMessage="部门名称" />,
+      dataIndex: 'deptName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.dept.order_num" defaultMessage="显示顺序" />,
+      dataIndex: 'orderNum',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.dept.status" defaultMessage="部门状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:dept:edit')}
+          onClick={() => {
+            getDeptListExcludeChild(record.deptId).then((res) => {
+              if (res.code === 200) {
+                let depts = buildTreeData(res.data, 'deptId', 'deptName', '', '', '');
+                if(depts.length === 0) {
+                  depts = [{ id: 0, title: '无上级', children: undefined, key: 0, value: 0 }];
+                }
+                setDeptTree(depts);
+                setModalVisible(true);
+                setCurrentRow(record);
+              } else {
+                message.warning(res.msg);
+              }
+            });
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('system:dept:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.Dept>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="deptId"
+          key="deptList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:dept:add')}
+              onClick={async () => {
+                getDeptList().then((res) => {
+                  if (res.code === 200) {
+                    setDeptTree(buildTreeData(res.data, 'deptId', 'deptName', '', '', ''));
+                    setCurrentRow(undefined);
+                    setModalVisible(true);
+                  } else {
+                    message.warning(res.msg);
+                  }
+                });
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:dept:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() {},
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getDeptList({ ...params } as API.System.DeptListParams).then((res) => {
+              const result = {
+                data: buildTreeData(res.data, 'deptId', '', '', '', ''),
+                total: res.data.length,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:dept:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.deptId) {
+            success = await handleUpdate({ ...values } as API.System.Dept);
+          } else {
+            success = await handleAdd({ ...values } as API.System.Dept);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        deptTree={deptTree}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default DeptTableList;
diff --git a/react-ui/src/pages/System/Dict/edit.tsx b/react-ui/src/pages/System/Dict/edit.tsx
new file mode 100644
index 0000000..882c1cb
--- /dev/null
+++ b/react-ui/src/pages/System/Dict/edit.tsx
@@ -0,0 +1,152 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormRadio,
+  ProFormTextArea,
+  } from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type DictTypeFormData = Record<string, unknown> & Partial<API.System.DictType>;
+
+export type DictTypeFormProps = {
+  onCancel: (flag?: boolean, formVals?: DictTypeFormData) => void;
+  onSubmit: (values: DictTypeFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.DictType>;
+  statusOptions: DictValueEnumObj;
+};
+
+const DictTypeForm: React.FC<DictTypeFormProps> = (props) => {
+  const [form] = Form.useForm();
+
+  const { statusOptions } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+			dictId: props.values.dictId,
+			dictName: props.values.dictName,
+			dictType: props.values.dictType,
+			status: props.values.status,
+			createBy: props.values.createBy,
+			createTime: props.values.createTime,
+			updateBy: props.values.updateBy,
+			updateTime: props.values.updateTime,
+			remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as DictTypeFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.dict.title',
+        defaultMessage: '编辑字典类型',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+		  <ProForm
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal"
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="dictId"
+          label={intl.formatMessage({
+            id: 'system.dict.dict_id',
+            defaultMessage: '字典主键',
+          })}
+          placeholder="请输入字典主键"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典主键!" defaultMessage="请输入字典主键!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="dictName"
+          label={intl.formatMessage({
+            id: 'system.dict.dict_name',
+            defaultMessage: '字典名称',
+          })}
+          placeholder="请输入字典名称"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典名称!" defaultMessage="请输入字典名称!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="dictType"
+          label={intl.formatMessage({
+            id: 'system.dict.dict_type',
+            defaultMessage: '字典类型',
+          })}
+          placeholder="请输入字典类型"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典类型!" defaultMessage="请输入字典类型!" />,
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.dict.status',
+            defaultMessage: '状态',
+          })}
+          initialValue={'0'}
+          placeholder="请输入状态"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入状态!" defaultMessage="请输入状态!" />,
+            },
+          ]}
+        />
+        <ProFormTextArea
+          name="remark"
+          label={intl.formatMessage({
+            id: 'system.dict.remark',
+            defaultMessage: '备注',
+          })}
+          placeholder="请输入备注"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入备注!" defaultMessage="请输入备注!" />,
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default DictTypeForm;
diff --git a/react-ui/src/pages/System/Dict/index.tsx b/react-ui/src/pages/System/Dict/index.tsx
new file mode 100644
index 0000000..ccdbc2a
--- /dev/null
+++ b/react-ui/src/pages/System/Dict/index.tsx
@@ -0,0 +1,394 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess, history } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getDictTypeList, removeDictType, addDictType, updateDictType, exportDictType } from '@/services/system/dict';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.DictType) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addDictType({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.DictType) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateDictType(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.DictType[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeDictType(selectedRows.map((row) => row.dictId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.DictType) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.dictId];
+    const resp = await removeDictType(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportDictType();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const DictTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.DictType>();
+  const [selectedRows, setSelectedRows] = useState<API.System.DictType[]>([]);
+
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.System.DictType>[] = [
+    {
+      title: <FormattedMessage id="system.dict.dict_id" defaultMessage="字典编号" />,
+      dataIndex: 'dictId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.dict.dict_name" defaultMessage="字典名称" />,
+      dataIndex: 'dictName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.dict.dict_type" defaultMessage="字典类型" />,
+      dataIndex: 'dictType',
+      valueType: 'text',
+      render: (dom, record) => {
+        return (
+          <a
+            onClick={() => {
+              history.push(`/system/dict-data/index/${record.dictId}`);
+            }}
+          >
+            {dom}
+          </a>
+        );
+      },
+    },
+    {
+      title: <FormattedMessage id="system.dict.status" defaultMessage="状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="system.dict.remark" defaultMessage="备注" />,
+      dataIndex: 'remark',
+      valueType: 'textarea',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.role.create_time" defaultMessage="创建时间" />,
+      dataIndex: 'createTime',
+      valueType: 'dateRange',
+      render: (_, record) => {
+        return (<span>{record.createTime.toString()} </span>);
+      },
+      search: {
+        transform: (value) => {
+          return {
+            'params[beginTime]': value[0],
+            'params[endTime]': value[1],
+          };
+        },
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:dictType:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('system:dictType:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.DictType>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="dictId"
+          key="dictTypeList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:dictType:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:dictType:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() {},
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('system:dictType:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getDictTypeList({ ...params } as API.System.DictTypeListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:dictType:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.dictId) {
+            success = await handleUpdate({ ...values } as API.System.DictType);
+          } else {
+            success = await handleAdd({ ...values } as API.System.DictType);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default DictTableList;
diff --git a/react-ui/src/pages/System/DictData/edit.tsx b/react-ui/src/pages/System/DictData/edit.tsx
new file mode 100644
index 0000000..188e01b
--- /dev/null
+++ b/react-ui/src/pages/System/DictData/edit.tsx
@@ -0,0 +1,252 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormSelect,
+  ProFormRadio,
+  ProFormTextArea,
+} from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type DataFormData = Record<string, unknown> & Partial<API.System.DictData>;
+
+export type DataFormProps = {
+  onCancel: (flag?: boolean, formVals?: DataFormData) => void;
+  onSubmit: (values: DataFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.DictData>;
+  statusOptions: DictValueEnumObj;
+};
+
+const DictDataForm: React.FC<DataFormProps> = (props) => {
+  const [form] = Form.useForm();
+
+  const { statusOptions } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+      dictCode: props.values.dictCode,
+      dictSort: props.values.dictSort,
+      dictLabel: props.values.dictLabel,
+      dictValue: props.values.dictValue,
+      dictType: props.values.dictType,
+      cssClass: props.values.cssClass,
+      listClass: props.values.listClass,
+      isDefault: props.values.isDefault,
+      status: props.values.status,
+      createBy: props.values.createBy,
+      createTime: props.values.createTime,
+      updateBy: props.values.updateBy,
+      updateTime: props.values.updateTime,
+      remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as DataFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.dict.data.title',
+        defaultMessage: '编辑字典数据',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProForm
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal"
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="dictCode"
+          label={intl.formatMessage({
+            id: 'system.dict.data.dict_code',
+            defaultMessage: '字典编码',
+          })}
+          colProps={{ md: 24, xl: 24 }}
+          placeholder="请输入字典编码"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典编码!" defaultMessage="请输入字典编码!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="dictType"
+          label={intl.formatMessage({
+            id: 'system.dict.data.dict_type',
+            defaultMessage: '字典类型',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入字典类型"
+          disabled
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典类型!" defaultMessage="请输入字典类型!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="dictLabel"
+          label={intl.formatMessage({
+            id: 'system.dict.data.dict_label',
+            defaultMessage: '字典标签',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入字典标签"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典标签!" defaultMessage="请输入字典标签!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="dictValue"
+          label={intl.formatMessage({
+            id: 'system.dict.data.dict_value',
+            defaultMessage: '字典键值',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入字典键值"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典键值!" defaultMessage="请输入字典键值!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="cssClass"
+          label={intl.formatMessage({
+            id: 'system.dict.data.css_class',
+            defaultMessage: '样式属性',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入样式属性"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入样式属性!" defaultMessage="请输入样式属性!" />,
+            },
+          ]}
+        />
+        <ProFormSelect
+          name="listClass"
+          label={intl.formatMessage({
+            id: 'system.dict.data.list_class',
+            defaultMessage: '回显样式',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入回显样式"
+          valueEnum={{
+            'default': '默认',
+            'primary': '主要',
+            'success': '成功',
+            'info': '信息',
+            'warning': '警告',
+            'danger': '危险',
+          }}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入回显样式!" defaultMessage="请输入回显样式!" />,
+            },
+          ]}
+        />
+        <ProFormDigit
+          name="dictSort"
+          label={intl.formatMessage({
+            id: 'system.dict.data.dict_sort',
+            defaultMessage: '字典排序',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入字典排序"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入字典排序!" defaultMessage="请输入字典排序!" />,
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          name="isDefault"
+          label={intl.formatMessage({
+            id: 'system.dict.data.is_default',
+            defaultMessage: '是否默认',
+          })}
+          valueEnum={{
+            'Y': '是',
+            'N': '否',
+          }}
+          initialValue={'N'}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入是否默认"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入是否默认!" defaultMessage="请输入是否默认!" />,
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.dict.data.status',
+            defaultMessage: '状态',
+          })}
+          initialValue={'0'}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入状态"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入状态!" defaultMessage="请输入状态!" />,
+            },
+          ]}
+        />
+        <ProFormTextArea
+          name="remark"
+          label={intl.formatMessage({
+            id: 'system.dict.data.remark',
+            defaultMessage: '备注',
+          })}
+          colProps={{ md: 24, xl: 24 }}
+          placeholder="请输入备注"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入备注!" defaultMessage="请输入备注!" />,
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default DictDataForm;
diff --git a/react-ui/src/pages/System/DictData/index.tsx b/react-ui/src/pages/System/DictData/index.tsx
new file mode 100644
index 0000000..2bf1069
--- /dev/null
+++ b/react-ui/src/pages/System/DictData/index.tsx
@@ -0,0 +1,439 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess, history, useParams } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getDictDataList, removeDictData, addDictData, updateDictData, exportDictData } from '@/services/system/dictdata';
+import UpdateForm from './edit';
+import { getDictValueEnum, getDictType, getDictTypeOptionSelect } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.DictData) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addDictData({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.DictData) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateDictData(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.DictData[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeDictData(selectedRows.map((row) => row.dictCode).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.DictData) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.dictCode];
+    const resp = await removeDictData(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportDictData();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+export type DictTypeArgs = {
+  id: string;
+};
+
+
+const DictDataTableList: React.FC = () => {
+
+  const formTableRef = useRef<FormInstance>();
+
+  const [dictId, setDictId] = useState<string>('');
+  const [dictType, setDictType] = useState<string>('');
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.DictData>();
+  const [selectedRows, setSelectedRows] = useState<API.System.DictData[]>([]);
+
+  const [dictTypeOptions, setDictTypeOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  const params = useParams();
+  if (params.id === undefined) {
+    history.push('/system/dict');
+  }
+  const id = params.id || '0';
+
+  useEffect(() => {
+    if (dictId !== id) {
+      setDictId(id);
+      getDictTypeOptionSelect().then((res) => {
+        if (res.code === 200) {
+          const opts: any = {};
+          res.data.forEach((item: any) => {
+            opts[item.dictType] = item.dictName;
+          });
+          setDictTypeOptions(opts);
+        }
+      });
+      getDictValueEnum('sys_normal_disable').then((data) => {
+        setStatusOptions(data);
+      });
+      getDictType(id).then((res) => {
+        if (res.code === 200) {
+          setDictType(res.data.dictType);
+          formTableRef.current?.setFieldsValue({
+            dictType: res.data.dictType,
+          });
+          actionRef.current?.reloadAndRest?.();
+        } else {
+          message.error(res.msg);
+        }
+      });
+    }
+  }, [dictId, dictType, params]);
+
+  const columns: ProColumns<API.System.DictData>[] = [
+    {
+      title: <FormattedMessage id="system.dict.data.dict_code" defaultMessage="字典编码" />,
+      dataIndex: 'dictCode',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.dict.data.dict_label" defaultMessage="字典标签" />,
+      dataIndex: 'dictLabel',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.dict.data.dict_type" defaultMessage="字典类型" />,
+      dataIndex: 'dictType',
+      valueType: 'select',
+      hideInTable: true,
+      valueEnum: dictTypeOptions,
+      search: {
+        transform: (value) => {
+          setDictType(value);
+          return value;
+        },
+      },
+    },
+    {
+      title: <FormattedMessage id="system.dict.data.dict_value" defaultMessage="字典键值" />,
+      dataIndex: 'dictValue',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.dict.data.dict_sort" defaultMessage="字典排序" />,
+      dataIndex: 'dictSort',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.dict.data.status" defaultMessage="状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="system.dict.data.remark" defaultMessage="备注" />,
+      dataIndex: 'remark',
+      valueType: 'textarea',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.dict.data.create_time" defaultMessage="创建时间" />,
+      dataIndex: 'createTime',
+      valueType: 'dateRange',
+      render: (_, record) => {
+        return (<span>{record.createTime.toString()} </span>);
+      },
+      search: {
+        transform: (value) => {
+          return {
+            'params[beginTime]': value[0],
+            'params[endTime]': value[1],
+          };
+        },
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '120px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:data:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('system:data:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.DictData>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="dictCode"
+          key="dataList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:data:add')}
+              onClick={async () => {
+                setCurrentRow({ dictType: dictType, isDefault: 'N', status: '0' } as API.System.DictData);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:data:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('system:data:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getDictDataList({ ...params, dictType } as API.System.DictDataListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:data:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.dictCode) {
+            success = await handleUpdate({ ...values } as API.System.DictData);
+          } else {
+            success = await handleAdd({ ...values } as API.System.DictData);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default DictDataTableList;
diff --git a/react-ui/src/pages/System/Logininfor/edit.tsx b/react-ui/src/pages/System/Logininfor/edit.tsx
new file mode 100644
index 0000000..5243191
--- /dev/null
+++ b/react-ui/src/pages/System/Logininfor/edit.tsx
@@ -0,0 +1,216 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormRadio,
+  ProFormTimePicker,
+  } from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type LogininforFormData = Record<string, unknown> & Partial<API.Monitor.Logininfor>;
+
+export type LogininforFormProps = {
+  onCancel: (flag?: boolean, formVals?: LogininforFormData) => void;
+  onSubmit: (values: LogininforFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.Monitor.Logininfor>;
+  statusOptions: DictValueEnumObj;
+};
+
+const LogininforForm: React.FC<LogininforFormProps> = (props) => {
+  const [form] = Form.useForm();
+  
+  const { statusOptions, } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+			infoId: props.values.infoId,
+			userName: props.values.userName,
+			ipaddr: props.values.ipaddr,
+			loginLocation: props.values.loginLocation,
+			browser: props.values.browser,
+			os: props.values.os,
+			status: props.values.status,
+			msg: props.values.msg,
+			loginTime: props.values.loginTime,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+    form.resetFields();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as LogininforFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.logininfor.title',
+        defaultMessage: '编辑系统访问记录',
+      })}
+      open={props.open}
+      destroyOnClose
+      forceRender
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+		  <ProForm 
+        form={form}
+        grid={true}
+        layout="horizontal" 
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="infoId"
+          label={intl.formatMessage({
+            id: 'system.logininfor.info_id',
+            defaultMessage: '访问编号',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入访问编号"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入访问编号!" defaultMessage="请输入访问编号!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="userName"
+          label={intl.formatMessage({
+            id: 'system.logininfor.user_name',
+            defaultMessage: '用户账号',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入用户账号"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入用户账号!" defaultMessage="请输入用户账号!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="ipaddr"
+          label={intl.formatMessage({
+            id: 'system.logininfor.ipaddr',
+            defaultMessage: '登录IP地址',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入登录IP地址"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入登录IP地址!" defaultMessage="请输入登录IP地址!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="loginLocation"
+          label={intl.formatMessage({
+            id: 'system.logininfor.login_location',
+            defaultMessage: '登录地点',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入登录地点"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入登录地点!" defaultMessage="请输入登录地点!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="browser"
+          label={intl.formatMessage({
+            id: 'system.logininfor.browser',
+            defaultMessage: '浏览器类型',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入浏览器类型"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入浏览器类型!" defaultMessage="请输入浏览器类型!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="os"
+          label={intl.formatMessage({
+            id: 'system.logininfor.os',
+            defaultMessage: '操作系统',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入操作系统"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入操作系统!" defaultMessage="请输入操作系统!" />,                  
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.logininfor.status',
+            defaultMessage: '登录状态',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入登录状态"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入登录状态!" defaultMessage="请输入登录状态!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="msg"
+          label={intl.formatMessage({
+            id: 'system.logininfor.msg',
+            defaultMessage: '提示消息',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入提示消息"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入提示消息!" defaultMessage="请输入提示消息!" />,                  
+            },
+          ]}
+        />
+        <ProFormTimePicker
+          name="loginTime"
+          label={intl.formatMessage({
+            id: 'system.logininfor.login_time',
+            defaultMessage: '访问时间',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入访问时间"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入访问时间!" defaultMessage="请输入访问时间!" />,                  
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default LogininforForm;
diff --git a/react-ui/src/pages/System/Logininfor/index.tsx b/react-ui/src/pages/System/Logininfor/index.tsx
new file mode 100644
index 0000000..ee5c1e5
--- /dev/null
+++ b/react-ui/src/pages/System/Logininfor/index.tsx
@@ -0,0 +1,321 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, UnlockOutlined } from '@ant-design/icons';
+import { getLogininforList, removeLogininfor, exportLogininfor, unlockLogininfor, cleanLogininfor } from '@/services/monitor/logininfor';
+import DictTag from '@/components/DictTag';
+import { getDictValueEnum } from '@/services/system/dict';
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.Monitor.Logininfor[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeLogininfor(selectedRows.map((row) => row.infoId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleClean = async () => {
+  const hide = message.loading('请稍候');
+  try {
+    const resp = await cleanLogininfor();
+    hide();
+    if (resp.code === 200) {
+      message.success('清空成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('请求失败,请重试');
+    return false;
+  }
+};
+
+const handleUnlock = async (userName: string) => {
+  const hide = message.loading('正在解锁');
+  try {
+    const resp = await unlockLogininfor(userName);
+    hide();
+    if (resp.code === 200) {
+      message.success('解锁成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('解锁失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ * @param id
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportLogininfor();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const LogininforTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const actionRef = useRef<ActionType>();
+  const [selectedRows, setSelectedRows] = useState<API.Monitor.Logininfor[]>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_common_status', true).then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.Monitor.Logininfor>[] = [
+    {
+      title: <FormattedMessage id="monitor.logininfor.info_id" defaultMessage="访问编号" />,
+      dataIndex: 'infoId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.user_name" defaultMessage="用户账号" />,
+      dataIndex: 'userName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.ipaddr" defaultMessage="登录IP地址" />,
+      dataIndex: 'ipaddr',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.login_location" defaultMessage="登录地点" />,
+      dataIndex: 'loginLocation',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.browser" defaultMessage="浏览器类型" />,
+      dataIndex: 'browser',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.os" defaultMessage="操作系统" />,
+      dataIndex: 'os',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.status" defaultMessage="登录状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.msg" defaultMessage="提示消息" />,
+      dataIndex: 'msg',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.logininfor.login_time" defaultMessage="访问时间" />,
+      dataIndex: 'loginTime',
+      valueType: 'dateTime',
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.Monitor.Logininfor>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="infoId"
+          key="logininforList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:logininfor:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="clean"
+              danger
+              hidden={!access.hasPerms('monitor:logininfor:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认清空所有数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleClean();
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.cleanAll" defaultMessage="清空" />
+            </Button>,
+            <Button
+              type="primary"
+              key="unlock"
+              hidden={selectedRows?.length === 0 || !access.hasPerms('monitor:logininfor:unlock')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认解锁该用户的数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleUnlock(selectedRows[0].userName);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <UnlockOutlined />
+              <FormattedMessage id="monitor.logininfor.unlock" defaultMessage="解锁" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('monitor:logininfor:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getLogininforList({ ...params } as API.Monitor.LogininforListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('monitor:logininfor:remove')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+    </PageContainer>
+  );
+};
+
+export default LogininforTableList;
diff --git a/react-ui/src/pages/System/Menu/edit.tsx b/react-ui/src/pages/System/Menu/edit.tsx
new file mode 100644
index 0000000..e094255
--- /dev/null
+++ b/react-ui/src/pages/System/Menu/edit.tsx
@@ -0,0 +1,386 @@
+import React, { useEffect, useState } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormRadio,
+  ProFormTreeSelect,
+  ProFormSelect,
+} from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DataNode } from 'antd/es/tree';
+import { createIcon } from '@/utils/IconUtil';
+import { DictValueEnumObj } from '@/components/DictTag';
+import IconSelector from '@/components/IconSelector';
+
+export type MenuFormData = Record<string, unknown> & Partial<API.System.Menu>;
+
+export type MenuFormProps = {
+  onCancel: (flag?: boolean, formVals?: MenuFormData) => void;
+  onSubmit: (values: MenuFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.Menu>;
+  visibleOptions: DictValueEnumObj;
+  statusOptions: DictValueEnumObj;
+  menuTree: DataNode[];
+};
+
+const MenuForm: React.FC<MenuFormProps> = (props) => {
+
+  const [form] = Form.useForm();
+
+  const [menuTypeId, setMenuTypeId] = useState<any>('M');
+  const [menuIconName, setMenuIconName] = useState<any>();
+  const [iconSelectorOpen, setIconSelectorOpen] = useState<boolean>(false);
+
+  const { menuTree, visibleOptions, statusOptions } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    setMenuIconName(props.values.icon);
+    form.setFieldsValue({
+      menuId: props.values.menuId,
+      menuName: props.values.menuName,
+      parentId: props.values.parentId,
+      orderNum: props.values.orderNum,
+      path: props.values.path,
+      component: props.values.component,
+      query: props.values.query,
+      isFrame: props.values.isFrame,
+      isCache: props.values.isCache,
+      menuType: props.values.menuType,
+      visible: props.values.visible,
+      status: props.values.status,
+      perms: props.values.perms,
+      icon: props.values.icon,
+      createBy: props.values.createBy,
+      createTime: props.values.createTime,
+      updateBy: props.values.updateBy,
+      updateTime: props.values.updateTime,
+      remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as MenuFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.menu.title',
+        defaultMessage: '编辑菜单权限',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProForm
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal"
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="menuId"
+          label={intl.formatMessage({
+            id: 'system.menu.menu_id',
+            defaultMessage: '菜单编号',
+          })}
+          placeholder="请输入菜单编号"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入菜单编号!" defaultMessage="请输入菜单编号!" />,
+            },
+          ]}
+        />
+        <ProFormTreeSelect
+          name="parentId"
+          label={intl.formatMessage({
+            id: 'system.menu.parent_id',
+            defaultMessage: '上级菜单',
+          })}
+          params={{menuTree}}
+          request={async () => {
+            return menuTree;
+          }}
+          placeholder="请输入父菜单编号"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入父菜单编号!" defaultMessage="请输入父菜单编号!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: 0
+          }}
+        />
+        <ProFormRadio.Group
+          name="menuType"
+          valueEnum={{
+            M: '目录',
+            C: '菜单',
+            F: '按钮',
+          }}
+          label={intl.formatMessage({
+            id: 'system.menu.menu_type',
+            defaultMessage: '菜单类型',
+          })}
+          placeholder="请输入菜单类型"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入菜单类型!" defaultMessage="请输入菜单类型!" />,
+            },
+          ]}
+          fieldProps={{
+            defaultValue: 'M',
+            onChange: (e) => {
+              setMenuTypeId(e.target.value);
+            },
+          }}
+        />
+        <ProFormSelect
+          name="icon"
+          label={intl.formatMessage({
+            id: 'system.menu.icon',
+            defaultMessage: '菜单图标',
+          })}
+          valueEnum={{}}
+          hidden={menuTypeId === 'F'}
+          addonBefore={createIcon(menuIconName)}
+          fieldProps={{
+            onClick: () => {
+              setIconSelectorOpen(true);
+            },
+          }}
+          placeholder="请输入菜单图标"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入菜单图标!" defaultMessage="请输入菜单图标!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="menuName"
+          label={intl.formatMessage({
+            id: 'system.menu.menu_name',
+            defaultMessage: '菜单名称',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入菜单名称"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入菜单名称!" defaultMessage="请输入菜单名称!" />,
+            },
+          ]}
+        />
+        <ProFormDigit
+          name="orderNum"
+          label={intl.formatMessage({
+            id: 'system.menu.order_num',
+            defaultMessage: '显示顺序',
+          })}
+          width="lg"
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入显示顺序"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入显示顺序!" defaultMessage="请输入显示顺序!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: 1
+          }}
+        />
+        <ProFormRadio.Group
+          name="isFrame"
+          valueEnum={{
+            0: '是',
+            1: '否',
+          }}
+          initialValue="1"
+          label={intl.formatMessage({
+            id: 'system.menu.is_frame',
+            defaultMessage: '是否为外链',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入是否为外链"
+          hidden={menuTypeId === 'F'}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入是否为外链!" defaultMessage="请输入是否为外链!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: '1'
+          }}
+        />
+        <ProFormText
+          name="path"
+          label={intl.formatMessage({
+            id: 'system.menu.path',
+            defaultMessage: '路由地址',
+          })}
+          width="lg"
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入路由地址"
+          hidden={menuTypeId === 'F'}
+          rules={[
+            {
+              required: menuTypeId !== 'F',
+              message: <FormattedMessage id="请输入路由地址!" defaultMessage="请输入路由地址!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="component"
+          label={intl.formatMessage({
+            id: 'system.menu.component',
+            defaultMessage: '组件路径',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入组件路径"
+          hidden={menuTypeId !== 'C'}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入组件路径!" defaultMessage="请输入组件路径!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="query"
+          label={intl.formatMessage({
+            id: 'system.menu.query',
+            defaultMessage: '路由参数',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入路由参数"
+          hidden={menuTypeId !== 'C'}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入路由参数!" defaultMessage="请输入路由参数!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="perms"
+          label={intl.formatMessage({
+            id: 'system.menu.perms',
+            defaultMessage: '权限标识',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入权限标识"
+          hidden={menuTypeId === 'M'}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入权限标识!" defaultMessage="请输入权限标识!" />,
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          name="isCache"
+          valueEnum={{
+            0: '缓存',
+            1: '不缓存',
+          }}
+          label={intl.formatMessage({
+            id: 'system.menu.is_cache',
+            defaultMessage: '是否缓存',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入是否缓存"
+          hidden={menuTypeId !== 'C'}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入是否缓存!" defaultMessage="请输入是否缓存!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: 0
+          }}
+        />
+        <ProFormRadio.Group
+          name="visible"
+          valueEnum={visibleOptions}
+          label={intl.formatMessage({
+            id: 'system.menu.visible',
+            defaultMessage: '显示状态',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入显示状态"
+          hidden={menuTypeId === 'F'}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入显示状态!" defaultMessage="请输入显示状态!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: '0'
+          }}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.menu.status',
+            defaultMessage: '菜单状态',
+          })}
+          colProps={{ md: 12, xl: 12 }}
+          placeholder="请输入菜单状态"
+          hidden={menuTypeId === 'F'}
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入菜单状态!" defaultMessage="请输入菜单状态!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: '0'
+          }}
+        />
+      </ProForm>
+      <Modal
+        width={800}
+        open={iconSelectorOpen}
+        onCancel={() => {
+          setIconSelectorOpen(false);
+        }}
+        footer={null}
+      >
+        <IconSelector
+          onSelect={(name: string) => {
+            form.setFieldsValue({ icon: name });
+            setMenuIconName(name);
+            setIconSelectorOpen(false);
+          }}
+        />
+      </Modal>
+    </Modal>
+  );
+};
+
+export default MenuForm;
diff --git a/react-ui/src/pages/System/Menu/index.tsx b/react-ui/src/pages/System/Menu/index.tsx
new file mode 100644
index 0000000..95ca2de
--- /dev/null
+++ b/react-ui/src/pages/System/Menu/index.tsx
@@ -0,0 +1,339 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getMenuList, removeMenu, addMenu, updateMenu } from '@/services/system/menu';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import { buildTreeData } from '@/utils/tree';
+import { DataNode } from 'antd/es/tree';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.Menu) => {
+  const hide = message.loading('正在添加');
+  try {
+    await addMenu({ ...fields });
+    hide();
+    message.success('添加成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.Menu) => {
+  const hide = message.loading('正在配置');
+  try {
+    await updateMenu(fields);
+    hide();
+    message.success('配置成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.Menu[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    await removeMenu(selectedRows.map((row) => row.menuId).join(','));
+    hide();
+    message.success('删除成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.Menu) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.menuId];
+    await removeMenu(params.join(','));
+    hide();
+    message.success('删除成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+
+const MenuTableList: React.FC = () => {
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.Menu>();
+  const [selectedRows, setSelectedRows] = useState<API.System.Menu[]>([]);
+
+  const [menuTree, setMenuTree] = useState<DataNode[]>([]);
+  const [visibleOptions, setVisibleOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_show_hide').then((data) => {
+      setVisibleOptions(data);
+    });
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.System.Menu>[] = [
+    {
+      title: <FormattedMessage id="system.menu.menu_name" defaultMessage="菜单名称" />,
+      dataIndex: 'menuName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.menu.icon" defaultMessage="菜单图标" />,
+      dataIndex: 'icon',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.menu.order_num" defaultMessage="显示顺序" />,
+      dataIndex: 'orderNum',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.menu.component" defaultMessage="组件路径" />,
+      dataIndex: 'component',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.menu.perms" defaultMessage="权限标识" />,
+      dataIndex: 'perms',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.menu.status" defaultMessage="菜单状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:menu:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('system:menu:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.Menu>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          rowKey="menuId"
+          key="menuList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:menu:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:menu:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() {},
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getMenuList({ ...params } as API.System.MenuListParams).then((res) => {
+              const rootMenu = { id: 0, label: '主类目', children: [] as DataNode[], value: 0 };
+              const memuData = buildTreeData(res.data, 'menuId', 'menuName', '', '', '');
+              rootMenu.children = memuData;
+              const treeData: any = [];
+              treeData.push(rootMenu);
+              setMenuTree(treeData);
+              return {
+                data: memuData,
+                total: res.data.length,
+                success: true,
+              };
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:menu:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.menuId) {
+            success = await handleUpdate({ ...values } as API.System.Menu);
+          } else {
+            success = await handleAdd({ ...values } as API.System.Menu);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        visibleOptions={visibleOptions}
+        statusOptions={statusOptions}
+        menuTree={menuTree}
+      />
+    </PageContainer>
+  );
+};
+
+export default MenuTableList;
diff --git a/react-ui/src/pages/System/Notice/edit.tsx b/react-ui/src/pages/System/Notice/edit.tsx
new file mode 100644
index 0000000..0b111ce
--- /dev/null
+++ b/react-ui/src/pages/System/Notice/edit.tsx
@@ -0,0 +1,174 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormSelect,
+  ProFormTextArea,
+  ProFormRadio,
+  } from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type NoticeFormData = Record<string, unknown> & Partial<API.System.Notice>;
+
+export type NoticeFormProps = {
+  onCancel: (flag?: boolean, formVals?: NoticeFormData) => void;
+  onSubmit: (values: NoticeFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.Notice>;
+  noticeTypeOptions: DictValueEnumObj;
+  statusOptions: DictValueEnumObj;
+};
+
+const NoticeForm: React.FC<NoticeFormProps> = (props) => {
+  const [form] = Form.useForm();
+  
+  const { noticeTypeOptions,statusOptions, } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+			noticeId: props.values.noticeId,
+			noticeTitle: props.values.noticeTitle,
+			noticeType: props.values.noticeType,
+			noticeContent: props.values.noticeContent,
+			status: props.values.status,
+			createBy: props.values.createBy,
+			createTime: props.values.createTime,
+			updateBy: props.values.updateBy,
+			updateTime: props.values.updateTime,
+			remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as NoticeFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.notice.title',
+        defaultMessage: '编辑通知公告',
+      })}
+      forceRender
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+		  <ProForm 
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal" 
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="noticeId"
+          label={intl.formatMessage({
+            id: 'system.notice.notice_id',
+            defaultMessage: '公告编号',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入公告编号"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入公告编号!" defaultMessage="请输入公告编号!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="noticeTitle"
+          label={intl.formatMessage({
+            id: 'system.notice.notice_title',
+            defaultMessage: '公告标题',
+          })}
+          placeholder="请输入公告标题"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入公告标题!" defaultMessage="请输入公告标题!" />,                  
+            },
+          ]}
+        />
+        <ProFormSelect
+          valueEnum={noticeTypeOptions}
+          name="noticeType"
+          label={intl.formatMessage({
+            id: 'system.notice.notice_type',
+            defaultMessage: '公告类型',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入公告类型"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入公告类型!" defaultMessage="请输入公告类型!" />,                  
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.notice.status',
+            defaultMessage: '公告状态',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入公告状态"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入公告状态!" defaultMessage="请输入公告状态!" />,                  
+            },
+          ]}
+        />
+        <ProFormTextArea
+          name="noticeContent"
+          label={intl.formatMessage({
+            id: 'system.notice.notice_content',
+            defaultMessage: '公告内容',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入公告内容"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入公告内容!" defaultMessage="请输入公告内容!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="remark"
+          label={intl.formatMessage({
+            id: 'system.notice.remark',
+            defaultMessage: '备注',
+          })}
+          colProps={{ md: 12, xl: 24 }}
+          placeholder="请输入备注"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入备注!" defaultMessage="请输入备注!" />,                  
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default NoticeForm;
diff --git a/react-ui/src/pages/System/Notice/index.tsx b/react-ui/src/pages/System/Notice/index.tsx
new file mode 100644
index 0000000..ea5b063
--- /dev/null
+++ b/react-ui/src/pages/System/Notice/index.tsx
@@ -0,0 +1,366 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getNoticeList, removeNotice, addNotice, updateNotice } from '@/services/system/notice';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.Notice) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addNotice({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.Notice) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateNotice(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.Notice[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeNotice(selectedRows.map((row) => row.noticeId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.Notice) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.noticeId];
+    const resp = await removeNotice(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+
+
+const NoticeTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.Notice>();
+  const [selectedRows, setSelectedRows] = useState<API.System.Notice[]>([]);
+
+  const [noticeTypeOptions, setNoticeTypeOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_notice_type').then((data) => {
+      setNoticeTypeOptions(data);
+    });
+    getDictValueEnum('sys_notice_status').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.System.Notice>[] = [
+    {
+      title: <FormattedMessage id="system.notice.notice_id" defaultMessage="公告编号" />,
+      dataIndex: 'noticeId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.notice.notice_title" defaultMessage="公告标题" />,
+      dataIndex: 'noticeTitle',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.notice.notice_type" defaultMessage="公告类型" />,
+      dataIndex: 'noticeType',
+      valueType: 'select',
+      valueEnum: noticeTypeOptions,
+    },
+    {
+      title: <FormattedMessage id="system.notice.notice_content" defaultMessage="公告内容" />,
+      dataIndex: 'noticeContent',
+      valueType: 'text',
+      hideInTable: true,
+    },
+    {
+      title: <FormattedMessage id="system.notice.status" defaultMessage="公告状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="system.notice.remark" defaultMessage="备注" />,
+      dataIndex: 'remark',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.notice.create_time" defaultMessage="创建时间" />,
+      dataIndex: 'createTime',
+      valueType: 'dateRange',
+      render: (_, record) => {
+        return (<span>{record.createTime.toString()} </span>);
+      },
+      search: {
+        transform: (value) => {
+          return {
+            'params[beginTime]': value[0],
+            'params[endTime]': value[1],
+          };
+        },
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '120px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:notice:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('system:notice:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.Notice>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="noticeId"
+          key="noticeList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:notice:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:notice:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() {},
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getNoticeList({ ...params } as API.System.NoticeListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:notice:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.noticeId) {
+            success = await handleUpdate({ ...values } as API.System.Notice);
+          } else {
+            success = await handleAdd({ ...values } as API.System.Notice);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        noticeTypeOptions={noticeTypeOptions}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default NoticeTableList;
diff --git a/react-ui/src/pages/System/Operlog/detail.tsx b/react-ui/src/pages/System/Operlog/detail.tsx
new file mode 100644
index 0000000..f5b4f65
--- /dev/null
+++ b/react-ui/src/pages/System/Operlog/detail.tsx
@@ -0,0 +1,115 @@
+import React from 'react';
+import { Descriptions, Modal } from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+import { getValueEnumLabel } from '@/utils/options';
+
+export type OperlogFormData = Record<string, unknown> & Partial<API.Monitor.Operlog>;
+
+export type OperlogFormProps = {
+  onCancel: (flag?: boolean, formVals?: OperlogFormData) => void;
+  onSubmit: (values: OperlogFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.Monitor.Operlog>;
+  businessTypeOptions: DictValueEnumObj;
+  operatorTypeOptions: DictValueEnumObj;
+  statusOptions: DictValueEnumObj;
+};
+
+const OperlogDetailForm: React.FC<OperlogFormProps> = (props) => {
+
+  const { values, businessTypeOptions, operatorTypeOptions, statusOptions, } = props;
+
+  const intl = useIntl();
+  const handleOk = () => {
+    console.log("handle ok");
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'monitor.operlog.title',
+        defaultMessage: '编辑操作日志记录',
+      })}
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <Descriptions column={24}>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.module" defaultMessage="操作模块" />}
+        >
+          {`${values.title}/${getValueEnumLabel(businessTypeOptions, values.businessType)}`}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.request_method" defaultMessage="请求方式" />}
+        >
+          {values.requestMethod}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.oper_name" defaultMessage="操作人员" />}
+        >
+          {`${values.operName}/${values.operIp}`}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.operator_type" defaultMessage="操作类别" />}
+        >
+          {getValueEnumLabel(operatorTypeOptions, values.operatorType)}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.method" defaultMessage="方法名称" />}
+        >
+          {values.method}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.oper_url" defaultMessage="请求URL" />}
+        >
+          {values.operUrl}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.oper_param" defaultMessage="请求参数" />}
+        >
+          {values.operParam}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.json_result" defaultMessage="返回参数" />}
+        >
+          {values.jsonResult}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={24}
+          label={<FormattedMessage id="monitor.operlog.error_msg" defaultMessage="错误消息" />}
+        >
+          {values.errorMsg}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.status" defaultMessage="操作状态" />}
+        >
+          {getValueEnumLabel(statusOptions, values.status)}
+        </Descriptions.Item>
+        <Descriptions.Item
+          span={12}
+          label={<FormattedMessage id="monitor.operlog.oper_time" defaultMessage="操作时间" />}
+        >
+          {values.operTime?.toString()}
+        </Descriptions.Item>
+      </Descriptions>
+    </Modal>
+  );
+};
+
+export default OperlogDetailForm;
diff --git a/react-ui/src/pages/System/Operlog/index.tsx b/react-ui/src/pages/System/Operlog/index.tsx
new file mode 100644
index 0000000..be46709
--- /dev/null
+++ b/react-ui/src/pages/System/Operlog/index.tsx
@@ -0,0 +1,411 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getOperlogList, removeOperlog, addOperlog, updateOperlog, cleanAllOperlog, exportOperlog } from '@/services/monitor/operlog';
+import UpdateForm from './detail';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.Monitor.Operlog) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addOperlog({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.Monitor.Operlog) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateOperlog(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.Monitor.Operlog[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeOperlog(selectedRows.map((row) => row.operId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 清空所有记录
+ *
+ */
+const handleCleanAll = async () => {
+  const hide = message.loading('正在清空');
+  try {
+    const resp = await cleanAllOperlog();
+    hide();
+    if (resp.code === 200) {
+      message.success('清空成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('清空失败,请重试');
+    return false;
+  }
+};
+
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportOperlog();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const OperlogTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.Monitor.Operlog>();
+  const [selectedRows, setSelectedRows] = useState<API.Monitor.Operlog[]>([]);
+
+  const [businessTypeOptions, setBusinessTypeOptions] = useState<any>([]);
+  const [operatorTypeOptions, setOperatorTypeOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_oper_type', true).then((data) => {
+      setBusinessTypeOptions(data);
+    });
+    getDictValueEnum('sys_oper_type', true).then((data) => {
+      setOperatorTypeOptions(data);
+    });
+    getDictValueEnum('sys_common_status', true).then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.Monitor.Operlog>[] = [
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_id" defaultMessage="日志主键" />,
+      dataIndex: 'operId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.title" defaultMessage="操作模块" />,
+      dataIndex: 'title',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.business_type" defaultMessage="业务类型" />,
+      dataIndex: 'businessType',
+      valueType: 'select',
+      valueEnum: businessTypeOptions,
+      render: (_, record) => {
+        return (<DictTag enums={businessTypeOptions} value={record.businessType} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.request_method" defaultMessage="请求方式" />,
+      dataIndex: 'requestMethod',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.operator_type" defaultMessage="操作类别" />,
+      dataIndex: 'operatorType',
+      valueType: 'select',
+      valueEnum: operatorTypeOptions,
+      render: (_, record) => {
+        return (<DictTag enums={operatorTypeOptions} value={record.operatorType} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_name" defaultMessage="操作人员" />,
+      dataIndex: 'operName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_ip" defaultMessage="主机地址" />,
+      dataIndex: 'operIp',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_location" defaultMessage="操作地点" />,
+      dataIndex: 'operLocation',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.status" defaultMessage="操作状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag key="status" enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="monitor.operlog.oper_time" defaultMessage="操作时间" />,
+      dataIndex: 'operTime',
+      valueType: 'dateTime',
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '120px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:operlog:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          详细
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.Monitor.Operlog>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="operId"
+          key="operlogList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:operlog:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:operlog:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="clean"
+              danger
+              hidden={!access.hasPerms('system:operlog:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认清空所有数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleCleanAll();
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.cleanAll" defaultMessage="清空" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('system:operlog:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getOperlogList({ ...params } as API.Monitor.OperlogListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:operlog:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.operId) {
+            success = await handleUpdate({ ...values } as API.Monitor.Operlog);
+          } else {
+            success = await handleAdd({ ...values } as API.Monitor.Operlog);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        businessTypeOptions={businessTypeOptions}
+        operatorTypeOptions={operatorTypeOptions}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default OperlogTableList;
diff --git a/react-ui/src/pages/System/Post/edit.tsx b/react-ui/src/pages/System/Post/edit.tsx
new file mode 100644
index 0000000..555fcde
--- /dev/null
+++ b/react-ui/src/pages/System/Post/edit.tsx
@@ -0,0 +1,166 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormRadio,
+  ProFormTextArea,
+  } from '@ant-design/pro-components';
+import { Form, Modal} from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type PostFormData = Record<string, unknown> & Partial<API.System.Post>;
+
+export type PostFormProps = {
+  onCancel: (flag?: boolean, formVals?: PostFormData) => void;
+  onSubmit: (values: PostFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.Post>;
+  statusOptions: DictValueEnumObj;
+};
+
+const PostForm: React.FC<PostFormProps> = (props) => {
+  const [form] = Form.useForm();
+
+  const { statusOptions, } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+			postId: props.values.postId,
+			postCode: props.values.postCode,
+			postName: props.values.postName,
+			postSort: props.values.postSort,
+			status: props.values.status,
+			createBy: props.values.createBy,
+			createTime: props.values.createTime,
+			updateBy: props.values.updateBy,
+			updateTime: props.values.updateTime,
+			remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as PostFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.post.title',
+        defaultMessage: '编辑岗位信息',
+      })}
+      open={props.open}
+      forceRender
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+		  <ProForm 
+        form={form}
+        grid={true}
+        submitter={false}
+        layout="horizontal" 
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="postId"
+          label={intl.formatMessage({
+            id: 'system.post.post_id',
+            defaultMessage: '岗位编号',
+          })}
+          placeholder="请输入岗位编号"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入岗位编号!" defaultMessage="请输入岗位编号!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="postName"
+          label={intl.formatMessage({
+            id: 'system.post.post_name',
+            defaultMessage: '岗位名称',
+          })}
+          placeholder="请输入岗位名称"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入岗位名称!" defaultMessage="请输入岗位名称!" />,                  
+            },
+          ]}
+        />
+        <ProFormText
+          name="postCode"
+          label={intl.formatMessage({
+            id: 'system.post.post_code',
+            defaultMessage: '岗位编码',
+          })}
+          placeholder="请输入岗位编码"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入岗位编码!" defaultMessage="请输入岗位编码!" />,                  
+            },
+          ]}
+        />
+        <ProFormDigit
+          name="postSort"
+          label={intl.formatMessage({
+            id: 'system.post.post_sort',
+            defaultMessage: '显示顺序',
+          })}
+          placeholder="请输入显示顺序"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入显示顺序!" defaultMessage="请输入显示顺序!" />,                  
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.post.status',
+            defaultMessage: '状态',
+          })}
+          placeholder="请输入状态"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入状态!" defaultMessage="请输入状态!" />,                  
+            },
+          ]}
+        />
+        <ProFormTextArea
+          name="remark"
+          label={intl.formatMessage({
+            id: 'system.post.remark',
+            defaultMessage: '备注',
+          })}
+          placeholder="请输入备注"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入备注!" defaultMessage="请输入备注!" />,                  
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default PostForm;
diff --git a/react-ui/src/pages/System/Post/index.tsx b/react-ui/src/pages/System/Post/index.tsx
new file mode 100644
index 0000000..ad98aae
--- /dev/null
+++ b/react-ui/src/pages/System/Post/index.tsx
@@ -0,0 +1,366 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import type { FormInstance } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
+import { getPostList, removePost, addPost, updatePost, exportPost } from '@/services/system/post';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.Post) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addPost({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.Post) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updatePost(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.Post[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removePost(selectedRows.map((row) => row.postId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.Post) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.postId];
+    const resp = await removePost(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportPost();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const PostTableList: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.Post>();
+  const [selectedRows, setSelectedRows] = useState<API.System.Post[]>([]);
+
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const columns: ProColumns<API.System.Post>[] = [
+    {
+      title: <FormattedMessage id="system.post.post_id" defaultMessage="岗位编号" />,
+      dataIndex: 'postId',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.post.post_code" defaultMessage="岗位编码" />,
+      dataIndex: 'postCode',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.post.post_name" defaultMessage="岗位名称" />,
+      dataIndex: 'postName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.post.post_sort" defaultMessage="显示顺序" />,
+      dataIndex: 'postSort',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.post.status" defaultMessage="状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          hidden={!access.hasPerms('system:post:edit')}
+          onClick={() => {
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          hidden={!access.hasPerms('system:post:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.Post>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="postId"
+          key="postList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:post:add')}
+              onClick={async () => {
+                setCurrentRow(undefined);
+                setModalVisible(true);
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:post:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() {},
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('system:post:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getPostList({ ...params } as API.System.PostListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:post:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.postId) {
+            success = await handleUpdate({ ...values } as API.System.Post);
+          } else {
+            success = await handleAdd({ ...values } as API.System.Post);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        statusOptions={statusOptions}
+      />
+    </PageContainer>
+  );
+};
+
+export default PostTableList;
diff --git a/react-ui/src/pages/System/Role/authUser.tsx b/react-ui/src/pages/System/Role/authUser.tsx
new file mode 100644
index 0000000..c553d99
--- /dev/null
+++ b/react-ui/src/pages/System/Role/authUser.tsx
@@ -0,0 +1,274 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess, history, useParams } from '@umijs/max';
+import { Button, Modal, message } from 'antd';
+import { ActionType, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, RollbackOutlined } from '@ant-design/icons';
+import { authUserSelectAll, authUserCancel, authUserCancelAll, allocatedUserList, unallocatedUserList } from '@/services/system/role';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+import UserSelectorModal from './components/UserSelectorModal';
+import { HttpResult } from '@/enums/httpEnum';
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const cancelAuthUserAll = async (roleId: string, selectedRows: API.System.User[]) => {
+	const hide = message.loading('正在取消授权');
+	if (!selectedRows) return true;
+	try {
+		const userIds = selectedRows.map((row) => row.userId).join(',');
+		const resp = await authUserCancelAll({roleId, userIds});
+		hide();
+		if (resp.code === 200) {
+			message.success('取消授权成功,即将刷新');
+		} else {
+			message.error(resp.msg);
+		}
+		return true;
+	} catch (error) {
+		hide();
+		message.error('取消授权失败,请重试');
+		return false;
+	}
+};
+
+const cancelAuthUser = async (roleId: string, userId: number) => {
+	const hide = message.loading('正在取消授权');
+	try {
+		const resp = await authUserCancel({ userId, roleId });
+		hide();
+		if (resp.code === 200) {
+			message.success('取消授权成功,即将刷新');
+		} else {
+			message.error(resp.msg);
+		}
+		return true;
+	} catch (error) {
+		hide();
+		message.error('取消授权失败,请重试');
+		return false;
+	}
+};
+
+
+const AuthUserTableList: React.FC = () => {
+
+	const [modalVisible, setModalVisible] = useState<boolean>(false);
+
+	const actionRef = useRef<ActionType>();
+	const [selectedRows, setSelectedRows] = useState<API.System.User[]>([]);
+	const [statusOptions, setStatusOptions] = useState<any>([]);
+
+	const access = useAccess();
+
+	/** 国际化配置 */
+	const intl = useIntl();
+
+	const params = useParams();
+	if (params.id === undefined) {
+		history.back();
+	}
+	const roleId = params.id || '0';
+
+	useEffect(() => {
+		getDictValueEnum('sys_normal_disable').then((data) => {
+			setStatusOptions(data);
+		});
+	}, []);
+
+	const columns: ProColumns<API.System.User>[] = [
+		{
+			title: <FormattedMessage id="system.user.user_id" defaultMessage="用户编号" />,
+			dataIndex: 'deptId',
+			valueType: 'text',
+		},
+		{
+			title: <FormattedMessage id="system.user.user_name" defaultMessage="用户账号" />,
+			dataIndex: 'userName',
+			valueType: 'text',
+		},
+		{
+			title: <FormattedMessage id="system.user.nick_name" defaultMessage="用户昵称" />,
+			dataIndex: 'nickName',
+			valueType: 'text',
+		},
+		{
+			title: <FormattedMessage id="system.user.phonenumber" defaultMessage="手机号码" />,
+			dataIndex: 'phonenumber',
+			valueType: 'text',
+		},
+		{
+			title: <FormattedMessage id="system.role.create_time" defaultMessage="创建时间" />,
+			dataIndex: 'createTime',
+			valueType: 'dateRange',
+			render: (_, record) => {
+				return (<span>{record.createTime.toString()} </span>);
+			},
+			hideInSearch: true,
+		},
+		{
+			title: <FormattedMessage id="system.user.status" defaultMessage="帐号状态" />,
+			dataIndex: 'status',
+			valueType: 'select',
+			valueEnum: statusOptions,
+			render: (_, record) => {
+				return (<DictTag enums={statusOptions} value={record.status} />);
+			},
+		},
+		{
+			title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+			dataIndex: 'option',
+			width: '60px',
+			valueType: 'option',
+			render: (_, record) => [
+				<Button
+					type="link"
+					size="small"
+					danger
+					icon={<DeleteOutlined />}
+					key="remove"
+					hidden={!access.hasPerms('system:role:remove')}
+					onClick={async () => {
+						Modal.confirm({
+							title: '删除',
+							content: '确认要取消该用户' + record.userName + '"角色授权吗?',
+							okText: '确认',
+							cancelText: '取消',
+							onOk: async () => {
+								const success = await cancelAuthUser(roleId, record.userId);
+								if (success) {
+									if (actionRef.current) {
+										actionRef.current.reload();
+									}
+								}
+							},
+						});
+					}}
+				>
+					取消授权
+				</Button>,
+			],
+		},
+	];
+
+	return (
+		<PageContainer>
+			<div style={{ width: '100%', float: 'right' }}>
+				<ProTable<API.System.User>
+					headerTitle={intl.formatMessage({
+						id: 'pages.searchTable.title',
+						defaultMessage: '信息',
+					})}
+					actionRef={actionRef}
+					rowKey="userId"
+					key="userList"
+					search={{
+						labelWidth: 120,
+					}}
+					toolBarRender={() => [
+						<Button
+							type="primary"
+							key="add"
+							hidden={!access.hasPerms('system:role:add')}
+							onClick={async () => {
+								setModalVisible(true);
+							}}
+						>
+							<PlusOutlined /> <FormattedMessage id="system.role.auth.addUser" defaultMessage="添加用户" />
+						</Button>,
+						<Button
+							type="primary"
+							key="remove"
+              danger
+							hidden={selectedRows?.length === 0 || !access.hasPerms('system:role:remove')}
+							onClick={async () => {
+								Modal.confirm({
+									title: '是否确认删除所选数据项?',
+									icon: <ExclamationCircleOutlined />,
+									content: '请谨慎操作',
+									async onOk() {
+										const success = await cancelAuthUserAll(roleId, selectedRows);
+										if (success) {
+											setSelectedRows([]);
+											actionRef.current?.reloadAndRest?.();
+										}
+									},
+									onCancel() { },
+								});
+							}}
+						>
+							<DeleteOutlined />
+							<FormattedMessage id="system.role.auth.cancelAll" defaultMessage="批量取消授权" />
+						</Button>,
+						<Button
+							type="primary"
+							key="back"
+							onClick={async () => {
+								history.back();
+							}}
+						>
+							<RollbackOutlined />
+							<FormattedMessage id="pages.goback" defaultMessage="返回" />
+						</Button>,
+					]}
+					request={(params) =>
+						allocatedUserList({ ...params, roleId } as API.System.RoleListParams).then((res) => {
+							const result = {
+								data: res.rows,
+								total: res.total,
+								success: true,
+							};
+							return result;
+						})
+					}
+					columns={columns}
+					rowSelection={{
+						onChange: (_, selectedRows) => {
+							setSelectedRows(selectedRows);
+						},
+					}}
+				/>
+			</div>
+			<UserSelectorModal
+				open={modalVisible}
+				onSubmit={(values: React.Key[]) => {
+					const userIds = values.join(",");
+					if (userIds === "") {
+						message.warning("请选择要分配的用户");
+						return;
+					}
+					authUserSelectAll({ roleId: roleId, userIds: userIds }).then(resp => {
+						if (resp.code === HttpResult.SUCCESS) {
+							message.success('更新成功!');
+							if (actionRef.current) {
+								actionRef.current.reload();
+							}
+						} else {
+							message.warning(resp.msg);
+						}
+					})
+					setModalVisible(false);
+				}}
+				onCancel={() => {
+					setModalVisible(false);
+				}}
+				params={{roleId}}
+				request={(params) =>
+					unallocatedUserList({ ...params } as API.System.RoleListParams).then((res) => {
+						const result = {
+							data: res.rows,
+							total: res.rows.length,
+							success: true,
+						};
+						return result;
+					})
+				}
+			/>
+		</PageContainer>
+	);
+};
+
+export default AuthUserTableList;
diff --git a/react-ui/src/pages/System/Role/components/DataScope.tsx b/react-ui/src/pages/System/Role/components/DataScope.tsx
new file mode 100644
index 0000000..a3dcce0
--- /dev/null
+++ b/react-ui/src/pages/System/Role/components/DataScope.tsx
@@ -0,0 +1,233 @@
+import React, { useEffect, useState } from 'react';
+import { Checkbox, Col, Form, Modal, Row, Tree } from 'antd';
+import { FormattedMessage, useIntl } from '@umijs/max';
+import { Key, ProForm, ProFormDigit, ProFormSelect, ProFormText } from '@ant-design/pro-components';
+import { DataNode } from 'antd/es/tree';
+import { CheckboxValueType } from 'antd/es/checkbox/Group';
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/06
+ * 
+ * */
+
+export type FormValueType = any & Partial<API.System.Dept>;
+
+export type DataScopeFormProps = {
+    onCancel: (flag?: boolean, formVals?: FormValueType) => void;
+    onSubmit: (values: FormValueType) => Promise<void>;
+    open: boolean;
+    values: Partial<API.System.Role>;
+    deptTree: DataNode[];
+    deptCheckedKeys: string[];
+};
+
+const DataScopeForm: React.FC<DataScopeFormProps> = (props) => {
+    const [form] = Form.useForm();
+
+    const { deptTree, deptCheckedKeys } = props;
+    const [dataScopeType, setDataScopeType] = useState<string | undefined>('1');
+    const [deptIds, setDeptIds] = useState<string[] | {checked: string[], halfChecked: string[]}>([]);
+    const [deptTreeExpandKey, setDeptTreeExpandKey] = useState<Key[]>([]);
+    const [checkStrictly, setCheckStrictly] = useState<boolean>(true);
+
+
+    useEffect(() => {
+        setDeptIds(deptCheckedKeys);
+        form.resetFields();
+        form.setFieldsValue({
+            roleId: props.values.roleId,
+            roleName: props.values.roleName,
+            roleKey: props.values.roleKey,
+            dataScope: props.values.dataScope,
+        });
+        setDataScopeType(props.values.dataScope);
+    }, [props.values]);
+
+    const intl = useIntl();
+    const handleOk = () => {
+        form.submit();
+    };
+    const handleCancel = () => {
+        props.onCancel();
+    };
+    const handleFinish = async (values: Record<string, any>) => {
+        props.onSubmit({ ...values, deptIds } as FormValueType);
+    };
+
+    const getAllDeptNode = (node: DataNode[]) => {
+        let keys: any[] = [];
+        node.forEach(value => {
+            keys.push(value.key);
+            if(value.children) {
+                keys = keys.concat(getAllDeptNode(value.children));
+            }
+        });
+        return keys;
+    }
+
+    const deptAllNodes = getAllDeptNode(deptTree);
+
+
+    const onDeptOptionChange = (checkedValues: CheckboxValueType[]) => {
+        if(checkedValues.includes('deptExpand')) {
+            setDeptTreeExpandKey(deptAllNodes);
+        } else {
+            setDeptTreeExpandKey([]);
+        }
+        if(checkedValues.includes('deptNodeAll')) {
+            setDeptIds(deptAllNodes);
+        } else {
+            setDeptIds([]);
+        }
+        
+        if(checkedValues.includes('deptCheckStrictly')) {
+            setCheckStrictly(false);
+        } else {
+            setCheckStrictly(true);
+        }
+    };
+
+    return (
+        <Modal
+            width={640}
+            title={intl.formatMessage({
+                id: 'system.user.auth.role',
+                defaultMessage: '分配角色',
+            })}
+            open={props.open}
+            destroyOnClose
+            forceRender
+            onOk={handleOk}
+            onCancel={handleCancel}
+        >
+            <ProForm
+                form={form}
+                grid={true}
+                layout="horizontal"
+                onFinish={handleFinish}
+                initialValues={{
+                    login_password: '',
+                    confirm_password: '',
+                }}
+            >
+
+                <ProFormDigit
+                    name="roleId"
+                    label={intl.formatMessage({
+                        id: 'system.role.role_id',
+                        defaultMessage: '角色编号',
+                    })}
+                    colProps={{ md: 12, xl: 12 }}
+                    placeholder="请输入角色编号"
+                    disabled
+                    hidden={true}
+                    rules={[
+                        {
+                            required: false,
+                            message: <FormattedMessage id="请输入角色编号!" defaultMessage="请输入角色编号!" />,
+                        },
+                    ]}
+                />
+                <ProFormText
+                    name="roleName"
+                    label={intl.formatMessage({
+                        id: 'system.role.role_name',
+                        defaultMessage: '角色名称',
+                    })}
+                    disabled
+                    placeholder="请输入角色名称"
+                    rules={[
+                        {
+                            required: true,
+                            message: <FormattedMessage id="请输入角色名称!" defaultMessage="请输入角色名称!" />,
+                        },
+                    ]}
+                />
+                <ProFormText
+                    name="roleKey"
+                    label={intl.formatMessage({
+                        id: 'system.role.role_key',
+                        defaultMessage: '权限字符串',
+                    })}
+                    disabled
+                    placeholder="请输入角色权限字符串"
+                    rules={[
+                        {
+                            required: true,
+                            message: <FormattedMessage id="请输入角色权限字符串!" defaultMessage="请输入角色权限字符串!" />,
+                        },
+                    ]}
+                />
+                <ProFormSelect
+                    name="dataScope"
+                    label='权限范围'
+                    initialValue={'1'}
+                    placeholder="请输入用户性别"
+                    valueEnum={{
+                        "1": "全部数据权限",
+                        "2": "自定数据权限",
+                        "3": "本部门数据权限",
+                        "4": "本部门及以下数据权限",
+                        "5": "仅本人数据权限"
+                    }}
+                    rules={[
+                        {
+                            required: true,
+                        },
+                    ]}
+                    fieldProps={{
+                        onChange: (value) => {
+                            setDataScopeType(value);
+                        },
+                    }}
+                />
+                <ProForm.Item
+                    name="deptIds"
+                    label={intl.formatMessage({
+                        id: 'system.role.auth',
+                        defaultMessage: '菜单权限',
+                    })}
+                    required={dataScopeType === '1'}
+                    hidden={dataScopeType !== '1'}
+                >
+                    <Row gutter={[16, 16]}>
+                        <Col md={24}>
+                            <Checkbox.Group
+                                options={[
+                                    { label: '展开/折叠', value: 'deptExpand' },
+                                    { label: '全选/全不选', value: 'deptNodeAll' },
+                                    // { label: '父子联动', value: 'deptCheckStrictly' },
+                                ]}
+                                onChange={onDeptOptionChange} />
+                        </Col>
+                        <Col md={24}>
+                            <Tree
+                                checkable={true}
+                                checkStrictly={checkStrictly}
+                                expandedKeys={deptTreeExpandKey}
+                                treeData={deptTree}
+                                checkedKeys={deptIds}
+                                defaultCheckedKeys={deptCheckedKeys}
+                                onCheck={(checkedKeys: any, checkInfo: any) => {
+                                    console.log(checkedKeys, checkInfo);
+                                    if(checkStrictly) {
+                                        return setDeptIds(checkedKeys.checked);
+                                    } else {
+                                        return setDeptIds({checked: checkedKeys, halfChecked: checkInfo.halfCheckedKeys});
+                                    }
+                                }}
+                                onExpand={(expandedKeys: Key[]) => {
+                                    setDeptTreeExpandKey(deptTreeExpandKey.concat(expandedKeys));
+                                }}
+                            />
+                        </Col>
+                    </Row>
+                </ProForm.Item>
+            </ProForm>
+        </Modal>
+    );
+};
+
+export default DataScopeForm;
diff --git a/react-ui/src/pages/System/Role/components/UserSelectorModal.tsx b/react-ui/src/pages/System/Role/components/UserSelectorModal.tsx
new file mode 100644
index 0000000..fdcfb85
--- /dev/null
+++ b/react-ui/src/pages/System/Role/components/UserSelectorModal.tsx
@@ -0,0 +1,125 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { Modal } from 'antd';
+import { FormattedMessage, useIntl } from '@umijs/max';
+import { ActionType, ParamsType, ProColumns, ProTable, RequestData } from '@ant-design/pro-components';
+import { getDictValueEnum } from '@/services/system/dict';
+import DictTag from '@/components/DictTag';
+
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/10
+ * 
+ * */
+
+export type DataScopeFormProps = {
+  onCancel: () => void;
+  onSubmit: (values: React.Key[]) => void;
+  open: boolean;
+  params: ParamsType;
+  request?: (params: Record<string, any>) => Promise<Partial<RequestData<API.System.User>>>;
+};
+
+const UserSelectorModal: React.FC<DataScopeFormProps> = (props) => {
+  
+  const actionRef = useRef<ActionType>();
+  const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  useEffect(() => {
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, [props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    props.onSubmit(selectedRowKeys);
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+
+  const columns: ProColumns<API.System.User>[] = [
+    {
+      title: <FormattedMessage id="system.user.user_id" defaultMessage="用户编号" />,
+      dataIndex: 'userId',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.user.user_name" defaultMessage="用户账号" />,
+      dataIndex: 'userName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.user.nick_name" defaultMessage="用户昵称" />,
+      dataIndex: 'nickName',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.user.phonenumber" defaultMessage="手机号码" />,
+      dataIndex: 'phonenumber',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.user.status" defaultMessage="帐号状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      hideInSearch: true,
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (<DictTag enums={statusOptions} value={record.status} />);
+      },
+    },
+    {
+      title: <FormattedMessage id="system.user.create_time" defaultMessage="创建时间" />,
+      dataIndex: 'createTime',
+      valueType: 'dateRange',
+      hideInSearch: true,
+      render: (_, record) => {
+        return (<span>{record.createTime.toString()} </span>);
+      },
+    }
+  ];
+
+  return (
+    <Modal
+      width={800}
+      title={intl.formatMessage({
+        id: 'system.role.auth.user',
+        defaultMessage: '选择用户',
+      })}
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProTable<API.System.User>
+        headerTitle={intl.formatMessage({
+          id: 'pages.searchTable.title',
+          defaultMessage: '信息',
+        })}
+        actionRef={actionRef}
+        rowKey="userId"
+        key="userList"
+        search={{
+          labelWidth: 120,
+        }}
+        toolbar={{}}
+        params={props.params}
+        request={props.request}
+        columns={columns}
+        rowSelection={{
+          onChange: (selectedRowKeys: React.Key[]) => {
+            setSelectedRowKeys(selectedRowKeys);
+          },
+        }}
+      />
+    </Modal>
+  );
+};
+
+export default UserSelectorModal;
diff --git a/react-ui/src/pages/System/Role/edit.tsx b/react-ui/src/pages/System/Role/edit.tsx
new file mode 100644
index 0000000..580493a
--- /dev/null
+++ b/react-ui/src/pages/System/Role/edit.tsx
@@ -0,0 +1,199 @@
+import React, { useEffect, useState } from 'react';
+import {
+  ProForm,
+  ProFormDigit,
+  ProFormText,
+  ProFormRadio,
+  ProFormTextArea,
+} from '@ant-design/pro-components';
+import { Form, Modal } from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import Tree, { DataNode } from 'antd/es/tree';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+export type RoleFormData = Record<string, unknown> & Partial<API.System.Role>;
+
+export type RoleFormProps = {
+  onCancel: (flag?: boolean, formVals?: RoleFormData) => void;
+  onSubmit: (values: RoleFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.Role>;
+  menuTree: DataNode[];
+  menuCheckedKeys: string[];
+  statusOptions: DictValueEnumObj;
+};
+
+const RoleForm: React.FC<RoleFormProps> = (props) => {
+  const [form] = Form.useForm();
+  const { menuTree, menuCheckedKeys } = props;
+  const [menuIds, setMenuIds] = useState<string[]>([]);
+  const { statusOptions } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+      roleId: props.values.roleId,
+      roleName: props.values.roleName,
+      roleKey: props.values.roleKey,
+      roleSort: props.values.roleSort,
+      dataScope: props.values.dataScope,
+      menuCheckStrictly: props.values.menuCheckStrictly,
+      deptCheckStrictly: props.values.deptCheckStrictly,
+      status: props.values.status,
+      delFlag: props.values.delFlag,
+      createBy: props.values.createBy,
+      createTime: props.values.createTime,
+      updateBy: props.values.updateBy,
+      updateTime: props.values.updateTime,
+      remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit({ ...values, menuIds } as RoleFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.role.title',
+        defaultMessage: '编辑角色信息',
+      })}
+      forceRender
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProForm
+        form={form}
+        grid={true}
+        layout="horizontal"
+        submitter={false}
+        onFinish={handleFinish}>
+        <ProFormDigit
+          name="roleId"
+          label={intl.formatMessage({
+            id: 'system.role.role_id',
+            defaultMessage: '角色编号',
+          })}
+          placeholder="请输入角色编号"
+          disabled
+          hidden={true}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入角色编号!" defaultMessage="请输入角色编号!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="roleName"
+          label={intl.formatMessage({
+            id: 'system.role.role_name',
+            defaultMessage: '角色名称',
+          })}
+          placeholder="请输入角色名称"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入角色名称!" defaultMessage="请输入角色名称!" />,
+            },
+          ]}
+        />
+        <ProFormText
+          name="roleKey"
+          label={intl.formatMessage({
+            id: 'system.role.role_key',
+            defaultMessage: '权限字符串',
+          })}
+          placeholder="请输入角色权限字符串"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入角色权限字符串!" defaultMessage="请输入角色权限字符串!" />,
+            },
+          ]}
+        />
+        <ProFormDigit
+          name="roleSort"
+          label={intl.formatMessage({
+            id: 'system.role.role_sort',
+            defaultMessage: '显示顺序',
+          })}
+          placeholder="请输入显示顺序"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入显示顺序!" defaultMessage="请输入显示顺序!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: 1
+          }}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.role.status',
+            defaultMessage: '角色状态',
+          })}
+          placeholder="请输入角色状态"
+          rules={[
+            {
+              required: true,
+              message: <FormattedMessage id="请输入角色状态!" defaultMessage="请输入角色状态!" />,
+            },
+          ]}
+          fieldProps = {{
+            defaultValue: "0"
+          }}
+        />
+        <ProForm.Item
+          name="menuIds"
+          label={intl.formatMessage({
+            id: 'system.role.auth',
+            defaultMessage: '菜单权限',
+          })}
+        >
+          <Tree
+            checkable={true}
+            multiple={true}
+            checkStrictly={true}
+            defaultExpandAll={false}
+            treeData={menuTree}
+            defaultCheckedKeys={menuCheckedKeys}
+            onCheck={(checkedKeys: any) => {             
+              return setMenuIds(checkedKeys.checked);
+            }}
+          />
+        </ProForm.Item>
+        <ProFormTextArea
+          name="remark"
+          label={intl.formatMessage({
+            id: 'system.role.remark',
+            defaultMessage: '备注',
+          })}
+          placeholder="请输入备注"
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入备注!" defaultMessage="请输入备注!" />,
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default RoleForm;
diff --git a/react-ui/src/pages/System/Role/index.tsx b/react-ui/src/pages/System/Role/index.tsx
new file mode 100644
index 0000000..ea15b2e
--- /dev/null
+++ b/react-ui/src/pages/System/Role/index.tsx
@@ -0,0 +1,513 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess, history } from '@umijs/max';
+import { DataNode } from 'antd/es/tree';
+import { Button, message, Modal, Dropdown, FormInstance, Space, Switch } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, EditOutlined, DeleteOutlined, ExclamationCircleOutlined, DownOutlined } from '@ant-design/icons';
+import { getRoleList, removeRole, addRole, updateRole, exportRole, getRoleMenuList, changeRoleStatus, updateRoleDataScope, getDeptTreeSelect, getRole } from '@/services/system/role';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import { formatTreeData } from '@/utils/tree';
+import { getMenuTree } from '@/services/system/menu';
+import DataScopeForm from './components/DataScope';
+
+const { confirm } = Modal;
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.Role) => {
+  const hide = message.loading('正在添加');
+  try {
+    const resp = await addRole({ ...fields });
+    hide();
+    if (resp.code === 200) {
+      message.success('添加成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.Role) => {
+  const hide = message.loading('正在更新');
+  try {
+    const resp = await updateRole(fields);
+    hide();
+    if (resp.code === 200) {
+      message.success('更新成功');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.Role[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    const resp = await removeRole(selectedRows.map((row) => row.roleId).join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.Role) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.roleId];
+    const resp = await removeRole(params.join(','));
+    hide();
+    if (resp.code === 200) {
+      message.success('删除成功,即将刷新');
+    } else {
+      message.error(resp.msg);
+    }
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportRole();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+
+const RoleTableList: React.FC = () => {
+
+  const [messageApi, contextHolder] = message.useMessage();
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+  const [dataScopeModalOpen, setDataScopeModalOpen] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.Role>();
+  const [selectedRows, setSelectedRows] = useState<API.System.Role[]>([]);
+
+  const [menuTree, setMenuTree] = useState<DataNode[]>();
+  const [menuIds, setMenuIds] = useState<string[]>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const showChangeStatusConfirm = (record: API.System.Role) => {
+    let text = record.status === "1" ? "启用" : "停用";
+    const newStatus = record.status === '0' ? '1' : '0';
+    confirm({
+      title: `确认要${text}${record.roleName}角色吗?`,
+      onOk() {
+        changeRoleStatus(record.roleId, newStatus).then(resp => {
+          if (resp.code === 200) {
+            messageApi.open({
+              type: 'success',
+              content: '更新成功!',
+            });
+            actionRef.current?.reload();
+          } else {
+            messageApi.open({
+              type: 'error',
+              content: '更新失败!',
+            });
+          }
+        });
+      },
+    });
+  };
+
+  const columns: ProColumns<API.System.Role>[] = [
+    {
+      title: <FormattedMessage id="system.role.role_id" defaultMessage="角色编号" />,
+      dataIndex: 'roleId',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.role.role_name" defaultMessage="角色名称" />,
+      dataIndex: 'roleName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.role.role_key" defaultMessage="角色权限字符串" />,
+      dataIndex: 'roleKey',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.role.role_sort" defaultMessage="显示顺序" />,
+      dataIndex: 'roleSort',
+      valueType: 'text',
+      hideInSearch: true,
+    },
+    {
+      title: <FormattedMessage id="system.role.status" defaultMessage="角色状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (
+          <Switch
+            checked={record.status === '0'}
+            checkedChildren="正常"
+            unCheckedChildren="停用"
+            defaultChecked
+            onClick={() => showChangeStatusConfirm(record)}
+          />)
+      },
+    },
+    {
+      title: <FormattedMessage id="system.role.create_time" defaultMessage="创建时间" />,
+      dataIndex: 'createTime',
+      valueType: 'dateRange',
+      render: (_, record) => {
+        return (<span>{record.createTime.toString()} </span>);
+      },
+      search: {
+        transform: (value) => {
+          return {
+            'params[beginTime]': value[0],
+            'params[endTime]': value[1],
+          };
+        },
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          icon=<EditOutlined />
+          hidden={!access.hasPerms('system:role:edit')}
+          onClick={() => {
+            getRoleMenuList(record.roleId).then((res) => {
+              if (res.code === 200) {
+                const treeData = formatTreeData(res.menus);
+                setMenuTree(treeData);
+                setMenuIds(res.checkedKeys.map(item => {
+                  return `${item}`
+                }));
+                setModalVisible(true);
+                setCurrentRow(record);
+              } else {
+                message.warning(res.msg);
+              }
+            });
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="batchRemove"
+          icon=<DeleteOutlined />
+          hidden={!access.hasPerms('system:role:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+        <Dropdown
+          key="more"
+          menu={{
+            items: [
+              {
+                label: '数据权限',
+                key: 'datascope',
+                disabled: !access.hasPerms('system:role:edit'),
+              },
+              {
+                label: '分配用户',
+                key: 'authUser',
+                disabled: !access.hasPerms('system:role:edit'),
+              },
+            ],
+            onClick: ({ key }: any) => {
+              if (key === 'datascope') {
+                getRole(record.roleId).then(resp => {
+                  if(resp.code === 200) {
+                    setCurrentRow(resp.data);
+                    setDataScopeModalOpen(true);
+                  }
+                })
+                getDeptTreeSelect(record.roleId).then(resp => {
+                  if (resp.code === 200) {
+                    setMenuTree(formatTreeData(resp.depts));
+                    setMenuIds(resp.checkedKeys.map((item:number) => {
+                      return `${item}`
+                    }));
+                  }
+                })
+              }
+              else if (key === 'authUser') {
+                history.push(`/system/role-auth/user/${record.roleId}`);
+              }
+            }
+          }}
+        >
+          <a onClick={(e) => e.preventDefault()}>
+            <Space>
+              <DownOutlined />
+              更多
+            </Space>
+          </a>
+        </Dropdown>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      {contextHolder}
+      <div style={{ width: '100%', float: 'right' }}>
+        <ProTable<API.System.Role>
+          headerTitle={intl.formatMessage({
+            id: 'pages.searchTable.title',
+            defaultMessage: '信息',
+          })}
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="roleId"
+          key="roleList"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="add"
+              hidden={!access.hasPerms('system:role:add')}
+              onClick={async () => {
+                getMenuTree().then((res: any) => {
+                  if (res.code === 200) {
+                    const treeData = formatTreeData(res.data);
+                    setMenuTree(treeData);
+                    setMenuIds([]);
+                    setModalVisible(true);
+                    setCurrentRow(undefined);
+                  } else {
+                    message.warning(res.msg);
+                  }
+                });
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+            </Button>,
+            <Button
+              type="primary"
+              key="remove"
+              danger
+              hidden={selectedRows?.length === 0 || !access.hasPerms('system:role:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '是否确认删除所选数据项?',
+                  icon: <ExclamationCircleOutlined />,
+                  content: '请谨慎操作',
+                  async onOk() {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                  onCancel() { },
+                });
+              }}
+            >
+              <DeleteOutlined />
+              <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+            </Button>,
+            <Button
+              type="primary"
+              key="export"
+              hidden={!access.hasPerms('system:role:export')}
+              onClick={async () => {
+                handleExport();
+              }}
+            >
+              <PlusOutlined />
+              <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getRoleList({ ...params } as API.System.RoleListParams).then((res) => {
+              const result = {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+              return result;
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+      </div>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:role:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.roleId) {
+            success = await handleUpdate({ ...values } as API.System.Role);
+          } else {
+            success = await handleAdd({ ...values } as API.System.Role);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        menuTree={menuTree || []}
+        menuCheckedKeys={menuIds || []}
+        statusOptions={statusOptions}
+      />
+      <DataScopeForm
+        onSubmit={async (values: any) => {
+          const success = await updateRoleDataScope(values);
+          if (success) {
+            setDataScopeModalOpen(false);
+            setSelectedRows([]);
+            setCurrentRow(undefined);
+            message.success('配置成功。');
+          }
+        }}
+        onCancel={() => {
+          setDataScopeModalOpen(false);
+          setSelectedRows([]);
+          setCurrentRow(undefined);
+        }}
+        open={dataScopeModalOpen}
+        values={currentRow || {}}
+        deptTree={menuTree || []}
+        deptCheckedKeys={menuIds || []}
+      />
+    </PageContainer>
+  );
+};
+
+export default RoleTableList;
diff --git a/react-ui/src/pages/System/User/components/AuthRole.tsx b/react-ui/src/pages/System/User/components/AuthRole.tsx
new file mode 100644
index 0000000..120d966
--- /dev/null
+++ b/react-ui/src/pages/System/User/components/AuthRole.tsx
@@ -0,0 +1,81 @@
+import React, { useEffect } from 'react';
+import { Form, Modal } from 'antd';
+import { useIntl } from '@umijs/max';
+import { ProForm, ProFormSelect } from '@ant-design/pro-components';
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/06
+ * 
+ * */
+
+export type FormValueType = any & Partial<API.System.Dept>;
+
+export type AuthRoleFormProps = {
+    onCancel: (flag?: boolean, formVals?: FormValueType) => void;
+    onSubmit: (values: FormValueType) => Promise<void>;
+    open: boolean;
+    roleIds: number[];
+    roles: string[];
+};
+
+const AuthRoleForm: React.FC<AuthRoleFormProps> = (props) => {
+    const [form] = Form.useForm();
+
+    useEffect(() => {
+        form.resetFields();
+        form.setFieldValue( 'roleIds', props.roleIds);
+    });
+
+    const intl = useIntl();
+    const handleOk = () => {
+        form.submit();
+    };
+    const handleCancel = () => {
+        props.onCancel();
+    };
+    const handleFinish = async (values: Record<string, any>) => {
+        props.onSubmit(values as FormValueType);
+    };
+
+    return (
+        <Modal
+            width={640}
+            title={intl.formatMessage({
+                id: 'system.user.auth.role',
+                defaultMessage: '分配角色',
+            })}
+            open={props.open}
+            destroyOnClose
+            forceRender
+            onOk={handleOk}
+            onCancel={handleCancel}
+        >
+            <ProForm
+                form={form}
+                grid={true}
+                layout="horizontal"
+                onFinish={handleFinish}
+                initialValues={{
+                    login_password: '',
+                    confirm_password: '',
+                }}
+            >
+                <ProFormSelect
+                    name="roleIds"
+                    mode="multiple"
+                    label={intl.formatMessage({
+                        id: 'system.user.role',
+                        defaultMessage: '角色',
+                    })}
+                    options={props.roles}
+                    placeholder="请选择角色"
+                    rules={[{ required: true, message: '请选择角色!' }]}
+                />
+            </ProForm>
+        </Modal>
+    );
+};
+
+export default AuthRoleForm;
diff --git a/react-ui/src/pages/System/User/components/DeptTree.tsx b/react-ui/src/pages/System/User/components/DeptTree.tsx
new file mode 100644
index 0000000..d1a085c
--- /dev/null
+++ b/react-ui/src/pages/System/User/components/DeptTree.tsx
@@ -0,0 +1,69 @@
+import React, { useState, useEffect } from 'react';
+import { Tree, message } from 'antd';
+import { getDeptTree } from '@/services/system/user';
+
+const { DirectoryTree } = Tree;
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/06
+ * 
+ * */
+
+
+export type TreeProps = {
+  onSelect: (values: any) => Promise<void>;
+};
+
+const DeptTree: React.FC<TreeProps> = (props) => {
+  const [treeData, setTreeData] = useState<any>([]);
+  const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
+  const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true);
+
+  const fetchDeptList = async () => {
+    const hide = message.loading('正在查询');
+    try {
+      await getDeptTree({}).then((res: any) => {
+        const exKeys = [];
+        exKeys.push('1');
+        setTreeData(res);
+        exKeys.push(res[0].children[0].id);
+        setExpandedKeys(exKeys);
+        props.onSelect(res[0].children[0]);
+      });
+      hide();
+      return true;
+    } catch (error) {
+      hide();
+      return false;
+    }
+  };
+
+  useEffect(() => {
+    fetchDeptList();
+  }, []);
+
+  const onSelect = (keys: React.Key[], info: any) => {
+    props.onSelect(info.node);
+  };
+
+  const onExpand = (expandedKeysValue: React.Key[]) => {
+    setExpandedKeys(expandedKeysValue);
+    setAutoExpandParent(false);
+  };
+
+  return (
+    <DirectoryTree
+      // multiple
+      defaultExpandAll
+      onExpand={onExpand}
+      expandedKeys={expandedKeys}
+      autoExpandParent={autoExpandParent}
+      onSelect={onSelect}
+      treeData={treeData}
+    />
+  );
+};
+
+export default DeptTree;
diff --git a/react-ui/src/pages/System/User/components/ResetPwd.tsx b/react-ui/src/pages/System/User/components/ResetPwd.tsx
new file mode 100644
index 0000000..5523cd6
--- /dev/null
+++ b/react-ui/src/pages/System/User/components/ResetPwd.tsx
@@ -0,0 +1,95 @@
+import React from 'react';
+import { Form, Modal } from 'antd';
+import { useIntl } from '@umijs/max';
+import { ProForm, ProFormText } from '@ant-design/pro-components';
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/06
+ * 
+ * */
+
+export type FormValueType = any & Partial<API.System.User>;
+
+export type UpdateFormProps = {
+  onCancel: (flag?: boolean, formVals?: FormValueType) => void;
+  onSubmit: (values: FormValueType) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.User>;
+};
+
+const UpdateForm: React.FC<UpdateFormProps> = (props) => {
+  const [form] = Form.useForm();
+  const loginPassword = Form.useWatch('password', form);
+  const userId = props.values.userId;
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit({ ...values, userId } as FormValueType);
+  };
+
+  const checkPassword = (rule: any, value: string) => {
+    if (value === loginPassword) {
+      // 校验条件自定义
+      return Promise.resolve();
+    }
+    return Promise.reject(new Error('两次密码输入不一致'));
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.user.reset.password',
+        defaultMessage: '密码重置',
+      })}
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProForm
+        grid={true}
+        form={form}
+        layout="horizontal"
+        onFinish={handleFinish}
+        initialValues={{
+          password: '',
+          confirm_password: '',
+        }}
+      >
+        <p>请输入用户{props.values.userName}的新密码!</p>
+        <ProFormText.Password
+          name="password"
+          label="登录密码"
+          rules={[
+            {
+              required: true,
+              message: '登录密码不可为空。',
+            },
+          ]}
+        />
+        <ProFormText.Password
+          name="confirm_password"
+          label="确认密码"
+          rules={[
+            {
+              required: true,
+              message: "确认密码",
+            },
+            { validator: checkPassword },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default UpdateForm;
diff --git a/react-ui/src/pages/System/User/edit.tsx b/react-ui/src/pages/System/User/edit.tsx
new file mode 100644
index 0000000..31d442b
--- /dev/null
+++ b/react-ui/src/pages/System/User/edit.tsx
@@ -0,0 +1,279 @@
+import React, { useEffect } from 'react';
+import {
+  ProForm,
+  ProFormText,
+  ProFormSelect,
+  ProFormRadio,
+  ProFormTextArea,
+  ProFormTreeSelect,
+} from '@ant-design/pro-components';
+import { Form, Modal } from 'antd';
+import { useIntl, FormattedMessage } from '@umijs/max';
+import { DataNode } from 'antd/es/tree';
+import { DictValueEnumObj } from '@/components/DictTag';
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/06
+ * 
+ * */
+
+
+export type UserFormData = Record<string, unknown> & Partial<API.System.User>;
+
+export type UserFormProps = {
+  onCancel: (flag?: boolean, formVals?: UserFormData) => void;
+  onSubmit: (values: UserFormData) => Promise<void>;
+  open: boolean;
+  values: Partial<API.System.User>;
+  sexOptions: DictValueEnumObj;
+  statusOptions: DictValueEnumObj;
+  postIds: number[];
+  posts: string[];
+  roleIds: number[];
+  roles: string[];
+  depts: DataNode[];
+};
+
+const UserForm: React.FC<UserFormProps> = (props) => {
+  const [form] = Form.useForm();
+  const userId = Form.useWatch('userId', form);
+  const { sexOptions, statusOptions, } = props;
+  const { roles, posts, depts } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+      userId: props.values.userId,
+      deptId: props.values.deptId,
+      postIds: props.postIds,
+      roleIds: props.roleIds,
+      userName: props.values.userName,
+      nickName: props.values.nickName,
+      email: props.values.email,
+      phonenumber: props.values.phonenumber,
+      sex: props.values.sex,
+      avatar: props.values.avatar,
+      status: props.values.status,
+      delFlag: props.values.delFlag,
+      loginIp: props.values.loginIp,
+      loginDate: props.values.loginDate,
+      remark: props.values.remark,
+    });
+  }, [form, props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    form.submit();
+  };
+  const handleCancel = () => {
+    props.onCancel();
+  };
+  const handleFinish = async (values: Record<string, any>) => {
+    props.onSubmit(values as UserFormData);
+  };
+
+  return (
+    <Modal
+      width={640}
+      title={intl.formatMessage({
+        id: 'system.user.title',
+        defaultMessage: '编辑用户信息',
+      })}
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <ProForm
+        grid={true}
+        form={form}
+        layout="horizontal"
+        submitter={false}
+        onFinish={handleFinish}>
+        <ProFormText
+          name="nickName"
+          label={intl.formatMessage({
+            id: 'system.user.nick_name',
+            defaultMessage: '用户昵称',
+          })}
+          placeholder="请输入用户昵称"
+          colProps={{ xs: 24, md: 12, xl: 12 }}
+          rules={[
+            {
+              required: true,
+              message: (
+                <FormattedMessage id="请输入用户昵称!" defaultMessage="请输入用户昵称!" />
+              ),
+            },
+          ]}
+        />
+        <ProFormTreeSelect
+          name="deptId"
+          label={intl.formatMessage({
+            id: 'system.user.dept_name',
+            defaultMessage: '部门',
+          })}
+          request={async () => {
+            return depts;
+          }}
+          placeholder="请输入用户部门"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[
+            {
+              required: true,
+              message: (
+                <FormattedMessage id="请输入用户部门!" defaultMessage="请输入用户部门!" />
+              ),
+            },
+          ]}
+        />
+        <ProFormText
+          name="phonenumber"
+          label={intl.formatMessage({
+            id: 'system.user.phonenumber',
+            defaultMessage: '手机号码',
+          })}
+          placeholder="请输入手机号码"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[
+            {
+              required: false,
+              message: (
+                <FormattedMessage id="请输入手机号码!" defaultMessage="请输入手机号码!" />
+              ),
+            },
+          ]}
+        />
+        <ProFormText
+          name="email"
+          label={intl.formatMessage({
+            id: 'system.user.email',
+            defaultMessage: '用户邮箱',
+          })}
+          placeholder="请输入用户邮箱"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[
+            {
+              required: false,
+              message: (
+                <FormattedMessage id="请输入用户邮箱!" defaultMessage="请输入用户邮箱!" />
+              ),
+            },
+          ]}
+        />
+        <ProFormText
+          name="userName"
+          label={intl.formatMessage({
+            id: 'system.user.user_name',
+            defaultMessage: '用户账号',
+          })}
+          hidden={userId}
+          placeholder="请输入用户账号"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[
+            {
+              required: true,
+            },
+          ]}
+        />
+        <ProFormText.Password
+          name="password"
+          label={intl.formatMessage({
+            id: 'system.user.password',
+            defaultMessage: '密码',
+          })}
+          hidden={userId}
+          placeholder="请输入密码"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入密码!" defaultMessage="请输入密码!" />,
+            },
+          ]}
+        />
+        <ProFormSelect
+          valueEnum={sexOptions}
+          name="sex"
+          label={intl.formatMessage({
+            id: 'system.user.sex',
+            defaultMessage: '用户性别',
+          })}
+          initialValue={'0'}
+          placeholder="请输入用户性别"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[
+            {
+              required: false,
+              message: (
+                <FormattedMessage id="请输入用户性别!" defaultMessage="请输入用户性别!" />
+              ),
+            },
+          ]}
+        />
+        <ProFormRadio.Group
+          valueEnum={statusOptions}
+          name="status"
+          label={intl.formatMessage({
+            id: 'system.user.status',
+            defaultMessage: '帐号状态',
+          })}
+          initialValue={'0'}
+          placeholder="请输入帐号状态"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[
+            {
+              required: false,
+              message: (
+                <FormattedMessage id="请输入帐号状态!" defaultMessage="请输入帐号状态!" />
+              ),
+            },
+          ]}
+        />
+        <ProFormSelect
+          name="postIds"
+          mode="multiple"
+          label={intl.formatMessage({
+            id: 'system.user.post',
+            defaultMessage: '岗位',
+          })}
+          options={posts}
+          placeholder="请选择岗位"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[{ required: true, message: '请选择岗位!' }]}
+        />
+        <ProFormSelect
+          name="roleIds"
+          mode="multiple"
+          label={intl.formatMessage({
+            id: 'system.user.role',
+            defaultMessage: '角色',
+          })}
+          options={roles}
+          placeholder="请选择角色"
+          colProps={{ md: 12, xl: 12 }}
+          rules={[{ required: true, message: '请选择角色!' }]}
+        />
+        <ProFormTextArea
+          name="remark"
+          label={intl.formatMessage({
+            id: 'system.user.remark',
+            defaultMessage: '备注',
+          })}
+          placeholder="请输入备注"
+          colProps={{ md: 24, xl: 24 }}
+          rules={[
+            {
+              required: false,
+              message: <FormattedMessage id="请输入备注!" defaultMessage="请输入备注!" />,
+            },
+          ]}
+        />
+      </ProForm>
+    </Modal>
+  );
+};
+
+export default UserForm;
diff --git a/react-ui/src/pages/System/User/index.tsx b/react-ui/src/pages/System/User/index.tsx
new file mode 100644
index 0000000..6435787
--- /dev/null
+++ b/react-ui/src/pages/System/User/index.tsx
@@ -0,0 +1,561 @@
+
+import React, { useState, useRef, useEffect } from 'react';
+import { useIntl, FormattedMessage, useAccess } from '@umijs/max';
+import { Card, Col, Dropdown, FormInstance, Row, Space, Switch } from 'antd';
+import { Button, message, Modal } from 'antd';
+import { ActionType, FooterToolbar, PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, DownOutlined, EditOutlined } from '@ant-design/icons';
+import { getUserList, removeUser, addUser, updateUser, exportUser, getUser, changeUserStatus, updateAuthRole, resetUserPwd } from '@/services/system/user';
+import UpdateForm from './edit';
+import { getDictValueEnum } from '@/services/system/dict';
+import { DataNode } from 'antd/es/tree';
+import { getDeptTree } from '@/services/system/user';
+import DeptTree from './components/DeptTree';
+import ResetPwd from './components/ResetPwd';
+import { getPostList } from '@/services/system/post';
+import { getRoleList } from '@/services/system/role';
+import AuthRoleForm from './components/AuthRole';
+
+const { confirm } = Modal;
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2023/02/06
+ *
+ * */
+
+/**
+ * 添加节点
+ *
+ * @param fields
+ */
+const handleAdd = async (fields: API.System.User) => {
+  const hide = message.loading('正在添加');
+  try {
+    await addUser({ ...fields });
+    hide();
+    message.success('添加成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('添加失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 更新节点
+ *
+ * @param fields
+ */
+const handleUpdate = async (fields: API.System.User) => {
+  const hide = message.loading('正在配置');
+  try {
+    await updateUser(fields);
+    hide();
+    message.success('配置成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: API.System.User[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    await removeUser(selectedRows.map((row) => row.userId).join(','));
+    hide();
+    message.success('删除成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: API.System.User) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.userId];
+    await removeUser(params.join(','));
+    hide();
+    message.success('删除成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+/**
+ * 导出数据
+ *
+ *
+ */
+const handleExport = async () => {
+  const hide = message.loading('正在导出');
+  try {
+    await exportUser();
+    hide();
+    message.success('导出成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('导出失败,请重试');
+    return false;
+  }
+};
+
+const UserTableList: React.FC = () => {
+  const [messageApi, contextHolder] = message.useMessage();
+
+  const formTableRef = useRef<FormInstance>();
+
+  const [modalVisible, setModalVisible] = useState<boolean>(false);
+  const [resetPwdModalVisible, setResetPwdModalVisible] = useState<boolean>(false);
+  const [authRoleModalVisible, setAuthRoleModalVisible] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<API.System.User>();
+  const [selectedRows, setSelectedRows] = useState<API.System.User[]>([]);
+
+  const [selectDept, setSelectDept] = useState<any>({ id: 0 });
+  const [sexOptions, setSexOptions] = useState<any>([]);
+  const [statusOptions, setStatusOptions] = useState<any>([]);
+
+  const [postIds, setPostIds] = useState<number[]>();
+  const [postList, setPostList] = useState<any[]>();
+  const [roleIds, setRoleIds] = useState<number[]>();
+  const [roleList, setRoleList] = useState<any[]>();
+  const [deptTree, setDeptTree] = useState<DataNode[]>();
+
+  const access = useAccess();
+
+  /** 国际化配置 */
+  const intl = useIntl();
+
+  useEffect(() => {
+    getDictValueEnum('sys_user_sex').then((data) => {
+      setSexOptions(data);
+    });
+    getDictValueEnum('sys_normal_disable').then((data) => {
+      setStatusOptions(data);
+    });
+  }, []);
+
+  const showChangeStatusConfirm = (record: API.System.User) => {
+    let text = record.status === "1" ? "启用" : "停用";
+    const newStatus = record.status === '0' ? '1' : '0';
+    confirm({
+      title: `确认要${text}${record.userName}用户吗?`,
+      onOk() {
+        changeUserStatus(record.userId, newStatus).then(resp => {
+          if (resp.code === 200) {
+            messageApi.open({
+              type: 'success',
+              content: '更新成功!',
+            });
+            actionRef.current?.reload();
+          } else {
+            messageApi.open({
+              type: 'error',
+              content: '更新失败!',
+            });
+          }
+        });
+      },
+    });
+  };
+
+  const fetchUserInfo = async (userId: number) => {
+    const res = await getUser(userId);
+    setPostIds(res.postIds);
+    setPostList(
+      res.posts.map((item: any) => {
+        return {
+          value: item.postId,
+          label: item.postName,
+        };
+      }),
+    );
+    setRoleIds(res.roleIds);
+    setRoleList(
+      res.roles.map((item: any) => {
+        return {
+          value: item.roleId,
+          label: item.roleName,
+        };
+      }),
+    );
+  };
+
+  const columns: ProColumns<API.System.User>[] = [
+    {
+      title: <FormattedMessage id="system.user.user_id" defaultMessage="用户编号" />,
+      dataIndex: 'deptId',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.user.user_name" defaultMessage="用户账号" />,
+      dataIndex: 'userName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.user.nick_name" defaultMessage="用户昵称" />,
+      dataIndex: 'nickName',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.user.dept_name" defaultMessage="部门" />,
+      dataIndex: ['dept', 'deptName'],
+      valueType: 'text',
+      hideInSearch: true
+    },
+    {
+      title: <FormattedMessage id="system.user.phonenumber" defaultMessage="手机号码" />,
+      dataIndex: 'phonenumber',
+      valueType: 'text',
+    },
+    {
+      title: <FormattedMessage id="system.user.status" defaultMessage="帐号状态" />,
+      dataIndex: 'status',
+      valueType: 'select',
+      valueEnum: statusOptions,
+      render: (_, record) => {
+        return (
+          <Switch
+            checked={record.status === '0'}
+            checkedChildren="正常"
+            unCheckedChildren="停用"
+            defaultChecked
+            onClick={() => showChangeStatusConfirm(record)}
+          />)
+      },
+    },
+    {
+      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="操作" />,
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="edit"
+          icon=<EditOutlined />
+          hidden={!access.hasPerms('system:user:edit')}
+          onClick={async () => {
+            fetchUserInfo(record.userId);
+            const treeData = await getDeptTree({});
+            setDeptTree(treeData);
+            setModalVisible(true);
+            setCurrentRow(record);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          icon=<DeleteOutlined />
+          key="batchRemove"
+          hidden={!access.hasPerms('system:user:remove')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除',
+              content: '确定删除该项吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+        <Dropdown
+          key="more"
+          menu={{
+            items: [
+              {
+                label: <FormattedMessage id="system.user.reset.password" defaultMessage="密码重置" />,
+                key: 'reset',
+                disabled: !access.hasPerms('system:user:edit'),
+              },
+              {
+                label: '分配角色',
+                key: 'authRole',
+                disabled: !access.hasPerms('system:user:edit'),
+              },
+            ],
+            onClick: ({ key }) => {
+              if (key === 'reset') {
+                setResetPwdModalVisible(true);
+                setCurrentRow(record);
+              }
+              else if (key === 'authRole') {
+                fetchUserInfo(record.userId);
+                setAuthRoleModalVisible(true);
+                setCurrentRow(record);
+              }
+            }
+          }}
+        >
+          <a onClick={(e) => e.preventDefault()}>
+            <Space>
+              <DownOutlined />
+              更多
+            </Space>
+          </a>
+        </Dropdown>,
+      ],
+    },
+  ];
+
+  return (
+    <PageContainer>
+      {contextHolder}
+      <Row gutter={[16, 24]}>
+        <Col lg={6} md={24}>
+          <Card>
+            <DeptTree
+              onSelect={async (value: any) => {
+                setSelectDept(value);
+                if (actionRef.current) {
+                  formTableRef?.current?.submit();
+                }
+              }}
+            />
+          </Card>
+        </Col>
+        <Col lg={18} md={24}>
+          <ProTable<API.System.User>
+            headerTitle={intl.formatMessage({
+              id: 'pages.searchTable.title',
+              defaultMessage: '信息',
+            })}
+            actionRef={actionRef}
+            formRef={formTableRef}
+            rowKey="userId"
+            key="userList"
+            search={{
+              labelWidth: 120,
+            }}
+            toolBarRender={() => [
+              <Button
+                type="primary"
+                key="add"
+                hidden={!access.hasPerms('system:user:add')}
+                onClick={async () => {
+                  const treeData = await getDeptTree({});
+                  setDeptTree(treeData);
+
+                  const postResp = await getPostList()
+                  if (postResp.code === 200) {
+                    setPostList(
+                      postResp.rows.map((item: any) => {
+                        return {
+                          value: item.postId,
+                          label: item.postName,
+                        };
+                      }),
+                    );
+                  }
+
+                  const roleResp = await getRoleList()
+                  if (roleResp.code === 200) {
+                    setRoleList(
+                      roleResp.rows.map((item: any) => {
+                        return {
+                          value: item.roleId,
+                          label: item.roleName,
+                        };
+                      }),
+                    );
+                  }
+                  setCurrentRow(undefined);
+                  setModalVisible(true);
+                }}
+              >
+                <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="新建" />
+              </Button>,
+              <Button
+                type="primary"
+                key="remove"
+                danger
+                hidden={selectedRows?.length === 0 || !access.hasPerms('system:user:remove')}
+                onClick={async () => {
+                  Modal.confirm({
+                    title: '是否确认删除所选数据项?',
+                    icon: <ExclamationCircleOutlined />,
+                    content: '请谨慎操作',
+                    async onOk() {
+                      const success = await handleRemove(selectedRows);
+                      if (success) {
+                        setSelectedRows([]);
+                        actionRef.current?.reloadAndRest?.();
+                      }
+                    },
+                    onCancel() { },
+                  });
+                }}
+              >
+                <DeleteOutlined />
+                <FormattedMessage id="pages.searchTable.delete" defaultMessage="删除" />
+              </Button>,
+              <Button
+                type="primary"
+                key="export"
+                hidden={!access.hasPerms('system:user:export')}
+                onClick={async () => {
+                  handleExport();
+                }}
+              >
+                <PlusOutlined />
+                <FormattedMessage id="pages.searchTable.export" defaultMessage="导出" />
+              </Button>,
+            ]}
+            request={(params) =>
+              getUserList({ ...params, deptId: selectDept.id } as API.System.UserListParams).then((res) => {
+                const result = {
+                  data: res.rows,
+                  total: res.total,
+                  success: true,
+                };
+                return result;
+              })
+            }
+            columns={columns}
+            rowSelection={{
+              onChange: (_, selectedRows) => {
+                setSelectedRows(selectedRows);
+              },
+            }}
+          />
+        </Col>
+      </Row>
+      {selectedRows?.length > 0 && (
+        <FooterToolbar
+          extra={
+            <div>
+              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />
+              <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>
+              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+            </div>
+          }
+        >
+          <Button
+            key="remove"
+            danger
+            hidden={!access.hasPerms('system:user:del')}
+            onClick={async () => {
+              Modal.confirm({
+                title: '删除',
+                content: '确定删除该项吗?',
+                okText: '确认',
+                cancelText: '取消',
+                onOk: async () => {
+                  const success = await handleRemove(selectedRows);
+                  if (success) {
+                    setSelectedRows([]);
+                    actionRef.current?.reloadAndRest?.();
+                  }
+                },
+              });
+            }}
+          >
+            <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+          </Button>
+        </FooterToolbar>
+      )}
+      <UpdateForm
+        onSubmit={async (values) => {
+          let success = false;
+          if (values.userId) {
+            success = await handleUpdate({ ...values } as API.System.User);
+          } else {
+            success = await handleAdd({ ...values } as API.System.User);
+          }
+          if (success) {
+            setModalVisible(false);
+            setCurrentRow(undefined);
+            if (actionRef.current) {
+              actionRef.current.reload();
+            }
+          }
+        }}
+        onCancel={() => {
+          setModalVisible(false);
+          setCurrentRow(undefined);
+        }}
+        open={modalVisible}
+        values={currentRow || {}}
+        sexOptions={sexOptions}
+        statusOptions={statusOptions}
+        posts={postList || []}
+        postIds={postIds || []}
+        roles={roleList || []}
+        roleIds={roleIds || []}
+        depts={deptTree || []}
+      />
+      <ResetPwd
+        onSubmit={async (values: any) => {
+          const success = await resetUserPwd(values.userId, values.password);
+          if (success) {
+            setResetPwdModalVisible(false);
+            setSelectedRows([]);
+            setCurrentRow(undefined);
+            message.success('密码重置成功。');
+          }
+        }}
+        onCancel={() => {
+          setResetPwdModalVisible(false);
+          setSelectedRows([]);
+          setCurrentRow(undefined);
+        }}
+        open={resetPwdModalVisible}
+        values={currentRow || {}}
+      />
+      <AuthRoleForm
+        onSubmit={async (values: any) => {
+          const success = await updateAuthRole(values);
+          if (success) {
+            setAuthRoleModalVisible(false);
+            setSelectedRows([]);
+            setCurrentRow(undefined);
+            message.success('配置成功。');
+          }
+        }}
+        onCancel={() => {
+          setAuthRoleModalVisible(false);
+          setSelectedRows([]);
+          setCurrentRow(undefined);
+        }}
+        open={authRoleModalVisible}
+        roles={roleList || []}
+        roleIds={roleIds || []}
+      />
+    </PageContainer>
+  );
+};
+
+export default UserTableList;
diff --git a/react-ui/src/pages/Tool/Gen/components/BaseInfo.tsx b/react-ui/src/pages/Tool/Gen/components/BaseInfo.tsx
new file mode 100644
index 0000000..6a0466f
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/components/BaseInfo.tsx
@@ -0,0 +1,124 @@
+import { Button, Col, Form, message, Row } from 'antd';
+import React, { Fragment, useEffect } from 'react';
+import { history } from '@umijs/max';
+import styles from '../style.less';
+import { ProForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
+
+export type BaseInfoProps = {
+  values?: any;
+  onStepSubmit?: any;
+};
+
+const BaseInfo: React.FC<BaseInfoProps> = (props) => {
+  const [form] = Form.useForm();
+  const { onStepSubmit } = props;
+
+  useEffect(() => {
+    form.resetFields();
+    form.setFieldsValue({
+      tableName: props.values.tableName,
+    });
+  });
+
+  const onValidateForm = async () => {
+    const values = await form.validateFields();
+    if (onStepSubmit) {
+      onStepSubmit('base', values);
+    }
+  };
+
+  return (
+    <Fragment>
+      <Row>
+        <Col span={24}>
+          <ProForm
+            form={form}
+            onFinish={async () => {
+              message.success('提交成功');
+            }}
+            initialValues={{
+              tableName: props.values?.tableName,
+              tableComment: props.values?.tableComment,
+              className: props.values?.className,
+              functionAuthor: props.values?.functionAuthor,
+              remark: props.values?.remark,
+            }}
+            submitter={{
+              resetButtonProps: {
+                style: { display: 'none' },
+              },
+              submitButtonProps: {
+                style: { display: 'none' },
+              },
+            }}
+          >
+            <Row>
+              <Col span={12} order={1}>
+                <ProFormText
+
+                  name="tableName"
+                  label="表名称"
+                  rules={[
+                    {
+                      required: true,
+                      message: '表名称不可为空。',
+                    },
+                  ]}
+                />
+              </Col>
+              <Col span={12} order={2}>
+                <ProFormText
+                  name="tableComment"
+                  label="表描述"
+                />
+              </Col>
+            </Row>
+            <Row>
+              <Col span={12} order={1}>
+                <ProFormText
+
+                  name="className"
+                  label="实体类名称"
+                  rules={[
+                    {
+                      required: true,
+                      message: '实体类名称不可为空。',
+                    },
+                  ]}
+                />
+              </Col>
+              <Col span={12} order={2}>
+                <ProFormText name="functionAuthor" label="作者" />
+              </Col>
+            </Row>
+            <Row>
+              <Col span={24}>
+                <ProFormTextArea name="remark" label="备注" />
+              </Col>
+            </Row>
+          </ProForm>
+        </Col>
+      </Row>
+      <Row justify="center">
+        <Col span={4}>
+          <Button
+            type="primary"
+            className={styles.step_buttons}
+            onClick={() => {
+              history.back();
+            }}
+          >
+            返回
+          </Button>
+        </Col>
+        <Col span={4}>
+          <Button type="primary" onClick={onValidateForm}>
+            下一步
+          </Button>
+        </Col>
+      </Row>
+    </Fragment>
+  );
+};
+
+export default BaseInfo;
diff --git a/react-ui/src/pages/Tool/Gen/components/ColumnInfo.tsx b/react-ui/src/pages/Tool/Gen/components/ColumnInfo.tsx
new file mode 100644
index 0000000..2db5de4
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/components/ColumnInfo.tsx
@@ -0,0 +1,304 @@
+import React, { Fragment, useEffect, useRef, useState } from 'react';
+import type { GenCodeType } from '../data';
+import { Button, Checkbox, Col, Row, Tag } from 'antd';
+import type { FormInstance } from 'antd';
+import { history } from '@umijs/max';
+import styles from '../style.less';
+import { EditableProTable, ProColumns } from '@ant-design/pro-components';
+
+export type ColumnInfoProps = {
+  parentType?: string;
+  data?: any[];
+  dictData?: any[];
+  onStepSubmit?: any;
+};
+
+const booleanEnum = [
+  {
+    label: 'true',
+    value: '1',
+  },
+  {
+    label: 'false',
+    value: '0',
+  },
+];
+
+const ColumnInfo: React.FC<ColumnInfoProps> = (props) => {
+  const formRef = useRef<FormInstance>();
+
+  const [dataSource, setDataSource] = useState<any[]>();
+
+  const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
+
+  const { data, dictData, onStepSubmit } = props;
+
+  const columns: ProColumns<GenCodeType>[] = [
+    {
+      title: '编号',
+      dataIndex: 'columnId',
+      editable: false,
+      width: 80,
+    },
+    {
+      title: '字段名',
+      dataIndex: 'columnName',
+      editable: false,
+    },
+    {
+      title: '字段描述',
+      dataIndex: 'columnComment',
+      hideInForm: true,
+      hideInSearch: true,
+      width: 200,
+    },
+    {
+      title: '字段类型',
+      dataIndex: 'columnType',
+      editable: false,
+    },
+    {
+      title: 'Java类型',
+      dataIndex: 'javaType',
+      valueType: 'select',
+      valueEnum: {
+        Long: {
+          text: 'Long',
+        },
+        String: {
+          text: 'String',
+        },
+        Integer: {
+          text: 'Integer',
+        },
+        Double: {
+          text: 'Double',
+        },
+        BigDecimal: {
+          text: 'BigDecimal',
+        },
+        Date: {
+          text: 'Date',
+        },
+      },
+    },
+    {
+      title: 'Java属性',
+      dataIndex: 'javaField',
+    },
+    {
+      title: '插入',
+      dataIndex: 'isInsert',
+      valueType: 'select',
+      fieldProps: {
+        options: booleanEnum,
+      },
+      render: (_, record) => {
+        return <Checkbox checked={record.isInsert === '1'} />;
+      },
+    },
+    {
+      title: '编辑',
+      dataIndex: 'isEdit',
+      valueType: 'select',
+      fieldProps: {
+        options: booleanEnum,
+      },
+      render: (_, record) => {
+        return <Checkbox checked={record.isEdit === '1'} />;
+      },
+    },
+    {
+      title: '列表',
+      dataIndex: 'isList',
+      valueType: 'select',
+      fieldProps: {
+        options: booleanEnum,
+      },
+      render: (_, record) => {
+        return <Checkbox checked={record.isList === '1'} />;
+      },
+    },
+    {
+      title: '查询',
+      dataIndex: 'isQuery',
+      valueType: 'select',
+      fieldProps: {
+        options: booleanEnum,
+      },
+      render: (_, record) => {
+        return <Checkbox checked={record.isQuery === '1'} />;
+      },
+    },
+    {
+      title: '查询方式',
+      dataIndex: 'queryType',
+      valueType: 'select',
+      valueEnum: {
+        EQ: {
+          text: '=',
+        },
+        NE: {
+          text: '!=',
+        },
+        GT: {
+          text: '>',
+        },
+        GTE: {
+          text: '>=',
+        },
+        LT: {
+          text: '<',
+        },
+        LTE: {
+          text: '<=',
+        },
+        LIKE: {
+          text: 'LIKE',
+        },
+        BETWEEN: {
+          text: 'BETWEEN',
+        },
+      },
+    },
+    {
+      title: '必填',
+      dataIndex: 'isRequired',
+      valueType: 'select',
+      fieldProps: {
+        options: booleanEnum,
+      },
+      render: (_, record) => {
+        return <Checkbox checked={record.isRequired === '1'} />;
+      },
+    },
+    {
+      title: '显示类型',
+      dataIndex: 'htmlType',
+      hideInSearch: true,
+      valueType: 'select',
+      valueEnum: {
+        input: {
+          text: '文本框',
+        },
+        textarea: {
+          text: '文本域',
+        },
+        select: {
+          text: '下拉框',
+        },
+        radio: {
+          text: '单选框',
+        },
+        checkbox: {
+          text: '复选框',
+        },
+        datetime: {
+          text: '日期控件',
+        },
+        imageUpload: {
+          text: '图片上传',
+        },
+        fileUpload: {
+          text: '文件上传',
+        },
+        editor: {
+          text: '富文本控件',
+        },
+      },
+    },
+    {
+      title: '字典类型',
+      dataIndex: 'dictType',
+      hideInSearch: true,
+      valueType: 'select',
+      fieldProps: {
+        options: dictData,
+      },
+      render: (text) => {
+        return <Tag color="#108ee9">{text}</Tag>;
+      },
+    },
+  ];
+
+  useEffect(() => {
+    setDataSource(data);
+    if (data) {
+      setEditableRowKeys(data.map((item) => item.columnId));
+    }
+  }, [data]);
+
+  const onSubmit = (direction: string) => {
+    if (onStepSubmit) {
+      onStepSubmit('column', dataSource, direction);
+    }
+  };
+
+  const onDataChange = (value: readonly GenCodeType[]) => {
+    setDataSource({ ...value } as []);
+  };
+
+  return (
+    <Fragment>
+      <Row>
+        <Col span={24}>
+          <EditableProTable<GenCodeType>
+            formRef={formRef}
+            rowKey="columnId"
+            search={false}
+            columns={columns}
+            value={dataSource}
+            editable={{
+              type: 'multiple',
+              editableKeys,
+              onChange: setEditableRowKeys,
+              actionRender: (row, config, defaultDoms) => {
+                return [defaultDoms.delete];
+              },
+              onValuesChange: (record, recordList) => {
+                setDataSource(recordList);
+              },
+            }}
+            onChange={onDataChange}
+            recordCreatorProps={false}
+          />
+        </Col>
+      </Row>
+      <Row justify="center">
+        <Col span={4}>
+          <Button
+            type="primary"
+            onClick={() => {
+              history.back();
+            }}
+          >
+            返回
+          </Button>
+        </Col>
+        <Col span={4}>
+          <Button
+            type="primary"
+            className={styles.step_buttons}
+            onClick={() => {
+              onSubmit('prev');
+            }}
+          >
+            上一步
+          </Button>
+        </Col>
+        <Col span={4}>
+          <Button
+            type="primary"
+            onClick={() => {
+              onSubmit('next');
+            }}
+          >
+            下一步
+          </Button>
+        </Col>
+      </Row>
+    </Fragment>
+  );
+};
+
+export default ColumnInfo;
diff --git a/react-ui/src/pages/Tool/Gen/components/GenInfo.tsx b/react-ui/src/pages/Tool/Gen/components/GenInfo.tsx
new file mode 100644
index 0000000..97901ec
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/components/GenInfo.tsx
@@ -0,0 +1,330 @@
+import { Button, Col, Divider, Form, Row, TreeSelect } from 'antd';
+import React, { Fragment, useEffect, useState } from 'react';
+import { history } from '@umijs/max';
+import type { TableInfo } from '../data';
+import styles from '../style.less';
+import { DataNode } from 'antd/es/tree';
+import { ProForm, ProFormRadio, ProFormSelect, ProFormText } from '@ant-design/pro-components';
+
+export type GenInfoProps = {
+  values?: any;
+  menuData?: DataNode[];
+  tableInfo?: TableInfo[];
+  onStepSubmit?: any;
+};
+
+const GenInfo: React.FC<GenInfoProps> = (props) => {
+  const [form] = Form.useForm();
+
+  const [pathType, setPathType] = useState<string>('0');
+  const [tlpType, setTlpType] = useState<string>('curd');
+
+  const [subTablesColumnOptions, setSubTablesColumnOptions] = useState<any[]>();
+
+  const { menuData, tableInfo, onStepSubmit } = props;
+
+  const tablesOptions = tableInfo?.map((item: any) => {
+    return {
+      value: item.tableName,
+      label: `${item.tableName}:${item.tableComment}`,
+    };
+  });
+
+  if (tableInfo) {
+    for (let index = 0; index < tableInfo?.length; index += 1) {
+      const tbl = tableInfo[index];
+      if (tbl.tableName === props.values.subTableName) {
+        const opts = [];
+        tbl.columns.forEach((item) => {
+          opts.push({
+            value: item.columnName,
+            label: `${item.columnName}: ${item.columnComment}`,
+          });
+        });
+        break;
+      }
+    }
+  }
+
+  const treeColumns = props.values.columns.map((item: any) => {
+    return {
+      value: item.columnName,
+      label: `${item.columnName}: ${item.columnComment}`,
+    };
+  });
+
+  const onSubmit = async (direction: string) => {
+    const values = await form.validateFields();
+    onStepSubmit('gen', values, direction);
+  };
+
+  useEffect(() => {
+    setPathType(props.values.genType);
+    setTlpType(props.values.tplCategory);
+  }, [props.values.genType, props.values.tplCategory]);
+
+  return (
+    <Fragment>
+      <Row>
+        <Col span={24}>
+          <ProForm
+            form={form}
+            onFinish={async () => {
+              const values = await form.validateFields();
+              onStepSubmit('gen', values);
+            }}
+            initialValues={{
+              curd: props.values.curd,
+              tree: props.values.tree,
+              sub: props.values.sub,
+              tplCategory: props.values.tplCategory,
+              packageName: props.values.packageName,
+              moduleName: props.values.moduleName,
+              businessName: props.values.businessName,
+              functionName: props.values.functionName,
+              parentMenuId: props.values.parentMenuId,
+              genType: props.values.genType,
+              genPath: props.values.genPath,
+              treeCode: props.values.treeCode,
+              treeParentCode: props.values.treeParentCode,
+              treeName: props.values.treeName,
+              subTableName: props.values.subTableName,
+              subTableFkName: props.values.subTableFkName,
+            }}
+            submitter={{
+              resetButtonProps: {
+                style: { display: 'none' },
+              },
+              submitButtonProps: {
+                style: { display: 'none' },
+              },
+            }}
+          >
+            <Row gutter={[16, 16]}>
+              <Col span={12} order={1}>
+                <ProFormSelect
+                  fieldProps={{
+                    onChange: (val) => {
+                      setTlpType(val);
+                    },
+                  }}
+                  valueEnum={{
+                    crud: '单表(增删改查)',
+                    tree: '树表(增删改查)',
+                    sub: '主子表(增删改查)',
+                  }}
+                  name="tplCategory"
+                  label="生成模板"
+                  rules={[
+                    {
+                      required: true,
+                      message: '选择类型',
+                    },
+                  ]}
+                />
+              </Col>
+              <Col span={12} order={2}>
+                <ProFormText name="packageName" label="生成包路径" />
+              </Col>
+            </Row>
+            <Row gutter={[16, 16]}>
+              <Col span={12} order={1}>
+                <ProFormText name="moduleName" label="生成模块名" />
+              </Col>
+              <Col span={12} order={2}>
+                <ProFormText name="businessName" label="生成业务名" />
+              </Col>
+            </Row>
+            <Row gutter={[16, 16]}>
+              <Col span={12} order={1}>
+                <ProFormText name="functionName" label="生成功能名" />
+              </Col>
+              <Col span={12} order={2}>
+                <ProForm.Item
+                  labelCol={{ span: 20 }}
+                  name="parentMenuId"
+                  label="父菜单"
+                >
+                  <TreeSelect
+                    style={{ width: '74%' }}
+                    defaultValue={props.values.parentMenuId}
+                    treeData={menuData}
+                    placeholder="请选择父菜单"
+                  />
+                </ProForm.Item>
+              </Col>
+            </Row>
+            <Row gutter={[16, 16]}>
+              <Col span={24}>
+                <ProFormRadio.Group
+                  valueEnum={{
+                    '0': 'zip压缩包',
+                    '1': '自定义路径',
+                  }}
+                  name="genType"
+                  label="生成代码方式"
+                  rules={[
+                    {
+                      required: true,
+                      message: '选择类型',
+                    },
+                  ]}
+                  fieldProps={{
+                    onChange: (e) => {
+                      setPathType(e.target.value);
+                    },
+                  }}
+                />
+              </Col>
+            </Row>
+            <Row gutter={[16, 16]}>
+              <Col span={24} order={1}>
+                <ProFormText
+                  hidden={pathType === '0'}
+                  width="md"
+                  name="genPath"
+                  label="自定义路径"
+                />
+              </Col>
+            </Row>
+            <div hidden={tlpType !== 'tree'}>
+              <Divider orientation="left">其他信息</Divider>
+              <Row gutter={[16, 16]}>
+                <Col span={12} order={1}>
+                  <ProFormSelect
+                    name="treeCode"
+                    label="树编码字段"
+                    options={treeColumns}
+                    rules={[
+                      {
+                        required: tlpType === 'tree',
+                        message: '树编码字段',
+                      },
+                    ]}
+                  />
+                </Col>
+                <Col span={12} order={2}>
+                  <ProFormSelect
+                    name="treeParentCode"
+                    label="树父编码字段"
+                    options={treeColumns}
+                    rules={[
+                      {
+                        required: tlpType === 'tree',
+                        message: '树父编码字段',
+                      },
+                    ]}
+                  />
+                </Col>
+              </Row>
+              <Row gutter={[16, 16]}>
+                <Col span={12} order={1}>
+                  <ProFormSelect
+                    name="treeName"
+                    label="树名称字段"
+                    options={treeColumns}
+                    rules={[
+                      {
+                        required: tlpType === 'tree',
+                        message: '树名称字段',
+                      },
+                    ]}
+                  />
+                </Col>
+              </Row>
+            </div>
+            <div hidden={tlpType !== 'sub'}>
+              <Divider orientation="left">关联信息</Divider>
+              <Row gutter={[16, 16]}>
+                <Col span={12} order={1}>
+                  <ProFormSelect
+                    name="subTableName"
+                    label="关联子表的表名"
+                    options={tablesOptions}
+                    rules={[
+                      {
+                        required: tlpType === 'sub',
+                        message: '关联子表的表名',
+                      },
+                    ]}
+                    fieldProps={{
+                      onChange: (val) => {
+                        form.setFieldsValue({
+                          subTableFkName: '',
+                        });
+                        if (tableInfo) {
+                          for (let index = 0; index < tableInfo?.length; index += 1) {
+                            const tbl = tableInfo[index];
+                            if (tbl.tableName === val) {
+                              const opts: any[] = [];
+                              tbl.columns.forEach((item) => {
+                                opts.push({
+                                  value: item.columnName,
+                                  label: `${item.columnName}:${item.columnComment}`,
+                                });
+                              });
+                              setSubTablesColumnOptions(opts);
+                              break;
+                            }
+                          }
+                        }
+                      },
+                    }}
+                  />
+                </Col>
+                <Col span={12} order={2}>
+                  <ProFormSelect
+                    name="subTableFkName"
+                    options={subTablesColumnOptions}
+                    label="子表关联的外键名"
+                    rules={[
+                      {
+                        required: tlpType === 'sub',
+                        message: '子表关联的外键名',
+                      },
+                    ]}
+                  />
+                </Col>
+              </Row>
+            </div>
+          </ProForm>
+        </Col>
+      </Row>
+      <Row justify="center">
+        <Col span={4}>
+          <Button
+            type="primary"
+            onClick={() => {
+              history.back();
+            }}
+          >
+            返回
+          </Button>
+        </Col>
+        <Col span={4}>
+          <Button
+            type="primary"
+            className={styles.step_buttons}
+            onClick={() => {
+              onSubmit('prev');
+            }}
+          >
+            上一步
+          </Button>
+        </Col>
+        <Col span={4}>
+          <Button
+            type="primary"
+            onClick={() => {
+              onSubmit('next');
+            }}
+          >
+            提交
+          </Button>
+        </Col>
+      </Row>
+    </Fragment>
+  );
+};
+
+export default GenInfo;
diff --git a/react-ui/src/pages/Tool/Gen/components/PreviewCode.tsx b/react-ui/src/pages/Tool/Gen/components/PreviewCode.tsx
new file mode 100644
index 0000000..f3611ad
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/components/PreviewCode.tsx
@@ -0,0 +1,50 @@
+import React, { useEffect } from 'react';
+import { useIntl } from '@umijs/max';
+import { Modal, Tabs } from 'antd';
+import type { TabsProps } from 'antd';
+import Highlight from 'react-highlight';
+import 'highlight.js/styles/base16/material.css';
+
+interface PreviewTableProps {
+  open: boolean;
+  data?: any;
+  onHide: () => void;
+}
+
+const PreviewTableCode: React.FC<PreviewTableProps> = (props) => {
+  const intl = useIntl();
+  const panes: any = [];
+  const keys = Object.keys(props.data);
+  keys.forEach((key) => {
+    panes.push({
+        key: key + '1',
+        label: key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm')),
+        children: <Highlight className="java">{props.data[key]}</Highlight>,
+      } as TabsProps);
+  });
+
+  useEffect(() => {}, []);
+
+  return (
+    <Modal
+      width={900}
+      title={intl.formatMessage({
+        id: 'gen.preview',
+        defaultMessage: '预览',
+      })}
+      open={props.open}
+      destroyOnClose
+      footer={false}
+      onOk={() => {
+        props.onHide();
+      }}
+      onCancel={() => {
+        props.onHide();
+      }}
+    >
+      <Tabs defaultActiveKey="1" items={panes}></Tabs>
+    </Modal>
+  );
+};
+
+export default PreviewTableCode;
diff --git a/react-ui/src/pages/Tool/Gen/data.d.ts b/react-ui/src/pages/Tool/Gen/data.d.ts
new file mode 100644
index 0000000..a63ea9d
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/data.d.ts
@@ -0,0 +1,106 @@
+export type GenCodeType = {
+  columnId: string;
+  businessName: string;
+  className: string;
+  createBy: string;
+  createTime: string;
+  crud: string;
+  functionAuthor: string;
+  functionName: string;
+  genPath: string;
+  genType: string;
+  moduleName: string;
+  options: string;
+  packageName: string;
+  params: string;
+  parentMenuId: string;
+  parentMenuName: string;
+  remark: string;
+  status: string;
+  isList: string;
+  isEdit: string;
+  isQuery: string;
+  isInsert: string;
+  isRequired: string;
+  tableComment: string;
+  tableId: string;
+  tableName: string;
+  tplCategory: string;
+  tree: string;
+  treeCode: number;
+  treeName: string;
+  treeParentCode: number;
+  updateBy: string;
+  updateTime: string;
+  columns: any;
+};
+
+export type GenCodePagination = {
+  total: number;
+  pageSize: number;
+  current: number;
+};
+
+export type GenCodeListData = {
+  list: GenCodeType[];
+  pagination: Partial<GenCodePagination>;
+};
+
+export type GenCodeTableListParams = {
+  businessName?: string;
+  className?: string;
+  createBy?: string;
+  createTime?: string;
+  crud?: string;
+  functionAuthor?: string;
+  functionName?: string;
+  genPath?: string;
+  genType?: string;
+  moduleName?: string;
+  isList?: string;
+  isEdit?: string;
+  isQuery?: string;
+  isInsert?: string;
+  isRequired?: string;
+  options?: string;
+  params?: any;
+  packageName?: string;
+  parentMenuId?: string;
+  columns?: any;
+  parentMenuName?: string;
+  remark?: string;
+  tableComment?: string;
+  tableId?: string;
+  tableName?: string;
+  tplCategory?: string;
+  tree?: string;
+  treeCode?: string;
+  treeName?: string;
+  treeParentCode?: string;
+  updateBy?: string;
+  updateTime?: string;
+  pageSize?: string;
+  currentPage?: string;
+  filter?: string;
+  sorter?: string;
+};
+
+export type TableColumnInfo = {
+  columnName: string;
+  columnComment: string;
+};
+
+export type TableInfo = {
+  tableName: string;
+  tableComment: string;
+  columns: TableColumnInfo[];
+};
+
+export type ImportTableListItem = {
+  tableName: string;
+  tableComment: string;
+};
+
+export type ImportTableParams = {
+  tables: string;
+};
diff --git a/react-ui/src/pages/Tool/Gen/edit.tsx b/react-ui/src/pages/Tool/Gen/edit.tsx
new file mode 100644
index 0000000..99a0d1a
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/edit.tsx
@@ -0,0 +1,183 @@
+import React, { useEffect, useState } from 'react';
+import BaseInfo from './components/BaseInfo';
+import { Card, Layout, message, Steps } from 'antd';
+import ColumnInfo from './components/ColumnInfo';
+import GenInfo from './components/GenInfo';
+import { getGenCode, updateData } from './service';
+import { formatTreeData } from '@/utils/tree';
+import styles from './style.less';
+import type { GenCodeType } from './data';
+import { getMenuTree } from '@/services/system/menu';
+import { getDictTypeList } from '@/services/system/dict';
+import queryString from 'query-string';
+import { useLocation } from '@umijs/max';
+
+const { Content } = Layout;
+
+export type GenCodeArgs = {
+  id: string;
+};
+
+const TableList: React.FC = () => {
+  const location = useLocation();
+  const query = queryString.parse(location.search);
+  const { id } = query as GenCodeArgs;
+  const tableId = id;
+
+  const [currentStep, setCurrentStep] = useState<number>(0);
+  const [columnData, setColumnData] = useState<any>([]);
+  const [baseInfoData, setBaseInfoData] = useState<any>([]);
+  const [genInfoData, setGenInfoData] = useState<any>([]);
+  const [menuTree, setMenuTree] = useState<any>([]);
+  const [dictData, setDictData] = useState<any>([]);
+  const [tableInfo, setTableInfo] = useState<any>([]);
+  const [formData, setFormData] = useState<any>([]);
+  const [stepComponent, setStepComponent] = useState<any>([]);
+  const [stepKey, setStepKey] = useState<string>('');
+
+  const getCurrentStepAndComponent = (key?: string) => {
+    if (key === 'base') {
+      return (
+        <BaseInfo
+          values={baseInfoData}
+          // eslint-disable-next-line @typescript-eslint/no-use-before-define
+          onStepSubmit={onNextStep}
+        />
+      );
+    }
+    if (key === 'column') {
+      return (
+        <ColumnInfo
+          data={columnData}
+          dictData={dictData}
+          // eslint-disable-next-line @typescript-eslint/no-use-before-define
+          onStepSubmit={onNextStep}
+        />
+      );
+    }
+    if (key === 'gen') {
+      return (
+        <GenInfo
+          values={genInfoData}
+          menuData={menuTree}
+          tableInfo={tableInfo}
+          // eslint-disable-next-line @typescript-eslint/no-use-before-define
+          onStepSubmit={onNextStep}
+        />
+      );
+    }
+    return null;
+  };
+
+  const onNextStep = (step: string, values: any, direction: string) => {
+    let stepKey = 'base';
+    if (step === 'base') {
+      setStepKey('column');
+      setCurrentStep(1);
+      setFormData(values);
+      setStepComponent(getCurrentStepAndComponent(stepKey));
+    } else if (step === 'column') {
+      if (direction === 'prev') {
+        setStepKey('base');
+        setCurrentStep(0);
+      } else {
+        setStepKey('gen');
+        const tableData: GenCodeType = formData || ({} as GenCodeType);
+        tableData.columns = values;
+        setCurrentStep(2);
+        setFormData(tableData);
+      }
+      setStepComponent(getCurrentStepAndComponent(stepKey));
+    } else if (step === 'gen') {
+      if (direction === 'prev') {
+        setStepKey('column');
+        setCurrentStep(1);
+        setStepComponent(getCurrentStepAndComponent(stepKey));
+      } else {
+        const postData: GenCodeType = {
+          ...formData,
+          ...values,
+          params: values,
+          tableId: tableId,
+        };
+        setFormData(postData);
+        updateData({ ...postData } as GenCodeType).then((res) => {
+          if (res.code === 200) {
+            message.success('提交成功');
+            history.back();
+          } else {
+            message.success('提交失败');
+          }
+        });
+      }
+    }
+  };
+  useEffect(() => {
+    setStepComponent(getCurrentStepAndComponent(stepKey));
+  }, [stepKey]);
+
+  useEffect(() => {
+    getGenCode(tableId).then((res) => {
+      if (res.code === 200) {
+        setBaseInfoData(res.data.info);
+        setColumnData(res.data.rows);
+        setGenInfoData(res.data.info);
+        setTableInfo(res.data.tables);
+        setStepKey('base');
+      } else {
+        message.error(res.msg);
+      }
+    });
+    getMenuTree().then((res) => {
+      if (res.code === 200) {
+        const treeData = formatTreeData(res.data);
+        setMenuTree(treeData);
+      } else {
+        message.error(res.msg);
+      }
+    });
+
+    getDictTypeList().then((res: any) => {
+      if (res.code === 200) {
+        const dicts = res.rows.map((item: any) => {
+          return {
+            label: item.dictName,
+            value: item.dictType,
+          };
+        });
+        setDictData(dicts);
+      } else {
+        message.error(res.msg);
+      }
+    });
+  }, []);
+
+  // const onFinish = (values: any) => {
+  //   console.log('Success:', values);
+  // };
+
+  // const onFinishFailed = (errorInfo: any) => {
+  //   console.log('Failed:', errorInfo);
+  // };
+
+  return (
+    <Content>
+      <Card className={styles.tabsCard} bordered={false}>
+        <Steps current={currentStep} className={styles.steps} items={[
+          {
+            title: '基本信息',
+          },
+          {
+            title: '字段信息',
+          },
+          {
+            title: '生成信息',
+          },
+        ]} />
+        {stepComponent}
+      </Card>
+    </Content>
+  );
+};
+
+export default TableList;
diff --git a/react-ui/src/pages/Tool/Gen/import.tsx b/react-ui/src/pages/Tool/Gen/import.tsx
new file mode 100644
index 0000000..6e2c8df
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/import.tsx
@@ -0,0 +1,107 @@
+import { Button, Card, message, Layout } from 'antd';
+import React, { useState } from 'react';
+import { history, FormattedMessage } from '@umijs/max';
+import { importTables, queryTableList } from './service';
+import type { GenCodeType } from './data.d';
+import { ProColumns, ProTable } from '@ant-design/pro-components';
+import { PlusOutlined, RollbackOutlined } from '@ant-design/icons';
+
+const { Content } = Layout;
+
+const handleImport = async (tables: string) => {
+  const hide = message.loading('正在配置');
+  try {
+    await importTables(tables);
+    hide();
+    message.success('配置成功');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('配置失败请重试!');
+    return false;
+  }
+};
+
+const ImportTableList: React.FC = () => {
+  const [selectTables, setSelectTables] = useState<string[]>([]);
+
+  const columns: ProColumns<GenCodeType>[] = [
+    {
+      title: '表名称',
+      dataIndex: 'tableName',
+    },
+    {
+      title: '表描述',
+      dataIndex: 'tableComment',
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'createTime',
+      valueType: 'textarea',
+      hideInSearch: true,
+    },
+  ];
+
+  return (
+    <Content>
+      <Card bordered={false}>
+        <ProTable<GenCodeType>
+          headerTitle="代码生成信息"
+          rowKey="tableName"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="primary"
+              onClick={async () => {
+                if (selectTables.length < 1) {
+                  message.error('请选择要导入的表!');
+                  return;
+                }
+                const success = await handleImport(selectTables.join(','));
+                if (success) {
+                  history.back();
+                }
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="gen.submit" defaultMessage="提交" />
+            </Button>,
+            <Button
+              type="primary"
+              key="goback"
+              onClick={() => {
+                history.back();
+              }}
+            >
+              <RollbackOutlined /> <FormattedMessage id="gen.goback" defaultMessage="返回" />
+            </Button>,
+          ]}
+          request={(params) =>
+            queryTableList({ ...params }).then((res) => {
+              return {
+                data: res.rows,
+                total: res.total,
+                success: true,
+              };
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              if (selectedRows && selectedRows.length > 0) {
+                const tables = selectedRows.map((row) => {
+                  return row.tableName;
+                });
+                setSelectTables(tables);
+              }
+            },
+          }}
+        />
+      </Card>
+    </Content>
+  );
+};
+
+export default ImportTableList;
diff --git a/react-ui/src/pages/Tool/Gen/index.tsx b/react-ui/src/pages/Tool/Gen/index.tsx
new file mode 100644
index 0000000..9db00d7
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/index.tsx
@@ -0,0 +1,360 @@
+import { DownloadOutlined, PlusOutlined } from '@ant-design/icons';
+import { Button, message, Drawer, Modal, Card, Layout } from 'antd';
+import type { FormInstance } from 'antd';
+import React, { useState, useRef } from 'react';
+import { history, FormattedMessage, useAccess } from '@umijs/max';
+import PreviewForm from './components/PreviewCode';
+import type { GenCodeTableListParams, GenCodeType } from './data.d';
+import {
+  batchGenCode,
+  genCode,
+  previewCode,
+  getGenCodeList,
+  removeData,
+  syncDbInfo,
+} from './service';
+import {
+  ActionType,
+  FooterToolbar,
+  ProColumns,
+  ProDescriptions,
+  ProDescriptionsItemProps,
+  ProTable,
+} from '@ant-design/pro-components';
+
+const { Content } = Layout;
+
+/**
+ * 删除节点
+ *
+ * @param selectedRows
+ */
+const handleRemove = async (selectedRows: GenCodeType[]) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRows) return true;
+  try {
+    await removeData({
+      ids: selectedRows.map((row) => row.tableId),
+    });
+    hide();
+    message.success('删除成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const handleRemoveOne = async (selectedRow: GenCodeType) => {
+  const hide = message.loading('正在删除');
+  if (!selectedRow) return true;
+  try {
+    const params = [selectedRow.tableId];
+    await removeData({
+      ids: params,
+    });
+    hide();
+    message.success('删除成功,即将刷新');
+    return true;
+  } catch (error) {
+    hide();
+    message.error('删除失败,请重试');
+    return false;
+  }
+};
+
+const GenCodeView: React.FC = () => {
+  const formTableRef = useRef<FormInstance>();
+
+  const [showDetail, setShowDetail] = useState<boolean>(false);
+  const [showPreview, setShowPreview] = useState<boolean>(false);
+  const [preivewData, setPreivewData] = useState<boolean>(false);
+
+  const actionRef = useRef<ActionType>();
+  const [currentRow, setCurrentRow] = useState<GenCodeType>();
+  const [selectedRows, setSelectedRows] = useState<GenCodeType[]>([]);
+
+  const access = useAccess();
+
+  const columns: ProColumns<GenCodeType>[] = [
+    {
+      title: '编号',
+      dataIndex: 'tableId',
+      tip: '编号',
+      render: (dom, entity) => {
+        return (
+          <a
+            onClick={() => {
+              setCurrentRow(entity);
+              setShowDetail(true);
+            }}
+          >
+            {dom}
+          </a>
+        );
+      },
+    },
+    {
+      title: '表名',
+      dataIndex: 'tableName',
+      valueType: 'textarea',
+    },
+    {
+      title: '表描述',
+      dataIndex: 'tableComment',
+      hideInForm: true,
+      hideInSearch: true,
+    },
+    {
+      title: '实体',
+      dataIndex: 'className',
+      valueType: 'textarea',
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'createTime',
+      valueType: 'textarea',
+      hideInSearch: true,
+    },
+    {
+      title: '更新时间',
+      dataIndex: 'updateTime',
+      valueType: 'textarea',
+      hideInSearch: true,
+    },
+    {
+      title: '操作',
+      dataIndex: 'option',
+      width: '220px',
+      valueType: 'option',
+      render: (_, record) => [
+        <Button
+          type="link"
+          size="small"
+          key="preview"
+          hidden={!access.hasPerms('tool:gen:edit')}
+          onClick={() => {
+            previewCode(record.tableId).then((res) => {
+              if (res.code === 200) {
+                setPreivewData(res.data);
+                setShowPreview(true);
+              } else {
+                message.error('获取数据失败');
+              }
+            });
+          }}
+        >
+          预览
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          key="config"
+          hidden={!access.hasPerms('tool:gen:edit')}
+          onClick={() => {
+            history.push(`/tool/gen/edit?id=${record.tableId}`);
+          }}
+        >
+          编辑
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          danger
+          key="delete"
+          hidden={!access.hasPerms('tool:gen:del')}
+          onClick={async () => {
+            Modal.confirm({
+              title: '删除任务',
+              content: '确定删除该任务吗?',
+              okText: '确认',
+              cancelText: '取消',
+              onOk: async () => {
+                const success = await handleRemoveOne(record);
+                if (success) {
+                  if (actionRef.current) {
+                    actionRef.current.reload();
+                  }
+                }
+              },
+            });
+          }}
+        >
+          删除
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          key="sync"
+          hidden={!access.hasPerms('tool:gen:edit')}
+          onClick={() => {
+            syncDbInfo(record.tableName).then((res) => {
+              if (res.code === 200) {
+                message.success('同步成功');
+              } else {
+                message.error('同步失败');
+              }
+            });
+          }}
+        >
+          同步
+        </Button>,
+        <Button
+          type="link"
+          size="small"
+          key="gencode"
+          hidden={!access.hasPerms('tool:gen:edit')}
+          onClick={() => {
+            if (record.genType === '1') {
+              genCode(record.tableName).then((res) => {
+                if (res.code === 200) {
+                  message.success(`成功生成到自定义路径:${record.genPath}`);
+                } else {
+                  message.error(res.msg);
+                }
+              });
+            } else {
+              batchGenCode(record.tableName);
+            }
+          }}
+        >
+          生成代码
+        </Button>,
+      ],
+    },
+  ];
+
+  return (
+    <Content>
+      <Card bordered={false}>
+        <ProTable<GenCodeType>
+          headerTitle="代码生成信息"
+          actionRef={actionRef}
+          formRef={formTableRef}
+          rowKey="tableId"
+          search={{
+            labelWidth: 120,
+          }}
+          toolBarRender={() => [
+            <Button
+              type="primary"
+              key="gen"
+              hidden={!access.hasPerms('tool:gen:edit')}
+              onClick={() => {
+                if (selectedRows.length === 0) {
+                  message.error('请选择要生成的数据');
+                  return;
+                }
+                const tableNames = selectedRows.map((row) => row.tableName);
+                if (selectedRows[0].genType === '1') {
+                  genCode(tableNames.join(',')).then((res) => {
+                    if (res.code === 200) {
+                      message.success(`成功生成到自定义路径:${selectedRows[0].genPath}`);
+                    } else {
+                      message.error(res.msg);
+                    }
+                  });
+                } else {
+                  batchGenCode(tableNames.join(','));
+                }
+              }}
+            >
+              <DownloadOutlined /> <FormattedMessage id="gen.gencode" defaultMessage="生成" />
+            </Button>,
+            <Button
+              type="primary"
+              key="import"
+              hidden={!access.hasPerms('tool:gen:add')}
+              onClick={() => {
+                history.push('/tool/gen/import');
+              }}
+            >
+              <PlusOutlined /> <FormattedMessage id="gen.import" defaultMessage="导入" />
+            </Button>,
+          ]}
+          request={(params) =>
+            getGenCodeList({ ...params } as GenCodeTableListParams).then((res) => {
+              return {
+                data: res.rows,
+                total: res.rows.length,
+                success: true,
+              };
+            })
+          }
+          columns={columns}
+          rowSelection={{
+            onChange: (_, selectedRows) => {
+              setSelectedRows(selectedRows);
+            },
+          }}
+        />
+        {selectedRows?.length > 0 && (
+          <FooterToolbar
+            extra={
+              <div>
+                <FormattedMessage id="pages.searchTable.chosen" defaultMessage="已选择" />{' '}
+                <a style={{ fontWeight: 600 }}>{selectedRows.length}</a>{' '}
+                <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
+              </div>
+            }
+          >
+            <Button
+              key="delete"
+              hidden={!access.hasPerms('tool:gen:remove')}
+              onClick={async () => {
+                Modal.confirm({
+                  title: '删除任务',
+                  content: '确定删除该任务吗?',
+                  okText: '确认',
+                  cancelText: '取消',
+                  onOk: async () => {
+                    const success = await handleRemove(selectedRows);
+                    if (success) {
+                      setSelectedRows([]);
+                      actionRef.current?.reloadAndRest?.();
+                    }
+                  },
+                });
+              }}
+            >
+              <FormattedMessage id="pages.searchTable.batchDeletion" defaultMessage="批量删除" />
+            </Button>
+          </FooterToolbar>
+        )}
+        <PreviewForm
+          open={showPreview}
+          data={preivewData}
+          onHide={() => {
+            setShowPreview(false);
+          }}
+        />
+        <Drawer
+          width={600}
+          open={showDetail}
+          onClose={() => {
+            setCurrentRow(undefined);
+            setShowDetail(false);
+          }}
+          closable={false}
+        >
+          {currentRow?.tableName && (
+            <ProDescriptions<GenCodeType>
+              column={2}
+              title={currentRow?.tableName}
+              request={async () => ({
+                data: currentRow || {},
+              })}
+              params={{
+                id: currentRow?.tableName,
+              }}
+              columns={columns as ProDescriptionsItemProps<GenCodeType>[]}
+            />
+          )}
+        </Drawer>
+      </Card>
+    </Content>
+  );
+};
+
+export default GenCodeView;
diff --git a/react-ui/src/pages/Tool/Gen/locales/zh-CN.ts b/react-ui/src/pages/Tool/Gen/locales/zh-CN.ts
new file mode 100644
index 0000000..9c3a5d4
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/locales/zh-CN.ts
@@ -0,0 +1,8 @@
+export default {
+  'gen.import': '导入',
+  'gen.title': '表信息',
+  'gen.goback': '返回',
+  'gen.submit': '提交',
+  'gen.gencode': '生成',
+  'gen.preview': '预览',
+};
diff --git a/react-ui/src/pages/Tool/Gen/service.ts b/react-ui/src/pages/Tool/Gen/service.ts
new file mode 100644
index 0000000..0e472ee
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/service.ts
@@ -0,0 +1,106 @@
+import { request } from '@umijs/max';
+import { downLoadZip } from '@/utils/downloadfile';
+import type { GenCodeType, GenCodeTableListParams } from './data.d';
+
+// 查询分页列表
+export async function getGenCodeList(params?: GenCodeTableListParams) {
+  const queryString = new URLSearchParams(params).toString();
+  return request(`/api/code/gen/list?${queryString}`, {
+    data: params,
+    method: 'get',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
+
+// 查询表信息
+export async function getGenCode(id?: string) {
+  return request(`/api/code/gen/${id}`, {
+    method: 'get',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
+
+// 查询数据表信息
+export async function queryTableList(params?: any) {
+  const queryString = new URLSearchParams(params).toString();
+  return request(`/api/code/gen/db/list?${queryString}`, {
+    data: params,
+    method: 'get',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
+
+// 导入数据表信息
+export async function importTables(tables?: string) {
+  return request(`/api/code/gen/importTable?tables=${tables}`, {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
+
+// 删除
+export async function removeData(params: { ids: string[] }) {
+  return request(`/api/code/gen/${params.ids}`, {
+    method: 'delete',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
+
+// 添加数据
+export async function addData(params: GenCodeType) {
+  return request('/api/code/gen', {
+    method: 'POST',
+    data: {
+      ...params,
+    },
+  });
+}
+
+// 更新数据
+export async function updateData(params: GenCodeType) {
+  return request('/api/code/gen', {
+    method: 'PUT',
+    data: {
+      ...params,
+    },
+  });
+}
+
+// 更新状态
+export async function syncDbInfo(tableName: string) {
+  return request(`/api/code/gen/synchDb/${tableName}`, {
+    method: 'GET',
+  });
+}
+
+// 生成代码(自定义路径)
+export async function genCode(tableName: string) {
+  return request(`/api/code/gen/genCode/${tableName}`, {
+    method: 'GET',
+  });
+}
+
+// 生成代码(压缩包)
+export async function batchGenCode(tableName: string) {
+  return downLoadZip(`/api/code/gen/batchGenCode?tables=${tableName}`);
+}
+
+// 预览
+export async function previewCode(id: string) {
+  return request(`/api/code/gen/preview/${id}`, {
+    method: 'GET',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+    },
+  });
+}
diff --git a/react-ui/src/pages/Tool/Gen/style.less b/react-ui/src/pages/Tool/Gen/style.less
new file mode 100644
index 0000000..6999d7a
--- /dev/null
+++ b/react-ui/src/pages/Tool/Gen/style.less
@@ -0,0 +1,4 @@
+.steps:global(.ant-steps) {
+  max-width: 750px;
+  margin: 16px auto;
+}
diff --git a/react-ui/src/pages/Tool/Swagger/index.tsx b/react-ui/src/pages/Tool/Swagger/index.tsx
new file mode 100644
index 0000000..adcc968
--- /dev/null
+++ b/react-ui/src/pages/Tool/Swagger/index.tsx
@@ -0,0 +1,31 @@
+import React, { useEffect } from 'react';
+
+/**
+ *
+ * @author whiteshader@163.com
+ *
+ * */
+
+const CacheInfo: React.FC = () => {
+  useEffect(() => {
+    const frame = document.getElementById('bdIframe');
+    if (frame) {
+      const deviceWidth = document.documentElement.clientWidth;
+      const deviceHeight = document.documentElement.clientHeight;
+      frame.style.width = `${Number(deviceWidth) - 260}px`;
+      frame.style.height = `${Number(deviceHeight) - 120}px`;
+    }
+  });
+
+  return (
+    <div style={{}}>
+      <iframe
+        style={{ width: '100%', border: '0px', height: '100%' }}
+        src={`/api/swagger-ui/index.html`}
+        id="bdIframe"
+      />
+    </div>
+  );
+};
+
+export default CacheInfo;
diff --git a/react-ui/src/pages/User/Center/Center.less b/react-ui/src/pages/User/Center/Center.less
new file mode 100644
index 0000000..430d88e
--- /dev/null
+++ b/react-ui/src/pages/User/Center/Center.less
@@ -0,0 +1,61 @@
+
+.avatarHolder {
+  margin-bottom: 16px;
+  text-align: center;
+  position: relative;
+  display: inline-block;
+  height: 120px;
+  
+  & > img {
+    width: 120px;
+    height: 120px;
+    margin-bottom: 20px;
+    border-radius: 50%;
+  }
+  &:hover:after {
+    position: absolute;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    color: #eee;
+    font-size: 24px;
+    font-style: normal;
+    line-height: 110px;
+    background: rgba(0, 0, 0, 0.5);
+    border-radius: 50%;
+    cursor: pointer;
+    content: '+';
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+  }
+}
+
+.teamTitle {
+  margin-bottom: 12px;
+  color: @heading-color;
+  font-weight: 500;
+}
+
+.team {
+  :global {
+    .ant-avatar {
+      margin-right: 12px;
+    }
+  }
+
+  a {
+    display: block;
+    margin-bottom: 24px;
+    overflow: hidden;
+    color: @text-color;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    word-break: break-all;
+    transition: color 0.3s;
+
+    &:hover {
+      color: @primary-color;
+    }
+  }
+}
diff --git a/react-ui/src/pages/User/Center/components/AvatarCropper/cropper.css b/react-ui/src/pages/User/Center/components/AvatarCropper/cropper.css
new file mode 100644
index 0000000..7f2f350
--- /dev/null
+++ b/react-ui/src/pages/User/Center/components/AvatarCropper/cropper.css
@@ -0,0 +1,309 @@
+/*!
+ * Cropper.js v1.5.13
+ * https://fengyuanchen.github.io/cropperjs
+ *
+ * Copyright 2015-present Chen Fengyuan
+ * Released under the MIT license
+ *
+ * Date: 2022-11-20T05:30:43.444Z
+ */
+
+.cropper-container {
+  direction: ltr;
+  font-size: 0;
+  line-height: 0;
+  position: relative;
+  -ms-touch-action: none;
+      touch-action: none;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+
+.cropper-container img {
+    -webkit-backface-visibility: hidden;
+            backface-visibility: hidden;
+    display: block;
+    height: 100%;
+    image-orientation: 0deg;
+    max-height: none !important;
+    max-width: none !important;
+    min-height: 0 !important;
+    min-width: 0 !important;
+    width: 100%;
+  }
+
+.cropper-wrap-box,
+.cropper-canvas,
+.cropper-drag-box,
+.cropper-crop-box,
+.cropper-modal {
+  bottom: 0;
+  left: 0;
+  position: absolute;
+  right: 0;
+  top: 0;
+}
+
+.cropper-wrap-box,
+.cropper-canvas {
+  overflow: hidden;
+}
+
+.cropper-drag-box {
+  background-color: #fff;
+  opacity: 0;
+}
+
+.cropper-modal {
+  background-color: #000;
+  opacity: 0.5;
+}
+
+.cropper-view-box {
+  display: block;
+  height: 100%;
+  outline: 1px solid #39f;
+  outline-color: rgba(51, 153, 255, 75%);
+  overflow: hidden;
+  width: 100%;
+}
+
+.cropper-dashed {
+  border: 0 dashed #eee;
+  display: block;
+  opacity: 0.5;
+  position: absolute;
+}
+
+.cropper-dashed.dashed-h {
+    border-bottom-width: 1px;
+    border-top-width: 1px;
+    height: calc(100% / 3);
+    left: 0;
+    top: calc(100% / 3);
+    width: 100%;
+  }
+
+.cropper-dashed.dashed-v {
+    border-left-width: 1px;
+    border-right-width: 1px;
+    height: 100%;
+    left: calc(100% / 3);
+    top: 0;
+    width: calc(100% / 3);
+  }
+
+.cropper-center {
+  display: block;
+  height: 0;
+  left: 50%;
+  opacity: 0.75;
+  position: absolute;
+  top: 50%;
+  width: 0;
+}
+
+.cropper-center::before,
+  .cropper-center::after {
+    background-color: #eee;
+    content: " ";
+    display: block;
+    position: absolute;
+  }
+
+.cropper-center::before {
+    height: 1px;
+    left: -3px;
+    top: 0;
+    width: 7px;
+  }
+
+.cropper-center::after {
+    height: 7px;
+    left: 0;
+    top: -3px;
+    width: 1px;
+  }
+
+.cropper-face,
+.cropper-line,
+.cropper-point {
+  display: block;
+  height: 100%;
+  opacity: 0.1;
+  position: absolute;
+  width: 100%;
+}
+
+.cropper-face {
+  background-color: #fff;
+  left: 0;
+  top: 0;
+}
+
+.cropper-line {
+  background-color: #39f;
+}
+
+.cropper-line.line-e {
+    cursor: ew-resize;
+    right: -3px;
+    top: 0;
+    width: 5px;
+  }
+
+.cropper-line.line-n {
+    cursor: ns-resize;
+    height: 5px;
+    left: 0;
+    top: -3px;
+  }
+
+.cropper-line.line-w {
+    cursor: ew-resize;
+    left: -3px;
+    top: 0;
+    width: 5px;
+  }
+
+.cropper-line.line-s {
+    bottom: -3px;
+    cursor: ns-resize;
+    height: 5px;
+    left: 0;
+  }
+
+.cropper-point {
+  background-color: #39f;
+  height: 5px;
+  opacity: 0.75;
+  width: 5px;
+}
+
+.cropper-point.point-e {
+    cursor: ew-resize;
+    margin-top: -3px;
+    right: -3px;
+    top: 50%;
+  }
+
+.cropper-point.point-n {
+    cursor: ns-resize;
+    left: 50%;
+    margin-left: -3px;
+    top: -3px;
+  }
+
+.cropper-point.point-w {
+    cursor: ew-resize;
+    left: -3px;
+    margin-top: -3px;
+    top: 50%;
+  }
+
+.cropper-point.point-s {
+    bottom: -3px;
+    cursor: s-resize;
+    left: 50%;
+    margin-left: -3px;
+  }
+
+.cropper-point.point-ne {
+    cursor: nesw-resize;
+    right: -3px;
+    top: -3px;
+  }
+
+.cropper-point.point-nw {
+    cursor: nwse-resize;
+    left: -3px;
+    top: -3px;
+  }
+
+.cropper-point.point-sw {
+    bottom: -3px;
+    cursor: nesw-resize;
+    left: -3px;
+  }
+
+.cropper-point.point-se {
+    bottom: -3px;
+    cursor: nwse-resize;
+    height: 20px;
+    opacity: 1;
+    right: -3px;
+    width: 20px;
+  }
+
+@media (min-width: 768px) {
+
+.cropper-point.point-se {
+      height: 15px;
+      width: 15px;
+  }
+    }
+
+@media (min-width: 992px) {
+
+.cropper-point.point-se {
+      height: 10px;
+      width: 10px;
+  }
+    }
+
+@media (min-width: 1200px) {
+
+.cropper-point.point-se {
+      height: 5px;
+      opacity: 0.75;
+      width: 5px;
+  }
+    }
+
+.cropper-point.point-se::before {
+    background-color: #39f;
+    bottom: -50%;
+    content: " ";
+    display: block;
+    height: 200%;
+    opacity: 0;
+    position: absolute;
+    right: -50%;
+    width: 200%;
+  }
+
+.cropper-invisible {
+  opacity: 0;
+}
+
+.cropper-bg {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
+}
+
+.cropper-hide {
+  display: block;
+  height: 0;
+  position: absolute;
+  width: 0;
+}
+
+.cropper-hidden {
+  display: none !important;
+}
+
+.cropper-move {
+  cursor: move;
+}
+
+.cropper-crop {
+  cursor: crosshair;
+}
+
+.cropper-disabled .cropper-drag-box,
+.cropper-disabled .cropper-face,
+.cropper-disabled .cropper-line,
+.cropper-disabled .cropper-point {
+  cursor: not-allowed;
+}
diff --git a/react-ui/src/pages/User/Center/components/AvatarCropper/images/bg.png b/react-ui/src/pages/User/Center/components/AvatarCropper/images/bg.png
new file mode 100644
index 0000000..3c7056b
--- /dev/null
+++ b/react-ui/src/pages/User/Center/components/AvatarCropper/images/bg.png
Binary files differ
diff --git a/react-ui/src/pages/User/Center/components/AvatarCropper/index.less b/react-ui/src/pages/User/Center/components/AvatarCropper/index.less
new file mode 100644
index 0000000..dc3fecf
--- /dev/null
+++ b/react-ui/src/pages/User/Center/components/AvatarCropper/index.less
@@ -0,0 +1,10 @@
+.avatarPreview {
+	position: absolute;
+	top: 50%;
+	transform: translate(50%, -50%);
+	width: 200px;
+	height: 200px;
+	border-radius: 50%;
+	box-shadow: 0 0 4px #ccc;
+	overflow: hidden; 
+}
\ No newline at end of file
diff --git a/react-ui/src/pages/User/Center/components/AvatarCropper/index.tsx b/react-ui/src/pages/User/Center/components/AvatarCropper/index.tsx
new file mode 100644
index 0000000..83d0bcf
--- /dev/null
+++ b/react-ui/src/pages/User/Center/components/AvatarCropper/index.tsx
@@ -0,0 +1,144 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { Modal, Row, Col, Button, Space, Upload, message } from 'antd';
+import { useIntl } from '@umijs/max';
+import { uploadAvatar } from '@/services/system/user';
+import { Cropper } from 'react-cropper';
+import './cropper.css';
+import styles from './index.less';
+import {
+  MinusOutlined,
+  PlusOutlined,
+  RedoOutlined,
+  UndoOutlined,
+  UploadOutlined,
+} from '@ant-design/icons';
+
+/* *
+ *
+ * @author whiteshader@163.com
+ * @datetime  2022/02/24
+ *
+ * */
+
+export type AvatarCropperProps = {
+  onFinished: (isSuccess: boolean) => void;
+  open: boolean;
+  data: any;
+};
+
+const AvatarCropperForm: React.FC<AvatarCropperProps> = (props) => {
+  const cropperRef = useRef<HTMLImageElement>(null);
+  const [avatarData, setAvatarData] = useState<any>();
+  const [previewData, setPreviewData] = useState();
+
+  useEffect(() => {
+    setAvatarData(props.data);
+  }, [props]);
+
+  const intl = useIntl();
+  const handleOk = () => {
+    const imageElement: any = cropperRef?.current;
+    const cropper: any = imageElement?.cropper;
+    cropper.getCroppedCanvas().toBlob((blob: Blob) => {
+      const formData = new FormData();
+      formData.append('avatarfile', blob);
+      uploadAvatar(formData).then((res) => {
+        if (res.code === 200) {
+          message.success(res.msg);          
+          props.onFinished(true);
+        } else {
+          message.warning(res.msg);
+        }
+      });
+    }, 'image/png');
+  };
+  const handleCancel = () => {
+    props.onFinished(false);
+  };
+  const onCrop = () => {
+    const imageElement: any = cropperRef?.current;
+    const cropper: any = imageElement?.cropper;
+    setPreviewData(cropper.getCroppedCanvas().toDataURL());
+  };
+  const onRotateRight = () => {
+    const imageElement: any = cropperRef?.current;
+    const cropper: any = imageElement?.cropper;
+    cropper.rotate(90);
+  };
+  const onRotateLeft = () => {
+    const imageElement: any = cropperRef?.current;
+    const cropper: any = imageElement?.cropper;
+    cropper.rotate(-90);
+  };
+  const onZoomIn = () => {
+    const imageElement: any = cropperRef?.current;
+    const cropper: any = imageElement?.cropper;
+    cropper.zoom(0.1);
+  };
+  const onZoomOut = () => {
+    const imageElement: any = cropperRef?.current;
+    const cropper: any = imageElement?.cropper;
+    cropper.zoom(-0.1);
+  };
+  const beforeUpload = (file: any) => {
+    const reader = new FileReader();
+    reader.readAsDataURL(file);
+    reader.onload = () => {
+      setAvatarData(reader.result);
+    };
+  };
+  return (
+    <Modal
+      width={800}
+      title={intl.formatMessage({
+        id: 'system.user.modify_avatar',
+        defaultMessage: '修改头像',
+      })}
+      open={props.open}
+      destroyOnClose
+      onOk={handleOk}
+      onCancel={handleCancel}
+    >
+      <Row gutter={[16, 16]}>
+        <Col span={12} order={1}>
+          <Cropper
+            ref={cropperRef}
+            src={avatarData}
+            style={{ height: 350, width: '100%', marginBottom: '16px' }}
+            initialAspectRatio={1}
+            guides={false}
+            crop={onCrop}
+            zoomable={true}
+            zoomOnWheel={true}
+            rotatable={true}
+          />
+        </Col>
+        <Col span={12} order={2}>
+          <div className={styles.avatarPreview}>
+            <img src={previewData} style={{ height: '100%', width: '100%' }} />
+          </div>
+        </Col>
+      </Row>
+      <Row gutter={[16, 16]}>
+        <Col span={6}>
+          <Upload beforeUpload={beforeUpload} maxCount={1}>
+            <Button>
+              <UploadOutlined />
+              上传
+            </Button>
+          </Upload>
+        </Col>
+        <Col>
+          <Space>
+            <Button icon={<RedoOutlined />} onClick={onRotateRight} />
+            <Button icon={<UndoOutlined />} onClick={onRotateLeft} />
+            <Button icon={<PlusOutlined />} onClick={onZoomIn} />
+            <Button icon={<MinusOutlined />} onClick={onZoomOut} />
+          </Space>
+        </Col>
+      </Row>
+    </Modal>
+  );
+};
+
+export default AvatarCropperForm;
diff --git a/react-ui/src/pages/User/Center/components/BaseInfo/index.tsx b/react-ui/src/pages/User/Center/components/BaseInfo/index.tsx
new file mode 100644
index 0000000..5cdca5f
--- /dev/null
+++ b/react-ui/src/pages/User/Center/components/BaseInfo/index.tsx
@@ -0,0 +1,119 @@
+import React from 'react';
+import { Form, message, Row } from 'antd';
+import { FormattedMessage, useIntl } from '@umijs/max';
+import { ProForm, ProFormRadio, ProFormText } from '@ant-design/pro-components';
+import { updateUserProfile } from '@/services/system/user';
+
+
+export type BaseInfoProps = {
+  values: Partial<API.CurrentUser> | undefined;
+};
+
+const BaseInfo: React.FC<BaseInfoProps> = (props) => {
+  const [form] = Form.useForm();
+  const intl = useIntl();
+
+  const handleFinish = async (values: Record<string, any>) => {
+    const data = { ...props.values, ...values } as API.CurrentUser;
+    const resp = await updateUserProfile(data);
+    if (resp.code === 200) {
+      message.success('修改成功');
+    } else {
+      message.warning(resp.msg);
+    }
+  };
+
+  return (
+    <>
+      <ProForm form={form} onFinish={handleFinish} initialValues={props.values}>
+        <Row>
+          <ProFormText
+            name="nickName"
+            label={intl.formatMessage({
+              id: 'system.user.nick_name',
+              defaultMessage: '用户昵称',
+            })}
+            width="xl"
+            placeholder="请输入用户昵称"
+            rules={[
+              {
+                required: true,
+                message: (
+                  <FormattedMessage id="请输入用户昵称!" defaultMessage="请输入用户昵称!" />
+                ),
+              },
+            ]}
+          />
+        </Row>
+        <Row>
+          <ProFormText
+            name="phonenumber"
+            label={intl.formatMessage({
+              id: 'system.user.phonenumber',
+              defaultMessage: '手机号码',
+            })}
+            width="xl"
+            placeholder="请输入手机号码"
+            rules={[
+              {
+                required: false,
+                message: (
+                  <FormattedMessage id="请输入手机号码!" defaultMessage="请输入手机号码!" />
+                ),
+              },
+            ]}
+          />
+        </Row>
+        <Row>
+          <ProFormText
+            name="email"
+            label={intl.formatMessage({
+              id: 'system.user.email',
+              defaultMessage: '邮箱',
+            })}
+            width="xl"
+            placeholder="请输入邮箱"
+            rules={[
+              {
+                type: 'email',
+                message: '无效的邮箱地址!',
+              },
+              {
+                required: false,
+                message: <FormattedMessage id="请输入邮箱!" defaultMessage="请输入邮箱!" />,
+              },
+            ]}
+          />
+        </Row>
+        <Row>
+          <ProFormRadio.Group
+            options={[
+              {
+                label: '男',
+                value: '0',
+              },
+              {
+                label: '女',
+                value: '1',
+              },
+            ]}
+            name="sex"
+            label={intl.formatMessage({
+              id: 'system.user.sex',
+              defaultMessage: 'sex',
+            })}
+            width="xl"
+            rules={[
+              {
+                required: false,
+                message: <FormattedMessage id="请输入性别!" defaultMessage="请输入性别!" />,
+              },
+            ]}
+          />
+        </Row>
+      </ProForm>
+    </>
+  );
+};
+
+export default BaseInfo;
diff --git a/react-ui/src/pages/User/Center/components/ResetPassword/index.tsx b/react-ui/src/pages/User/Center/components/ResetPassword/index.tsx
new file mode 100644
index 0000000..26825d4
--- /dev/null
+++ b/react-ui/src/pages/User/Center/components/ResetPassword/index.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { Form, message } from 'antd';
+import { FormattedMessage, useIntl } from '@umijs/max';
+import { updateUserPwd } from '@/services/system/user';
+import { ProForm, ProFormText } from '@ant-design/pro-components';
+
+const ResetPassword: React.FC = () => {
+  const [form] = Form.useForm();
+  const intl = useIntl();
+
+  const handleFinish = async (values: Record<string, any>) => {
+    const resp = await updateUserPwd(values.oldPassword, values.newPassword);
+    if (resp.code === 200) {
+      message.success('密码重置成功。');
+    } else {
+      message.warning(resp.msg);
+    }
+  };
+
+  const checkPassword = (rule: any, value: string) => {
+    const login_password = form.getFieldValue('newPassword');
+    if (value === login_password) {
+      return Promise.resolve();
+    }
+    return Promise.reject(new Error('两次密码输入不一致'));
+  };
+
+  return (
+    <>
+      <ProForm form={form} onFinish={handleFinish}>       
+          <ProFormText.Password
+            name="oldPassword"
+            label={intl.formatMessage({
+              id: 'system.user.old_password',
+              defaultMessage: '旧密码',
+            })}
+            width="xl"
+            placeholder="请输入旧密码"
+            rules={[
+              {
+                required: true,
+                message: <FormattedMessage id="请输入旧密码!" defaultMessage="请输入旧密码!" />,
+              },
+            ]}
+          />
+          <ProFormText.Password
+            name="newPassword"
+            label={intl.formatMessage({
+              id: 'system.user.new_password',
+              defaultMessage: '新密码',
+            })}
+            width="xl"
+            placeholder="请输入新密码"
+            rules={[
+              {
+                required: true,
+                message: <FormattedMessage id="请输入新密码!" defaultMessage="请输入新密码!" />,
+              },
+            ]}
+          />
+          <ProFormText.Password
+            name="confirmPassword"
+            label={intl.formatMessage({
+              id: 'system.user.confirm_password',
+              defaultMessage: '确认密码',
+            })}
+            width="xl"
+            placeholder="请输入确认密码"
+            rules={[
+              {
+                required: true,
+                message: (
+                  <FormattedMessage id="请输入确认密码!" defaultMessage="请输入确认密码!" />
+                ),
+              },
+              { validator: checkPassword },
+            ]}
+          />
+      </ProForm>
+    </>
+  );
+};
+
+export default ResetPassword;
diff --git a/react-ui/src/pages/User/Center/index.tsx b/react-ui/src/pages/User/Center/index.tsx
new file mode 100644
index 0000000..2ce308c
--- /dev/null
+++ b/react-ui/src/pages/User/Center/index.tsx
@@ -0,0 +1,200 @@
+import {
+  ClusterOutlined,
+  MailOutlined,
+  TeamOutlined,
+  UserOutlined,
+  MobileOutlined,
+  ManOutlined,
+} from '@ant-design/icons';
+import { Card, Col, Divider, List, Row } from 'antd';
+import React, { useState } from 'react';
+import styles from './Center.less';
+import BaseInfo from './components/BaseInfo';
+import ResetPassword from './components/ResetPassword';
+import AvatarCropper from './components/AvatarCropper';
+import { useRequest } from '@umijs/max';
+import { getUserInfo } from '@/services/session';
+import { PageLoading } from '@ant-design/pro-components';
+
+const operationTabList = [
+  {
+    key: 'base',
+    tab: (
+      <span>
+        基本资料
+      </span>
+    ),
+  },
+  {
+    key: 'password',
+    tab: (
+      <span>
+        重置密码
+      </span>
+    ),
+  },
+];
+
+export type tabKeyType = 'base' | 'password';
+
+const Center: React.FC = () => {
+  
+  const [tabKey, setTabKey] = useState<tabKeyType>('base');
+  
+  const [cropperModalOpen, setCropperModalOpen] = useState<boolean>(false);
+  
+  //  获取用户信息
+  const { data: userInfo, loading } = useRequest(async () => {
+    return { data: await getUserInfo()};
+  });
+  if (loading) {
+    return <div>loading...</div>;
+  }
+
+  const currentUser = userInfo?.user;
+
+  //  渲染用户信息
+  const renderUserInfo = ({
+    userName,
+    phonenumber,
+    email,
+    sex,
+    dept,
+  }: Partial<API.CurrentUser>) => {
+    return (
+      <List>
+        <List.Item>
+          <div>
+            <UserOutlined
+              style={{
+                marginRight: 8,
+              }}
+            />
+            用户名
+          </div>
+          <div>{userName}</div>
+        </List.Item>
+        <List.Item>
+          <div>
+            <ManOutlined
+              style={{
+                marginRight: 8,
+              }}
+            />
+            性别
+          </div>
+          <div>{sex === '1' ? '女' : '男'}</div>
+        </List.Item>
+        <List.Item>
+          <div>
+            <MobileOutlined
+              style={{
+                marginRight: 8,
+              }}
+            />
+            电话
+          </div>
+          <div>{phonenumber}</div>
+        </List.Item>
+        <List.Item>
+          <div>
+            <MailOutlined
+              style={{
+                marginRight: 8,
+              }}
+            />
+            邮箱
+          </div>
+          <div>{email}</div>
+        </List.Item>
+        <List.Item>
+          <div>
+            <ClusterOutlined
+              style={{
+                marginRight: 8,
+              }}
+            />
+            部门
+          </div>
+          <div>{dept?.deptName}</div>
+        </List.Item>
+      </List>
+    );
+  };
+
+  // 渲染tab切换
+  const renderChildrenByTabKey = (tabValue: tabKeyType) => {
+    if (tabValue === 'base') {
+      return <BaseInfo values={currentUser} />;
+    }
+    if (tabValue === 'password') {
+      return <ResetPassword />;
+    }
+    return null;
+  };
+
+  if (!currentUser) {
+    return <PageLoading />;
+  }
+
+  return (
+    <div>
+      <Row gutter={[16, 24]}>
+        <Col lg={8} md={24}>
+          <Card
+            title="个人信息"
+            bordered={false}
+            loading={loading}
+          >
+            {!loading && (
+              <div style={{ textAlign: "center"}}>
+                <div className={styles.avatarHolder} onClick={()=>{setCropperModalOpen(true)}}>
+                  <img alt="" src={currentUser.avatar} />
+                </div>
+                {renderUserInfo(currentUser)}
+                <Divider dashed />
+                <div className={styles.team}>
+                  <div className={styles.teamTitle}>角色</div>
+                  <Row gutter={36}>
+                    {currentUser.roles &&
+                      currentUser.roles.map((item: any) => (
+                        <Col key={item.roleId} lg={24} xl={12}>
+                          <TeamOutlined
+                            style={{
+                              marginRight: 8,
+                            }}
+                          />
+                          {item.roleName}
+                        </Col>
+                      ))}
+                  </Row>
+                </div>
+              </div>
+            )}
+          </Card>
+        </Col>
+        <Col lg={16} md={24}>
+          <Card
+            bordered={false}
+            tabList={operationTabList}
+            activeTabKey={tabKey}
+            onTabChange={(_tabKey: string) => {
+              setTabKey(_tabKey as tabKeyType);
+            }}
+          >
+            {renderChildrenByTabKey(tabKey)}
+          </Card>
+        </Col>
+      </Row>
+      <AvatarCropper
+        onFinished={() => {
+          setCropperModalOpen(false);     
+        }}
+        open={cropperModalOpen}
+        data={currentUser.avatar}
+      />
+    </div>
+  );
+};
+
+export default Center;
diff --git a/react-ui/src/pages/User/Login/index.tsx b/react-ui/src/pages/User/Login/index.tsx
new file mode 100644
index 0000000..a18f785
--- /dev/null
+++ b/react-ui/src/pages/User/Login/index.tsx
@@ -0,0 +1,436 @@
+import Footer from '@/components/Footer';
+import { getCaptchaImg, login } from '@/services/system/auth';
+import { getFakeCaptcha } from '@/services/ant-design-pro/login';
+import {
+  AlipayCircleOutlined,
+  LockOutlined,
+  MobileOutlined,
+  TaobaoCircleOutlined,
+  UserOutlined,
+  WeiboCircleOutlined,
+} from '@ant-design/icons';
+import {
+  LoginForm,
+  ProFormCaptcha,
+  ProFormCheckbox,
+  ProFormText,
+} from '@ant-design/pro-components';
+import { useEmotionCss } from '@ant-design/use-emotion-css';
+import { FormattedMessage, history, SelectLang, useIntl, useModel, Helmet } from '@umijs/max';
+import { Alert, Col, message, Row, Tabs, Image } from 'antd';
+import Settings from '../../../../config/defaultSettings';
+import React, { useEffect, useState } from 'react';
+import { flushSync } from 'react-dom';
+import { clearSessionToken, setSessionToken } from '@/access';
+
+const ActionIcons = () => {
+  const langClassName = useEmotionCss(({ token }) => {
+    return {
+      marginLeft: '8px',
+      color: 'rgba(0, 0, 0, 0.2)',
+      fontSize: '24px',
+      verticalAlign: 'middle',
+      cursor: 'pointer',
+      transition: 'color 0.3s',
+      '&:hover': {
+        color: token.colorPrimaryActive,
+      },
+    };
+  });
+
+  return (
+    <>
+      <AlipayCircleOutlined key="AlipayCircleOutlined" className={langClassName} />
+      <TaobaoCircleOutlined key="TaobaoCircleOutlined" className={langClassName} />
+      <WeiboCircleOutlined key="WeiboCircleOutlined" className={langClassName} />
+    </>
+  );
+};
+
+const Lang = () => {
+  const langClassName = useEmotionCss(({ token }) => {
+    return {
+      width: 42,
+      height: 42,
+      lineHeight: '42px',
+      position: 'fixed',
+      right: 16,
+      borderRadius: token.borderRadius,
+      ':hover': {
+        backgroundColor: token.colorBgTextHover,
+      },
+    };
+  });
+
+  return (
+    <div className={langClassName} data-lang>
+      {SelectLang && <SelectLang />}
+    </div>
+  );
+};
+
+const LoginMessage: React.FC<{
+  content: string;
+}> = ({ content }) => {
+  return (
+    <Alert
+      style={{
+        marginBottom: 24,
+      }}
+      message={content}
+      type="error"
+      showIcon
+    />
+  );
+};
+
+const Login: React.FC = () => {
+  const [userLoginState, setUserLoginState] = useState<API.LoginResult>({code: 200});
+  const [type, setType] = useState<string>('account');
+  const { initialState, setInitialState } = useModel('@@initialState');
+  const [captchaCode, setCaptchaCode] = useState<string>('');
+  const [uuid, setUuid] = useState<string>('');
+
+  const containerClassName = useEmotionCss(() => {
+    return {
+      display: 'flex',
+      flexDirection: 'column',
+      height: '100vh',
+      overflow: 'auto',
+      backgroundImage:
+        "url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
+      backgroundSize: '100% 100%',
+    };
+  });
+
+  const intl = useIntl();
+
+  const getCaptchaCode = async () => {
+    const response = await getCaptchaImg();
+    const imgdata = `data:image/png;base64,${response.img}`;
+    setCaptchaCode(imgdata);
+    setUuid(response.uuid);
+  };
+
+  const fetchUserInfo = async () => {
+    const userInfo = await initialState?.fetchUserInfo?.();
+    if (userInfo) {
+      flushSync(() => {
+        setInitialState((s) => ({
+          ...s,
+          currentUser: userInfo,
+        }));
+      });
+    }
+  };
+
+  const handleSubmit = async (values: API.LoginParams) => {
+    try {
+      // 登录
+      const response = await login({ ...values, uuid });
+      if (response.code === 200) {
+        const defaultLoginSuccessMessage = intl.formatMessage({
+          id: 'pages.login.success',
+          defaultMessage: '登录成功!',
+        });
+        const current = new Date();
+        const expireTime = current.setTime(current.getTime() + 1000 * 12 * 60 * 60);
+        console.log('login response: ', response);
+        setSessionToken(response?.token, response?.token, expireTime);
+        message.success(defaultLoginSuccessMessage);
+        await fetchUserInfo();
+        console.log('login ok');
+        const urlParams = new URL(window.location.href).searchParams;
+        history.push(urlParams.get('redirect') || '/');
+        return;
+      } else {
+        console.log(response.msg);
+        clearSessionToken();
+        // 如果失败去设置用户错误信息
+        setUserLoginState({ ...response, type });
+        getCaptchaCode();
+      }
+    } catch (error) {
+      const defaultLoginFailureMessage = intl.formatMessage({
+        id: 'pages.login.failure',
+        defaultMessage: '登录失败,请重试!',
+      });
+      console.log(error);
+      message.error(defaultLoginFailureMessage);
+    }
+  };
+  const { code } = userLoginState;
+  const loginType = type;
+
+  useEffect(() => {
+    getCaptchaCode();
+  }, []);
+
+  return (
+    <div className={containerClassName}>
+      <Helmet>
+        <title>
+          {intl.formatMessage({
+            id: 'menu.login',
+            defaultMessage: '登录页',
+          })}
+          - {Settings.title}
+        </title>
+      </Helmet>
+      <Lang />
+      <div
+        style={{
+          flex: '1',
+          padding: '32px 0',
+        }}
+      >
+        <LoginForm
+          contentStyle={{
+            minWidth: 280,
+            maxWidth: '75vw',
+          }}
+          logo={<img alt="logo" src="/logo.svg" />}
+          title="Ant Design"
+          subTitle={intl.formatMessage({ id: 'pages.layouts.userLayout.title' })}
+          initialValues={{
+            autoLogin: true,
+          }}
+          actions={[
+            <FormattedMessage
+              key="loginWith"
+              id="pages.login.loginWith"
+              defaultMessage="其他登录方式"
+            />,
+            <ActionIcons key="icons" />,
+          ]}
+          onFinish={async (values) => {
+            await handleSubmit(values as API.LoginParams);
+          }}
+        >
+          <Tabs
+            activeKey={type}
+            onChange={setType}
+            centered
+            items={[
+              {
+                key: 'account',
+                label: intl.formatMessage({
+                  id: 'pages.login.accountLogin.tab',
+                  defaultMessage: '账户密码登录',
+                }),
+              },
+              {
+                key: 'mobile',
+                label: intl.formatMessage({
+                  id: 'pages.login.phoneLogin.tab',
+                  defaultMessage: '手机号登录',
+                }),
+              },
+            ]}
+          />
+
+          {code !== 200 && loginType === 'account' && (
+            <LoginMessage
+              content={intl.formatMessage({
+                id: 'pages.login.accountLogin.errorMessage',
+                defaultMessage: '账户或密码错误(admin/admin123)',
+              })}
+            />
+          )}
+          {type === 'account' && (
+            <>
+              <ProFormText
+                name="username"
+                initialValue="admin"
+                fieldProps={{
+                  size: 'large',
+                  prefix: <UserOutlined />,
+                }}
+                placeholder={intl.formatMessage({
+                  id: 'pages.login.username.placeholder',
+                  defaultMessage: '用户名: admin',
+                })}
+                rules={[
+                  {
+                    required: true,
+                    message: (
+                      <FormattedMessage
+                        id="pages.login.username.required"
+                        defaultMessage="请输入用户名!"
+                      />
+                    ),
+                  },
+                ]}
+              />
+              <ProFormText.Password
+                name="password"
+                initialValue="admin123"
+                fieldProps={{
+                  size: 'large',
+                  prefix: <LockOutlined />,
+                }}
+                placeholder={intl.formatMessage({
+                  id: 'pages.login.password.placeholder',
+                  defaultMessage: '密码: admin123',
+                })}
+                rules={[
+                  {
+                    required: true,
+                    message: (
+                      <FormattedMessage
+                        id="pages.login.password.required"
+                        defaultMessage="请输入密码!"
+                      />
+                    ),
+                  },
+                ]}
+              />
+              <Row>
+                <Col flex={3}>
+                  <ProFormText
+                    style={{
+                      float: 'right',
+                    }}
+                    name="code"
+                    placeholder={intl.formatMessage({
+                      id: 'pages.login.captcha.placeholder',
+                      defaultMessage: '请输入验证',
+                    })}
+                    rules={[
+                      {
+                        required: true,
+                        message: (
+                          <FormattedMessage
+                            id="pages.searchTable.updateForm.ruleName.nameRules"
+                            defaultMessage="请输入验证啊"
+                          />
+                        ),
+                      },
+                    ]}
+                  />
+                </Col>
+                <Col flex={2}>
+                  <Image
+                    src={captchaCode}
+                    alt="验证码"
+                    style={{
+                      display: 'inline-block',
+                      verticalAlign: 'top',
+                      cursor: 'pointer',
+                      paddingLeft: '10px',
+                      width: '100px',
+                    }}
+                    preview={false}
+                    onClick={() => getCaptchaCode()}
+                  />
+                </Col>
+              </Row>
+            </>
+          )}
+
+          {code !== 200 && loginType === 'mobile' && <LoginMessage content="验证码错误" />}
+          {type === 'mobile' && (
+            <>
+              <ProFormText
+                fieldProps={{
+                  size: 'large',
+                  prefix: <MobileOutlined />,
+                }}
+                name="mobile"
+                placeholder={intl.formatMessage({
+                  id: 'pages.login.phoneNumber.placeholder',
+                  defaultMessage: '手机号',
+                })}
+                rules={[
+                  {
+                    required: true,
+                    message: (
+                      <FormattedMessage
+                        id="pages.login.phoneNumber.required"
+                        defaultMessage="请输入手机号!"
+                      />
+                    ),
+                  },
+                  {
+                    pattern: /^1\d{10}$/,
+                    message: (
+                      <FormattedMessage
+                        id="pages.login.phoneNumber.invalid"
+                        defaultMessage="手机号格式错误!"
+                      />
+                    ),
+                  },
+                ]}
+              />
+              <ProFormCaptcha
+                fieldProps={{
+                  size: 'large',
+                  prefix: <LockOutlined />,
+                }}
+                captchaProps={{
+                  size: 'large',
+                }}
+                placeholder={intl.formatMessage({
+                  id: 'pages.login.captcha.placeholder',
+                  defaultMessage: '请输入验证码',
+                })}
+                captchaTextRender={(timing, count) => {
+                  if (timing) {
+                    return `${count} ${intl.formatMessage({
+                      id: 'pages.getCaptchaSecondText',
+                      defaultMessage: '获取验证码',
+                    })}`;
+                  }
+                  return intl.formatMessage({
+                    id: 'pages.login.phoneLogin.getVerificationCode',
+                    defaultMessage: '获取验证码',
+                  });
+                }}
+                name="captcha"
+                rules={[
+                  {
+                    required: true,
+                    message: (
+                      <FormattedMessage
+                        id="pages.login.captcha.required"
+                        defaultMessage="请输入验证码!"
+                      />
+                    ),
+                  },
+                ]}
+                onGetCaptcha={async (phone) => {
+                  const result = await getFakeCaptcha({
+                    phone,
+                  });
+                  if (!result) {
+                    return;
+                  }
+                  message.success('获取验证码成功!验证码为:1234');
+                }}
+              />
+            </>
+          )}
+          <div
+            style={{
+              marginBottom: 24,
+            }}
+          >
+            <ProFormCheckbox noStyle name="autoLogin">
+              <FormattedMessage id="pages.login.rememberMe" defaultMessage="自动登录" />
+            </ProFormCheckbox>
+            <a
+              style={{
+                float: 'right',
+              }}
+            >
+              <FormattedMessage id="pages.login.forgotPassword" defaultMessage="忘记密码" />
+            </a>
+          </div>
+        </LoginForm>
+      </div>
+      <Footer />
+    </div>
+  );
+};
+
+export default Login;
diff --git a/react-ui/src/pages/User/Settings/index.tsx b/react-ui/src/pages/User/Settings/index.tsx
new file mode 100644
index 0000000..f29d0b9
--- /dev/null
+++ b/react-ui/src/pages/User/Settings/index.tsx
@@ -0,0 +1,20 @@
+import { PageContainer } from '@ant-design/pro-components';
+import { Card } from 'antd';
+import React from 'react';
+
+/**
+ *
+ * @author whiteshader@163.com
+ *
+ * */
+
+
+const Settings: React.FC = () => {
+  return (
+    <PageContainer>
+      <Card title="Developing" />
+    </PageContainer>
+  );
+};
+
+export default Settings;
diff --git a/react-ui/src/pages/Welcome.tsx b/react-ui/src/pages/Welcome.tsx
new file mode 100644
index 0000000..d0c49f7
--- /dev/null
+++ b/react-ui/src/pages/Welcome.tsx
@@ -0,0 +1,164 @@
+import { PageContainer } from '@ant-design/pro-components';
+import { useModel } from '@umijs/max';
+import { Card, theme } from 'antd';
+import React from 'react';
+
+/**
+ * 每个单独的卡片,为了复用样式抽成了组件
+ * @param param0
+ * @returns
+ */
+const InfoCard: React.FC<{
+  title: string;
+  index: number;
+  desc: string;
+  href: string;
+}> = ({ title, href, index, desc }) => {
+  const { useToken } = theme;
+
+  const { token } = useToken();
+
+  return (
+    <div
+      style={{
+        backgroundColor: token.colorBgContainer,
+        boxShadow: token.boxShadow,
+        borderRadius: '8px',
+        fontSize: '14px',
+        color: token.colorTextSecondary,
+        lineHeight: '22px',
+        padding: '16px 19px',
+        minWidth: '220px',
+        flex: 1,
+      }}
+    >
+      <div
+        style={{
+          display: 'flex',
+          gap: '4px',
+          alignItems: 'center',
+        }}
+      >
+        <div
+          style={{
+            width: 48,
+            height: 48,
+            lineHeight: '22px',
+            backgroundSize: '100%',
+            textAlign: 'center',
+            padding: '8px 16px 16px 12px',
+            color: '#FFF',
+            fontWeight: 'bold',
+            backgroundImage:
+              "url('https://gw.alipayobjects.com/zos/bmw-prod/daaf8d50-8e6d-4251-905d-676a24ddfa12.svg')",
+          }}
+        >
+          {index}
+        </div>
+        <div
+          style={{
+            fontSize: '16px',
+            color: token.colorText,
+            paddingBottom: 8,
+          }}
+        >
+          {title}
+        </div>
+      </div>
+      <div
+        style={{
+          fontSize: '14px',
+          color: token.colorTextSecondary,
+          textAlign: 'justify',
+          lineHeight: '22px',
+          marginBottom: 8,
+        }}
+      >
+        {desc}
+      </div>
+      <a href={href} target="_blank" rel="noreferrer">
+        了解更多 {'>'}
+      </a>
+    </div>
+  );
+};
+
+const Welcome: React.FC = () => {
+  const { token } = theme.useToken();
+  const { initialState } = useModel('@@initialState');
+  return (
+    <PageContainer>
+      <Card
+        style={{
+          borderRadius: 8,
+        }}
+        bodyStyle={{
+          backgroundImage:
+            initialState?.settings?.navTheme === 'realDark'
+              ? 'background-image: linear-gradient(75deg, #1A1B1F 0%, #191C1F 100%)'
+              : 'background-image: linear-gradient(75deg, #FBFDFF 0%, #F5F7FF 100%)',
+        }}
+      >
+        <div
+          style={{
+            backgroundPosition: '100% -30%',
+            backgroundRepeat: 'no-repeat',
+            backgroundSize: '274px auto',
+            backgroundImage:
+              "url('https://gw.alipayobjects.com/mdn/rms_a9745b/afts/img/A*BuFmQqsB2iAAAAAAAAAAAAAAARQnAQ')",
+          }}
+        >
+          <div
+            style={{
+              fontSize: '20px',
+              color: token.colorTextHeading,
+            }}
+          >
+            欢迎使用 Ant Design Pro
+          </div>
+          <p
+            style={{
+              fontSize: '14px',
+              color: token.colorTextSecondary,
+              lineHeight: '22px',
+              marginTop: 16,
+              marginBottom: 32,
+              width: '65%',
+            }}
+          >
+            Ant Design Pro 是一个整合了 umi,Ant Design 和 ProComponents
+            的脚手架方案。致力于在设计规范和基础组件的基础上,继续向上构建,提炼出典型模板/业务组件/配套设计资源,进一步提升企业级中后台产品设计研发过程中的『用户』和『设计者』的体验。
+          </p>
+          <div
+            style={{
+              display: 'flex',
+              flexWrap: 'wrap',
+              gap: 16,
+            }}
+          >
+            <InfoCard
+              index={1}
+              href="https://umijs.org/docs/introduce/introduce"
+              title="了解 umi"
+              desc="umi 是一个可扩展的企业级前端应用框架,umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。"
+            />
+            <InfoCard
+              index={2}
+              title="了解 ant design"
+              href="https://ant.design"
+              desc="antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。"
+            />
+            <InfoCard
+              index={3}
+              title="了解 Pro Components"
+              href="https://procomponents.ant.design"
+              desc="ProComponents 是一个基于 Ant Design 做了更高抽象的模板组件,以 一个组件就是一个页面为开发理念,为中后台开发带来更好的体验。"
+            />
+          </div>
+        </div>
+      </Card>
+    </PageContainer>
+  );
+};
+
+export default Welcome;