parent
9f8cec8233
commit
ed5d3dba95
@ -0,0 +1,58 @@ |
||||
.color-red { |
||||
color: red |
||||
} |
||||
|
||||
.text-center { |
||||
text-align: center |
||||
} |
||||
|
||||
.p-10 { |
||||
padding: 10px |
||||
} |
||||
|
||||
.construc-container table { |
||||
display: table; |
||||
; |
||||
} |
||||
|
||||
.searchBarBox { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
margin-bottom: 10px; |
||||
width: 100%; |
||||
|
||||
|
||||
} |
||||
|
||||
.search-bar-left, |
||||
.search-bar-right { |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
} |
||||
|
||||
.search-bar-left { |
||||
width: 70%; |
||||
flex-wrap: wrap; |
||||
|
||||
|
||||
} |
||||
.ant-form-items{ |
||||
margin-bottom: 5px; |
||||
} |
||||
|
||||
.search-bar-right { |
||||
width: 30%; |
||||
position: relative; |
||||
&::after { |
||||
content: ''; |
||||
width: 2px; |
||||
height: 90%; |
||||
background-color: #eeeeee; |
||||
position: absolute; |
||||
top: 50%; |
||||
transform: translateY(-50%); |
||||
left: 0; |
||||
} |
||||
} |
@ -0,0 +1,510 @@ |
||||
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 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 { 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 [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 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('获取菜单权限失败'); |
||||
} |
||||
}; |
||||
|
||||
const handleSaveMenuAuth = async () => { |
||||
if (!currentRoleId) return; |
||||
|
||||
try { |
||||
const params = { |
||||
role_id: currentRoleId, |
||||
menu_ids: checkedKeys, |
||||
}; |
||||
const [, res] = await apiInterceptors(addRoleAndMenus(params)); |
||||
|
||||
if (res) { |
||||
message.success('菜单权限保存成功'); |
||||
setMenuModalVisible(false); |
||||
setCurrentRoleId(null); |
||||
setMenuTreeData([]); |
||||
setCheckedKeys([]); |
||||
} else { |
||||
message.error('菜单权限保存失败'); |
||||
} |
||||
} catch (err) { |
||||
message.error('菜单权限保存失败'); |
||||
} |
||||
}; |
||||
const onCheck: TreeProps['onCheck'] = (checkedKeysValue: number[]) => { |
||||
setCheckedKeys(checkedKeysValue.map(key => Number(key))); |
||||
}; |
||||
|
||||
const DeleteBtn: React.FC<{ record: IPrompt; }> = ({ record }) => { |
||||
const userInfo = useUser(); |
||||
const { t } = useTranslation(); |
||||
|
||||
const { message } = App.useApp(); |
||||
|
||||
// 删除prompt
|
||||
const { run: deletePromptRun, loading: deleteLoading } = useRequest( |
||||
async record => { |
||||
await deleteRole(record?.id); // 使用正确的角色删除接口
|
||||
}, |
||||
{ |
||||
manual: true, |
||||
onSuccess: async () => { |
||||
message.success('删除成功'); |
||||
await getPrompts(); |
||||
}, |
||||
onError: (err: any) => { |
||||
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> |
||||
); |
||||
}; |
||||
const getColumns = (t: TFunction, handleEdit: (prompt: IPrompt) => void): ColumnsType<IPrompt> => [ |
||||
{ |
||||
title: t('role_id'), |
||||
dataIndex: 'id', |
||||
key: 'id', |
||||
width: '8%', |
||||
ellipsis: true, |
||||
showSorterTooltip: true, |
||||
}, |
||||
{ |
||||
title: t('role_name'), |
||||
dataIndex: 'name', |
||||
key: 'name', |
||||
// width: '10%',
|
||||
}, |
||||
{ |
||||
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) => ( |
||||
<Switch |
||||
checked={status === 0} |
||||
checkedChildren="开启" |
||||
unCheckedChildren="关闭" |
||||
onChange={(checked) => handleStatusChange(record.id, checked)} |
||||
/> |
||||
), |
||||
}, { |
||||
title: t('create_time'), |
||||
dataIndex: 'create_time', |
||||
width: '20%', |
||||
key: 'create_time', |
||||
}, { |
||||
title: t('Operation'), |
||||
dataIndex: 'operate', |
||||
key: 'operate', |
||||
width: '20%', |
||||
render: (_, record) => ( |
||||
<Space align='center'> |
||||
<Button |
||||
onClick={() => { |
||||
handleEdit(record); |
||||
}} |
||||
type="link" |
||||
> |
||||
{t('Edit')} |
||||
</Button> |
||||
<Button |
||||
onClick={() => { |
||||
handleMenuAuth(record); |
||||
}} |
||||
type="link" |
||||
> |
||||
菜单权限 |
||||
</Button> |
||||
<DeleteBtn record={record} /> |
||||
</Space> |
||||
), |
||||
}, |
||||
]; |
||||
const handleEditBtn = async (prompt: IPrompt) => { |
||||
setModalType('edit'); |
||||
setEditId(prompt.id); |
||||
try { |
||||
const [_, data] = await apiInterceptors(getRoleDetail(prompt.id)); |
||||
if (data) { |
||||
addForm.setFieldsValue({ |
||||
...data, |
||||
status: data.status === 0 // 根据接口返回0/1转换Switch状态
|
||||
}); |
||||
setModalVisible(true); |
||||
} |
||||
} catch (err) { |
||||
message.error('获取详情失败'); |
||||
} |
||||
}; |
||||
|
||||
const handleStatusChange = async (roleId: number, checked: boolean) => { |
||||
try { |
||||
const [_, res] = await apiInterceptors( |
||||
updateRoleStatus({ |
||||
id: roleId, |
||||
status: checked ? 0 : 1 // 根据Switch状态转换
|
||||
}) |
||||
); |
||||
debugger |
||||
if (res) { |
||||
message.success('状态更新成功'); |
||||
await getPrompts(); |
||||
// 局部更新数据(可选)
|
||||
} else { |
||||
message.error('状态更新失败'); |
||||
} |
||||
} catch (err) { |
||||
message.error('状态更新失败'); |
||||
} |
||||
}; |
||||
const { |
||||
run: getPrompts, loading: loadingOrgs |
||||
} = 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({ |
||||
...params, |
||||
pageNum: params?.page || 1, // 添加分页参数
|
||||
pageSize: params?.page_size || 10 |
||||
}) |
||||
); |
||||
return data; |
||||
}, |
||||
{ |
||||
manual: true, |
||||
onSuccess: data => { |
||||
// 移除部门树形结构转换逻辑
|
||||
setPromptList(data); // 直接使用接口返回数据
|
||||
setTotal(data.total_count); // 设置总条数
|
||||
}, |
||||
}, |
||||
); |
||||
const handleSearch = async () => { |
||||
try { |
||||
const values = await addForm.validateFields(); |
||||
const timeRange = values.createTime; // 获取时间范围值
|
||||
|
||||
await getPrompts({ |
||||
...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 |
||||
}); |
||||
|
||||
setPagination(prev => ({ |
||||
...prev, |
||||
pageNum: 1, |
||||
page: 1 // 同步新增的page状态
|
||||
})); |
||||
} catch (err) { |
||||
console.error('表单验证失败:', err); |
||||
} |
||||
}; |
||||
const handleReset = async () => { |
||||
addForm.resetFields(); // 清空表单
|
||||
await getPrompts(); // 重新获取全部数据
|
||||
}; |
||||
const [modalVisible, setModalVisible] = useState(false); |
||||
|
||||
const buttonItemLayout = |
||||
formLayout === 'inline' |
||||
? { |
||||
wrapperCol: { span: 14, offset: 6 }, |
||||
} |
||||
: null; |
||||
|
||||
useEffect(() => { |
||||
getPrompts(); |
||||
}, []); |
||||
|
||||
return ( |
||||
<SystemConstructConstruct> |
||||
<div className={`px-6 py-2 ${styles['construc-container']} h-[90vh] overflow-y-auto`}> |
||||
<Form className={`${styles['p-10']} ${styles['color-red ']}`} |
||||
onFinish={handleSearch} |
||||
layout={'inline'} |
||||
form={addForm} |
||||
initialValues={{ layout: formLayout }} |
||||
> |
||||
<div className={`${styles['searchBarBox']}`}> |
||||
<div className={`${styles['search-bar-left']}`}> |
||||
<Form.Item className={`mb-1`} label="角色名称" name="name"> |
||||
<Input placeholder="请输入角色名称" /> |
||||
</Form.Item> |
||||
<Form.Item className={`mb-1`} label="角色标识" name="code" > |
||||
<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> |
||||
<Form.Item className={`mb-1`} label="创建时间" name="createTime"> |
||||
<RangePicker /> |
||||
</Form.Item> |
||||
</div> |
||||
<div className={`${styles['search-bar-right']}`}> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button htmlType="submit" type="primary">搜索</Button> |
||||
</Form.Item> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button onClick={handleReset} type="primary">重置</Button> |
||||
</Form.Item> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button type="primary" onClick={() => { |
||||
setModalType('add'); |
||||
setModalVisible(true); |
||||
addForm.resetFields(); |
||||
}}>新增</Button> |
||||
</Form.Item> |
||||
</div> |
||||
</div> |
||||
</Form> |
||||
<Modal |
||||
title={modalType === 'add' ? '新增角色' : '编辑角色'} |
||||
open={modalVisible} |
||||
onCancel={() => { |
||||
setModalVisible(false); |
||||
addForm.resetFields(); |
||||
}} |
||||
footer={null} |
||||
> |
||||
<Form |
||||
form={addForm} |
||||
initialValues={{ |
||||
status: true // 确保新增时默认开启
|
||||
}} |
||||
labelCol={{ span: 6 }} |
||||
wrapperCol={{ span: 16 }} |
||||
onFinish={async (values) => { |
||||
setAddLoading(true); |
||||
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 }); // 使用角色更新接口
|
||||
} else { |
||||
apiCall = addRole(params); // 使用角色新增接口
|
||||
} |
||||
|
||||
const [_, data] = await apiInterceptors(apiCall); |
||||
if (data) { |
||||
message.success(modalType === 'edit' ? '编辑成功' : '新增成功'); |
||||
setModalVisible(false); |
||||
addForm.resetFields(); |
||||
await getPrompts(); |
||||
setEditId(null); |
||||
} |
||||
} catch (err) { |
||||
console.error('操作失败:', err); |
||||
} finally { |
||||
setAddLoading(false); |
||||
} |
||||
}} |
||||
> |
||||
{/* 角色名称 */} |
||||
<Form.Item label="角色名称" name="name" rules={[{ required: true }]}> |
||||
<Input placeholder="请输入角色名称" /> |
||||
</Form.Item> |
||||
|
||||
{/* 角色标识 */} |
||||
<Form.Item label="角色标识" name="code" rules={[ |
||||
{ required: true }, |
||||
{ pattern: /^[a-z_]+$/, message: '只允许小写字母和下划线' } |
||||
]}> |
||||
<Input placeholder="请输入唯一标识(如:admin)" /> |
||||
</Form.Item> |
||||
|
||||
{/* 排序 */} |
||||
<Form.Item label="排序" name="sort" rules={[{ required: true, type: 'number' }]}> |
||||
<InputNumber min={0} style={{ width: '100%' }} /> |
||||
</Form.Item> |
||||
|
||||
{/* 备注 */} |
||||
<Form.Item label="备注" name="remark"> |
||||
<Input.TextArea placeholder="请输入角色描述" rows={3} /> |
||||
</Form.Item> |
||||
|
||||
{/* 状态 */} |
||||
{/* <Form.Item label="状态" name="status" valuePropName="checked"> |
||||
<Switch |
||||
checkedChildren="开启" |
||||
unCheckedChildren="关闭" |
||||
// defaultChecked={true} // 默认开启状态
|
||||
// onChange={switchOnChange}
|
||||
/> |
||||
</Form.Item> */} |
||||
|
||||
<Form.Item wrapperCol={{ offset: 8, span: 16 }}> |
||||
<Space> |
||||
<Button onClick={() => setModalVisible(false)}>取消</Button> |
||||
<Button type="primary" htmlType="submit" loading={addLoading}>提交</Button> |
||||
</Space> |
||||
</Form.Item> |
||||
</Form> |
||||
</Modal> |
||||
<Modal |
||||
title="菜单权限" |
||||
open={menuModalVisible} |
||||
onCancel={() => { |
||||
setMenuModalVisible(false); |
||||
setCurrentRoleId(null); |
||||
setMenuTreeData([]); |
||||
setCheckedKeys([]); |
||||
}} |
||||
footer={[ |
||||
<Button key="cancel" onClick={() => setMenuModalVisible(false)}> |
||||
取消 |
||||
</Button>, |
||||
<Button key="submit" type="primary" onClick={handleSaveMenuAuth}> |
||||
确定 |
||||
</Button>, |
||||
]} |
||||
> |
||||
<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> |
||||
|
||||
</div> |
||||
</Modal> |
||||
<Table |
||||
columns={getColumns(t, handleEditBtn)} |
||||
dataSource={promptList?.roles || []} // 改为使用roles字段
|
||||
rowKey={record => record.id} |
||||
loading={loadingOrgs} |
||||
// 移除childrenColumnName和defaultExpandAllRows属性
|
||||
pagination={{ |
||||
current: pagination.page, // 使用page字段
|
||||
pageSize: pagination.page_size, |
||||
total: total, |
||||
onChange: (page, pageSize) => { |
||||
setPagination(prev => ({ |
||||
...prev, |
||||
page: page, |
||||
page_size: pageSize |
||||
})); |
||||
getPrompts({ |
||||
page, |
||||
page_size: pageSize |
||||
}); |
||||
} |
||||
}} |
||||
/> |
||||
</div> |
||||
</SystemConstructConstruct> |
||||
); |
||||
}; |
||||
|
||||
export default Prompt; |
@ -0,0 +1,58 @@ |
||||
.color-red { |
||||
color: red |
||||
} |
||||
|
||||
.text-center { |
||||
text-align: center |
||||
} |
||||
|
||||
.p-10 { |
||||
padding: 10px |
||||
} |
||||
|
||||
.construc-container table { |
||||
display: table; |
||||
; |
||||
} |
||||
|
||||
.searchBarBox { |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
margin-bottom: 10px; |
||||
width: 100%; |
||||
|
||||
|
||||
} |
||||
|
||||
.search-bar-left, |
||||
.search-bar-right { |
||||
display: flex; |
||||
align-items: center; |
||||
|
||||
} |
||||
|
||||
.search-bar-left { |
||||
width: 70%; |
||||
flex-wrap: wrap; |
||||
|
||||
|
||||
} |
||||
.ant-form-items{ |
||||
margin-bottom: 5px; |
||||
} |
||||
|
||||
.search-bar-right { |
||||
width: 30%; |
||||
position: relative; |
||||
&::after { |
||||
content: ''; |
||||
width: 2px; |
||||
height: 90%; |
||||
background-color: #eeeeee; |
||||
position: absolute; |
||||
top: 50%; |
||||
transform: translateY(-50%); |
||||
left: 0; |
||||
} |
||||
} |
@ -0,0 +1,510 @@ |
||||
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 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 { 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 [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 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('获取菜单权限失败'); |
||||
} |
||||
}; |
||||
|
||||
const handleSaveMenuAuth = async () => { |
||||
if (!currentRoleId) return; |
||||
|
||||
try { |
||||
const params = { |
||||
role_id: currentRoleId, |
||||
menu_ids: checkedKeys, |
||||
}; |
||||
const [, res] = await apiInterceptors(addRoleAndMenus(params)); |
||||
|
||||
if (res) { |
||||
message.success('菜单权限保存成功'); |
||||
setMenuModalVisible(false); |
||||
setCurrentRoleId(null); |
||||
setMenuTreeData([]); |
||||
setCheckedKeys([]); |
||||
} else { |
||||
message.error('菜单权限保存失败'); |
||||
} |
||||
} catch (err) { |
||||
message.error('菜单权限保存失败'); |
||||
} |
||||
}; |
||||
const onCheck: TreeProps['onCheck'] = (checkedKeysValue: number[]) => { |
||||
setCheckedKeys(checkedKeysValue.map(key => Number(key))); |
||||
}; |
||||
|
||||
const DeleteBtn: React.FC<{ record: IPrompt; }> = ({ record }) => { |
||||
const userInfo = useUser(); |
||||
const { t } = useTranslation(); |
||||
|
||||
const { message } = App.useApp(); |
||||
|
||||
// 删除prompt
|
||||
const { run: deletePromptRun, loading: deleteLoading } = useRequest( |
||||
async record => { |
||||
await deleteRole(record?.id); // 使用正确的角色删除接口
|
||||
}, |
||||
{ |
||||
manual: true, |
||||
onSuccess: async () => { |
||||
message.success('删除成功'); |
||||
await getPrompts(); |
||||
}, |
||||
onError: (err: any) => { |
||||
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> |
||||
); |
||||
}; |
||||
const getColumns = (t: TFunction, handleEdit: (prompt: IPrompt) => void): ColumnsType<IPrompt> => [ |
||||
{ |
||||
title: t('role_id'), |
||||
dataIndex: 'id', |
||||
key: 'id', |
||||
width: '8%', |
||||
ellipsis: true, |
||||
showSorterTooltip: true, |
||||
}, |
||||
{ |
||||
title: t('role_name'), |
||||
dataIndex: 'name', |
||||
key: 'name', |
||||
// width: '10%',
|
||||
}, |
||||
{ |
||||
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) => ( |
||||
<Switch |
||||
checked={status === 0} |
||||
checkedChildren="开启" |
||||
unCheckedChildren="关闭" |
||||
onChange={(checked) => handleStatusChange(record.id, checked)} |
||||
/> |
||||
), |
||||
}, { |
||||
title: t('create_time'), |
||||
dataIndex: 'create_time', |
||||
width: '20%', |
||||
key: 'create_time', |
||||
}, { |
||||
title: t('Operation'), |
||||
dataIndex: 'operate', |
||||
key: 'operate', |
||||
width: '20%', |
||||
render: (_, record) => ( |
||||
<Space align='center'> |
||||
<Button |
||||
onClick={() => { |
||||
handleEdit(record); |
||||
}} |
||||
type="link" |
||||
> |
||||
{t('Edit')} |
||||
</Button> |
||||
<Button |
||||
onClick={() => { |
||||
handleMenuAuth(record); |
||||
}} |
||||
type="link" |
||||
> |
||||
菜单权限 |
||||
</Button> |
||||
<DeleteBtn record={record} /> |
||||
</Space> |
||||
), |
||||
}, |
||||
]; |
||||
const handleEditBtn = async (prompt: IPrompt) => { |
||||
setModalType('edit'); |
||||
setEditId(prompt.id); |
||||
try { |
||||
const [_, data] = await apiInterceptors(getRoleDetail(prompt.id)); |
||||
if (data) { |
||||
addForm.setFieldsValue({ |
||||
...data, |
||||
status: data.status === 0 // 根据接口返回0/1转换Switch状态
|
||||
}); |
||||
setModalVisible(true); |
||||
} |
||||
} catch (err) { |
||||
message.error('获取详情失败'); |
||||
} |
||||
}; |
||||
|
||||
const handleStatusChange = async (roleId: number, checked: boolean) => { |
||||
try { |
||||
const [_, res] = await apiInterceptors( |
||||
updateRoleStatus({ |
||||
id: roleId, |
||||
status: checked ? 0 : 1 // 根据Switch状态转换
|
||||
}) |
||||
); |
||||
debugger |
||||
if (res) { |
||||
message.success('状态更新成功'); |
||||
await getPrompts(); |
||||
// 局部更新数据(可选)
|
||||
} else { |
||||
message.error('状态更新失败'); |
||||
} |
||||
} catch (err) { |
||||
message.error('状态更新失败'); |
||||
} |
||||
}; |
||||
const { |
||||
run: getPrompts, loading: loadingOrgs |
||||
} = 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({ |
||||
...params, |
||||
pageNum: params?.page || 1, // 添加分页参数
|
||||
pageSize: params?.page_size || 10 |
||||
}) |
||||
); |
||||
return data; |
||||
}, |
||||
{ |
||||
manual: true, |
||||
onSuccess: data => { |
||||
// 移除部门树形结构转换逻辑
|
||||
setPromptList(data); // 直接使用接口返回数据
|
||||
setTotal(data.total_count); // 设置总条数
|
||||
}, |
||||
}, |
||||
); |
||||
const handleSearch = async () => { |
||||
try { |
||||
const values = await addForm.validateFields(); |
||||
const timeRange = values.createTime; // 获取时间范围值
|
||||
|
||||
await getPrompts({ |
||||
...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 |
||||
}); |
||||
|
||||
setPagination(prev => ({ |
||||
...prev, |
||||
pageNum: 1, |
||||
page: 1 // 同步新增的page状态
|
||||
})); |
||||
} catch (err) { |
||||
console.error('表单验证失败:', err); |
||||
} |
||||
}; |
||||
const handleReset = async () => { |
||||
addForm.resetFields(); // 清空表单
|
||||
await getPrompts(); // 重新获取全部数据
|
||||
}; |
||||
const [modalVisible, setModalVisible] = useState(false); |
||||
|
||||
const buttonItemLayout = |
||||
formLayout === 'inline' |
||||
? { |
||||
wrapperCol: { span: 14, offset: 6 }, |
||||
} |
||||
: null; |
||||
|
||||
useEffect(() => { |
||||
getPrompts(); |
||||
}, []); |
||||
|
||||
return ( |
||||
<SystemConstructConstruct> |
||||
<div className={`px-6 py-2 ${styles['construc-container']} h-[90vh] overflow-y-auto`}> |
||||
<Form className={`${styles['p-10']} ${styles['color-red ']}`} |
||||
onFinish={handleSearch} |
||||
layout={'inline'} |
||||
form={addForm} |
||||
initialValues={{ layout: formLayout }} |
||||
> |
||||
<div className={`${styles['searchBarBox']}`}> |
||||
<div className={`${styles['search-bar-left']}`}> |
||||
<Form.Item className={`mb-1`} label="角色名称" name="name"> |
||||
<Input placeholder="请输入角色名称" /> |
||||
</Form.Item> |
||||
<Form.Item className={`mb-1`} label="角色标识" name="code" > |
||||
<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> |
||||
<Form.Item className={`mb-1`} label="创建时间" name="createTime"> |
||||
<RangePicker /> |
||||
</Form.Item> |
||||
</div> |
||||
<div className={`${styles['search-bar-right']}`}> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button htmlType="submit" type="primary">搜索</Button> |
||||
</Form.Item> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button onClick={handleReset} type="primary">重置</Button> |
||||
</Form.Item> |
||||
<Form.Item {...buttonItemLayout}> |
||||
<Button type="primary" onClick={() => { |
||||
setModalType('add'); |
||||
setModalVisible(true); |
||||
addForm.resetFields(); |
||||
}}>新增</Button> |
||||
</Form.Item> |
||||
</div> |
||||
</div> |
||||
</Form> |
||||
<Modal |
||||
title={modalType === 'add' ? '新增角色' : '编辑角色'} |
||||
open={modalVisible} |
||||
onCancel={() => { |
||||
setModalVisible(false); |
||||
addForm.resetFields(); |
||||
}} |
||||
footer={null} |
||||
> |
||||
<Form |
||||
form={addForm} |
||||
initialValues={{ |
||||
status: true // 确保新增时默认开启
|
||||
}} |
||||
labelCol={{ span: 6 }} |
||||
wrapperCol={{ span: 16 }} |
||||
onFinish={async (values) => { |
||||
setAddLoading(true); |
||||
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 }); // 使用角色更新接口
|
||||
} else { |
||||
apiCall = addRole(params); // 使用角色新增接口
|
||||
} |
||||
|
||||
const [_, data] = await apiInterceptors(apiCall); |
||||
if (data) { |
||||
message.success(modalType === 'edit' ? '编辑成功' : '新增成功'); |
||||
setModalVisible(false); |
||||
addForm.resetFields(); |
||||
await getPrompts(); |
||||
setEditId(null); |
||||
} |
||||
} catch (err) { |
||||
console.error('操作失败:', err); |
||||
} finally { |
||||
setAddLoading(false); |
||||
} |
||||
}} |
||||
> |
||||
{/* 角色名称 */} |
||||
<Form.Item label="角色名称" name="name" rules={[{ required: true }]}> |
||||
<Input placeholder="请输入角色名称" /> |
||||
</Form.Item> |
||||
|
||||
{/* 角色标识 */} |
||||
<Form.Item label="角色标识" name="code" rules={[ |
||||
{ required: true }, |
||||
{ pattern: /^[a-z_]+$/, message: '只允许小写字母和下划线' } |
||||
]}> |
||||
<Input placeholder="请输入唯一标识(如:admin)" /> |
||||
</Form.Item> |
||||
|
||||
{/* 排序 */} |
||||
<Form.Item label="排序" name="sort" rules={[{ required: true, type: 'number' }]}> |
||||
<InputNumber min={0} style={{ width: '100%' }} /> |
||||
</Form.Item> |
||||
|
||||
{/* 备注 */} |
||||
<Form.Item label="备注" name="remark"> |
||||
<Input.TextArea placeholder="请输入角色描述" rows={3} /> |
||||
</Form.Item> |
||||
|
||||
{/* 状态 */} |
||||
{/* <Form.Item label="状态" name="status" valuePropName="checked"> |
||||
<Switch |
||||
checkedChildren="开启" |
||||
unCheckedChildren="关闭" |
||||
// defaultChecked={true} // 默认开启状态
|
||||
// onChange={switchOnChange}
|
||||
/> |
||||
</Form.Item> */} |
||||
|
||||
<Form.Item wrapperCol={{ offset: 8, span: 16 }}> |
||||
<Space> |
||||
<Button onClick={() => setModalVisible(false)}>取消</Button> |
||||
<Button type="primary" htmlType="submit" loading={addLoading}>提交</Button> |
||||
</Space> |
||||
</Form.Item> |
||||
</Form> |
||||
</Modal> |
||||
<Modal |
||||
title="菜单权限" |
||||
open={menuModalVisible} |
||||
onCancel={() => { |
||||
setMenuModalVisible(false); |
||||
setCurrentRoleId(null); |
||||
setMenuTreeData([]); |
||||
setCheckedKeys([]); |
||||
}} |
||||
footer={[ |
||||
<Button key="cancel" onClick={() => setMenuModalVisible(false)}> |
||||
取消 |
||||
</Button>, |
||||
<Button key="submit" type="primary" onClick={handleSaveMenuAuth}> |
||||
确定 |
||||
</Button>, |
||||
]} |
||||
> |
||||
<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> |
||||
|
||||
</div> |
||||
</Modal> |
||||
<Table |
||||
columns={getColumns(t, handleEditBtn)} |
||||
dataSource={promptList?.roles || []} // 改为使用roles字段
|
||||
rowKey={record => record.id} |
||||
loading={loadingOrgs} |
||||
// 移除childrenColumnName和defaultExpandAllRows属性
|
||||
pagination={{ |
||||
current: pagination.page, // 使用page字段
|
||||
pageSize: pagination.page_size, |
||||
total: total, |
||||
onChange: (page, pageSize) => { |
||||
setPagination(prev => ({ |
||||
...prev, |
||||
page: page, |
||||
page_size: pageSize |
||||
})); |
||||
getPrompts({ |
||||
page, |
||||
page_size: pageSize |
||||
}); |
||||
} |
||||
}} |
||||
/> |
||||
</div> |
||||
</SystemConstructConstruct> |
||||
); |
||||
}; |
||||
|
||||
export default Prompt; |
Loading…
Reference in new issue