可正常启用

This commit is contained in:
戚辰先生 2024-12-01 21:07:34 +08:00
parent a4bd4f07d0
commit 750f446844
8 changed files with 639 additions and 500 deletions

View File

@ -38,6 +38,7 @@ export function useTableData<T extends { id: number }, P extends SortParams>({
...params,
pageNum: state.pagination.current,
pageSize: state.pagination.pageSize,
sortField: params?.sortField,
sortOrder: params?.sortOrder === 'ascend' ? 'asc' : params?.sortOrder === 'descend' ? 'desc' : undefined
}
});

View File

@ -1,9 +1,8 @@
import React, { useEffect, useState } from 'react';
import { Modal, Tree, message, Spin } from 'antd';
import type { DataNode } from 'antd/es/tree';
import type { MenuDTO } from '../../Menu/types';
import { getMenuTree } from '../../Menu/service';
import { getRoleMenuIds, updateRoleMenus } from '../service';
import { Modal, Table, message, Spin, Tag } from 'antd';
import type { Permission } from '../types';
import { PermissionType, PermissionTypeText } from '../types';
import { getRolePermissions, assignPermissions } from '../service';
interface PermissionModalProps {
roleId: number;
@ -18,35 +17,17 @@ const PermissionModal: React.FC<PermissionModalProps> = ({
onCancel,
onSuccess
}) => {
const [menuTree, setMenuTree] = useState<MenuDTO[]>([]);
const [permissions, setPermissions] = useState<Permission[]>([]);
const [selectedKeys, setSelectedKeys] = useState<number[]>([]);
const [loading, setLoading] = useState(false);
const [expandedKeys, setExpandedKeys] = useState<number[]>([]);
// 获取菜单树和角色已有权限
// 获取角色已有权限
const fetchData = async () => {
try {
setLoading(true);
const [menus, menuIds] = await Promise.all([
getMenuTree(),
getRoleMenuIds(roleId)
]);
setMenuTree(menus);
setSelectedKeys(menuIds);
// 设置展开的节点
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);
const data = await getRolePermissions(roleId);
setPermissions(data);
setSelectedKeys(data.map(p => p.id));
} catch (error) {
message.error('获取权限数据失败');
} finally {
@ -63,7 +44,7 @@ const PermissionModal: React.FC<PermissionModalProps> = ({
const handleOk = async () => {
try {
setLoading(true);
await updateRoleMenus(roleId, selectedKeys);
await assignPermissions(roleId, selectedKeys);
message.success('权限更新成功');
onSuccess();
} catch (error) {
@ -73,15 +54,47 @@ const PermissionModal: React.FC<PermissionModalProps> = ({
}
};
// 转换菜单数据为Tree组件需要的格式
const getTreeData = (menus: MenuDTO[]): DataNode[] => {
return menus.map(menu => ({
title: menu.name,
key: menu.id,
children: menu.children ? getTreeData(menu.children) : undefined,
disabled: menu.type === 0 // 目录不可选
}));
const getPermissionTypeTag = (type: PermissionType) => {
const colorMap = {
[PermissionType.MENU]: 'blue',
[PermissionType.BUTTON]: 'green',
[PermissionType.API]: 'orange'
};
return (
<Tag color={colorMap[type]}>
{PermissionTypeText[type]}
</Tag>
);
};
const columns = [
{
title: '权限编码',
dataIndex: 'code',
width: 150
},
{
title: '权限名称',
dataIndex: 'name',
width: 150
},
{
title: '权限类型',
dataIndex: 'type',
width: 120,
render: (type: PermissionType) => getPermissionTypeTag(type)
},
{
title: '所属菜单',
dataIndex: 'menuName',
width: 150
},
{
title: '描述',
dataIndex: 'description',
ellipsis: true
}
];
return (
<Modal
@ -89,18 +102,22 @@ const PermissionModal: React.FC<PermissionModalProps> = ({
open={visible}
onOk={handleOk}
onCancel={onCancel}
width={600}
width={800}
confirmLoading={loading}
destroyOnClose
>
<Spin spinning={loading}>
<Tree
checkable
expandedKeys={expandedKeys}
onExpand={(keys) => setExpandedKeys(keys as number[])}
checkedKeys={selectedKeys}
onCheck={(checked) => setSelectedKeys(checked as number[])}
treeData={getTreeData(menuTree)}
<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>

View 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;

View File

@ -1,238 +1,160 @@
import React, {useState, useEffect} from 'react';
import {Table, Button, Modal, Form, Input, Space, message, InputNumber, Switch, Select, Tag, ColorPicker} from 'antd';
import {PlusOutlined, EditOutlined, DeleteOutlined, TagOutlined, SettingOutlined} from '@ant-design/icons';
import type {RoleResponse, RoleTagResponse} from './types';
import {useTableData} from '@/hooks/useTableData';
import type {FixedType} from 'rc-table/lib/interface';
import React, { useEffect, useState } from 'react';
import { Card, Button, Table, Space, Modal, Form, Input, InputNumber, Switch, message, Tag } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TagsOutlined } from '@ant-design/icons';
import type { RoleResponse, RoleTagResponse, RoleQuery } from './types';
import { getRoleList, createRole, updateRole, deleteRole } from './service';
import type { TablePaginationConfig } from 'antd/es/table';
import type {FilterValue, SorterResult} from 'antd/es/table/interface';
import {assignTags, createRoleTag, deleteRoleTag, getAllTags, updateRoleTag} from './service';
import type { SorterResult } from 'antd/es/table/interface';
import PermissionModal from './components/PermissionModal';
import TagModal from './components/TagModal';
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 [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 [selectedRole, setSelectedRole] = useState<RoleResponse | null>(null);
const [selectedTags, setSelectedTags] = useState<number[]>([]);
const [allTags, setAllTags] = useState<RoleTagResponse[] | null>([]);
const [tagManageVisible, setTagManageVisible] = useState(false);
const [editingTag, setEditingTag] = useState<RoleTagResponse | null>(null);
const [tagForm] = Form.useForm();
const [loading, setLoading] = useState(false);
const [roles, setRoles] = useState<RoleResponse[]>([]);
const [pagination, setPagination] = useState<TablePaginationConfig>({
current: 1,
pageSize: 10,
total: 0,
showSizeChanger: true,
showQuickJumper: true
});
const [sortField, setSortField] = useState<string>('sort');
const [sortOrder, setSortOrder] = useState<'ascend' | 'descend'>('ascend');
useEffect(() => {
const fetchTags = async () => { // 定义一个异步函数
const fetchRoles = async (params: RoleQuery = {}) => {
try {
const tags = await getAllTags(); // 在这里使用 await
setAllTags(tags);
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) {
console.error("获取标签失败", error); // 处理错误
message.error('获取角色列表失败');
} finally {
setLoading(false);
}
};
fetchTags(); // 调用异步函数
useEffect(() => {
fetchRoles();
}, []);
const handleAdd = () => {
setEditingRole(null);
setEditingId(null);
form.resetFields();
// 获取当前最大排序值
const maxSort = Math.max(0, ...roles.map(role => role.sort));
form.setFieldsValue({
enabled: true,
sort: maxSort + 1 // 默认比最大值大1
sort: 0
});
setModalVisible(true);
setVisible(true);
};
const handleEdit = (record: RoleResponse) => {
setEditingRole(record);
setEditingId(record.id);
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 () => {
try {
const values = await form.validateFields();
if (editingRole) {
const success = await handleUpdate(editingRole.id, {
...values,
version: editingRole.version
});
if (success) {
setModalVisible(false);
fetchRoles();
}
setConfirmLoading(true);
if (editingId) {
await updateRole(editingId, values);
message.success('更新成功');
} else {
const success = await handleCreate(values);
if (success) {
setModalVisible(false);
await createRole(values);
message.success('创建成功');
}
setVisible(false);
fetchRoles();
}
}
} catch (error) {
console.error('操作失败:', error);
message.error('操作失败');
} finally {
setConfirmLoading(false);
}
};
const handleAssignPermissions = (record: RoleResponse) => {
setSelectedRole(record);
setPermissionModalVisible(true);
};
const handleAssignTags = (record: RoleResponse) => {
setSelectedRole(record);
if (record.tags && record.tags.length > 0) {
setSelectedTags(record.tags.map(tag => tag.id));
} else {
setSelectedTags([]);
}
setTagModalVisible(true);
};
const handleAssignTagSubmit = async () => {
if (selectedRole) {
await assignTags(selectedRole.id, selectedTags);
setTagModalVisible(false);
fetchRoles();
}
};
const handleTagManage = () => {
setTagManageVisible(true);
setEditingTag(null);
tagForm.resetFields();
};
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 handleTableChange = (
pagination: TablePaginationConfig,
_: any,
sorter: SorterResult<RoleResponse>
) => {
const { field, order } = sorter;
setSortField(field as string);
setSortOrder(order as 'ascend' | 'descend');
fetchRoles({
pageNum: pagination.current,
pageSize: pagination.pageSize,
sortField: field as string,
sortOrder: order === 'ascend' ? 'asc' : order === 'descend' ? 'desc' : undefined
});
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
width: 60,
fixed: 'left' as FixedType,
sorter: true
},
{
title: '角色编码',
dataIndex: 'code',
key: 'code',
width: 120,
width: 150,
sorter: true
},
{
title: '角色名称',
dataIndex: 'name',
key: 'name',
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
width: 150
},
{
title: '标签',
dataIndex: 'tags',
key: 'tags',
width: 200,
render: (tags: RoleTagResponse[]) => (
<Space>
<Space size={[0, 4]} wrap>
{tags?.map(tag => (
<Tag key={tag.id} color={tag.color}>
{tag.name}
@ -241,16 +163,39 @@ const RolePage: React.FC = () => {
</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: '操作',
key: 'action',
width: 180,
fixed: 'right' as FixedType,
width: 280,
render: (_: any, record: RoleResponse) => (
<Space size={0}>
<Space>
<Button
type="link"
size="small"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
@ -258,7 +203,20 @@ const RolePage: React.FC = () => {
</Button>
<Button
type="link"
size="small"
icon={<KeyOutlined />}
onClick={() => handleAssignPermissions(record)}
>
</Button>
<Button
type="link"
icon={<TagsOutlined />}
onClick={() => handleAssignTags(record)}
>
</Button>
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record.id)}
@ -266,63 +224,34 @@ const RolePage: React.FC = () => {
>
</Button>
<Button
type="link"
size="small"
icon={<TagOutlined/>}
onClick={() => handleAssignTags(record)}
>
</Button>
</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 (
<div style={{padding: '24px'}}>
<Card>
<div style={{ marginBottom: 16 }}>
<Space>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
</Button>
<Button icon={<SettingOutlined/>} onClick={handleTagManage}>
</Button>
</Space>
</div>
<Table
loading={loading}
columns={columns}
dataSource={roles}
rowKey="id"
scroll={{x: 1500}}
loading={loading}
pagination={pagination}
onChange={handleTableChange}
size="middle"
bordered
/>
<Modal
title={editingRole ? '编辑角色' : '新增角色'}
open={modalVisible}
title={editingId ? '编辑角色' : '新建角色'}
open={visible}
onOk={handleSubmit}
onCancel={() => setModalVisible(false)}
width={600}
destroyOnClose
onCancel={() => setVisible(false)}
confirmLoading={confirmLoading}
>
<Form
form={form}
@ -333,7 +262,7 @@ const RolePage: React.FC = () => {
label="角色编码"
rules={[{ required: true, message: '请输入角色编码' }]}
>
<Input placeholder="请输入角色编码" disabled={!!editingRole}/>
<Input placeholder="请输入角色编码" disabled={!!editingId} />
</Form.Item>
<Form.Item
@ -344,158 +273,56 @@ const RolePage: React.FC = () => {
<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
name="description"
label="描述"
>
<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>
</Modal>
<Modal
title="分配标签"
open={tagModalVisible}
onOk={handleAssignTagSubmit}
{selectedRole && (
<>
<PermissionModal
roleId={selectedRole.id}
visible={permissionModalVisible}
onCancel={() => setPermissionModalVisible(false)}
onSuccess={() => {
setPermissionModalVisible(false);
fetchRoles();
}}
/>
<TagModal
roleId={selectedRole.id}
visible={tagModalVisible}
onCancel={() => setTagModalVisible(false)}
width={600}
destroyOnClose
>
<Form layout="vertical">
<Form.Item label="选择标签">
<Select
mode="multiple"
placeholder="请选择标签"
value={selectedTags}
onChange={setSelectedTags}
style={{width: '100%'}}
>
{allTags?.map(tag => (
<Select.Option key={tag.id} value={tag.id}>
<Tag color={tag.color}>{tag.name}</Tag>
</Select.Option>
))}
</Select>
</Form.Item>
</Form>
</Modal>
<Modal
title="标签管理"
open={tagManageVisible}
onCancel={() => {
setTagManageVisible(false);
setEditingTag(null);
onSuccess={() => {
setTagModalVisible(false);
fetchRoles();
}}
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}
selectedTags={selectedRole.tags}
/>
{(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>
</Card>
);
};

View File

@ -1,13 +1,13 @@
import request from '@/utils/request';
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 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);
@ -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 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);

View File

@ -1,21 +1,39 @@
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 {
pageNum?: number;
pageSize?: number;
sortField?: string;
sortOrder?: 'ascend' | 'descend';
sortOrder?: 'asc' | 'desc';
code?: string;
name?: string;
enabled?: boolean;
}
// 角色请求参数
export interface RoleRequest {
code: string;
name: string;
description?: string;
sort: number;
sort?: number;
}
// 角色响应数据
export interface RoleResponse extends BaseResponse {
code: string;
name: string;
@ -23,16 +41,33 @@ export interface RoleResponse extends BaseResponse {
sort: number;
enabled: boolean;
tags?: RoleTagResponse[];
createTime: string;
updateTime: string;
}
// 角色标签请求参数
export interface RoleTagRequest {
name: string;
color?: string;
description?: string;
}
// 角色标签响应数据
export interface RoleTagResponse extends BaseResponse {
name: string;
color: string;
description?: string;
}
// 权限数据
export interface Permission {
id: number;
code: string;
name: string;
type: PermissionType;
menuId: number;
menuName?: string;
enabled: boolean;
sort: number;
description?: string;
}

View File

@ -1,5 +1,5 @@
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 type {UserResponse, Role} from './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 { getDepartmentTree } from '../Department/service'; // 导入部门树接口
interface TreeNode {
title: string;
value: number;
children?: TreeNode[];
}
const UserPage: React.FC = () => {
const {
list: users,
@ -73,34 +79,39 @@ const UserPage: React.FC = () => {
const values = await passwordForm.validateFields();
if (editingUser) {
await resetPassword(editingUser.id, values.password);
message.success('密码重置成功');
setResetPasswordModalVisible(false);
}
} catch (error) {
console.error('密码重置失败:', error);
} catch (error: any) {
message.error(error.message || '密码重置失败');
}
};
const handleSubmit = async () => {
const handleSubmit = async (values: any) => {
try {
const values = await form.validateFields();
if (editingUser) {
const success = await handleUpdate(editingUser.id, {
...values,
version: editingUser.version
enabled: values.enabled ?? true
});
if (success) {
message.success('更新成功');
setModalVisible(false);
fetchUsers();
}
} else {
const success = await handleCreate(values);
const success = await handleCreate({
...values,
enabled: values.enabled ?? true
});
if (success) {
message.success('创建成功');
setModalVisible(false);
fetchUsers();
}
}
} catch (error) {
console.error('操作失败:', error);
} catch (error: any) {
message.error(error.message || '操作失败');
}
};
@ -117,7 +128,7 @@ const UserPage: React.FC = () => {
loadDepartmentTree();
}, []);
const getTreeData = (departments: DepartmentDTO[]) => {
const getTreeData = (departments: DepartmentDTO[]): TreeNode[] => {
return departments.map(dept => ({
title: dept.name,
value: dept.id,
@ -172,8 +183,24 @@ const UserPage: React.FC = () => {
title: '邮箱',
dataIndex: 'email',
key: 'email',
width: 180,
ellipsis: true,
width: 200,
},
{
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: '手机号',
@ -204,16 +231,6 @@ const UserPage: React.FC = () => {
width: 150,
sorter: true
},
{
title: '状态',
dataIndex: 'enabled',
key: 'enabled',
width: 60,
align: 'center' as AlignType,
render: (enabled: boolean) => (
<Switch checked={enabled} disabled size="small"/>
),
},
{
title: '操作',
key: 'action',
@ -296,7 +313,7 @@ const UserPage: React.FC = () => {
<Modal
title={editingUser ? '编辑用户' : '新增用户'}
open={modalVisible}
onOk={handleSubmit}
onOk={() => form.validateFields().then(handleSubmit)}
onCancel={() => setModalVisible(false)}
width={600}
destroyOnClose

View File

@ -19,6 +19,7 @@ export interface UserRequest {
phone?: string;
password?: string;
enabled?: boolean;
departmentId?: number;
}
// 角色类型定义
@ -39,4 +40,8 @@ export interface UserResponse extends BaseResponse {
phone?: string;
enabled: boolean;
roles: Role[];
departmentId?: number;
departmentName?: string;
createTime: string;
updateTime: string;
}