|
|
|
@ -1,129 +1,166 @@ |
|
|
|
|
import { Space, Col, Row, Switch, Input, Table, Form, Button, Select, Popconfirm, App, Modal, InputNumber, TreeSelect, DatePicker, Tree, TreeProps } from 'antd'; |
|
|
|
|
import { apiInterceptors, getRoleList, getRoleDetail, addRole, deleteRole, updateRoleStatus, updateRole, getAllMenuTree, getRoleMenusId, addRoleAndMenus } from '@/client/api'; |
|
|
|
|
import { Space, Switch, Input, Table, Form, Button, Select, Popconfirm, App, Modal, InputNumber, TreeSelect, DatePicker, Tree, TreeProps } from 'antd'; |
|
|
|
|
import { apiInterceptors, getAllOrg, getRoleListAuth, getUserList, getUserDetail, addUsers, updateUser, deleteUser, updateUserPassword, updateUserStatus, getUserRole, addUserAndRoles } from '@/client/api'; |
|
|
|
|
import useUser from '@/hooks/use-user'; |
|
|
|
|
import SystemConstructConstruct from '@/new-components/layout/SystemConstruct'; |
|
|
|
|
import styles from './index.module.scss'; |
|
|
|
|
import type { ColumnsType } from 'antd/es/table'; |
|
|
|
|
import React, { useEffect, useState } from 'react'; |
|
|
|
|
import { useTranslation } from 'react-i18next'; |
|
|
|
|
import { IPrompt, orginzationResponse } from '@/types/prompt'; |
|
|
|
|
import { orginzationResponse, IUser, IOrganization } from '@/types/prompt'; |
|
|
|
|
import { useRequest } from 'ahooks'; |
|
|
|
|
import { TFunction } from 'i18next'; |
|
|
|
|
import dayjs from 'dayjs'; |
|
|
|
|
import 'dayjs/locale/zh-cn'; |
|
|
|
|
dayjs.locale('zh-cn'); |
|
|
|
|
const { Option } = Select; |
|
|
|
|
const { RangePicker } = DatePicker; |
|
|
|
|
type LayoutType = Parameters<typeof Form>[0]['layout']; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const Prompt = () => { |
|
|
|
|
const [editId, setEditId] = useState<number | null>(null); |
|
|
|
|
const [modalType, setModalType] = useState<'add' | 'edit'>('add'); |
|
|
|
|
const [addForm] = Form.useForm(); |
|
|
|
|
const { message } = App.useApp(); |
|
|
|
|
const [addLoading, setAddLoading] = useState(false); |
|
|
|
|
const { t } = useTranslation(); |
|
|
|
|
const [formLayout] = useState<LayoutType>('inline'); |
|
|
|
|
const [promptList, setPromptList] = useState<orginzationResponse>(); |
|
|
|
|
|
|
|
|
|
const [modalVisible, setModalVisible] = useState(false); |
|
|
|
|
const [modalType, setModalType] = useState<'add' | 'edit'>('add'); |
|
|
|
|
const [currentEditId, setCurrentEditId] = useState<number | null>(null); |
|
|
|
|
const addForm = Form.useForm()[0]; |
|
|
|
|
const [resetPasswordVisible, setResetPasswordVisible] = useState(false); |
|
|
|
|
const [currentUserId, setCurrentUserId] = useState<number | null>(null); |
|
|
|
|
const resetPasswordForm = Form.useForm()[0]; |
|
|
|
|
const [pagination, setPagination] = useState({ |
|
|
|
|
page: 1, // 改为page
|
|
|
|
|
page_size: 10 // 改为page_size
|
|
|
|
|
}); |
|
|
|
|
const [total, setTotal] = useState(0); |
|
|
|
|
const [menuModalVisible, setMenuModalVisible] = useState(false); |
|
|
|
|
const [currentRoleId, setCurrentRoleId] = useState<number | null>(null); |
|
|
|
|
const [menuTreeData, setMenuTreeData] = useState<any[]>([]); |
|
|
|
|
const [checkedKeys, setCheckedKeys] = useState<number[]>([]); |
|
|
|
|
const [roleName, setUserName] = useState<string[]>([]); |
|
|
|
|
const [roleCode, setUserCode] = useState<string[]>([]); |
|
|
|
|
const [deptTreeData, setDeptTreeData] = useState<IOrganization[]>([]); |
|
|
|
|
// 分配角色弹窗相关状态
|
|
|
|
|
const [assignRoleVisible, setAssignRoleVisible] = useState(false); |
|
|
|
|
const [currentUserIdForRole, setCurrentUserIdForRole] = useState<number | null>(null); |
|
|
|
|
const [currentUserNames, setCurrentUserNames] = useState<{ username: string; nickName: string }>({ |
|
|
|
|
username: '', |
|
|
|
|
nickName: '', |
|
|
|
|
}); |
|
|
|
|
const [roleList, setRoleList] = useState<{ id: number; name: string }[]>([]); |
|
|
|
|
const [selectedRoles, setSelectedRoles] = useState<number[]>([]); |
|
|
|
|
const [loadingRoles, setLoadingRoles] = useState(false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const buildTree = (data: IOrganization[]): IOrganization[] => { |
|
|
|
|
const map = new Map<number, IOrganization>(); |
|
|
|
|
const tree: IOrganization[] = []; |
|
|
|
|
|
|
|
|
|
const handleMenuAuth = async (roleId: any) => { |
|
|
|
|
setCurrentRoleId(roleId.id); |
|
|
|
|
try { |
|
|
|
|
const [, roleMenusRes] = await apiInterceptors(getRoleMenusId(roleId.id)); |
|
|
|
|
const menuIds = roleMenusRes?.menu_ids || []; |
|
|
|
|
|
|
|
|
|
const [, allMenusRes] = await apiInterceptors(getAllMenuTree()); |
|
|
|
|
const treeData = allMenusRes || []; |
|
|
|
|
debugger |
|
|
|
|
setMenuTreeData(treeData); |
|
|
|
|
setCheckedKeys(menuIds); |
|
|
|
|
setUserCode(roleId.code); |
|
|
|
|
setUserName(roleId.name); |
|
|
|
|
setMenuModalVisible(true); |
|
|
|
|
} catch (err) { |
|
|
|
|
message.error('获取菜单权限失败'); |
|
|
|
|
} |
|
|
|
|
data.forEach(item => { |
|
|
|
|
map.set(item.id, { ...item }); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
data.forEach(item => { |
|
|
|
|
const node = map.get(item.id)!; |
|
|
|
|
if (item.parent_id !== 0) { |
|
|
|
|
const parent = map.get(item.parent_id); |
|
|
|
|
if (parent) { |
|
|
|
|
// 只在需要时初始化children数组
|
|
|
|
|
parent.children = parent.children || []; |
|
|
|
|
parent.children.push(node); |
|
|
|
|
} else { |
|
|
|
|
tree.push(node); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
tree.push(node); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// 后处理:移除空children
|
|
|
|
|
const removeEmptyChildren = (nodes: IOrganization[]): IOrganization[] => { |
|
|
|
|
return nodes.map(node => ({ |
|
|
|
|
...node, |
|
|
|
|
children: node.children?.length ? removeEmptyChildren(node.children) : undefined |
|
|
|
|
})); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
return removeEmptyChildren(tree); |
|
|
|
|
}; |
|
|
|
|
// 获取并构建树
|
|
|
|
|
const { data: orgData } = useRequest(getAllOrg); |
|
|
|
|
useEffect(() => { |
|
|
|
|
if (orgData?.data?.data?.dept_list) { |
|
|
|
|
const treeData = buildTree(orgData.data.data.dept_list); // 构建层级树
|
|
|
|
|
const formatted = treeData.map(item => ({ |
|
|
|
|
...item, |
|
|
|
|
children: item.children ? formatToTreeSelect(item.children) : [], |
|
|
|
|
})); |
|
|
|
|
setDeptTreeData(formatted); |
|
|
|
|
} |
|
|
|
|
}, [orgData]); |
|
|
|
|
|
|
|
|
|
const handleSaveMenuAuth = async () => { |
|
|
|
|
if (!currentRoleId) return; |
|
|
|
|
const formatToTreeSelect = (list: IOrganization[]) => { |
|
|
|
|
return list.map(item => ({ |
|
|
|
|
...item, |
|
|
|
|
children: item.children ? formatToTreeSelect(item.children) : [], |
|
|
|
|
})); |
|
|
|
|
}; |
|
|
|
|
const assignRoles = async (user: IUser) => { |
|
|
|
|
setCurrentUserIdForRole(user.id); |
|
|
|
|
setCurrentUserNames({ |
|
|
|
|
username: user.username, |
|
|
|
|
nickName: user.nick_name, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
const params = { |
|
|
|
|
role_id: currentRoleId, |
|
|
|
|
menu_ids: checkedKeys, |
|
|
|
|
}; |
|
|
|
|
const [, res] = await apiInterceptors(addRoleAndMenus(params)); |
|
|
|
|
// 加载所有可用角色
|
|
|
|
|
setLoadingRoles(true); |
|
|
|
|
const [err, roleData] = await apiInterceptors(getRoleListAuth()); |
|
|
|
|
debugger; |
|
|
|
|
if (!err && roleData) { |
|
|
|
|
setRoleList(roleData || []); |
|
|
|
|
} |
|
|
|
|
setLoadingRoles(false); |
|
|
|
|
|
|
|
|
|
if (res) { |
|
|
|
|
message.success('菜单权限保存成功'); |
|
|
|
|
setMenuModalVisible(false); |
|
|
|
|
setCurrentRoleId(null); |
|
|
|
|
setMenuTreeData([]); |
|
|
|
|
setCheckedKeys([]); |
|
|
|
|
} else { |
|
|
|
|
message.error('菜单权限保存失败'); |
|
|
|
|
} |
|
|
|
|
} catch (err) { |
|
|
|
|
message.error('菜单权限保存失败'); |
|
|
|
|
// 加载当前用户已绑定的角色
|
|
|
|
|
const [userErr, userData] = await apiInterceptors(getUserRole(user.id)); |
|
|
|
|
if (!userErr && userData?.role_ids) { |
|
|
|
|
setSelectedRoles(userData.role_ids); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setAssignRoleVisible(true); |
|
|
|
|
}; |
|
|
|
|
const onCheck: TreeProps['onCheck'] = (checkedKeysValue: number[]) => { |
|
|
|
|
setCheckedKeys(checkedKeysValue.map(key => Number(key))); |
|
|
|
|
const handleResetPassword = (user: IUser) => { |
|
|
|
|
setCurrentUserId(user.id); |
|
|
|
|
setResetPasswordVisible(true); |
|
|
|
|
resetPasswordForm.resetFields(); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const DeleteBtn: React.FC<{ record: IPrompt; }> = ({ record }) => { |
|
|
|
|
const userInfo = useUser(); |
|
|
|
|
const { t } = useTranslation(); |
|
|
|
|
|
|
|
|
|
const DeleteBtn: React.FC<{ record: IUser }> = ({ record }) => { |
|
|
|
|
const { message } = App.useApp(); |
|
|
|
|
|
|
|
|
|
// 删除prompt
|
|
|
|
|
const { run: deletePromptRun, loading: deleteLoading } = useRequest( |
|
|
|
|
async record => { |
|
|
|
|
await deleteRole(record?.id); // 使用正确的角色删除接口
|
|
|
|
|
const { run: deleteUserRun, loading: deleteLoading } = useRequest( |
|
|
|
|
async () => { |
|
|
|
|
await deleteUser(record.id); |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
manual: true, |
|
|
|
|
onSuccess: async () => { |
|
|
|
|
onSuccess: async (res) => { |
|
|
|
|
console.log(res); |
|
|
|
|
message.success('删除成功'); |
|
|
|
|
await getPrompts(); |
|
|
|
|
await getUsers(); // 刷新列表
|
|
|
|
|
}, |
|
|
|
|
onError: (err: any) => { |
|
|
|
|
message.error(err?.message); |
|
|
|
|
message.error(err.message || '删除失败'); |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// if (userInfo?.id !== record?.id) {
|
|
|
|
|
// return null;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<Popconfirm title='确认删除吗?' onConfirm={async () => await deletePromptRun(record)}> |
|
|
|
|
<Button type="link" danger loading={deleteLoading}>{t('Delete')}</Button> |
|
|
|
|
<Popconfirm title="确认删除吗?" onConfirm={async () => await deleteUserRun()}> |
|
|
|
|
<Button type="link" size="small" danger loading={deleteLoading}> |
|
|
|
|
删除 |
|
|
|
|
</Button> |
|
|
|
|
</Popconfirm> |
|
|
|
|
); |
|
|
|
|
}; |
|
|
|
|
const getColumns = (t: TFunction, handleEdit: (prompt: IPrompt) => void): ColumnsType<IPrompt> => [ |
|
|
|
|
const getColumns = (t: TFunction, handleEdit: (user: IUser) => void): ColumnsType<IUser> => [ |
|
|
|
|
{ |
|
|
|
|
title: t('role_id'), |
|
|
|
|
title: t('user_id'), |
|
|
|
|
dataIndex: 'id', |
|
|
|
|
key: 'id', |
|
|
|
|
width: '8%', |
|
|
|
@ -131,101 +168,102 @@ const Prompt = () => { |
|
|
|
|
showSorterTooltip: true, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
title: t('role_name'), |
|
|
|
|
dataIndex: 'name', |
|
|
|
|
key: 'name', |
|
|
|
|
// width: '10%',
|
|
|
|
|
title: t('user_name'), |
|
|
|
|
dataIndex: 'username', |
|
|
|
|
key: 'username', |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
title: t('user_nickname'), |
|
|
|
|
dataIndex: 'nick_name', |
|
|
|
|
key: 'nick_name', |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
title: t('department_name'), |
|
|
|
|
dataIndex: 'dept_name', |
|
|
|
|
key: 'dept_name', |
|
|
|
|
render: (dept_name: string) => { |
|
|
|
|
// 可以通过 deptId 查询部门名称(如果后续需要),这里先显示 ID
|
|
|
|
|
return dept_name || '-'; |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
title: t('user_phone'), |
|
|
|
|
dataIndex: 'phone', |
|
|
|
|
key: 'phone', |
|
|
|
|
render: (phone: string | null) => phone || '-', |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
title: t('role_identity'), |
|
|
|
|
dataIndex: 'code', |
|
|
|
|
// width: '10%',
|
|
|
|
|
key: 'code', |
|
|
|
|
}, { |
|
|
|
|
title: t('sort'), |
|
|
|
|
dataIndex: 'sort', |
|
|
|
|
// width: '10%',
|
|
|
|
|
key: 'sort', |
|
|
|
|
}, { |
|
|
|
|
title: t('Remark'), |
|
|
|
|
dataIndex: 'remark', |
|
|
|
|
// width: '10%',
|
|
|
|
|
key: 'remark', |
|
|
|
|
}, { |
|
|
|
|
title: t('Status'), |
|
|
|
|
dataIndex: 'status', |
|
|
|
|
key: 'status', |
|
|
|
|
render: (status: number, record: IPrompt) => ( |
|
|
|
|
render: (status: string, record) => ( |
|
|
|
|
<Switch |
|
|
|
|
checked={status === 0} |
|
|
|
|
checked={status === '0'} |
|
|
|
|
checkedChildren="开启" |
|
|
|
|
unCheckedChildren="关闭" |
|
|
|
|
onChange={(checked) => handleStatusChange(record.id, checked)} |
|
|
|
|
/> |
|
|
|
|
), |
|
|
|
|
}, { |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
title: t('create_time'), |
|
|
|
|
dataIndex: 'create_time', |
|
|
|
|
width: '20%', |
|
|
|
|
key: 'create_time', |
|
|
|
|
}, { |
|
|
|
|
width: '13%', |
|
|
|
|
render: (time: string) => dayjs(time).format('YYYY-MM-DD HH:mm:ss'), |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
title: t('Operation'), |
|
|
|
|
dataIndex: 'operate', |
|
|
|
|
key: 'operate', |
|
|
|
|
width: '20%', |
|
|
|
|
render: (_, record) => ( |
|
|
|
|
<Space align='center'> |
|
|
|
|
<Button |
|
|
|
|
onClick={() => { |
|
|
|
|
handleEdit(record); |
|
|
|
|
}} |
|
|
|
|
type="link" |
|
|
|
|
> |
|
|
|
|
<Button type="link" size="small" onClick={() => handleEdit(record)}> |
|
|
|
|
{t('Edit')} |
|
|
|
|
</Button> |
|
|
|
|
<Button |
|
|
|
|
onClick={() => { |
|
|
|
|
handleMenuAuth(record); |
|
|
|
|
}} |
|
|
|
|
type="link" |
|
|
|
|
> |
|
|
|
|
菜单权限 |
|
|
|
|
<Button type="link" size="small" style={{ color: 'orange' }} onClick={() => handleResetPassword(record)}> |
|
|
|
|
重置密码 |
|
|
|
|
</Button> |
|
|
|
|
<Button type="link" size="small" onClick={() => assignRoles(record)}> |
|
|
|
|
分配角色 |
|
|
|
|
</Button> |
|
|
|
|
<DeleteBtn record={record} /> |
|
|
|
|
</Space> |
|
|
|
|
), |
|
|
|
|
}, |
|
|
|
|
]; |
|
|
|
|
const handleEditBtn = async (prompt: IPrompt) => { |
|
|
|
|
|
|
|
|
|
const handleEditBtn = async (record: IUser) => { |
|
|
|
|
setModalType('edit'); |
|
|
|
|
setEditId(prompt.id); |
|
|
|
|
setCurrentEditId(record.id); |
|
|
|
|
try { |
|
|
|
|
const [_, data] = await apiInterceptors(getRoleDetail(prompt.id)); |
|
|
|
|
const [_, data] = await apiInterceptors(getUserDetail(record.id)); |
|
|
|
|
debugger; |
|
|
|
|
if (data) { |
|
|
|
|
addForm.setFieldsValue({ |
|
|
|
|
...data, |
|
|
|
|
status: data.status === 0 // 根据接口返回0/1转换Switch状态
|
|
|
|
|
status: data.status === '0', |
|
|
|
|
}); |
|
|
|
|
setModalVisible(true); |
|
|
|
|
} |
|
|
|
|
} catch (err) { |
|
|
|
|
message.error('获取详情失败'); |
|
|
|
|
message.error('获取用户详情失败'); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const handleStatusChange = async (roleId: number, checked: boolean) => { |
|
|
|
|
const handleStatusChange = async (userId: number, checked: boolean) => { |
|
|
|
|
try { |
|
|
|
|
const [_, res] = await apiInterceptors( |
|
|
|
|
updateRoleStatus({ |
|
|
|
|
id: roleId, |
|
|
|
|
status: checked ? 0 : 1 // 根据Switch状态转换
|
|
|
|
|
updateUserStatus({ |
|
|
|
|
id: userId, |
|
|
|
|
status: checked ? '0' : '1', |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
debugger |
|
|
|
|
|
|
|
|
|
if (res) { |
|
|
|
|
message.success('状态更新成功'); |
|
|
|
|
await getPrompts(); |
|
|
|
|
// 局部更新数据(可选)
|
|
|
|
|
await getUsers(); // 刷新列表
|
|
|
|
|
} else { |
|
|
|
|
message.error('状态更新失败'); |
|
|
|
|
} |
|
|
|
@ -234,62 +272,55 @@ const Prompt = () => { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
const { |
|
|
|
|
run: getPrompts, loading: loadingOrgs |
|
|
|
|
run: getUsers, |
|
|
|
|
loading: loadingUsers, |
|
|
|
|
} = useRequest( |
|
|
|
|
async (params?: { |
|
|
|
|
name?: string; |
|
|
|
|
status?: string; |
|
|
|
|
code?: string; |
|
|
|
|
create_time_start?: string; |
|
|
|
|
create_time_end?: string; |
|
|
|
|
page?: number; |
|
|
|
|
page_size?: number; |
|
|
|
|
}) => { |
|
|
|
|
const [_, data] = await apiInterceptors( |
|
|
|
|
getRoleList({ |
|
|
|
|
getUserList({ |
|
|
|
|
...params, |
|
|
|
|
pageNum: params?.page || 1, // 添加分页参数
|
|
|
|
|
pageSize: params?.page_size || 10 |
|
|
|
|
page: params?.page || 1, |
|
|
|
|
page_size: params?.page_size || 10, |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
return data; |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
manual: true, |
|
|
|
|
onSuccess: data => { |
|
|
|
|
// 移除部门树形结构转换逻辑
|
|
|
|
|
setPromptList(data); // 直接使用接口返回数据
|
|
|
|
|
setTotal(data.total_count); // 设置总条数
|
|
|
|
|
onSuccess: (data) => { |
|
|
|
|
setPromptList(data); |
|
|
|
|
setTotal(data.total_count); |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const handleSearch = async () => { |
|
|
|
|
try { |
|
|
|
|
const values = await addForm.validateFields(); |
|
|
|
|
const timeRange = values.createTime; // 获取时间范围值
|
|
|
|
|
const timeRange = values.createTime; |
|
|
|
|
|
|
|
|
|
await getPrompts({ |
|
|
|
|
await getUsers({ |
|
|
|
|
...values, |
|
|
|
|
create_time_start: timeRange?.[0]?.format('YYYY-MM-DD'), // 转换开始时间
|
|
|
|
|
create_time_end: timeRange?.[1]?.format('YYYY-MM-DD'), // 转换结束时间
|
|
|
|
|
page: 1, // 使用page参数名
|
|
|
|
|
page_size: pagination.page_size |
|
|
|
|
create_time_start: timeRange?.[0]?.format('YYYY-MM-DD'), |
|
|
|
|
create_time_end: timeRange?.[1]?.format('YYYY-MM-DD'), |
|
|
|
|
page: 1, |
|
|
|
|
page_size: pagination.page_size, |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
setPagination(prev => ({ |
|
|
|
|
...prev, |
|
|
|
|
pageNum: 1, |
|
|
|
|
page: 1 // 同步新增的page状态
|
|
|
|
|
})); |
|
|
|
|
setPagination(prev => ({ ...prev, page: 1 })); |
|
|
|
|
} catch (err) { |
|
|
|
|
console.error('表单验证失败:', err); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
const handleReset = async () => { |
|
|
|
|
addForm.resetFields(); // 清空表单
|
|
|
|
|
await getPrompts(); // 重新获取全部数据
|
|
|
|
|
await getUsers(); // 重新获取全部数据
|
|
|
|
|
}; |
|
|
|
|
const [modalVisible, setModalVisible] = useState(false); |
|
|
|
|
|
|
|
|
|
const buttonItemLayout = |
|
|
|
|
formLayout === 'inline' |
|
|
|
@ -299,7 +330,7 @@ const Prompt = () => { |
|
|
|
|
: null; |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
getPrompts(); |
|
|
|
|
getUsers(); |
|
|
|
|
}, []); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
@ -313,20 +344,24 @@ const Prompt = () => { |
|
|
|
|
> |
|
|
|
|
<div className={`${styles['searchBarBox']}`}> |
|
|
|
|
<div className={`${styles['search-bar-left']}`}> |
|
|
|
|
<Form.Item className={`mb-1`} label="角色名称" name="name"> |
|
|
|
|
<Input placeholder="请输入角色名称" /> |
|
|
|
|
<Form.Item className={`mb-1`} label="用户名称" name="username"> |
|
|
|
|
<Input placeholder="请输入用户名称" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
<Form.Item className={`mb-1`} label="角色标识" name="code" > |
|
|
|
|
<Input placeholder="请输入角色标识" /> |
|
|
|
|
<Form.Item className={`mb-1`} label="用户昵称" name="nick_name" > |
|
|
|
|
<Input placeholder="请输入用户昵称" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
<Form.Item className={`mb-1`} label="角色状态" name="status"> |
|
|
|
|
<Select style={{ width: 100 }} placeholder="请选择角色状态"> |
|
|
|
|
<Option value="0">开启</Option> |
|
|
|
|
<Option value="1">关闭</Option> |
|
|
|
|
</Select> |
|
|
|
|
<Form.Item className={`mb-1`} label="手机号码" name="phone" > |
|
|
|
|
<Input placeholder="请输入手机号码" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
<Form.Item className={`mb-1`} label="创建时间" name="createTime"> |
|
|
|
|
<RangePicker /> |
|
|
|
|
<Form.Item className={`mb-1`} label="组织部门" name="dept_id"> |
|
|
|
|
<TreeSelect |
|
|
|
|
showSearch |
|
|
|
|
placeholder="请选择部门" |
|
|
|
|
allowClear |
|
|
|
|
treeDefaultExpandAll |
|
|
|
|
fieldNames={{ value: 'id', label: 'name', children: 'children' }} |
|
|
|
|
treeData={deptTreeData} |
|
|
|
|
/> |
|
|
|
|
</Form.Item> |
|
|
|
|
</div> |
|
|
|
|
<div className={`${styles['search-bar-right']}`}> |
|
|
|
@ -346,8 +381,9 @@ const Prompt = () => { |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</Form> |
|
|
|
|
|
|
|
|
|
<Modal |
|
|
|
|
title={modalType === 'add' ? '新增角色' : '编辑角色'} |
|
|
|
|
title={modalType === 'add' ? '新增用户' : '编辑用户'} |
|
|
|
|
open={modalVisible} |
|
|
|
|
onCancel={() => { |
|
|
|
|
setModalVisible(false); |
|
|
|
@ -357,149 +393,260 @@ const Prompt = () => { |
|
|
|
|
> |
|
|
|
|
<Form |
|
|
|
|
form={addForm} |
|
|
|
|
initialValues={{ |
|
|
|
|
status: true // 确保新增时默认开启
|
|
|
|
|
}} |
|
|
|
|
labelCol={{ span: 6 }} |
|
|
|
|
wrapperCol={{ span: 16 }} |
|
|
|
|
onFinish={async (values) => { |
|
|
|
|
setAddLoading(true); |
|
|
|
|
const params = { |
|
|
|
|
...values, |
|
|
|
|
status: '0', // Switch 值转换
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
const params = { |
|
|
|
|
name: values.name, |
|
|
|
|
code: values.code, |
|
|
|
|
sort: values.sort, |
|
|
|
|
// status: values.status ? 0 : 1, // 根据Switch值转换状态
|
|
|
|
|
status:0 , // 根据Switch值转换状态
|
|
|
|
|
remark: values.remark |
|
|
|
|
}; |
|
|
|
|
debugger |
|
|
|
|
let apiCall; |
|
|
|
|
if (modalType === 'edit' && editId) { |
|
|
|
|
apiCall = updateRole({ ...params, id: editId }); // 使用角色更新接口
|
|
|
|
|
if (modalType === 'edit' && currentEditId) { |
|
|
|
|
apiCall = updateUser({ ...params, id: currentEditId }); |
|
|
|
|
} else { |
|
|
|
|
apiCall = addRole(params); // 使用角色新增接口
|
|
|
|
|
apiCall = addUsers(params); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const [_, data] = await apiInterceptors(apiCall); |
|
|
|
|
|
|
|
|
|
if (data) { |
|
|
|
|
message.success(modalType === 'edit' ? '编辑成功' : '新增成功'); |
|
|
|
|
setModalVisible(false); |
|
|
|
|
addForm.resetFields(); |
|
|
|
|
await getPrompts(); |
|
|
|
|
setEditId(null); |
|
|
|
|
await getUsers(); // 刷新列表
|
|
|
|
|
} else { |
|
|
|
|
message.error(data.err_msg || '操作失败'); |
|
|
|
|
} |
|
|
|
|
} catch (err) { |
|
|
|
|
console.error('操作失败:', err); |
|
|
|
|
message.error('操作失败'); |
|
|
|
|
} finally { |
|
|
|
|
setAddLoading(false); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
{/* 角色名称 */} |
|
|
|
|
<Form.Item label="角色名称" name="name" rules={[{ required: true }]}> |
|
|
|
|
<Input placeholder="请输入角色名称" /> |
|
|
|
|
{/* 用户名 */} |
|
|
|
|
<Form.Item label="账号" name="username" rules={[{ required: true }]}> |
|
|
|
|
<Input placeholder="请输入账号" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
{/* 角色标识 */} |
|
|
|
|
<Form.Item label="角色标识" name="code" rules={[ |
|
|
|
|
{ required: true }, |
|
|
|
|
{ pattern: /^[a-z_]+$/, message: '只允许小写字母和下划线' } |
|
|
|
|
]}> |
|
|
|
|
<Input placeholder="请输入唯一标识(如:admin)" /> |
|
|
|
|
{/* 昵称 */} |
|
|
|
|
<Form.Item label="昵称" name="nick_name" rules={[{ required: true }]}> |
|
|
|
|
<Input placeholder="请输入昵称" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
{/* 排序 */} |
|
|
|
|
<Form.Item label="排序" name="sort" rules={[{ required: true, type: 'number' }]}> |
|
|
|
|
<InputNumber min={0} style={{ width: '100%' }} /> |
|
|
|
|
{/* 密码(仅新增时显示) */} |
|
|
|
|
{modalType === 'add' && ( |
|
|
|
|
<Form.Item label="密码" name="password" rules={[{ required: true }]}> |
|
|
|
|
<Input.Password placeholder="请输入密码" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
{/* 手机号码 */} |
|
|
|
|
<Form.Item label="手机号码" name="phone" rules={[{ required: true }]}> |
|
|
|
|
<Input placeholder="请输入手机号码" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
{/* 备注 */} |
|
|
|
|
<Form.Item label="备注" name="remark"> |
|
|
|
|
<Input.TextArea placeholder="请输入角色描述" rows={3} /> |
|
|
|
|
{/* 部门选择 */} |
|
|
|
|
<Form.Item label="所属部门" name="dept_id" rules={[{ required: true }]}> |
|
|
|
|
<TreeSelect |
|
|
|
|
showSearch |
|
|
|
|
placeholder="请选择部门" |
|
|
|
|
allowClear |
|
|
|
|
treeDefaultExpandAll |
|
|
|
|
fieldNames={{ value: 'id', label: 'name', children: 'children' }} |
|
|
|
|
treeData={deptTreeData} |
|
|
|
|
/> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
{/* 性别 */} |
|
|
|
|
<Form.Item label="性别" name="sex"> |
|
|
|
|
<Select placeholder="请选择性别"> |
|
|
|
|
<Option value="0">男</Option> |
|
|
|
|
<Option value="1">女</Option> |
|
|
|
|
<Option value="2">未知</Option> |
|
|
|
|
</Select> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
{/* 状态 */} |
|
|
|
|
{/* <Form.Item label="状态" name="status" valuePropName="checked"> |
|
|
|
|
<Switch |
|
|
|
|
checkedChildren="开启" |
|
|
|
|
unCheckedChildren="关闭" |
|
|
|
|
// defaultChecked={true} // 默认开启状态
|
|
|
|
|
// onChange={switchOnChange}
|
|
|
|
|
/> |
|
|
|
|
{/* <Form.Item label="状态" name="status" valuePropName="checked" initialValue={true}> |
|
|
|
|
<Switch checkedChildren="开启" unCheckedChildren="关闭" /> |
|
|
|
|
</Form.Item> */} |
|
|
|
|
|
|
|
|
|
<Form.Item wrapperCol={{ offset: 8, span: 16 }}> |
|
|
|
|
{/* 邮箱 */} |
|
|
|
|
<Form.Item label="邮箱" name="email"> |
|
|
|
|
<Input placeholder="请输入邮箱" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
{/* 备注 */} |
|
|
|
|
<Form.Item label="备注" name="remark"> |
|
|
|
|
<Input.TextArea rows={3} placeholder="请输入备注信息" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
{/* 按钮 */} |
|
|
|
|
<Form.Item wrapperCol={{ offset: 6, span: 16 }}> |
|
|
|
|
<Space> |
|
|
|
|
<Button onClick={() => setModalVisible(false)}>取消</Button> |
|
|
|
|
<Button type="primary" htmlType="submit" loading={addLoading}>提交</Button> |
|
|
|
|
<Button type="primary" htmlType="submit" loading={addLoading}> |
|
|
|
|
提交 |
|
|
|
|
</Button> |
|
|
|
|
</Space> |
|
|
|
|
</Form.Item> |
|
|
|
|
</Form> |
|
|
|
|
</Modal> |
|
|
|
|
<Modal |
|
|
|
|
title="菜单权限" |
|
|
|
|
open={menuModalVisible} |
|
|
|
|
title="重置密码" |
|
|
|
|
open={resetPasswordVisible} |
|
|
|
|
onCancel={() => { |
|
|
|
|
setMenuModalVisible(false); |
|
|
|
|
setCurrentRoleId(null); |
|
|
|
|
setMenuTreeData([]); |
|
|
|
|
setCheckedKeys([]); |
|
|
|
|
setResetPasswordVisible(false); |
|
|
|
|
resetPasswordForm.resetFields(); |
|
|
|
|
}} |
|
|
|
|
footer={[ |
|
|
|
|
<Button key="cancel" onClick={() => setMenuModalVisible(false)}> |
|
|
|
|
取消 |
|
|
|
|
</Button>, |
|
|
|
|
<Button key="submit" type="primary" onClick={handleSaveMenuAuth}> |
|
|
|
|
确定 |
|
|
|
|
</Button>, |
|
|
|
|
]} |
|
|
|
|
footer={null} |
|
|
|
|
> |
|
|
|
|
<div style={{ maxHeight: '400px', overflowY: 'auto' }}> |
|
|
|
|
<div className='mb-1 flex'> |
|
|
|
|
<div>角色名称: </div> |
|
|
|
|
<div>{roleName}</div> |
|
|
|
|
</div> |
|
|
|
|
<div className='mb-1 flex '> |
|
|
|
|
<div>角色标识: </div> |
|
|
|
|
<div>{roleCode}</div> |
|
|
|
|
</div> |
|
|
|
|
<div className='mb-1 flex'> |
|
|
|
|
<div>菜单权限: </div> |
|
|
|
|
<div> |
|
|
|
|
<Tree |
|
|
|
|
checkable |
|
|
|
|
fieldNames={{ title: 'name', key: 'id' }} |
|
|
|
|
treeData={menuTreeData} |
|
|
|
|
checkedKeys={checkedKeys} |
|
|
|
|
onCheck={onCheck} |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
<Form |
|
|
|
|
form={resetPasswordForm} |
|
|
|
|
onFinish={async (values) => { |
|
|
|
|
if (!currentUserId) return; |
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
|
try { |
|
|
|
|
const [err, data] = await apiInterceptors( |
|
|
|
|
updateUserPassword({ |
|
|
|
|
id: currentUserId, |
|
|
|
|
password: values.password, |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!err && data) { |
|
|
|
|
message.success('密码重置成功'); |
|
|
|
|
setResetPasswordVisible(false); |
|
|
|
|
} else { |
|
|
|
|
message.error(data?.err_msg || '密码重置失败'); |
|
|
|
|
} |
|
|
|
|
} catch (e) { |
|
|
|
|
message.error('请求失败,请重试'); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
<Form.Item |
|
|
|
|
label="请输入新密码" |
|
|
|
|
name="password" |
|
|
|
|
rules={[ |
|
|
|
|
{ required: true, message: '请输入新密码' }, |
|
|
|
|
{ min: 6, message: '密码长度不能小于6位' }, |
|
|
|
|
]} |
|
|
|
|
> |
|
|
|
|
<Input.Password placeholder="请输入新密码" /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
<Form.Item wrapperCol={{ offset: 6, span: 16 }}> |
|
|
|
|
<Space> |
|
|
|
|
<Button onClick={() => setResetPasswordVisible(false)}>取消</Button> |
|
|
|
|
<Button type="primary" htmlType="submit"> |
|
|
|
|
提交 |
|
|
|
|
</Button> |
|
|
|
|
</Space> |
|
|
|
|
</Form.Item> |
|
|
|
|
</Form> |
|
|
|
|
</Modal> |
|
|
|
|
<Modal |
|
|
|
|
title="分配角色" |
|
|
|
|
open={assignRoleVisible} |
|
|
|
|
onCancel={() => { |
|
|
|
|
setAssignRoleVisible(false); |
|
|
|
|
setSelectedRoles([]); // 清空选中角色
|
|
|
|
|
setCurrentUserIdForRole(null); // 可选:清空用户 ID
|
|
|
|
|
setCurrentUserNames({ username: '', nickName: '' }); // 可选:清空用户名
|
|
|
|
|
}} |
|
|
|
|
footer={null} |
|
|
|
|
> |
|
|
|
|
<Form layout="vertical"> |
|
|
|
|
<Form.Item label="用户名"> |
|
|
|
|
<Input value={currentUserNames.username} disabled /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
<Form.Item label="用户昵称"> |
|
|
|
|
<Input value={currentUserNames.nickName} disabled /> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
<Form.Item label="选择角色"> |
|
|
|
|
<Select |
|
|
|
|
mode="multiple" |
|
|
|
|
loading={loadingRoles} |
|
|
|
|
value={selectedRoles} |
|
|
|
|
onChange={(values) => setSelectedRoles(values as number[])} |
|
|
|
|
options={roleList.map((role) => ({ |
|
|
|
|
label: role.name, |
|
|
|
|
value: role.id, |
|
|
|
|
}))} |
|
|
|
|
placeholder="请选择角色" |
|
|
|
|
showSearch |
|
|
|
|
filterOption={(input, option) => |
|
|
|
|
option?.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 |
|
|
|
|
} |
|
|
|
|
/> |
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
|
|
<Form.Item wrapperCol={{ span: 16, offset: 8 }}> |
|
|
|
|
<Space> |
|
|
|
|
<Button |
|
|
|
|
onClick={() => { |
|
|
|
|
setAssignRoleVisible(false); |
|
|
|
|
setSelectedRoles([]); |
|
|
|
|
setCurrentUserIdForRole(null); |
|
|
|
|
setCurrentUserNames({ username: '', nickName: '' }); |
|
|
|
|
setRoleList([]); |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
取消 |
|
|
|
|
</Button> |
|
|
|
|
<Button |
|
|
|
|
type="primary" |
|
|
|
|
onClick={async () => { |
|
|
|
|
if (!currentUserIdForRole) return; |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
const [err, res] = await apiInterceptors( |
|
|
|
|
addUserAndRoles({ |
|
|
|
|
user_id: currentUserIdForRole, |
|
|
|
|
role_ids: selectedRoles, |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if (!err && res) { |
|
|
|
|
message.success('角色分配成功'); |
|
|
|
|
setAssignRoleVisible(false); |
|
|
|
|
setSelectedRoles([]); // 关闭前清空
|
|
|
|
|
setCurrentUserIdForRole(null); |
|
|
|
|
setCurrentUserNames({ username: '', nickName: '' }); |
|
|
|
|
setRoleList([]); |
|
|
|
|
} else { |
|
|
|
|
message.error(res?.err_msg || '角色分配失败'); |
|
|
|
|
} |
|
|
|
|
} catch (e) { |
|
|
|
|
message.error('请求失败,请重试'); |
|
|
|
|
} |
|
|
|
|
}} |
|
|
|
|
> |
|
|
|
|
保存 |
|
|
|
|
</Button> |
|
|
|
|
</Space> |
|
|
|
|
</Form.Item> |
|
|
|
|
</Form> |
|
|
|
|
</Modal> |
|
|
|
|
<Table |
|
|
|
|
columns={getColumns(t, handleEditBtn)} |
|
|
|
|
dataSource={promptList?.roles || []} // 改为使用roles字段
|
|
|
|
|
rowKey={record => record.id} |
|
|
|
|
loading={loadingOrgs} |
|
|
|
|
// 移除childrenColumnName和defaultExpandAllRows属性
|
|
|
|
|
dataSource={promptList?.user_list || []} |
|
|
|
|
rowKey="id" |
|
|
|
|
loading={loadingUsers} |
|
|
|
|
pagination={{ |
|
|
|
|
current: pagination.page, // 使用page字段
|
|
|
|
|
current: pagination.page, |
|
|
|
|
pageSize: pagination.page_size, |
|
|
|
|
total: total, |
|
|
|
|
onChange: (page, pageSize) => { |
|
|
|
|
setPagination(prev => ({ |
|
|
|
|
...prev, |
|
|
|
|
page: page, |
|
|
|
|
page_size: pageSize |
|
|
|
|
})); |
|
|
|
|
getPrompts({ |
|
|
|
|
page, |
|
|
|
|
page_size: pageSize |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
setPagination({ page, page_size: pageSize }); |
|
|
|
|
getUsers({ page, page_size: pageSize }); |
|
|
|
|
}, |
|
|
|
|
}} |
|
|
|
|
/> |
|
|
|
|
</div> |
|
|
|
|