可正常启用
This commit is contained in:
parent
a4bd4f07d0
commit
750f446844
@ -38,6 +38,7 @@ export function useTableData<T extends { id: number }, P extends SortParams>({
|
|||||||
...params,
|
...params,
|
||||||
pageNum: state.pagination.current,
|
pageNum: state.pagination.current,
|
||||||
pageSize: state.pagination.pageSize,
|
pageSize: state.pagination.pageSize,
|
||||||
|
sortField: params?.sortField,
|
||||||
sortOrder: params?.sortOrder === 'ascend' ? 'asc' : params?.sortOrder === 'descend' ? 'desc' : undefined
|
sortOrder: params?.sortOrder === 'ascend' ? 'asc' : params?.sortOrder === 'descend' ? 'desc' : undefined
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,110 +1,127 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Modal, Tree, message, Spin } from 'antd';
|
import { Modal, Table, message, Spin, Tag } from 'antd';
|
||||||
import type { DataNode } from 'antd/es/tree';
|
import type { Permission } from '../types';
|
||||||
import type { MenuDTO } from '../../Menu/types';
|
import { PermissionType, PermissionTypeText } from '../types';
|
||||||
import { getMenuTree } from '../../Menu/service';
|
import { getRolePermissions, assignPermissions } from '../service';
|
||||||
import { getRoleMenuIds, updateRoleMenus } from '../service';
|
|
||||||
|
|
||||||
interface PermissionModalProps {
|
interface PermissionModalProps {
|
||||||
roleId: number;
|
roleId: number;
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PermissionModal: React.FC<PermissionModalProps> = ({
|
const PermissionModal: React.FC<PermissionModalProps> = ({
|
||||||
roleId,
|
roleId,
|
||||||
visible,
|
visible,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSuccess
|
onSuccess
|
||||||
}) => {
|
}) => {
|
||||||
const [menuTree, setMenuTree] = useState<MenuDTO[]>([]);
|
const [permissions, setPermissions] = useState<Permission[]>([]);
|
||||||
const [selectedKeys, setSelectedKeys] = useState<number[]>([]);
|
const [selectedKeys, setSelectedKeys] = useState<number[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [expandedKeys, setExpandedKeys] = useState<number[]>([]);
|
|
||||||
|
|
||||||
// 获取菜单树和角色已有权限
|
// 获取角色已有权限
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const [menus, menuIds] = await Promise.all([
|
const data = await getRolePermissions(roleId);
|
||||||
getMenuTree(),
|
setPermissions(data);
|
||||||
getRoleMenuIds(roleId)
|
setSelectedKeys(data.map(p => p.id));
|
||||||
]);
|
} catch (error) {
|
||||||
setMenuTree(menus);
|
message.error('获取权限数据失败');
|
||||||
setSelectedKeys(menuIds);
|
} finally {
|
||||||
// 设置展开的节点
|
setLoading(false);
|
||||||
const keys: number[] = [];
|
}
|
||||||
const getExpandedKeys = (items: MenuDTO[]) => {
|
};
|
||||||
items.forEach(item => {
|
|
||||||
if (item.type === 0 || item.type === 1) { // 展开目录和菜单
|
|
||||||
keys.push(item.id);
|
|
||||||
}
|
|
||||||
if (item.children) {
|
|
||||||
getExpandedKeys(item.children);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
getExpandedKeys(menus);
|
|
||||||
setExpandedKeys(keys);
|
|
||||||
} catch (error) {
|
|
||||||
message.error('获取权限数据失败');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
fetchData();
|
fetchData();
|
||||||
}
|
}
|
||||||
}, [visible, roleId]);
|
}, [visible, roleId]);
|
||||||
|
|
||||||
const handleOk = async () => {
|
const handleOk = async () => {
|
||||||
try {
|
try {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await updateRoleMenus(roleId, selectedKeys);
|
await assignPermissions(roleId, selectedKeys);
|
||||||
message.success('权限更新成功');
|
message.success('权限更新成功');
|
||||||
onSuccess();
|
onSuccess();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
message.error('权限更新失败');
|
message.error('权限更新失败');
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 转换菜单数据为Tree组件需要的格式
|
const getPermissionTypeTag = (type: PermissionType) => {
|
||||||
const getTreeData = (menus: MenuDTO[]): DataNode[] => {
|
const colorMap = {
|
||||||
return menus.map(menu => ({
|
[PermissionType.MENU]: 'blue',
|
||||||
title: menu.name,
|
[PermissionType.BUTTON]: 'green',
|
||||||
key: menu.id,
|
[PermissionType.API]: 'orange'
|
||||||
children: menu.children ? getTreeData(menu.children) : undefined,
|
};
|
||||||
disabled: menu.type === 0 // 目录不可选
|
return (
|
||||||
}));
|
<Tag color={colorMap[type]}>
|
||||||
};
|
{PermissionTypeText[type]}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
const columns = [
|
||||||
<Modal
|
{
|
||||||
title="分配权限"
|
title: '权限编码',
|
||||||
open={visible}
|
dataIndex: 'code',
|
||||||
onOk={handleOk}
|
width: 150
|
||||||
onCancel={onCancel}
|
},
|
||||||
width={600}
|
{
|
||||||
confirmLoading={loading}
|
title: '权限名称',
|
||||||
destroyOnClose
|
dataIndex: 'name',
|
||||||
>
|
width: 150
|
||||||
<Spin spinning={loading}>
|
},
|
||||||
<Tree
|
{
|
||||||
checkable
|
title: '权限类型',
|
||||||
expandedKeys={expandedKeys}
|
dataIndex: 'type',
|
||||||
onExpand={(keys) => setExpandedKeys(keys as number[])}
|
width: 120,
|
||||||
checkedKeys={selectedKeys}
|
render: (type: PermissionType) => getPermissionTypeTag(type)
|
||||||
onCheck={(checked) => setSelectedKeys(checked as number[])}
|
},
|
||||||
treeData={getTreeData(menuTree)}
|
{
|
||||||
/>
|
title: '所属菜单',
|
||||||
</Spin>
|
dataIndex: 'menuName',
|
||||||
</Modal>
|
width: 150
|
||||||
);
|
},
|
||||||
|
{
|
||||||
|
title: '描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
ellipsis: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="分配权限"
|
||||||
|
open={visible}
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={onCancel}
|
||||||
|
width={800}
|
||||||
|
confirmLoading={loading}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<Table
|
||||||
|
rowSelection={{
|
||||||
|
type: 'checkbox',
|
||||||
|
selectedRowKeys: selectedKeys,
|
||||||
|
onChange: (keys) => setSelectedKeys(keys as number[])
|
||||||
|
}}
|
||||||
|
columns={columns}
|
||||||
|
dataSource={permissions}
|
||||||
|
rowKey="id"
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ y: 400 }}
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PermissionModal;
|
export default PermissionModal;
|
||||||
231
frontend/src/pages/System/Role/components/TagModal.tsx
Normal file
231
frontend/src/pages/System/Role/components/TagModal.tsx
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Modal, Table, message, Spin, Tag, Button, Form, Input, ColorPicker, Space } from 'antd';
|
||||||
|
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
|
||||||
|
import type { RoleTagResponse, RoleTagRequest } from '../types';
|
||||||
|
import { getAllTags, assignTags, createRoleTag, updateRoleTag, deleteRoleTag } from '../service';
|
||||||
|
|
||||||
|
interface TagModalProps {
|
||||||
|
roleId: number;
|
||||||
|
visible: boolean;
|
||||||
|
onCancel: () => void;
|
||||||
|
onSuccess: () => void;
|
||||||
|
selectedTags?: RoleTagResponse[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagModal: React.FC<TagModalProps> = ({
|
||||||
|
roleId,
|
||||||
|
visible,
|
||||||
|
onCancel,
|
||||||
|
onSuccess,
|
||||||
|
selectedTags = []
|
||||||
|
}) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [tags, setTags] = useState<RoleTagResponse[]>([]);
|
||||||
|
const [selectedKeys, setSelectedKeys] = useState<number[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [tagManageVisible, setTagManageVisible] = useState(false);
|
||||||
|
const [editingTag, setEditingTag] = useState<RoleTagResponse | null>(null);
|
||||||
|
|
||||||
|
// 获取所有标签
|
||||||
|
const fetchTags = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await getAllTags();
|
||||||
|
setTags(data);
|
||||||
|
} catch (error) {
|
||||||
|
message.error('获取标签数据失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
fetchTags();
|
||||||
|
// 设置已选标签
|
||||||
|
setSelectedKeys(selectedTags.map(tag => tag.id));
|
||||||
|
}
|
||||||
|
}, [visible, selectedTags]);
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
await assignTags(roleId, selectedKeys);
|
||||||
|
message.success('标签更新成功');
|
||||||
|
onSuccess();
|
||||||
|
} catch (error) {
|
||||||
|
message.error('标签更新失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTagManage = () => {
|
||||||
|
setTagManageVisible(true);
|
||||||
|
setEditingTag(null);
|
||||||
|
form.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTagManageSubmit = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
if (editingTag) {
|
||||||
|
await updateRoleTag(editingTag.id, {
|
||||||
|
...values,
|
||||||
|
color: values.color.toHexString?.() || values.color
|
||||||
|
});
|
||||||
|
message.success('标签更新成功');
|
||||||
|
} else {
|
||||||
|
await createRoleTag({
|
||||||
|
...values,
|
||||||
|
color: values.color.toHexString?.() || values.color
|
||||||
|
});
|
||||||
|
message.success('标签创建成功');
|
||||||
|
}
|
||||||
|
setTagManageVisible(false);
|
||||||
|
fetchTags();
|
||||||
|
} catch (error) {
|
||||||
|
message.error('操作失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTagDelete = async (id: number) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '确定要删除这个标签吗?',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await deleteRoleTag(id);
|
||||||
|
message.success('删除成功');
|
||||||
|
fetchTags();
|
||||||
|
} catch (error) {
|
||||||
|
message.error('删除失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '标签名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标签颜色',
|
||||||
|
dataIndex: 'color',
|
||||||
|
width: 100,
|
||||||
|
render: (color: string) => (
|
||||||
|
<Tag color={color}>{color}</Tag>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
width: 180,
|
||||||
|
render: (_: any, record: RoleTagResponse) => (
|
||||||
|
<Space>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
icon={<EditOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
setEditingTag(record);
|
||||||
|
form.setFieldsValue({
|
||||||
|
...record,
|
||||||
|
color: record.color
|
||||||
|
});
|
||||||
|
setTagManageVisible(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
danger
|
||||||
|
icon={<DeleteOutlined />}
|
||||||
|
onClick={() => handleTagDelete(record.id)}
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</Button>
|
||||||
|
</Space>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
title="分配标签"
|
||||||
|
open={visible}
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={onCancel}
|
||||||
|
width={800}
|
||||||
|
confirmLoading={loading}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<div style={{ marginBottom: 16 }}>
|
||||||
|
<Button type="primary" icon={<PlusOutlined />} onClick={handleTagManage}>
|
||||||
|
新建标签
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Spin spinning={loading}>
|
||||||
|
<Table
|
||||||
|
rowSelection={{
|
||||||
|
type: 'checkbox',
|
||||||
|
selectedRowKeys: selectedKeys,
|
||||||
|
onChange: (keys) => setSelectedKeys(keys as number[])
|
||||||
|
}}
|
||||||
|
columns={columns}
|
||||||
|
dataSource={tags}
|
||||||
|
rowKey="id"
|
||||||
|
pagination={false}
|
||||||
|
scroll={{ y: 400 }}
|
||||||
|
/>
|
||||||
|
</Spin>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title={editingTag ? '编辑标签' : '新建标签'}
|
||||||
|
open={tagManageVisible}
|
||||||
|
onOk={handleTagManageSubmit}
|
||||||
|
onCancel={() => setTagManageVisible(false)}
|
||||||
|
destroyOnClose
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="name"
|
||||||
|
label="标签名称"
|
||||||
|
rules={[{ required: true, message: '请输入标签名称' }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="请输入标签名称" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="color"
|
||||||
|
label="标签颜色"
|
||||||
|
rules={[{ required: true, message: '请选择标签颜色' }]}
|
||||||
|
>
|
||||||
|
<ColorPicker />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="description"
|
||||||
|
label="描述"
|
||||||
|
>
|
||||||
|
<Input.TextArea rows={4} placeholder="请输入描述" />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TagModal;
|
||||||
@ -1,238 +1,160 @@
|
|||||||
import React, {useState, useEffect} from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {Table, Button, Modal, Form, Input, Space, message, InputNumber, Switch, Select, Tag, ColorPicker} from 'antd';
|
import { Card, Button, Table, Space, Modal, Form, Input, InputNumber, Switch, message, Tag } from 'antd';
|
||||||
import {PlusOutlined, EditOutlined, DeleteOutlined, TagOutlined, SettingOutlined} from '@ant-design/icons';
|
import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TagsOutlined } from '@ant-design/icons';
|
||||||
import type {RoleResponse, RoleTagResponse} from './types';
|
import type { RoleResponse, RoleTagResponse, RoleQuery } from './types';
|
||||||
import {useTableData} from '@/hooks/useTableData';
|
import { getRoleList, createRole, updateRole, deleteRole } from './service';
|
||||||
import type {FixedType} from 'rc-table/lib/interface';
|
import type { TablePaginationConfig } from 'antd/es/table';
|
||||||
import type {TablePaginationConfig} from 'antd/es/table';
|
import type { SorterResult } from 'antd/es/table/interface';
|
||||||
import type {FilterValue, SorterResult} from 'antd/es/table/interface';
|
import PermissionModal from './components/PermissionModal';
|
||||||
import {assignTags, createRoleTag, deleteRoleTag, getAllTags, updateRoleTag} from './service';
|
import TagModal from './components/TagModal';
|
||||||
|
|
||||||
const RolePage: React.FC = () => {
|
const RolePage: React.FC = () => {
|
||||||
const {
|
|
||||||
list: roles,
|
|
||||||
pagination,
|
|
||||||
loading,
|
|
||||||
loadData: fetchRoles,
|
|
||||||
onPageChange,
|
|
||||||
handleCreate,
|
|
||||||
handleUpdate,
|
|
||||||
handleDelete
|
|
||||||
} = useTableData({
|
|
||||||
service: {
|
|
||||||
baseUrl: '/api/v1/role'
|
|
||||||
},
|
|
||||||
defaultParams: {
|
|
||||||
sortField: 'createTime',
|
|
||||||
sortOrder: 'descend'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const [modalVisible, setModalVisible] = useState(false);
|
|
||||||
const [editingRole, setEditingRole] = useState<RoleResponse | null>(null);
|
|
||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [editingId, setEditingId] = useState<number | null>(null);
|
||||||
|
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||||
|
const [permissionModalVisible, setPermissionModalVisible] = useState(false);
|
||||||
const [tagModalVisible, setTagModalVisible] = useState(false);
|
const [tagModalVisible, setTagModalVisible] = useState(false);
|
||||||
const [selectedRole, setSelectedRole] = useState<RoleResponse | null>(null);
|
const [selectedRole, setSelectedRole] = useState<RoleResponse | null>(null);
|
||||||
const [selectedTags, setSelectedTags] = useState<number[]>([]);
|
const [loading, setLoading] = useState(false);
|
||||||
const [allTags, setAllTags] = useState<RoleTagResponse[] | null>([]);
|
const [roles, setRoles] = useState<RoleResponse[]>([]);
|
||||||
const [tagManageVisible, setTagManageVisible] = useState(false);
|
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||||
const [editingTag, setEditingTag] = useState<RoleTagResponse | null>(null);
|
current: 1,
|
||||||
const [tagForm] = Form.useForm();
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showQuickJumper: true
|
||||||
|
});
|
||||||
|
const [sortField, setSortField] = useState<string>('sort');
|
||||||
|
const [sortOrder, setSortOrder] = useState<'ascend' | 'descend'>('ascend');
|
||||||
|
|
||||||
|
const fetchRoles = async (params: RoleQuery = {}) => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const { pageNum = 1, pageSize = 10 } = pagination;
|
||||||
|
const result = await getRoleList({
|
||||||
|
pageNum,
|
||||||
|
pageSize,
|
||||||
|
sortField,
|
||||||
|
sortOrder: sortOrder === 'ascend' ? 'asc' : sortOrder === 'descend' ? 'desc' : undefined,
|
||||||
|
...params
|
||||||
|
});
|
||||||
|
setRoles(result.content);
|
||||||
|
setPagination({
|
||||||
|
...pagination,
|
||||||
|
current: result.number + 1,
|
||||||
|
pageSize: result.size,
|
||||||
|
total: result.totalElements
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
message.error('获取角色列表失败');
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchTags = async () => { // 定义一个异步函数
|
fetchRoles();
|
||||||
try {
|
|
||||||
const tags = await getAllTags(); // 在这里使用 await
|
|
||||||
setAllTags(tags);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取标签失败", error); // 处理错误
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchTags(); // 调用异步函数
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
setEditingRole(null);
|
setEditingId(null);
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
|
|
||||||
// 获取当前最大排序值
|
|
||||||
const maxSort = Math.max(0, ...roles.map(role => role.sort));
|
|
||||||
|
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
sort: maxSort + 1 // 默认比最大值大1
|
sort: 0
|
||||||
});
|
});
|
||||||
setModalVisible(true);
|
setVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleEdit = (record: RoleResponse) => {
|
const handleEdit = (record: RoleResponse) => {
|
||||||
setEditingRole(record);
|
setEditingId(record.id);
|
||||||
form.setFieldsValue(record);
|
form.setFieldsValue(record);
|
||||||
setModalVisible(true);
|
setVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = async (id: number) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '确定要删除这个角色吗?',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await deleteRole(id);
|
||||||
|
message.success('删除成功');
|
||||||
|
fetchRoles();
|
||||||
|
} catch (error) {
|
||||||
|
message.error('删除失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async () => {
|
||||||
try {
|
try {
|
||||||
const values = await form.validateFields();
|
const values = await form.validateFields();
|
||||||
if (editingRole) {
|
setConfirmLoading(true);
|
||||||
const success = await handleUpdate(editingRole.id, {
|
|
||||||
...values,
|
if (editingId) {
|
||||||
version: editingRole.version
|
await updateRole(editingId, values);
|
||||||
});
|
message.success('更新成功');
|
||||||
if (success) {
|
|
||||||
setModalVisible(false);
|
|
||||||
fetchRoles();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
const success = await handleCreate(values);
|
await createRole(values);
|
||||||
if (success) {
|
message.success('创建成功');
|
||||||
setModalVisible(false);
|
|
||||||
fetchRoles();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setVisible(false);
|
||||||
|
fetchRoles();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('操作失败:', error);
|
message.error('操作失败');
|
||||||
|
} finally {
|
||||||
|
setConfirmLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAssignPermissions = (record: RoleResponse) => {
|
||||||
|
setSelectedRole(record);
|
||||||
|
setPermissionModalVisible(true);
|
||||||
|
};
|
||||||
|
|
||||||
const handleAssignTags = (record: RoleResponse) => {
|
const handleAssignTags = (record: RoleResponse) => {
|
||||||
setSelectedRole(record);
|
setSelectedRole(record);
|
||||||
if (record.tags && record.tags.length > 0) {
|
|
||||||
setSelectedTags(record.tags.map(tag => tag.id));
|
|
||||||
} else {
|
|
||||||
setSelectedTags([]);
|
|
||||||
}
|
|
||||||
setTagModalVisible(true);
|
setTagModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAssignTagSubmit = async () => {
|
const handleTableChange = (
|
||||||
if (selectedRole) {
|
pagination: TablePaginationConfig,
|
||||||
await assignTags(selectedRole.id, selectedTags);
|
_: any,
|
||||||
setTagModalVisible(false);
|
sorter: SorterResult<RoleResponse>
|
||||||
fetchRoles();
|
) => {
|
||||||
}
|
const { field, order } = sorter;
|
||||||
};
|
setSortField(field as string);
|
||||||
|
setSortOrder(order as 'ascend' | 'descend');
|
||||||
const handleTagManage = () => {
|
fetchRoles({
|
||||||
setTagManageVisible(true);
|
pageNum: pagination.current,
|
||||||
setEditingTag(null);
|
pageSize: pagination.pageSize,
|
||||||
tagForm.resetFields();
|
sortField: field as string,
|
||||||
};
|
sortOrder: order === 'ascend' ? 'asc' : order === 'descend' ? 'desc' : undefined
|
||||||
|
|
||||||
const handleTagManageSubmit = async () => {
|
|
||||||
try {
|
|
||||||
const values = await tagForm.validateFields();
|
|
||||||
if (values.id) {
|
|
||||||
await updateRoleTag(values.id, {
|
|
||||||
...values,
|
|
||||||
color: values.color.toHexString?.() || values.color
|
|
||||||
});
|
|
||||||
message.success("更新标签成功")
|
|
||||||
} else {
|
|
||||||
await createRoleTag({
|
|
||||||
...values,
|
|
||||||
color: values.color.toHexString?.() || values.color
|
|
||||||
});
|
|
||||||
message.success("标签添加成功")
|
|
||||||
}
|
|
||||||
setTagManageVisible(false);
|
|
||||||
const tags = await getAllTags();
|
|
||||||
setAllTags(tags);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('操作失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleTagDelete = async (id: number) => {
|
|
||||||
Modal.confirm({
|
|
||||||
title: '确认删除',
|
|
||||||
content: '确定要删除该记录吗?',
|
|
||||||
okText: '确定',
|
|
||||||
okType: 'danger',
|
|
||||||
cancelText: '取消',
|
|
||||||
onOk: async () => {
|
|
||||||
try {
|
|
||||||
await deleteRoleTag(id);
|
|
||||||
message.success("删除成功");
|
|
||||||
// 刷新标签列表
|
|
||||||
const tags = await getAllTags();
|
|
||||||
setAllTags(tags);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("角色标签删除失败", error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// onCancel: () => resolve(false)
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
|
||||||
title: 'ID',
|
|
||||||
dataIndex: 'id',
|
|
||||||
key: 'id',
|
|
||||||
width: 60,
|
|
||||||
fixed: 'left' as FixedType,
|
|
||||||
sorter: true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '角色编码',
|
title: '角色编码',
|
||||||
dataIndex: 'code',
|
dataIndex: 'code',
|
||||||
key: 'code',
|
width: 150,
|
||||||
width: 120,
|
|
||||||
sorter: true
|
sorter: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '角色名称',
|
title: '角色名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
key: 'name',
|
width: 150
|
||||||
width: 120,
|
|
||||||
sorter: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '描述',
|
|
||||||
dataIndex: 'description',
|
|
||||||
key: 'description',
|
|
||||||
width: 200
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '排序',
|
|
||||||
dataIndex: 'sort',
|
|
||||||
key: 'sort',
|
|
||||||
width: 80,
|
|
||||||
sorter: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'enabled',
|
|
||||||
key: 'enabled',
|
|
||||||
width: 80,
|
|
||||||
render: (enabled: boolean) => (
|
|
||||||
<Switch checked={enabled} disabled size="small"/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '创建时间',
|
|
||||||
dataIndex: 'createTime',
|
|
||||||
key: 'createTime',
|
|
||||||
width: 150,
|
|
||||||
sorter: true,
|
|
||||||
defaultSortOrder: 'descend'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '更新时间',
|
|
||||||
dataIndex: 'updateTime',
|
|
||||||
key: 'updateTime',
|
|
||||||
width: 150,
|
|
||||||
sorter: true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '标签',
|
title: '标签',
|
||||||
dataIndex: 'tags',
|
dataIndex: 'tags',
|
||||||
key: 'tags',
|
|
||||||
width: 200,
|
width: 200,
|
||||||
render: (tags: RoleTagResponse[]) => (
|
render: (tags: RoleTagResponse[]) => (
|
||||||
<Space>
|
<Space size={[0, 4]} wrap>
|
||||||
{tags?.map(tag => (
|
{tags?.map(tag => (
|
||||||
<Tag key={tag.id} color={tag.color}>
|
<Tag key={tag.id} color={tag.color}>
|
||||||
{tag.name}
|
{tag.name}
|
||||||
@ -241,88 +163,95 @@ const RolePage: React.FC = () => {
|
|||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '排序',
|
||||||
|
dataIndex: 'sort',
|
||||||
|
width: 80,
|
||||||
|
sorter: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'enabled',
|
||||||
|
width: 100,
|
||||||
|
render: (enabled: boolean) => (
|
||||||
|
<Switch checked={enabled} disabled />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
ellipsis: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createTime',
|
||||||
|
width: 180,
|
||||||
|
sorter: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
width: 180,
|
width: 280,
|
||||||
fixed: 'right' as FixedType,
|
|
||||||
render: (_: any, record: RoleResponse) => (
|
render: (_: any, record: RoleResponse) => (
|
||||||
<Space size={0}>
|
<Space>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
icon={<EditOutlined />}
|
||||||
icon={<EditOutlined/>}
|
|
||||||
onClick={() => handleEdit(record)}
|
onClick={() => handleEdit(record)}
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="link"
|
type="link"
|
||||||
size="small"
|
icon={<KeyOutlined />}
|
||||||
|
onClick={() => handleAssignPermissions(record)}
|
||||||
|
>
|
||||||
|
分配权限
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
icon={<TagsOutlined />}
|
||||||
|
onClick={() => handleAssignTags(record)}
|
||||||
|
>
|
||||||
|
分配标签
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
danger
|
danger
|
||||||
icon={<DeleteOutlined/>}
|
icon={<DeleteOutlined />}
|
||||||
onClick={() => handleDelete(record.id)}
|
onClick={() => handleDelete(record.id)}
|
||||||
disabled={record.code === 'admin'}
|
disabled={record.code === 'admin'}
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
size="small"
|
|
||||||
icon={<TagOutlined/>}
|
|
||||||
onClick={() => handleAssignTags(record)}
|
|
||||||
>
|
|
||||||
标签
|
|
||||||
</Button>
|
|
||||||
</Space>
|
</Space>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleTableChange = (
|
|
||||||
pagination: TablePaginationConfig,
|
|
||||||
filters: Record<string, FilterValue | null>,
|
|
||||||
sorter: SorterResult<RoleResponse> | SorterResult<RoleResponse>[]
|
|
||||||
) => {
|
|
||||||
const {field, order} = Array.isArray(sorter) ? sorter[0] : sorter;
|
|
||||||
fetchRoles({
|
|
||||||
sortField: field as string,
|
|
||||||
sortOrder: order
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{padding: '24px'}}>
|
<Card>
|
||||||
<div style={{marginBottom: 16}}>
|
<div style={{ marginBottom: 16 }}>
|
||||||
<Space>
|
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
|
||||||
<Button type="primary" icon={<PlusOutlined/>} onClick={handleAdd}>
|
新建角色
|
||||||
新增角色
|
</Button>
|
||||||
</Button>
|
|
||||||
<Button icon={<SettingOutlined/>} onClick={handleTagManage}>
|
|
||||||
标签管理
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Table
|
<Table
|
||||||
loading={loading}
|
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={roles}
|
dataSource={roles}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
scroll={{x: 1500}}
|
loading={loading}
|
||||||
pagination={pagination}
|
pagination={pagination}
|
||||||
onChange={handleTableChange}
|
onChange={handleTableChange}
|
||||||
size="middle"
|
|
||||||
bordered
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
title={editingRole ? '编辑角色' : '新增角色'}
|
title={editingId ? '编辑角色' : '新建角色'}
|
||||||
open={modalVisible}
|
open={visible}
|
||||||
onOk={handleSubmit}
|
onOk={handleSubmit}
|
||||||
onCancel={() => setModalVisible(false)}
|
onCancel={() => setVisible(false)}
|
||||||
width={600}
|
confirmLoading={confirmLoading}
|
||||||
destroyOnClose
|
|
||||||
>
|
>
|
||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
@ -331,171 +260,69 @@ const RolePage: React.FC = () => {
|
|||||||
<Form.Item
|
<Form.Item
|
||||||
name="code"
|
name="code"
|
||||||
label="角色编码"
|
label="角色编码"
|
||||||
rules={[{required: true, message: '请输入角色编码'}]}
|
rules={[{ required: true, message: '请输入角色编码' }]}
|
||||||
>
|
>
|
||||||
<Input placeholder="请输入角色编码" disabled={!!editingRole}/>
|
<Input placeholder="请输入角色编码" disabled={!!editingId} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="name"
|
name="name"
|
||||||
label="角色名称"
|
label="角色名称"
|
||||||
rules={[{required: true, message: '请输入角色名称'}]}
|
rules={[{ required: true, message: '请输入角色名称' }]}
|
||||||
>
|
>
|
||||||
<Input placeholder="请输入角色名称"/>
|
<Input placeholder="请输入角色名称" />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="sort"
|
||||||
|
label="排序"
|
||||||
|
initialValue={0}
|
||||||
|
>
|
||||||
|
<InputNumber min={0} />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="enabled"
|
||||||
|
label="状态"
|
||||||
|
valuePropName="checked"
|
||||||
|
initialValue={true}
|
||||||
|
>
|
||||||
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="description"
|
name="description"
|
||||||
label="描述"
|
label="描述"
|
||||||
>
|
>
|
||||||
<Input.TextArea rows={4} placeholder="请输入描述"/>
|
<Input.TextArea rows={4} placeholder="请输入描述" />
|
||||||
</Form.Item>
|
|
||||||
|
|
||||||
<Form.Item
|
|
||||||
name="sort"
|
|
||||||
label="显示排序"
|
|
||||||
tooltip="值越大排序越靠后,默认为当前最大值+1"
|
|
||||||
rules={[{ required: true, message: '请输入排序' }]}
|
|
||||||
>
|
|
||||||
<InputNumber
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
min={0}
|
|
||||||
placeholder="请输入排序"
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
<Modal
|
{selectedRole && (
|
||||||
title="分配标签"
|
<>
|
||||||
open={tagModalVisible}
|
<PermissionModal
|
||||||
onOk={handleAssignTagSubmit}
|
roleId={selectedRole.id}
|
||||||
onCancel={() => setTagModalVisible(false)}
|
visible={permissionModalVisible}
|
||||||
width={600}
|
onCancel={() => setPermissionModalVisible(false)}
|
||||||
destroyOnClose
|
onSuccess={() => {
|
||||||
>
|
setPermissionModalVisible(false);
|
||||||
<Form layout="vertical">
|
fetchRoles();
|
||||||
<Form.Item label="选择标签">
|
}}
|
||||||
<Select
|
/>
|
||||||
mode="multiple"
|
<TagModal
|
||||||
placeholder="请选择标签"
|
roleId={selectedRole.id}
|
||||||
value={selectedTags}
|
visible={tagModalVisible}
|
||||||
onChange={setSelectedTags}
|
onCancel={() => setTagModalVisible(false)}
|
||||||
style={{width: '100%'}}
|
onSuccess={() => {
|
||||||
>
|
setTagModalVisible(false);
|
||||||
{allTags?.map(tag => (
|
fetchRoles();
|
||||||
<Select.Option key={tag.id} value={tag.id}>
|
}}
|
||||||
<Tag color={tag.color}>{tag.name}</Tag>
|
selectedTags={selectedRole.tags}
|
||||||
</Select.Option>
|
/>
|
||||||
))}
|
</>
|
||||||
</Select>
|
)}
|
||||||
</Form.Item>
|
</Card>
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title="标签管理"
|
|
||||||
open={tagManageVisible}
|
|
||||||
onCancel={() => {
|
|
||||||
setTagManageVisible(false);
|
|
||||||
setEditingTag(null);
|
|
||||||
}}
|
|
||||||
width={800}
|
|
||||||
footer={null}
|
|
||||||
>
|
|
||||||
<div style={{marginBottom: 16}}>
|
|
||||||
<Button type="primary" icon={<PlusOutlined/>} onClick={() => {
|
|
||||||
tagForm.resetFields();
|
|
||||||
setEditingTag(null);
|
|
||||||
}}>
|
|
||||||
新增标签
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Table
|
|
||||||
dataSource={allTags}
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
title: '标签名称',
|
|
||||||
dataIndex: 'name'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '颜色',
|
|
||||||
dataIndex: 'color',
|
|
||||||
render: (color: string) => (
|
|
||||||
<div style={{
|
|
||||||
backgroundColor: color,
|
|
||||||
width: 20,
|
|
||||||
height: 20,
|
|
||||||
borderRadius: 4
|
|
||||||
}}/>
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '描述',
|
|
||||||
dataIndex: 'description'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
render: (_, record) => (
|
|
||||||
<Space>
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
icon={<EditOutlined/>}
|
|
||||||
onClick={() => {
|
|
||||||
tagForm.setFieldsValue(record);
|
|
||||||
setEditingTag(record);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
编辑
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
danger
|
|
||||||
icon={<DeleteOutlined/>}
|
|
||||||
onClick={() => handleTagDelete(record.id)}
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
rowKey="id"
|
|
||||||
pagination={false}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{(editingTag || !editingTag) && (
|
|
||||||
<Form form={tagForm} layout="vertical" style={{marginTop: 16}}>
|
|
||||||
<Form.Item name="id" hidden>
|
|
||||||
<Input/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="name"
|
|
||||||
label="标签名称"
|
|
||||||
rules={[{required: true}]}
|
|
||||||
>
|
|
||||||
<Input/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="color"
|
|
||||||
label="标签颜色"
|
|
||||||
rules={[{required: true}]}
|
|
||||||
>
|
|
||||||
<ColorPicker/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="description" label="描述">
|
|
||||||
<Input.TextArea/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item>
|
|
||||||
<Button type="primary" onClick={handleTagManageSubmit}>
|
|
||||||
保存
|
|
||||||
</Button>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import type {Page} from '@/types/base/page';
|
import type {Page} from '@/types/base/page';
|
||||||
import {RoleResponse, RoleRequest, RoleQuery, RoleTagResponse, RoleTagRequest} from './types';
|
import type {RoleResponse, RoleRequest, RoleQuery, RoleTagResponse, RoleTagRequest, Permission} from './types';
|
||||||
|
|
||||||
const BASE_URL = '/api/v1/role';
|
const BASE_URL = '/api/v1/role';
|
||||||
|
|
||||||
const ROLE_TAG_BASE_URL = '/api/v1/role-tag'
|
const ROLE_TAG_BASE_URL = '/api/v1/role-tag'
|
||||||
|
|
||||||
// 获取角色列表(分页)
|
// 获取角色列表(分页)
|
||||||
export const getRoles = (params?: RoleQuery) => request.get<Page<RoleResponse>>(`${BASE_URL}/page`, {params});
|
export const getRoleList = (params?: RoleQuery) => request.get<Page<RoleResponse>>(`${BASE_URL}/page`, {params});
|
||||||
|
|
||||||
// 创建角色
|
// 创建角色
|
||||||
export const createRole = (data: RoleRequest) => request.post<RoleResponse>(BASE_URL, data);
|
export const createRole = (data: RoleRequest) => request.post<RoleResponse>(BASE_URL, data);
|
||||||
@ -28,4 +28,10 @@ export const deleteRoleTag = (id: number) => request.delete(`${ROLE_TAG_BASE_URL
|
|||||||
|
|
||||||
export const updateRoleTag = (id: number, data: RoleTagRequest) => request.put(`${ROLE_TAG_BASE_URL}/${id}`, data);
|
export const updateRoleTag = (id: number, data: RoleTagRequest) => request.put(`${ROLE_TAG_BASE_URL}/${id}`, data);
|
||||||
|
|
||||||
export const createRoleTag = (data: RoleRequest) => request.post<RoleResponse>(ROLE_TAG_BASE_URL, data);
|
export const createRoleTag = (data: RoleTagRequest) => request.post<RoleTagResponse>(ROLE_TAG_BASE_URL, data);
|
||||||
|
|
||||||
|
// 获取角色的权限列表
|
||||||
|
export const getRolePermissions = (roleId: number) => request.get<Permission[]>(`${BASE_URL}/${roleId}/permissions`);
|
||||||
|
|
||||||
|
// 分配权限
|
||||||
|
export const assignPermissions = (roleId: number, permissionIds: number[]) => request.post(`${BASE_URL}/${roleId}/permissions`, permissionIds);
|
||||||
@ -1,21 +1,39 @@
|
|||||||
import type { BaseResponse } from '@/types/base/response';
|
import type { BaseResponse } from '@/types/base/response';
|
||||||
|
|
||||||
|
// 权限类型枚举
|
||||||
|
export enum PermissionType {
|
||||||
|
MENU = 'MENU',
|
||||||
|
BUTTON = 'BUTTON',
|
||||||
|
API = 'API'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 权限类型显示文本
|
||||||
|
export const PermissionTypeText = {
|
||||||
|
[PermissionType.MENU]: '菜单权限',
|
||||||
|
[PermissionType.BUTTON]: '按钮权限',
|
||||||
|
[PermissionType.API]: '接口权限'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 角色查询参数
|
||||||
export interface RoleQuery {
|
export interface RoleQuery {
|
||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
sortField?: string;
|
sortField?: string;
|
||||||
sortOrder?: 'ascend' | 'descend';
|
sortOrder?: 'asc' | 'desc';
|
||||||
code?: string;
|
code?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 角色请求参数
|
||||||
export interface RoleRequest {
|
export interface RoleRequest {
|
||||||
code: string;
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
sort: number;
|
sort?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 角色响应数据
|
||||||
export interface RoleResponse extends BaseResponse {
|
export interface RoleResponse extends BaseResponse {
|
||||||
code: string;
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
@ -23,16 +41,33 @@ export interface RoleResponse extends BaseResponse {
|
|||||||
sort: number;
|
sort: number;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
tags?: RoleTagResponse[];
|
tags?: RoleTagResponse[];
|
||||||
|
createTime: string;
|
||||||
|
updateTime: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 角色标签请求参数
|
||||||
export interface RoleTagRequest {
|
export interface RoleTagRequest {
|
||||||
name: string;
|
name: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 角色标签响应数据
|
||||||
export interface RoleTagResponse extends BaseResponse {
|
export interface RoleTagResponse extends BaseResponse {
|
||||||
name: string;
|
name: string;
|
||||||
color: string;
|
color: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 权限数据
|
||||||
|
export interface Permission {
|
||||||
|
id: number;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
type: PermissionType;
|
||||||
|
menuId: number;
|
||||||
|
menuName?: string;
|
||||||
|
enabled: boolean;
|
||||||
|
sort: number;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import React, {useEffect, useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import {Table, Button, Modal, Form, Input, Space, message, Switch, TreeSelect, Select} from 'antd';
|
import {Table, Button, Modal, Form, Input, Space, message, Switch, TreeSelect, Select, Tag} from 'antd';
|
||||||
import {PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TeamOutlined} from '@ant-design/icons';
|
import {PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TeamOutlined} from '@ant-design/icons';
|
||||||
import type {UserResponse, Role} from './types';
|
import type {UserResponse, Role} from './types';
|
||||||
import type {DepartmentDTO} from '../Department/types';
|
import type {DepartmentDTO} from '../Department/types';
|
||||||
@ -11,6 +11,12 @@ import {TablePaginationConfig} from "antd/es/table";
|
|||||||
import {FilterValue, SorterResult} from "antd/es/table/interface";
|
import {FilterValue, SorterResult} from "antd/es/table/interface";
|
||||||
import { getDepartmentTree } from '../Department/service'; // 导入部门树接口
|
import { getDepartmentTree } from '../Department/service'; // 导入部门树接口
|
||||||
|
|
||||||
|
interface TreeNode {
|
||||||
|
title: string;
|
||||||
|
value: number;
|
||||||
|
children?: TreeNode[];
|
||||||
|
}
|
||||||
|
|
||||||
const UserPage: React.FC = () => {
|
const UserPage: React.FC = () => {
|
||||||
const {
|
const {
|
||||||
list: users,
|
list: users,
|
||||||
@ -73,34 +79,39 @@ const UserPage: React.FC = () => {
|
|||||||
const values = await passwordForm.validateFields();
|
const values = await passwordForm.validateFields();
|
||||||
if (editingUser) {
|
if (editingUser) {
|
||||||
await resetPassword(editingUser.id, values.password);
|
await resetPassword(editingUser.id, values.password);
|
||||||
|
message.success('密码重置成功');
|
||||||
setResetPasswordModalVisible(false);
|
setResetPasswordModalVisible(false);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('密码重置失败:', error);
|
message.error(error.message || '密码重置失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = async () => {
|
const handleSubmit = async (values: any) => {
|
||||||
try {
|
try {
|
||||||
const values = await form.validateFields();
|
|
||||||
if (editingUser) {
|
if (editingUser) {
|
||||||
const success = await handleUpdate(editingUser.id, {
|
const success = await handleUpdate(editingUser.id, {
|
||||||
...values,
|
...values,
|
||||||
version: editingUser.version
|
enabled: values.enabled ?? true
|
||||||
});
|
});
|
||||||
if (success) {
|
if (success) {
|
||||||
|
message.success('更新成功');
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
fetchUsers();
|
fetchUsers();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const success = await handleCreate(values);
|
const success = await handleCreate({
|
||||||
|
...values,
|
||||||
|
enabled: values.enabled ?? true
|
||||||
|
});
|
||||||
if (success) {
|
if (success) {
|
||||||
|
message.success('创建成功');
|
||||||
setModalVisible(false);
|
setModalVisible(false);
|
||||||
fetchUsers();
|
fetchUsers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('操作失败:', error);
|
message.error(error.message || '操作失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,7 +128,7 @@ const UserPage: React.FC = () => {
|
|||||||
loadDepartmentTree();
|
loadDepartmentTree();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getTreeData = (departments: DepartmentDTO[]) => {
|
const getTreeData = (departments: DepartmentDTO[]): TreeNode[] => {
|
||||||
return departments.map(dept => ({
|
return departments.map(dept => ({
|
||||||
title: dept.name,
|
title: dept.name,
|
||||||
value: dept.id,
|
value: dept.id,
|
||||||
@ -172,8 +183,24 @@ const UserPage: React.FC = () => {
|
|||||||
title: '邮箱',
|
title: '邮箱',
|
||||||
dataIndex: 'email',
|
dataIndex: 'email',
|
||||||
key: 'email',
|
key: 'email',
|
||||||
width: 180,
|
width: 200,
|
||||||
ellipsis: true,
|
},
|
||||||
|
{
|
||||||
|
title: '部门',
|
||||||
|
dataIndex: 'departmentName',
|
||||||
|
key: 'departmentName',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'enabled',
|
||||||
|
key: 'enabled',
|
||||||
|
width: 100,
|
||||||
|
render: (enabled: boolean) => (
|
||||||
|
<Tag color={enabled ? 'success' : 'error'}>
|
||||||
|
{enabled ? '启用' : '禁用'}
|
||||||
|
</Tag>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '手机号',
|
title: '手机号',
|
||||||
@ -204,16 +231,6 @@ const UserPage: React.FC = () => {
|
|||||||
width: 150,
|
width: 150,
|
||||||
sorter: true
|
sorter: true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'enabled',
|
|
||||||
key: 'enabled',
|
|
||||||
width: 60,
|
|
||||||
align: 'center' as AlignType,
|
|
||||||
render: (enabled: boolean) => (
|
|
||||||
<Switch checked={enabled} disabled size="small"/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
@ -296,7 +313,7 @@ const UserPage: React.FC = () => {
|
|||||||
<Modal
|
<Modal
|
||||||
title={editingUser ? '编辑用户' : '新增用户'}
|
title={editingUser ? '编辑用户' : '新增用户'}
|
||||||
open={modalVisible}
|
open={modalVisible}
|
||||||
onOk={handleSubmit}
|
onOk={() => form.validateFields().then(handleSubmit)}
|
||||||
onCancel={() => setModalVisible(false)}
|
onCancel={() => setModalVisible(false)}
|
||||||
width={600}
|
width={600}
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export interface UserRequest {
|
|||||||
phone?: string;
|
phone?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
|
departmentId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色类型定义
|
// 角色类型定义
|
||||||
@ -39,4 +40,8 @@ export interface UserResponse extends BaseResponse {
|
|||||||
phone?: string;
|
phone?: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
roles: Role[];
|
roles: Role[];
|
||||||
|
departmentId?: number;
|
||||||
|
departmentName?: string;
|
||||||
|
createTime: string;
|
||||||
|
updateTime: string;
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user