'init_again'

Change-Id: Ib7ecdb9f5baeab1e4681152a57b936edf7475b35
diff --git a/src/pages/System/User/components/AuthRole.tsx b/src/pages/System/User/components/AuthRole.tsx
new file mode 100644
index 0000000..120d966
--- /dev/null
+++ b/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/src/pages/System/User/components/DeptTree.tsx b/src/pages/System/User/components/DeptTree.tsx
new file mode 100644
index 0000000..d1a085c
--- /dev/null
+++ b/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/src/pages/System/User/components/ResetPwd.tsx b/src/pages/System/User/components/ResetPwd.tsx
new file mode 100644
index 0000000..5523cd6
--- /dev/null
+++ b/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/src/pages/System/User/edit.tsx b/src/pages/System/User/edit.tsx
new file mode 100644
index 0000000..31d442b
--- /dev/null
+++ b/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/src/pages/System/User/index.tsx b/src/pages/System/User/index.tsx
new file mode 100644
index 0000000..6435787
--- /dev/null
+++ b/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;