菜单可正确被加载
This commit is contained in:
parent
c0027f4c13
commit
c9c591c97c
@ -38,10 +38,11 @@ src/
|
|||||||
├── utils/ # 工具函数
|
├── utils/ # 工具函数
|
||||||
├── hooks/ # 自定义 Hooks
|
├── hooks/ # 自定义 Hooks
|
||||||
└── types/ # TS 类型定义
|
└── types/ # TS 类型定义
|
||||||
|
│ └── pages.ts # 分页基础类
|
||||||
|
|
||||||
ModuleName/ # 模块结构
|
ModuleName/ # 模块结构
|
||||||
├── components/ # 模块私有组件
|
├── components/ # 模块私有组件
|
||||||
├── types/ # 类型定义
|
├── type.ts # 类型定义
|
||||||
├── service.ts # API 服务
|
├── service.ts # API 服务
|
||||||
└── index.tsx # 模块入口
|
└── index.tsx # 模块入口
|
||||||
```
|
```
|
||||||
@ -64,7 +65,7 @@ ModuleName/ # 模块结构
|
|||||||
- 事件处理:`handle` 前缀
|
- 事件处理:`handle` 前缀
|
||||||
- 异步函数:动词开头(`fetchData`)
|
- 异步函数:动词开头(`fetchData`)
|
||||||
|
|
||||||
3. 基础类型:
|
3. 已定义了基础类型,所以直接继承即可,把Response query request 的定义都模块的types.ts文件中:
|
||||||
```typescript
|
```typescript
|
||||||
interface BaseResponse {
|
interface BaseResponse {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
@ -1,127 +1,153 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Modal, Table, message, Spin, Tag } from 'antd';
|
import { Modal, Tree, Spin } from 'antd';
|
||||||
import type { Permission } from '../types';
|
import type { DataNode } from 'antd/es/tree';
|
||||||
import { PermissionType, PermissionTypeText } from '../types';
|
import type { Key } from 'rc-tree/lib/interface';
|
||||||
import { getRolePermissions, assignPermissions, getAllPermissions } from '../service';
|
import { getPermissionTree } from '../service';
|
||||||
|
|
||||||
interface PermissionModalProps {
|
interface PermissionModalProps {
|
||||||
roleId: number;
|
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
roleId: number;
|
||||||
onCancel: () => void;
|
onCancel: () => void;
|
||||||
onSuccess: () => void;
|
onOk: (permissionIds: number[]) => void;
|
||||||
|
defaultCheckedKeys?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Permission {
|
||||||
|
id: number;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
sort: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MenuItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
icon: string;
|
||||||
|
type: number;
|
||||||
|
permissionChildren: MenuItem[];
|
||||||
|
permissions: Permission[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const PermissionModal: React.FC<PermissionModalProps> = ({
|
const PermissionModal: React.FC<PermissionModalProps> = ({
|
||||||
roleId,
|
|
||||||
visible,
|
visible,
|
||||||
|
roleId,
|
||||||
onCancel,
|
onCancel,
|
||||||
onSuccess
|
onOk,
|
||||||
|
defaultCheckedKeys = []
|
||||||
}) => {
|
}) => {
|
||||||
const [permissions, setPermissions] = useState<Permission[]>([]);
|
|
||||||
const [selectedKeys, setSelectedKeys] = useState<number[]>([]);
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [treeData, setTreeData] = useState<DataNode[]>([]);
|
||||||
|
const [checkedKeys, setCheckedKeys] = useState<number[]>(defaultCheckedKeys);
|
||||||
|
|
||||||
// 获取角色已有权限和所有权限
|
// 将菜单和权限数据转换为Tree组件需要的格式
|
||||||
const fetchData = async () => {
|
const convertToTreeData = (menuList: MenuItem[]): { treeData: DataNode[], idMapping: Record<string, number> } => {
|
||||||
try {
|
const idMapping: Record<string, number> = {};
|
||||||
setLoading(true);
|
|
||||||
const [allPermissions, rolePermissions] = await Promise.all([
|
const convertMenuToNode = (menu: MenuItem): DataNode => {
|
||||||
getAllPermissions(),
|
const node: DataNode = {
|
||||||
getRolePermissions(roleId)
|
key: `menu-${menu.id}`,
|
||||||
]);
|
title: menu.name,
|
||||||
setPermissions(allPermissions);
|
children: []
|
||||||
setSelectedKeys(rolePermissions.map(p => p.id));
|
};
|
||||||
} catch (error) {
|
|
||||||
message.error('获取权限数据失败');
|
// 添加功能权限节点
|
||||||
} finally {
|
if (menu.permissions?.length > 0) {
|
||||||
setLoading(false);
|
const permissionNodes = menu.permissions.map(perm => {
|
||||||
}
|
const key = `permission-${perm.id}`;
|
||||||
|
idMapping[key] = perm.id;
|
||||||
|
return {
|
||||||
|
key,
|
||||||
|
title: perm.name,
|
||||||
|
isLeaf: true
|
||||||
|
};
|
||||||
|
});
|
||||||
|
node.children!.push(...permissionNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归处理子菜单
|
||||||
|
if (menu.permissionChildren?.length > 0) {
|
||||||
|
const childNodes = menu.permissionChildren.map(convertMenuToNode);
|
||||||
|
node.children!.push(...childNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
};
|
||||||
|
|
||||||
|
const treeData = menuList.map(convertMenuToNode);
|
||||||
|
return { treeData, idMapping };
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
const loadPermissionTree = async () => {
|
||||||
fetchData();
|
if (!visible) return;
|
||||||
}
|
|
||||||
}, [visible, roleId]);
|
|
||||||
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
await assignPermissions(roleId, selectedKeys);
|
try {
|
||||||
message.success('权限更新成功');
|
const response = await getPermissionTree();
|
||||||
onSuccess();
|
const { treeData, idMapping } = convertToTreeData(response);
|
||||||
} catch (error) {
|
setTreeData(treeData);
|
||||||
message.error('权限更新失败');
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPermissionTypeTag = (type: PermissionType) => {
|
// 将默认选中的ID转换为Tree需要的key
|
||||||
const colorMap = {
|
const defaultKeys = defaultCheckedKeys.map(id => {
|
||||||
[PermissionType.MENU]: 'blue',
|
const key = Object.entries(idMapping).find(([_, value]) => value === id)?.[0];
|
||||||
[PermissionType.BUTTON]: 'green',
|
return key;
|
||||||
[PermissionType.API]: 'orange'
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
setCheckedKeys(defaultCheckedKeys);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取权限树失败:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
|
||||||
<Tag color={colorMap[type]}>
|
loadPermissionTree();
|
||||||
{PermissionTypeText[type]}
|
}, [visible, defaultCheckedKeys]);
|
||||||
</Tag>
|
|
||||||
);
|
const handleCheck = (checked: Key[] | { checked: Key[]; halfChecked: Key[] }) => {
|
||||||
|
const checkedKeys = Array.isArray(checked) ? checked : checked.checked;
|
||||||
|
const permissionIds: number[] = [];
|
||||||
|
|
||||||
|
checkedKeys.forEach(key => {
|
||||||
|
const keyStr = key.toString();
|
||||||
|
if (keyStr.startsWith('permission-')) {
|
||||||
|
const id = parseInt(keyStr.replace('permission-', ''));
|
||||||
|
if (!isNaN(id)) {
|
||||||
|
permissionIds.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setCheckedKeys(permissionIds);
|
||||||
};
|
};
|
||||||
|
|
||||||
const columns = [
|
const handleOk = () => {
|
||||||
{
|
onOk(checkedKeys);
|
||||||
title: '权限编码',
|
};
|
||||||
dataIndex: 'code',
|
|
||||||
width: 150
|
// 将权限ID转换为Tree需要的key
|
||||||
},
|
const getTreeCheckedKeys = (): string[] => {
|
||||||
{
|
return checkedKeys.map(id => `permission-${id}`);
|
||||||
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 (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title="分配权限"
|
title="分配权限"
|
||||||
open={visible}
|
open={visible}
|
||||||
onOk={handleOk}
|
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
width={800}
|
onOk={handleOk}
|
||||||
confirmLoading={loading}
|
width={600}
|
||||||
destroyOnClose
|
bodyStyle={{ maxHeight: '60vh', overflow: 'auto' }}
|
||||||
>
|
>
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<Table
|
<Tree
|
||||||
rowSelection={{
|
checkable
|
||||||
type: 'checkbox',
|
checkedKeys={getTreeCheckedKeys()}
|
||||||
selectedRowKeys: selectedKeys,
|
onCheck={handleCheck}
|
||||||
onChange: (keys) => setSelectedKeys(keys as number[])
|
treeData={treeData}
|
||||||
}}
|
defaultExpandAll
|
||||||
columns={columns}
|
|
||||||
dataSource={permissions}
|
|
||||||
rowKey="id"
|
|
||||||
pagination={false}
|
|
||||||
scroll={{ y: 400 }}
|
|
||||||
size="small"
|
|
||||||
/>
|
/>
|
||||||
</Spin>
|
</Spin>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Card, Button, Table, Space, Modal, Form, Input, InputNumber, Switch, message, Tag } from 'antd';
|
import { Card, Button, Table, Space, Modal, Form, Input, InputNumber, message, Tag } from 'antd';
|
||||||
import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TagsOutlined } from '@ant-design/icons';
|
import { PlusOutlined, EditOutlined, DeleteOutlined, KeyOutlined, TagsOutlined } from '@ant-design/icons';
|
||||||
import type { RoleResponse, RoleTagResponse, RoleQuery } from './types';
|
import type { RoleResponse, RoleTagResponse, RoleQuery } from './types';
|
||||||
import { getRoleList, createRole, updateRole, deleteRole } from './service';
|
import { getRoleList, createRole, updateRole, deleteRole, getRolePermissions, assignPermissions } from './service';
|
||||||
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 PermissionModal from './components/PermissionModal';
|
||||||
import TagModal from './components/TagModal';
|
import TagModal from './components/TagModal';
|
||||||
|
|
||||||
@ -15,9 +15,10 @@ const RolePage: React.FC = () => {
|
|||||||
const [confirmLoading, setConfirmLoading] = useState(false);
|
const [confirmLoading, setConfirmLoading] = useState(false);
|
||||||
const [permissionModalVisible, setPermissionModalVisible] = 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>();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [roles, setRoles] = useState<RoleResponse[]>([]);
|
const [roles, setRoles] = useState<RoleResponse[]>([]);
|
||||||
|
const [defaultCheckedKeys, setDefaultCheckedKeys] = useState<number[]>([]);
|
||||||
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
const [pagination, setPagination] = useState<TablePaginationConfig>({
|
||||||
current: 1,
|
current: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@ -111,9 +112,54 @@ const RolePage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAssignPermissions = (record: RoleResponse) => {
|
// 处理表格变化
|
||||||
setSelectedRole(record);
|
const handleTableChange = (
|
||||||
setPermissionModalVisible(true);
|
newPagination: TablePaginationConfig,
|
||||||
|
filters: Record<string, FilterValue | null>,
|
||||||
|
sorter: SorterResult<RoleResponse> | SorterResult<RoleResponse>[]
|
||||||
|
) => {
|
||||||
|
const { field, order } = Array.isArray(sorter) ? sorter[0] : sorter;
|
||||||
|
setSortField(field as string);
|
||||||
|
setSortOrder(order as 'ascend' | 'descend');
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
current: newPagination.current,
|
||||||
|
pageSize: newPagination.pageSize,
|
||||||
|
sortField: field as string,
|
||||||
|
sortOrder: order === 'ascend' ? 'asc' : order === 'descend' ? 'desc' : undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchRoles({
|
||||||
|
pageNum: params.current,
|
||||||
|
pageSize: params.pageSize,
|
||||||
|
sortField: params.sortField,
|
||||||
|
sortOrder: params.sortOrder
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理分配权限
|
||||||
|
const handleAssignPermissions = async (record: RoleResponse) => {
|
||||||
|
try {
|
||||||
|
const permissions = await getRolePermissions(record.id);
|
||||||
|
setSelectedRole(record);
|
||||||
|
setPermissionModalVisible(true);
|
||||||
|
setDefaultCheckedKeys(permissions);
|
||||||
|
} catch (error) {
|
||||||
|
message.error('获取角色权限失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理权限分配确认
|
||||||
|
const handlePermissionAssign = async (permissionIds: number[]) => {
|
||||||
|
if (!selectedRole) return;
|
||||||
|
try {
|
||||||
|
await assignPermissions(selectedRole.id, permissionIds);
|
||||||
|
message.success('权限分配成功');
|
||||||
|
setPermissionModalVisible(false);
|
||||||
|
fetchRoles();
|
||||||
|
} catch (error) {
|
||||||
|
message.error('权限分配失败');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAssignTags = (record: RoleResponse) => {
|
const handleAssignTags = (record: RoleResponse) => {
|
||||||
@ -121,22 +167,6 @@ const RolePage: React.FC = () => {
|
|||||||
setTagModalVisible(true);
|
setTagModalVisible(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: '角色编码',
|
title: '角色编码',
|
||||||
@ -285,13 +315,11 @@ const RolePage: React.FC = () => {
|
|||||||
{selectedRole && (
|
{selectedRole && (
|
||||||
<>
|
<>
|
||||||
<PermissionModal
|
<PermissionModal
|
||||||
roleId={selectedRole.id}
|
|
||||||
visible={permissionModalVisible}
|
visible={permissionModalVisible}
|
||||||
|
roleId={selectedRole.id}
|
||||||
onCancel={() => setPermissionModalVisible(false)}
|
onCancel={() => setPermissionModalVisible(false)}
|
||||||
onSuccess={() => {
|
onOk={handlePermissionAssign}
|
||||||
setPermissionModalVisible(false);
|
defaultCheckedKeys={defaultCheckedKeys}
|
||||||
fetchRoles();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<TagModal
|
<TagModal
|
||||||
roleId={selectedRole.id}
|
roleId={selectedRole.id}
|
||||||
|
|||||||
@ -31,10 +31,13 @@ export const updateRoleTag = (id: number, data: RoleTagRequest) => request.put(`
|
|||||||
export const createRoleTag = (data: RoleTagRequest) => request.post<RoleTagResponse>(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 getRolePermissions = (roleId: number) => request.get<string[]>(`${BASE_URL}/${roleId}/permissions`);
|
||||||
|
|
||||||
// 分配权限
|
// 分配权限
|
||||||
export const assignPermissions = (roleId: number, permissionIds: number[]) => request.post(`${BASE_URL}/${roleId}/permissions`, permissionIds);
|
export const assignPermissions = (roleId: number, permissionIds: Number[]) => request.post(`${BASE_URL}/${roleId}/permissions`, permissionIds);
|
||||||
|
|
||||||
// 获取所有权限列表
|
// 获取所有权限列表
|
||||||
export const getAllPermissions = () => request.get<Permission[]>('/api/v1/permission/list');
|
export const getAllPermissions = () => request.get<Permission[]>('/api/v1/permission/list');
|
||||||
|
|
||||||
|
// 获取权限树
|
||||||
|
export const getPermissionTree = () => request.get('/api/v1/menu/permission-tree');
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import type { BaseResponse } from '@/types/base/response';
|
import type { BaseResponse } from '@/types/base/response';
|
||||||
|
import type { BaseQuery } from '@/types/base/query';
|
||||||
|
|
||||||
// 权限类型枚举
|
// 权限类型枚举
|
||||||
export enum PermissionType {
|
export enum PermissionType {
|
||||||
@ -15,14 +16,14 @@ export const PermissionTypeText = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 角色查询参数
|
// 角色查询参数
|
||||||
export interface RoleQuery {
|
export interface RoleQuery extends BaseQuery {
|
||||||
|
code?: string;
|
||||||
|
name?: string;
|
||||||
|
enabled?: boolean;
|
||||||
pageNum?: number;
|
pageNum?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
sortField?: string;
|
sortField?: string;
|
||||||
sortOrder?: 'asc' | 'desc';
|
sortOrder?: 'asc' | 'desc';
|
||||||
code?: string;
|
|
||||||
name?: string;
|
|
||||||
enabled?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色请求参数
|
// 角色请求参数
|
||||||
@ -31,6 +32,7 @@ export interface RoleRequest {
|
|||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
sort?: number;
|
sort?: number;
|
||||||
|
enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色响应数据
|
// 角色响应数据
|
||||||
@ -46,16 +48,16 @@ export interface RoleResponse extends BaseResponse {
|
|||||||
|
|
||||||
// 角色标签请求参数
|
// 角色标签请求参数
|
||||||
export interface RoleTagRequest {
|
export interface RoleTagRequest {
|
||||||
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
color?: string;
|
color: string;
|
||||||
description?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色标签响应数据
|
// 角色标签响应数据
|
||||||
export interface RoleTagResponse extends BaseResponse {
|
export interface RoleTagResponse extends BaseResponse {
|
||||||
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
color: string;
|
color: string;
|
||||||
description?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 权限数据
|
// 权限数据
|
||||||
@ -63,10 +65,6 @@ export interface Permission {
|
|||||||
id: number;
|
id: number;
|
||||||
code: string;
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
type: PermissionType;
|
type: string;
|
||||||
menuId: number;
|
|
||||||
menuName?: string;
|
|
||||||
enabled: boolean;
|
|
||||||
sort: number;
|
sort: number;
|
||||||
description?: string;
|
|
||||||
}
|
}
|
||||||
@ -35,9 +35,7 @@ const userSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setMenus: (state, action: PayloadAction<MenuResponse[]>) => {
|
setMenus: (state, action: PayloadAction<MenuResponse[]>) => {
|
||||||
state.menus = action.payload;
|
state.menus = action.payload;
|
||||||
console.log(action.payload)
|
localStorage.setItem('menus', JSON.stringify(action.payload));
|
||||||
// console.log(JSON.stringify(action.payload))
|
|
||||||
// localStorage.setItem('menus', action.payload);
|
|
||||||
},
|
},
|
||||||
logout: (state) => {
|
logout: (state) => {
|
||||||
state.token = null;
|
state.token = null;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user