feat: 初始化项目并完成基础功能开发
- 完成项目初始化
- 实现用户注册、登录功能
- 完成用户管理与权限管理模块
- 开发后端 Tracker 服务器项目管理接口
- 实现日志管理接口
Change-Id: Ia4bde1c9ff600352a7ff0caca0cc50b02cad1af7
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;