可正常启用

This commit is contained in:
戚辰先生 2024-12-01 13:01:31 +08:00
parent fa31848e9f
commit 76eeeb4b8e
6 changed files with 145 additions and 296 deletions

View File

@ -1,5 +1,5 @@
import {useState, useCallback, useEffect} from 'react'; import {useState, useCallback, useEffect} from 'react';
import {Modal} from 'antd'; import {message, Modal} from 'antd';
import type {TableState} from '@/types/table'; import type {TableState} from '@/types/table';
import {createInitialState} from '@/utils/table'; import {createInitialState} from '@/utils/table';
import request from '@/utils/request'; import request from '@/utils/request';
@ -38,8 +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,
sortOrder: params?.sortOrder === 'ascend' ? 'asc' : sortOrder: params?.sortOrder === 'ascend' ? 'asc' : params?.sortOrder === 'descend' ? 'desc' : undefined
params?.sortOrder === 'descend' ? 'desc' : undefined
} }
}); });
@ -60,10 +59,9 @@ export function useTableData<T extends { id: number }, P extends SortParams>({
// CRUD操作 // CRUD操作
const handleCreate = useCallback(async (data: Partial<T>) => { const handleCreate = useCallback(async (data: Partial<T>) => {
try { try {
await request.post<T>(service.baseUrl, data, { await request.post<T>(service.baseUrl, data);
successMessage: '创建成功' message.success("创建成功")
}); await loadData();
loadData();
return true; return true;
} catch (error) { } catch (error) {
return false; return false;
@ -72,10 +70,9 @@ export function useTableData<T extends { id: number }, P extends SortParams>({
const handleUpdate = useCallback(async (id: number, data: Partial<T>) => { const handleUpdate = useCallback(async (id: number, data: Partial<T>) => {
try { try {
await request.put<T>(`${service.baseUrl}/${id}`, data, { await request.put<T>(`${service.baseUrl}/${id}`, data);
successMessage: '更新成功' message.success("更新成功")
}); await loadData();
loadData();
return true; return true;
} catch (error) { } catch (error) {
return false; return false;
@ -92,9 +89,8 @@ export function useTableData<T extends { id: number }, P extends SortParams>({
cancelText: '取消', cancelText: '取消',
onOk: async () => { onOk: async () => {
try { try {
await request.delete(`${service.baseUrl}/${id}`, { await request.delete(`${service.baseUrl}/${id}`);
successMessage: '删除成功' message.success("删除成功")
});
loadData(); loadData();
resolve(true); resolve(true);
} catch (error) { } catch (error) {

View File

@ -51,19 +51,10 @@ const BasicLayout: React.FC = () => {
const initializeUserData = async () => { const initializeUserData = async () => {
// try { // try {
setLoading(true); setLoading(true);
const menuData = await getCurrentUserMenus().then((response) => { const menuData = await getCurrentUserMenus();
console.log(response) console.log(menuData)
dispatch(setMenus(response)); dispatch(setMenus(menuData));
setLoading(false); setLoading(false);
})
// } catch (error) {
// console.log(error);
// message.error('初始化用户数据失败');
// dispatch(logout());
// navigate('/login', {replace: true});
// } finally {
// setLoading(false);
// }
}; };
if (!userInfo) { if (!userInfo) {

View File

@ -1,13 +1,12 @@
import React, { useState, useEffect } from 'react'; import React, {useState, useEffect} from 'react';
import { Table, Button, Modal, Form, Input, Space, message, InputNumber, Switch, Select, Tag, ColorPicker } from 'antd'; 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 {PlusOutlined, EditOutlined, DeleteOutlined, TagOutlined, SettingOutlined} from '@ant-design/icons';
import type { RoleResponse, RoleTagResponse } from './types'; import type {RoleResponse, RoleTagResponse} from './types';
import { useTableData } from '@/hooks/useTableData'; import {useTableData} from '@/hooks/useTableData';
import type { FixedType } from 'rc-table/lib/interface'; import type {FixedType} from 'rc-table/lib/interface';
import type { TablePaginationConfig } from 'antd/es/table'; import type {TablePaginationConfig} from 'antd/es/table';
import type { FilterValue, SorterResult } from 'antd/es/table/interface'; import type {FilterValue, SorterResult} from 'antd/es/table/interface';
import { assignTags, getAllTags } from './service'; import {assignTags, createRoleTag, deleteRoleTag, getAllTags, updateRoleTag} from './service';
import request from '@/utils/request';
const RolePage: React.FC = () => { const RolePage: React.FC = () => {
const { const {
@ -42,9 +41,15 @@ const RolePage: React.FC = () => {
const [tagForm] = Form.useForm(); const [tagForm] = Form.useForm();
useEffect(() => { useEffect(() => {
getAllTags().then(response => { const fetchTags = async () => { // 定义一个异步函数
setAllTags(response); try {
}); const tags = await getAllTags(); // 在这里使用 await
setAllTags(tags);
} catch (error) {
console.error("获取标签失败", error); // 处理错误
}
};
fetchTags(); // 调用异步函数
}, []); }, []);
const handleAdd = () => { const handleAdd = () => {
@ -110,23 +115,18 @@ const RolePage: React.FC = () => {
const handleTagManageSubmit = async () => { const handleTagManageSubmit = async () => {
try { try {
const values = await tagForm.validateFields(); const values = await tagForm.validateFields();
const tagService = {
baseUrl: '/api/v1/role-tag'
};
const formData = {
...values,
color: values.color.toHexString?.() || values.color
};
if (values.id) { if (values.id) {
await request.put(`${tagService.baseUrl}/${values.id}`, formData, { await updateRoleTag(values.id, {
successMessage: '更新标签成功' ...values,
color: values.color.toHexString?.() || values.color
}); });
message.success("更新标签成功")
} else { } else {
await request.post(tagService.baseUrl, formData, { await createRoleTag({
successMessage: '创建标签成功' ...values,
color: values.color.toHexString?.() || values.color
}); });
message.success("标签添加成功")
} }
setTagManageVisible(false); setTagManageVisible(false);
const tags = await getAllTags(); const tags = await getAllTags();
@ -137,16 +137,25 @@ const RolePage: React.FC = () => {
}; };
const handleTagDelete = async (id: number) => { const handleTagDelete = async (id: number) => {
try { Modal.confirm({
await request.delete(`/api/v1/role-tag/${id}`, { title: '确认删除',
successMessage: '删除标签成功' content: '确定要删除该记录吗?',
}); okText: '确定',
// 刷新标签列表 okType: 'danger',
const tags = await getAllTags(); cancelText: '取消',
setAllTags(tags); onOk: async () => {
} catch (error) { try {
console.error('删除标签失败:', error); await deleteRoleTag(id);
} message.success("删除成功");
// 刷新标签列表
const tags = await getAllTags();
setAllTags(tags);
} catch (error) {
console.error("角色标签删除失败", error);
}
},
// onCancel: () => resolve(false)
});
}; };
const columns = [ const columns = [
@ -191,7 +200,7 @@ const RolePage: React.FC = () => {
key: 'enabled', key: 'enabled',
width: 80, width: 80,
render: (enabled: boolean) => ( render: (enabled: boolean) => (
<Switch checked={enabled} disabled size="small" /> <Switch checked={enabled} disabled size="small"/>
) )
}, },
{ {
@ -234,7 +243,7 @@ const RolePage: React.FC = () => {
<Button <Button
type="link" type="link"
size="small" size="small"
icon={<EditOutlined />} icon={<EditOutlined/>}
onClick={() => handleEdit(record)} onClick={() => handleEdit(record)}
> >
@ -243,7 +252,7 @@ const RolePage: React.FC = () => {
type="link" type="link"
size="small" size="small"
danger danger
icon={<DeleteOutlined />} icon={<DeleteOutlined/>}
onClick={() => handleDelete(record.id)} onClick={() => handleDelete(record.id)}
disabled={record.code === 'admin'} disabled={record.code === 'admin'}
> >
@ -252,7 +261,7 @@ const RolePage: React.FC = () => {
<Button <Button
type="link" type="link"
size="small" size="small"
icon={<TagOutlined />} icon={<TagOutlined/>}
onClick={() => handleAssignTags(record)} onClick={() => handleAssignTags(record)}
> >
@ -267,7 +276,7 @@ const RolePage: React.FC = () => {
filters: Record<string, FilterValue | null>, filters: Record<string, FilterValue | null>,
sorter: SorterResult<RoleResponse> | SorterResult<RoleResponse>[] sorter: SorterResult<RoleResponse> | SorterResult<RoleResponse>[]
) => { ) => {
const { field, order } = Array.isArray(sorter) ? sorter[0] : sorter; const {field, order} = Array.isArray(sorter) ? sorter[0] : sorter;
fetchRoles({ fetchRoles({
sortField: field as string, sortField: field as string,
sortOrder: order sortOrder: order
@ -275,13 +284,13 @@ const RolePage: React.FC = () => {
}; };
return ( return (
<div style={{ padding: '24px' }}> <div style={{padding: '24px'}}>
<div style={{ marginBottom: 16 }}> <div style={{marginBottom: 16}}>
<Space> <Space>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}> <Button type="primary" icon={<PlusOutlined/>} onClick={handleAdd}>
</Button> </Button>
<Button icon={<SettingOutlined />} onClick={handleTagManage}> <Button icon={<SettingOutlined/>} onClick={handleTagManage}>
</Button> </Button>
</Space> </Space>
@ -292,7 +301,7 @@ const RolePage: React.FC = () => {
columns={columns} columns={columns}
dataSource={roles} dataSource={roles}
rowKey="id" rowKey="id"
scroll={{ x: 1500 }} scroll={{x: 1500}}
pagination={pagination} pagination={pagination}
onChange={handleTableChange} onChange={handleTableChange}
size="middle" size="middle"
@ -314,32 +323,32 @@ 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={!!editingRole}/>
</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>
<Form.Item <Form.Item
name="description" name="description"
label="描述" label="描述"
> >
<Input.TextArea rows={4} placeholder="请输入描述" /> <Input.TextArea rows={4} placeholder="请输入描述"/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="sort" name="sort"
label="排序" label="排序"
rules={[{ required: true, message: '请输入排序' }]} rules={[{required: true, message: '请输入排序'}]}
> >
<InputNumber style={{ width: '100%' }} min={0} placeholder="请输入排序" /> <InputNumber style={{width: '100%'}} min={0} placeholder="请输入排序"/>
</Form.Item> </Form.Item>
</Form> </Form>
</Modal> </Modal>
@ -359,7 +368,7 @@ const RolePage: React.FC = () => {
placeholder="请选择标签" placeholder="请选择标签"
value={selectedTags} value={selectedTags}
onChange={setSelectedTags} onChange={setSelectedTags}
style={{ width: '100%' }} style={{width: '100%'}}
> >
{allTags?.map(tag => ( {allTags?.map(tag => (
<Select.Option key={tag.id} value={tag.id}> <Select.Option key={tag.id} value={tag.id}>
@ -381,8 +390,8 @@ const RolePage: React.FC = () => {
width={800} width={800}
footer={null} footer={null}
> >
<div style={{ marginBottom: 16 }}> <div style={{marginBottom: 16}}>
<Button type="primary" icon={<PlusOutlined />} onClick={() => { <Button type="primary" icon={<PlusOutlined/>} onClick={() => {
tagForm.resetFields(); tagForm.resetFields();
setEditingTag(null); setEditingTag(null);
}}> }}>
@ -401,12 +410,12 @@ const RolePage: React.FC = () => {
title: '颜色', title: '颜色',
dataIndex: 'color', dataIndex: 'color',
render: (color: string) => ( render: (color: string) => (
<div style={{ <div style={{
backgroundColor: color, backgroundColor: color,
width: 20, width: 20,
height: 20, height: 20,
borderRadius: 4 borderRadius: 4
}} /> }}/>
) )
}, },
{ {
@ -417,9 +426,9 @@ const RolePage: React.FC = () => {
title: '操作', title: '操作',
render: (_, record) => ( render: (_, record) => (
<Space> <Space>
<Button <Button
type="link" type="link"
icon={<EditOutlined />} icon={<EditOutlined/>}
onClick={() => { onClick={() => {
tagForm.setFieldsValue(record); tagForm.setFieldsValue(record);
setEditingTag(record); setEditingTag(record);
@ -427,10 +436,10 @@ const RolePage: React.FC = () => {
> >
</Button> </Button>
<Button <Button
type="link" type="link"
danger danger
icon={<DeleteOutlined />} icon={<DeleteOutlined/>}
onClick={() => handleTagDelete(record.id)} onClick={() => handleTagDelete(record.id)}
> >
@ -444,26 +453,26 @@ const RolePage: React.FC = () => {
/> />
{(editingTag || !editingTag) && ( {(editingTag || !editingTag) && (
<Form form={tagForm} layout="vertical" style={{ marginTop: 16 }}> <Form form={tagForm} layout="vertical" style={{marginTop: 16}}>
<Form.Item name="id" hidden> <Form.Item name="id" hidden>
<Input /> <Input/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="name" name="name"
label="标签名称" label="标签名称"
rules={[{ required: true }]} rules={[{required: true}]}
> >
<Input /> <Input/>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="color" name="color"
label="标签颜色" label="标签颜色"
rules={[{ required: true }]} rules={[{required: true}]}
> >
<ColorPicker /> <ColorPicker/>
</Form.Item> </Form.Item>
<Form.Item name="description" label="描述"> <Form.Item name="description" label="描述">
<Input.TextArea /> <Input.TextArea/>
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" onClick={handleTagManageSubmit}> <Button type="primary" onClick={handleTagManageSubmit}>

View File

@ -1,52 +1,31 @@
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 type { RoleResponse, RoleRequest, RoleQuery, RoleTagResponse } from './types'; import {RoleResponse, RoleRequest, RoleQuery, RoleTagResponse, RoleTagRequest} from './types';
const BASE_URL = '/api/v1/role'; const BASE_URL = '/api/v1/role';
const ROLE_TAG_BASE_URL = '/api/v1/role-tag'
// 获取角色列表(分页) // 获取角色列表(分页)
export const getRoles = (params?: RoleQuery) => export const getRoles = (params?: RoleQuery) => request.get<Page<RoleResponse>>(`${BASE_URL}/page`, {params});
request.get<Page<RoleResponse>>(`${BASE_URL}/page`, {
params
});
// 创建角色 // 创建角色
export const createRole = (data: RoleRequest) => export const createRole = (data: RoleRequest) => request.post<RoleResponse>(BASE_URL, data);
request.post<RoleResponse>(BASE_URL, data, {
successMessage: '创建角色成功'
});
// 更新角色 // 更新角色
export const updateRole = (id: number, data: RoleRequest) => export const updateRole = (id: number, data: RoleRequest) => request.put<RoleResponse>(`${BASE_URL}/${id}`, data);
request.put<RoleResponse>(`${BASE_URL}/${id}`, data, {
successMessage: '更新角色成功'
});
// 删除角色 // 删除角色
export const deleteRole = (id: number) => export const deleteRole = (id: number) => request.delete(`${BASE_URL}/${id}`);
request.delete(`${BASE_URL}/${id}`, {
successMessage: '删除角色成功'
});
export const assignMenus = async (roleId: number, menuIds: number[]) => {
return request.post(`/api/v1/role/${roleId}/menus`, menuIds, {
errorMessage: '分配菜单失败,请稍后重试'
});
};
export const getRoleMenus = async (roleId: number) => {
return request.get(`/api/v1/role/${roleId}/menus`, {
errorMessage: '获取角色菜单失败,请刷新重试'
});
};
// 分配标签 // 分配标签
export const assignTags = (roleId: number, tagIds: number[]) => export const assignTags = (roleId: number, tagIds: number[]) => request.post(`${BASE_URL}/${roleId}/tags`, tagIds);
request.post(`${BASE_URL}/${roleId}/tags`, tagIds, {
successMessage: '分配标签成功'
});
// 获取所有标签 // 获取所有标签
export const getAllTags = () => export const getAllTags = () => request.get<RoleTagResponse[]>(`${ROLE_TAG_BASE_URL}/list`);
request.get<RoleTagResponse[]>('/api/v1/role-tag/list');
export const deleteRoleTag = (id: number) => request.delete(`${ROLE_TAG_BASE_URL}/${id}`);
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);

View File

@ -1,124 +0,0 @@
import React, { useState } from 'react';
import { Table, Button, Modal, Form, Input, Space, ColorPicker } from 'antd';
import { PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
import type { RoleTagResponse } from './types';
import { useTableData } from '@/hooks/useTableData';
import type { Color } from 'antd/es/color-picker';
const RoleTagPage: React.FC = () => {
const {
list: tags,
loading,
handleCreate,
handleUpdate,
handleDelete
} = useTableData({
service: {
baseUrl: '/api/v1/role-tag'
}
});
const [modalVisible, setModalVisible] = useState(false);
const [editingTag, setEditingTag] = useState<RoleTagResponse | null>(null);
const [form] = Form.useForm();
const columns = [
{
title: '标签名称',
dataIndex: 'name',
key: 'name'
},
{
title: '颜色',
dataIndex: 'color',
key: 'color',
render: (color: string) => (
<div style={{
backgroundColor: color,
width: 20,
height: 20,
borderRadius: 4
}} />
)
},
{
title: '描述',
dataIndex: 'description',
key: 'description'
},
{
title: '操作',
key: 'action',
render: (_: any, record: RoleTagResponse) => (
<Space>
<Button
type="link"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
</Button>
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record.id)}
>
</Button>
</Space>
)
}
];
return (
<div style={{ padding: '24px' }}>
<div style={{ marginBottom: 16 }}>
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
</Button>
</div>
<Table
loading={loading}
columns={columns}
dataSource={tags}
rowKey="id"
/>
<Modal
title={editingTag ? '编辑标签' : '新增标签'}
open={modalVisible}
onOk={handleSubmit}
onCancel={() => setModalVisible(false)}
>
<Form form={form} layout="vertical">
<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>
</Modal>
</div>
);
};
export default RoleTagPage;

View File

@ -9,8 +9,6 @@ export interface Response<T = any> {
} }
export interface RequestOptions extends AxiosRequestConfig { export interface RequestOptions extends AxiosRequestConfig {
successMessage?: string;
errorMessage?: string;
retryCount?: number; retryCount?: number;
retryDelay?: number; retryDelay?: number;
} }
@ -32,6 +30,8 @@ const request = axios.create({
} }
}); });
const defaultErrorMessage = '操作失败';
// 创建请求配置 // 创建请求配置
const createRequestConfig = (options?: Partial<RequestOptions>): RequestOptions => { const createRequestConfig = (options?: Partial<RequestOptions>): RequestOptions => {
return { return {
@ -41,52 +41,51 @@ const createRequestConfig = (options?: Partial<RequestOptions>): RequestOptions
}; };
const responseHandler = (response: AxiosResponse<Response<any>>) => { const responseHandler = (response: AxiosResponse<Response<any>>) => {
const data = response.data; const result = response.data;
const config = response.config as RequestOptions; if (result.success && result.code === 200) {
return result.data;
if (data.success && data.code === 200) {
config.successMessage && message.success(config.successMessage);
return data.data;
} else { } else {
message.error(config.errorMessage || data.message); if (result.message !== undefined) {
return Promise.reject(response); // 隐式返回 Promise message.error(result.message || defaultErrorMessage);
return Promise.reject(response);
}
return Promise.reject(response);
} }
}; };
const statusMessages: Record<number, string> = {
401: '未授权,请重新登录',
403: '拒绝访问',
404: '请求错误,未找到该资源',
500: '服务器错误'
};
const defaultErrorMessage = '服务器异常,请稍后再试!';
const errorHandler = (error: any) => { const errorHandler = (error: any) => {
const config = error.config as RequestOptions;
// 检查是否有响应
if (!error.response) { if (!error.response) {
message.error('网络连接异常,请检查网络'); message.error('网络连接异常,请检查网络');
return Promise.reject(error); return Promise.reject(error);
} }
// 检查是否为取消请求
if (axios.isCancel(error)) { if (axios.isCancel(error)) {
return Promise.reject(error); return Promise.reject(error);
} }
const status = error.response.status; const status = error.response.status;
const msg = statusMessages[status] || defaultErrorMessage; // 获取相应的消息 let errorMessage = '';
switch (status) {
// 显示错误信息 case 401:
message.error(config.errorMessage || msg); errorMessage = '未授权,请重新登录';
break;
return Promise.reject(error); // 拦截错误并返回 case 403:
errorMessage = '拒绝访问';
break;
case 404:
errorMessage = '请求错误,未找到该资源';
break;
case 500:
errorMessage = '服务异常,请稍后再试';
break;
default:
errorMessage = '服务器异常,请稍后再试!';
}
message.error(errorMessage);
return Promise.reject(error);
}; };
request.interceptors.request.use( request.interceptors.request.use(
(config) => { (config) => {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
@ -101,9 +100,8 @@ request.interceptors.request.use(
request.interceptors.response.use(responseHandler, errorHandler); request.interceptors.response.use(responseHandler, errorHandler);
const http = { const http = {
get: <T = any>(url: string, config?: RequestOptions) => { get: <T = any>(url: string, config?: RequestOptions) =>
return request.get<any, T>(url, createRequestConfig(config)); request.get<any, T>(url, createRequestConfig(config)),
},
post: <T = any>(url: string, data?: any, config?: RequestOptions) => post: <T = any>(url: string, data?: any, config?: RequestOptions) =>
request.post<any, Response<T>>(url, data, createRequestConfig(config)), request.post<any, Response<T>>(url, data, createRequestConfig(config)),