646 lines
18 KiB
TypeScript
646 lines
18 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-use-before-define */
|
|
import {
|
|
ERROR_CODE,
|
|
GENDER_MAP,
|
|
PRIORITY_MAP,
|
|
STATUS_MAP,
|
|
USER_TYPE_MAP,
|
|
} from '@/constants/constants';
|
|
import CustomTree from '@/pages/components/customTree';
|
|
import {
|
|
deleteUser,
|
|
deleteUserGroup,
|
|
getGroupTree,
|
|
getUserList,
|
|
} from '@/services/userList';
|
|
import {
|
|
DeleteOutlined,
|
|
PlusOutlined,
|
|
RedoOutlined,
|
|
TeamOutlined,
|
|
} from '@ant-design/icons';
|
|
import { Button, Input, message, Popconfirm, Spin, Table, Tooltip } from 'antd';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import React, { useEffect, useState } from 'react';
|
|
import styles from './index.less';
|
|
import UserEditModal from './mod/eidtUser';
|
|
import CreatGroup from './mod/group';
|
|
import PasswordResetModal from './mod/passwordEdit';
|
|
|
|
const UserListPage: React.FC = () => {
|
|
const [orgTreeData, setOrgTreeData] = useState<User.OrganizationNode[]>([]);
|
|
const [selectedOrg, setSelectedOrg] = useState<any>();
|
|
|
|
// 用户列表
|
|
const [dataSource, setDataSource] = useState<User.UserItem[]>([]);
|
|
const [loading, setLoading] = useState<boolean>(false);
|
|
const [spinning, setSpinning] = useState<boolean>(false);
|
|
const [searchText, setSearchText] = useState<string>('');
|
|
const [selectedRowKeys, setSelectedRowKeys] = useState<[]>([]);
|
|
const [orgSearchText, setOrgSearchText] = useState<string>('');
|
|
|
|
// 分页参数
|
|
const [currentPage, setCurrentPage] = useState<number>(1);
|
|
const [pageSize, setPageSize] = useState<number>(10);
|
|
const [total, setTotal] = useState<number>(0);
|
|
// 编辑用户
|
|
const [currentUserInfo, setCurrentUserInfo] = useState<any>({
|
|
visible: false,
|
|
});
|
|
// 重置密码
|
|
const [eidtPassword, setEidtPassword] = useState<any>({
|
|
visible: false,
|
|
});
|
|
|
|
// 添加分组
|
|
const [visible, setVisible] = useState<boolean>(false);
|
|
|
|
// 获取用户分组组织树
|
|
useEffect(() => {
|
|
getUserGroupList();
|
|
}, []);
|
|
|
|
// 获取用户列表数据
|
|
useEffect(() => {
|
|
getDataSource();
|
|
}, [searchText, selectedOrg, currentPage, pageSize]);
|
|
|
|
const getUserGroupList = async () => {
|
|
setSpinning(true);
|
|
const params = {
|
|
type: 1,
|
|
};
|
|
try {
|
|
const result = await getGroupTree(params);
|
|
console.log('result=====', result);
|
|
const { code, data = [] } = result || {};
|
|
setSpinning(false);
|
|
if (code === ERROR_CODE) {
|
|
if (data.length > 0) {
|
|
const { children = [] } = data[0] || {};
|
|
setOrgTreeData(children);
|
|
// if (children.length > 0) {
|
|
// setSelectedOrg(children[0].id);
|
|
// }
|
|
}
|
|
} else {
|
|
message.error(result.message || '获取用户分组失败');
|
|
}
|
|
} catch (err) {
|
|
message.error('获取用户分组失败');
|
|
setSpinning(false);
|
|
}
|
|
};
|
|
|
|
const filterTreeData = (
|
|
data: User.OrganizationNode[],
|
|
searchTerm: string,
|
|
): User.OrganizationNode[] => {
|
|
if (!searchTerm) return data;
|
|
|
|
return data.reduce((acc: User.OrganizationNode[], node) => {
|
|
const isMatch = node.name
|
|
?.toLowerCase()
|
|
.includes(searchTerm.toLowerCase());
|
|
|
|
if (isMatch) {
|
|
// If current node matches, include it
|
|
acc.push({ ...node });
|
|
} else if (node.children && node.children.length > 0) {
|
|
// If current node doesn't match, check children
|
|
const filteredChildren = filterTreeData(node.children, searchTerm);
|
|
if (filteredChildren.length > 0) {
|
|
// If any children match, include current node with filtered children
|
|
acc.push({
|
|
...node,
|
|
children: filteredChildren,
|
|
});
|
|
}
|
|
}
|
|
return acc;
|
|
}, []);
|
|
};
|
|
|
|
const filteredOrgTreeData = filterTreeData(orgTreeData, orgSearchText);
|
|
|
|
const getDataSource = async () => {
|
|
const params: any = {
|
|
page_size: pageSize,
|
|
page_num: currentPage,
|
|
};
|
|
if (selectedOrg) {
|
|
params.user_group_id = selectedOrg;
|
|
}
|
|
if (searchText) {
|
|
params.user_name = searchText;
|
|
}
|
|
setLoading(true);
|
|
try {
|
|
const result = await getUserList(params);
|
|
console.log('res======', result);
|
|
const { code, data = {} } = result || {};
|
|
const { data: list, total = 0 } = data || {};
|
|
if (code === ERROR_CODE) {
|
|
setDataSource(list || []);
|
|
setTotal(total);
|
|
setLoading(false);
|
|
} else {
|
|
message.error(result.message || '获取用户列表失败');
|
|
setLoading(false);
|
|
}
|
|
} catch (err) {
|
|
message.error('获取用户列表失败');
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const onDelete = (user?: User.UserItem) => {
|
|
const { id } = user || {};
|
|
const payload = {
|
|
id: id ? id : selectedRowKeys,
|
|
};
|
|
deleteUser(payload as any).then((res) => {
|
|
console.log('res=====', res);
|
|
const { code } = res || {};
|
|
if (code === ERROR_CODE) {
|
|
message.success('删除成功');
|
|
setSelectedRowKeys([]);
|
|
getDataSource();
|
|
}
|
|
});
|
|
};
|
|
|
|
const handlePassword = (user: User.UserItem) => {
|
|
setEidtPassword({
|
|
recordData: { ...user },
|
|
visible: true,
|
|
});
|
|
};
|
|
|
|
const handleCreatUserInfo = () => {
|
|
setCurrentUserInfo({
|
|
visible: true,
|
|
});
|
|
};
|
|
|
|
const handleEditUserInfo = (user: User.UserItem) => {
|
|
setCurrentUserInfo({
|
|
recordData: { ...user },
|
|
visible: true,
|
|
});
|
|
};
|
|
|
|
const columns: ColumnsType<User.UserItem> = [
|
|
{
|
|
title: '序号',
|
|
dataIndex: 'order',
|
|
key: 'order',
|
|
width: 80,
|
|
align: 'center',
|
|
render: (_: any, record: any, index: number) => <span>{index + 1}</span>,
|
|
},
|
|
{
|
|
title: '用户名',
|
|
dataIndex: 'user_name',
|
|
key: 'user_name',
|
|
width: 150,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text) => {
|
|
return <Tooltip title={text || ''}>{text || '--'}</Tooltip>;
|
|
},
|
|
},
|
|
{
|
|
title: '用户分组',
|
|
dataIndex: 'user_group_name',
|
|
key: 'user_group_name',
|
|
width: 150,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text) => {
|
|
return <Tooltip title={text || ''}>{text || '--'}</Tooltip>;
|
|
},
|
|
},
|
|
{
|
|
title: '状态',
|
|
dataIndex: 'status',
|
|
key: 'status',
|
|
width: 150,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text) => {
|
|
const key = text as keyof typeof STATUS_MAP;
|
|
return (
|
|
<Tooltip title={STATUS_MAP[key] || ''}>
|
|
{STATUS_MAP[key] || '--'}
|
|
</Tooltip>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: '用户类别',
|
|
dataIndex: 'user_type',
|
|
key: 'user_type',
|
|
width: 150,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text: number) => {
|
|
const key = text as keyof typeof USER_TYPE_MAP;
|
|
return (
|
|
<Tooltip title={USER_TYPE_MAP[key] || ''}>
|
|
{USER_TYPE_MAP[key] || '--'}
|
|
</Tooltip>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: '优先级',
|
|
dataIndex: 'priority',
|
|
key: 'priority',
|
|
width: 150,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text: number) => {
|
|
const key = text as keyof typeof PRIORITY_MAP;
|
|
return (
|
|
<Tooltip title={PRIORITY_MAP[key] || ''}>
|
|
{PRIORITY_MAP[key] || '--'}
|
|
</Tooltip>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: '性别',
|
|
dataIndex: 'gender',
|
|
key: 'gender',
|
|
width: 150,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text: number) => {
|
|
const key = text as keyof typeof GENDER_MAP;
|
|
return (
|
|
<Tooltip title={GENDER_MAP[key] || ''}>
|
|
{GENDER_MAP[key] || '--'}
|
|
</Tooltip>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: '电话',
|
|
dataIndex: 'cell_phone',
|
|
key: 'cell_phone',
|
|
width: 180,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text: any) => {
|
|
return <Tooltip title={text || ''}>{text || '--'}</Tooltip>;
|
|
},
|
|
},
|
|
{
|
|
title: '出生日期',
|
|
dataIndex: 'birthday',
|
|
key: 'birthday',
|
|
width: 180,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text: any) => {
|
|
return <Tooltip title={text || ''}>{text || '--'}</Tooltip>;
|
|
},
|
|
},
|
|
{
|
|
title: '身份证号',
|
|
dataIndex: 'identity_no',
|
|
key: 'identity_no',
|
|
width: 230,
|
|
ellipsis: true,
|
|
align: 'center',
|
|
render: (text: any) => {
|
|
return <Tooltip title={text || ''}>{text || '--'}</Tooltip>;
|
|
},
|
|
},
|
|
{
|
|
title: '邮箱',
|
|
dataIndex: 'email',
|
|
key: 'email',
|
|
width: 150,
|
|
align: 'center',
|
|
ellipsis: true,
|
|
render: (text: any) => {
|
|
return <Tooltip title={text || ''}>{text || '--'}</Tooltip>;
|
|
},
|
|
},
|
|
{
|
|
title: '操作',
|
|
key: 'actions',
|
|
align: 'center',
|
|
width: 160,
|
|
fixed: 'right',
|
|
render: (_, record) => (
|
|
<div style={{ display: 'flex' }}>
|
|
{/* <Button type="link" onClick={() => handlePassword(record)}>
|
|
重置密码
|
|
</Button> */}
|
|
<Button type="link" onClick={() => handleEditUserInfo(record)}>
|
|
编辑
|
|
</Button>
|
|
<Popconfirm
|
|
title=""
|
|
description="删除操作不可逆,请确认是否删除?"
|
|
onConfirm={() => onDelete(record)}
|
|
// onCancel={cancel}
|
|
okText="删除"
|
|
cancelText="取消"
|
|
>
|
|
<Button type="link">删除</Button>
|
|
</Popconfirm>
|
|
</div>
|
|
),
|
|
},
|
|
];
|
|
|
|
const onOrgSelect = (selectedKeys: React.Key[]) => {
|
|
if (selectedKeys.length > 0) {
|
|
setSelectedOrg(selectedKeys[0]);
|
|
setCurrentPage(1);
|
|
}
|
|
};
|
|
|
|
const handlePageChange = (page: number, size: number) => {
|
|
setCurrentPage(page);
|
|
setPageSize(size);
|
|
};
|
|
|
|
const handlePageSizeChange = (current: number, size: number) => {
|
|
setCurrentPage(1);
|
|
setPageSize(size);
|
|
};
|
|
|
|
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
|
|
console.log('selectedRowKeys changed: ', newSelectedRowKeys);
|
|
setSelectedRowKeys(newSelectedRowKeys as any);
|
|
};
|
|
|
|
const onSaveGroup = () => {
|
|
getUserGroupList();
|
|
};
|
|
|
|
const onSaveUserCallback = () => {
|
|
setCurrentUserInfo({
|
|
recordData: {},
|
|
visible: false,
|
|
});
|
|
getDataSource();
|
|
};
|
|
|
|
const onDeleteGroup = async () => {
|
|
if (selectedOrg) {
|
|
// try {
|
|
// const params = {
|
|
// id: selectedOrg,
|
|
// };
|
|
// const res = await deleteUserGroup(params);
|
|
// const { code } = res || {};
|
|
// if (code === ERROR_CODE) {
|
|
// message.success('分组删除成功');
|
|
// setSelectedOrg(null);
|
|
// getUserGroupList();
|
|
// }
|
|
// } catch (error) {
|
|
// message.error('分组删除失败');
|
|
// }
|
|
const params: any = {
|
|
page_size: pageSize,
|
|
page_num: currentPage,
|
|
};
|
|
if (selectedOrg) {
|
|
params.user_group_id = selectedOrg;
|
|
}
|
|
try {
|
|
const result = await getUserList(params);
|
|
const { data = {} } = result || {};
|
|
const { total = 0 } = data || {};
|
|
if (total > 0) {
|
|
message.info('当前分组下有用户,请先删除用户', 5);
|
|
} else {
|
|
ondeleteCroupSave();
|
|
}
|
|
} catch (err) {}
|
|
}
|
|
};
|
|
|
|
const ondeleteCroupSave = async () => {
|
|
try {
|
|
const params = {
|
|
id: selectedOrg,
|
|
};
|
|
const res = await deleteUserGroup(params);
|
|
const { code } = res || {};
|
|
if (code === ERROR_CODE) {
|
|
message.success('分组删除成功');
|
|
setSelectedOrg(null);
|
|
getUserGroupList();
|
|
}
|
|
} catch (error) {
|
|
message.error('分组删除失败');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className={styles.user_content}>
|
|
<div className={styles.left_content}>
|
|
<div className={styles.search}>
|
|
<div style={{ paddingBottom: '5px' }}>
|
|
<Button
|
|
type="text"
|
|
style={{ marginRight: '8px', fontSize: '16px' }}
|
|
icon={<RedoOutlined />}
|
|
onClick={() => getUserGroupList()}
|
|
title="刷新"
|
|
/>
|
|
<Button
|
|
type="text"
|
|
style={{ marginRight: '8px', fontSize: '16px' }}
|
|
icon={<PlusOutlined />}
|
|
onClick={() => setVisible(true)}
|
|
title="新增"
|
|
/>
|
|
<Popconfirm
|
|
title=""
|
|
description="删除操作不可逆,请确认是否删除?"
|
|
onConfirm={() => onDeleteGroup()}
|
|
// onCancel={cancel}
|
|
okText="删除"
|
|
cancelText="取消"
|
|
disabled={!selectedOrg}
|
|
>
|
|
<Button
|
|
type="text"
|
|
style={{ fontSize: '16px' }}
|
|
icon={<DeleteOutlined />}
|
|
title="删除"
|
|
disabled={!selectedOrg}
|
|
/>
|
|
</Popconfirm>
|
|
</div>
|
|
<Input.Search
|
|
placeholder="请输入名称"
|
|
style={{ marginBottom: 6 }}
|
|
onSearch={(value) => setOrgSearchText(value)}
|
|
onChange={(e) => setOrgSearchText(e.target.value)}
|
|
/>
|
|
</div>
|
|
<div className={styles.tree_box}>
|
|
<Spin spinning={spinning} delay={100}>
|
|
<CustomTree
|
|
treeData={filteredOrgTreeData}
|
|
titleField="name"
|
|
keyField="id"
|
|
childrenField="children"
|
|
defaultExpandAll
|
|
onSelect={onOrgSelect}
|
|
selectedKeys={selectedOrg ? [selectedOrg] : []}
|
|
showIcon={true}
|
|
icon={<TeamOutlined style={{ fontSize: '15px' }} />}
|
|
/>
|
|
</Spin>
|
|
</div>
|
|
</div>
|
|
<div className={styles.right_content}>
|
|
<div className={styles.teble_content}>
|
|
<div
|
|
style={{
|
|
marginBottom: 16,
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
}}
|
|
>
|
|
<div>
|
|
<Button
|
|
style={{ marginRight: '8px' }}
|
|
onClick={() => handleCreatUserInfo()}
|
|
>
|
|
新建用户
|
|
</Button>
|
|
{/* <Popconfirm
|
|
title=""
|
|
description="删除操作不可逆,请确认是否删除?"
|
|
onConfirm={() => onDelete()}
|
|
// onCancel={cancel}
|
|
okText="删除"
|
|
cancelText="取消"
|
|
disabled={selectedRowKeys.length === 0}
|
|
>
|
|
<Button
|
|
disabled={selectedRowKeys.length === 0}
|
|
style={{ marginRight: '8px' }}
|
|
icon={<DeleteOutlined />}
|
|
>
|
|
删除
|
|
</Button>
|
|
</Popconfirm> */}
|
|
</div>
|
|
<div>
|
|
<div>
|
|
<Input.Search
|
|
placeholder="用户名"
|
|
value={searchText}
|
|
onChange={(e) => setSearchText(e.target.value)}
|
|
style={{ width: 300 }}
|
|
onSearch={(value) => {
|
|
console.log('Search user:', value);
|
|
setCurrentPage(1); // Reset to first page when searching
|
|
}}
|
|
/>
|
|
<Button
|
|
style={{ marginRight: '8px', marginLeft: '8px' }}
|
|
icon={<RedoOutlined />}
|
|
onClick={getDataSource}
|
|
loading={loading}
|
|
title="刷新"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className={styles.teble_box}>
|
|
<div className="images-list-table">
|
|
<Table
|
|
columns={columns}
|
|
dataSource={dataSource}
|
|
loading={loading}
|
|
rowKey="id"
|
|
pagination={{
|
|
current: currentPage,
|
|
pageSize: pageSize,
|
|
total: total,
|
|
onChange: handlePageChange,
|
|
onShowSizeChange: handlePageSizeChange,
|
|
showSizeChanger: true,
|
|
showQuickJumper: true,
|
|
pageSizeOptions: ['10', '20', '50', '100'],
|
|
showTotal: (total) => {
|
|
return `共${total}条数据`;
|
|
},
|
|
}}
|
|
// rowSelection={{
|
|
// selectedRowKeys,
|
|
// onChange: onSelectChange,
|
|
// }}
|
|
// scroll={{ x: 'max-content', y: 55 * 12 }}
|
|
scroll={{
|
|
y: 'max-content', // 关键:允许内容决定高度
|
|
}}
|
|
style={{
|
|
height: '100%',
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{visible && (
|
|
<CreatGroup
|
|
visible={visible}
|
|
type={1}
|
|
title="新增用户分组"
|
|
selectedOrg={selectedOrg}
|
|
onCancel={() => {
|
|
setVisible(false);
|
|
}}
|
|
onOk={onSaveGroup}
|
|
orgTreeData={orgTreeData}
|
|
/>
|
|
)}
|
|
{currentUserInfo.visible && (
|
|
<UserEditModal
|
|
selectedOrg={selectedOrg}
|
|
orgTreeData={orgTreeData}
|
|
currentUserInfo={currentUserInfo}
|
|
onCancel={() => {
|
|
setCurrentUserInfo({
|
|
recordData: {},
|
|
visible: false,
|
|
});
|
|
}}
|
|
onOk={() => {
|
|
onSaveUserCallback();
|
|
}}
|
|
/>
|
|
)}
|
|
<PasswordResetModal
|
|
visible={eidtPassword.visible}
|
|
onCancel={() => {
|
|
setEidtPassword({
|
|
recordData: {},
|
|
visible: false,
|
|
});
|
|
}}
|
|
onOk={() => {}}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default UserListPage;
|