任务三
This commit is contained in:
+34
-3
@@ -1,11 +1,42 @@
|
||||
/**
|
||||
* 权限配置 - 参考GVA的权限验证逻辑
|
||||
* @see https://umijs.org/docs/max/access#access
|
||||
* */
|
||||
*/
|
||||
|
||||
export default function access(
|
||||
initialState: { currentUser?: API.CurrentUser } | undefined,
|
||||
initialState: { currentUser?: API.UserInfo } | undefined,
|
||||
) {
|
||||
const { currentUser } = initialState ?? {};
|
||||
|
||||
// 获取当前用户的角色ID
|
||||
const authorityId = currentUser?.authorityId || currentUser?.authority?.authorityId;
|
||||
|
||||
// 超级管理员角色ID(与GVA保持一致,888为超级管理员)
|
||||
const isSuperAdmin = authorityId === 888;
|
||||
|
||||
// 普通管理员
|
||||
const isAdmin = !!currentUser && !!authorityId;
|
||||
|
||||
return {
|
||||
canAdmin: currentUser && currentUser.access === 'admin',
|
||||
// 是否为超级管理员
|
||||
canSuperAdmin: isSuperAdmin,
|
||||
// 是否为管理员(已登录且有角色)
|
||||
canAdmin: isAdmin,
|
||||
// 是否已登录
|
||||
isLogin: !!currentUser,
|
||||
// 按钮权限检查函数 - 参考GVA的btnAuth
|
||||
canAccess: (btnKey: string) => {
|
||||
// 超级管理员拥有所有权限
|
||||
if (isSuperAdmin) return true;
|
||||
// 检查当前路由的按钮权限
|
||||
const btns = (currentUser as any)?.btns || {};
|
||||
return !!btns[btnKey];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 按钮权限Hook - 参考GVA的useBtnAuth
|
||||
* 在组件中使用: const { canAccess } = useAccess();
|
||||
* 然后: canAccess('add') 检查是否有添加权限
|
||||
*/
|
||||
|
||||
+28
-120
@@ -2,7 +2,6 @@ import {
|
||||
AvatarDropdown,
|
||||
AvatarName,
|
||||
Footer,
|
||||
Question,
|
||||
SelectLang,
|
||||
} from "@/components";
|
||||
import { getUserInfo } from "@/services/system/user";
|
||||
@@ -18,38 +17,42 @@ const isDev = process.env.NODE_ENV === "development";
|
||||
const isDevOrTest = isDev || process.env.CI;
|
||||
const loginPath = "/user/login";
|
||||
|
||||
/**
|
||||
* 获取用户信息 - 参考GVA的GetUserInfo
|
||||
*/
|
||||
const fetchUserInfo = async (): Promise<API.UserInfo | undefined> => {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
const res = await getUserInfo();
|
||||
// 与GVA一致,code === 0 表示成功
|
||||
if (res.code === 0 && res.data?.userInfo) {
|
||||
// 同步更新localStorage中的userInfo - 与GVA一致
|
||||
localStorage.setItem('userInfo', JSON.stringify(res.data.userInfo));
|
||||
return res.data.userInfo;
|
||||
}
|
||||
return undefined;
|
||||
} catch (error) {
|
||||
// 错误已在requestErrorConfig中处理,这里不需要额外处理
|
||||
// 401错误会弹出Modal,用户点击确定后跳转登录页
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @see https://umijs.org/docs/api/runtime-config#getinitialstate
|
||||
* */
|
||||
*/
|
||||
export async function getInitialState(): Promise<{
|
||||
settings?: Partial<LayoutSettings>;
|
||||
settings?: Partial<LayoutSettings> & { showWatermark?: boolean };
|
||||
currentUser?: API.UserInfo;
|
||||
loading?: boolean;
|
||||
fetchUserInfo?: () => Promise<API.UserInfo | undefined>;
|
||||
}> {
|
||||
const fetchUserInfo = async () => {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
if (!token) {
|
||||
return undefined;
|
||||
}
|
||||
const res = await getUserInfo();
|
||||
if (res.code === 0 && res.data?.userInfo) {
|
||||
return res.data.userInfo;
|
||||
}
|
||||
return undefined;
|
||||
} catch (error) {
|
||||
history.push(loginPath);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
// 如果不是登录页面,执行
|
||||
// 如果不是登录页面,获取用户信息 - 与GVA的permission.js逻辑一致
|
||||
const { location } = history;
|
||||
if (
|
||||
![loginPath, "/user/register", "/user/register-result"].includes(
|
||||
location.pathname
|
||||
)
|
||||
) {
|
||||
if (![loginPath, "/user/register", "/user/register-result"].includes(location.pathname)) {
|
||||
const currentUser = await fetchUserInfo();
|
||||
return {
|
||||
fetchUserInfo,
|
||||
@@ -62,98 +65,3 @@ export async function getInitialState(): Promise<{
|
||||
settings: defaultSettings as Partial<LayoutSettings>,
|
||||
};
|
||||
}
|
||||
|
||||
// ProLayout 支持的api https://procomponents.ant.design/components/layout
|
||||
export const layout: RunTimeLayoutConfig = ({
|
||||
initialState,
|
||||
setInitialState,
|
||||
}) => {
|
||||
return {
|
||||
actionsRender: () => [
|
||||
<Question key="doc" />,
|
||||
<SelectLang key="SelectLang" />,
|
||||
],
|
||||
avatarProps: {
|
||||
src: initialState?.currentUser?.avatar,
|
||||
title: <AvatarName />,
|
||||
render: (_, avatarChildren) => (
|
||||
<AvatarDropdown>{avatarChildren}</AvatarDropdown>
|
||||
),
|
||||
},
|
||||
waterMarkProps: {
|
||||
content: initialState?.currentUser?.name,
|
||||
},
|
||||
footerRender: () => <Footer />,
|
||||
onPageChange: () => {
|
||||
const { location } = history;
|
||||
// 如果没有登录,重定向到 login
|
||||
if (!initialState?.currentUser && location.pathname !== loginPath) {
|
||||
history.push(loginPath);
|
||||
}
|
||||
},
|
||||
bgLayoutImgList: [
|
||||
{
|
||||
src: "https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr",
|
||||
left: 85,
|
||||
bottom: 100,
|
||||
height: "303px",
|
||||
},
|
||||
{
|
||||
src: "https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr",
|
||||
bottom: -68,
|
||||
right: -45,
|
||||
height: "303px",
|
||||
},
|
||||
{
|
||||
src: "https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: "331px",
|
||||
},
|
||||
],
|
||||
links: isDevOrTest
|
||||
? [
|
||||
<Link key="openapi" to="/umi/plugin/openapi" target="_blank">
|
||||
<LinkOutlined />
|
||||
<span>OpenAPI 文档</span>
|
||||
</Link>,
|
||||
]
|
||||
: [],
|
||||
menuHeaderRender: undefined,
|
||||
// 自定义 403 页面
|
||||
// unAccessible: <div>unAccessible</div>,
|
||||
// 增加一个 loading 的状态
|
||||
childrenRender: (children) => {
|
||||
// if (initialState?.loading) return <PageLoading />;
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
{isDevOrTest && (
|
||||
<SettingDrawer
|
||||
disableUrlParams
|
||||
enableDarkTheme
|
||||
settings={initialState?.settings}
|
||||
onSettingChange={(settings) => {
|
||||
setInitialState((preInitialState) => ({
|
||||
...preInitialState,
|
||||
settings,
|
||||
}));
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
},
|
||||
...initialState?.settings,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @name request 配置,可以配置错误处理
|
||||
* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
|
||||
* @doc https://umijs.org/docs/max/request#配置
|
||||
*/
|
||||
export const request: RequestConfig = {
|
||||
baseURL: isDev ? "" : "https://proapi.azurewebsites.net",
|
||||
...errorConfig,
|
||||
};
|
||||
|
||||
@@ -3,29 +3,31 @@ import { DefaultFooter } from '@ant-design/pro-components';
|
||||
import React from 'react';
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<DefaultFooter
|
||||
style={{
|
||||
background: 'none',
|
||||
}}
|
||||
copyright="Powered by Ant Desgin"
|
||||
copyright={`${currentYear} KRA Admin - Kratos React Admin`}
|
||||
links={[
|
||||
{
|
||||
key: 'Ant Design Pro',
|
||||
title: 'Ant Design Pro',
|
||||
href: 'https://pro.ant.design',
|
||||
key: 'KRA Admin',
|
||||
title: 'KRA Admin',
|
||||
href: 'https://github.com/your-org/kra',
|
||||
blankTarget: true,
|
||||
},
|
||||
{
|
||||
key: 'github',
|
||||
title: <GithubOutlined />,
|
||||
href: 'https://github.com/ant-design/ant-design-pro',
|
||||
href: 'https://github.com/your-org/kra',
|
||||
blankTarget: true,
|
||||
},
|
||||
{
|
||||
key: 'Ant Design',
|
||||
title: 'Ant Design',
|
||||
href: 'https://ant.design',
|
||||
key: 'Kratos',
|
||||
title: 'Powered by Kratos',
|
||||
href: 'https://go-kratos.dev',
|
||||
blankTarget: true,
|
||||
},
|
||||
]}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import {
|
||||
LogoutOutlined,
|
||||
SettingOutlined,
|
||||
SwapOutlined,
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { history, useModel } from "@umijs/max";
|
||||
import type { MenuProps } from "antd";
|
||||
import { Spin } from "antd";
|
||||
import { message, Spin } from "antd";
|
||||
import { createStyles } from "antd-style";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
import { flushSync } from "react-dom";
|
||||
import HeaderDropdown from "../HeaderDropdown";
|
||||
import { setUserAuthority } from "@/services/system/user";
|
||||
|
||||
export type GlobalHeaderRightProps = {
|
||||
menu?: boolean;
|
||||
@@ -41,14 +42,13 @@ const useStyles = createStyles(({ token }) => {
|
||||
});
|
||||
|
||||
export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
||||
menu,
|
||||
children,
|
||||
}) => {
|
||||
/**
|
||||
* 退出登录,并且将当前的 url 保存
|
||||
*/
|
||||
const { styles } = useStyles();
|
||||
const { initialState, setInitialState } = useModel("@@initialState");
|
||||
|
||||
// 退出登录
|
||||
const loginOut = async () => {
|
||||
// 清除本地存储的token和用户信息
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('userInfo');
|
||||
const { search, pathname } = window.location;
|
||||
@@ -56,9 +56,7 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
||||
const searchParams = new URLSearchParams({
|
||||
redirect: pathname + search,
|
||||
});
|
||||
/** 此方法会跳转到 redirect 参数所在的位置 */
|
||||
const redirect = urlParams.get("redirect");
|
||||
// Note: There may be security issues, please note
|
||||
if (window.location.pathname !== "/user/login" && !redirect) {
|
||||
history.replace({
|
||||
pathname: "/user/login",
|
||||
@@ -66,9 +64,21 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
||||
});
|
||||
}
|
||||
};
|
||||
const { styles } = useStyles();
|
||||
|
||||
const { initialState, setInitialState } = useModel("@@initialState");
|
||||
// 切换角色
|
||||
const changeUserAuth = async (authorityId: number) => {
|
||||
try {
|
||||
const res = await setUserAuthority({ authorityId });
|
||||
if (res.code === 0) {
|
||||
message.success('角色切换成功');
|
||||
window.sessionStorage.setItem('needCloseAll', 'true');
|
||||
window.sessionStorage.setItem('needToHome', 'true');
|
||||
window.location.reload();
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('角色切换失败');
|
||||
}
|
||||
};
|
||||
|
||||
const onMenuClick: MenuProps["onClick"] = (event) => {
|
||||
const { key } = event;
|
||||
@@ -83,18 +93,17 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
||||
history.push('/person');
|
||||
return;
|
||||
}
|
||||
history.push(`/account/${key}`);
|
||||
// 处理角色切换
|
||||
if (key.startsWith('authority_')) {
|
||||
const authorityId = parseInt(key.replace('authority_', ''), 10);
|
||||
changeUserAuth(authorityId);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const loading = (
|
||||
<span className={styles.action}>
|
||||
<Spin
|
||||
size="small"
|
||||
style={{
|
||||
marginLeft: 8,
|
||||
marginRight: 8,
|
||||
}}
|
||||
/>
|
||||
<Spin size="small" style={{ marginLeft: 8, marginRight: 8 }} />
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -108,30 +117,57 @@ export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({
|
||||
return loading;
|
||||
}
|
||||
|
||||
const menuItems = [
|
||||
...(menu
|
||||
? [
|
||||
{
|
||||
key: "center",
|
||||
icon: <UserOutlined />,
|
||||
label: "个人中心",
|
||||
},
|
||||
{
|
||||
key: "settings",
|
||||
icon: <SettingOutlined />,
|
||||
label: "个人设置",
|
||||
},
|
||||
{
|
||||
type: "divider" as const,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: "logout",
|
||||
// 构建菜单项 - 参考GVA的角色切换功能
|
||||
const menuItems = useMemo(() => {
|
||||
const items: MenuProps['items'] = [];
|
||||
|
||||
// 当前角色显示
|
||||
if (currentUser.authority?.authorityName) {
|
||||
items.push({
|
||||
key: 'currentRole',
|
||||
label: <span style={{ fontWeight: 'bold' }}>当前角色:{currentUser.authority.authorityName}</span>,
|
||||
disabled: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 可切换的其他角色
|
||||
if (currentUser.authorities && currentUser.authorities.length > 1) {
|
||||
const otherAuthorities = currentUser.authorities.filter(
|
||||
(auth: any) => auth.authorityId !== currentUser.authorityId
|
||||
);
|
||||
|
||||
if (otherAuthorities.length > 0) {
|
||||
items.push({ type: 'divider' });
|
||||
otherAuthorities.forEach((auth: any) => {
|
||||
items.push({
|
||||
key: `authority_${auth.authorityId}`,
|
||||
icon: <SwapOutlined />,
|
||||
label: `切换为:${auth.authorityName}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
items.push({ type: 'divider' });
|
||||
|
||||
// 个人信息
|
||||
items.push({
|
||||
key: 'center',
|
||||
icon: <UserOutlined />,
|
||||
label: '个人信息',
|
||||
});
|
||||
|
||||
items.push({ type: 'divider' });
|
||||
|
||||
// 退出登录
|
||||
items.push({
|
||||
key: 'logout',
|
||||
icon: <LogoutOutlined />,
|
||||
label: "退出登录",
|
||||
},
|
||||
];
|
||||
label: '退出登录',
|
||||
});
|
||||
|
||||
return items;
|
||||
}, [currentUser]);
|
||||
|
||||
return (
|
||||
<HeaderDropdown
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
* 布局组件
|
||||
*/
|
||||
import Footer from './Footer';
|
||||
import Logo from './Logo';
|
||||
import { Question, SelectLang } from './RightContent';
|
||||
import { AvatarDropdown, AvatarName } from './RightContent/AvatarDropdown';
|
||||
|
||||
export { AvatarDropdown, AvatarName, Footer, Question, SelectLang };
|
||||
export { AvatarDropdown, AvatarName, Footer, Logo, Question, SelectLang };
|
||||
|
||||
+136
-43
@@ -1,72 +1,165 @@
|
||||
import type { RequestOptions } from "@@/plugin-request/request";
|
||||
/**
|
||||
* 请求配置 - 完全参考GVA的request.js实现
|
||||
* 包含请求拦截器、响应拦截器、错误处理
|
||||
*/
|
||||
import type { RequestOptions } from "@@/plugin-request/request";
|
||||
import type { RequestConfig } from "@umijs/max";
|
||||
import { message as toast } from "antd";
|
||||
import { message, Modal } from "antd";
|
||||
import { history } from "@umijs/max";
|
||||
|
||||
// Define the structure of the expected response.
|
||||
interface ResponseStructure {
|
||||
code: number;
|
||||
reason?: string;
|
||||
message?: string;
|
||||
msg?: string;
|
||||
metadata?: Map<string, number>;
|
||||
}
|
||||
|
||||
const loginPath = "/user/login";
|
||||
|
||||
// Request configuration with error handling and interceptors.
|
||||
// 获取错误消息 - 与GVA的getErrorMessage一致
|
||||
function getErrorMessage(error: any): string {
|
||||
return error.response?.data?.msg || error.response?.statusText || '请求失败';
|
||||
}
|
||||
|
||||
// 清除存储并跳转登录 - 与GVA的ClearStorage一致
|
||||
function clearStorageAndRedirect() {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('userInfo');
|
||||
localStorage.removeItem('originSetting');
|
||||
sessionStorage.clear();
|
||||
history.push({ pathname: loginPath });
|
||||
}
|
||||
|
||||
// 预设错误信息 - 与GVA的presetErrors一致
|
||||
const presetErrors: Record<string | number, { title: string; tips: string }> = {
|
||||
500: {
|
||||
title: '服务器发生内部错误',
|
||||
tips: '此类错误内容常见于后台panic,请先查看后台日志',
|
||||
},
|
||||
404: {
|
||||
title: '资源未找到',
|
||||
tips: '此类错误多为接口未注册或请求路径与api路径不符',
|
||||
},
|
||||
401: {
|
||||
title: '身份认证失败',
|
||||
tips: '您的身份认证已过期或无效,请重新登录',
|
||||
},
|
||||
network: {
|
||||
title: '网络错误',
|
||||
tips: '无法连接到服务器,请检查您的网络连接',
|
||||
},
|
||||
};
|
||||
|
||||
// 显示错误弹窗 - 与GVA的ErrorPreview组件逻辑一致
|
||||
function showErrorModal(code: string | number, errorMessage: string, onConfirm?: () => void) {
|
||||
const preset = presetErrors[code] || { title: '请求错误', tips: '请检查控制台获取更多信息' };
|
||||
|
||||
Modal.error({
|
||||
title: preset.title,
|
||||
content: (
|
||||
<div>
|
||||
<p style={{ marginBottom: 8 }}>{errorMessage}</p>
|
||||
<p style={{ color: '#666', fontSize: 12 }}>{preset.tips}</p>
|
||||
</div>
|
||||
),
|
||||
okText: '确定',
|
||||
onOk: () => {
|
||||
onConfirm?.();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 请求配置
|
||||
export const errorConfig: RequestConfig = {
|
||||
timeout: 99999, // 超时时间 - 与GVA保持一致
|
||||
|
||||
errorConfig: {
|
||||
// 错误抛出器 - 与GVA一致,code !== 0 时抛出错误
|
||||
errorThrower: (res) => {
|
||||
const { code, reason, message, msg } = res as unknown as ResponseStructure;
|
||||
if (code !== 0 && code !== 200) {
|
||||
const error: any = new Error(message || msg);
|
||||
error.info = { reason, message: message || msg };
|
||||
const { code, msg, message: errorMsg } = res as any;
|
||||
if (code !== 0) {
|
||||
const error: any = new Error(msg || errorMsg || '请求失败');
|
||||
error.info = { code, message: msg || errorMsg };
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
// 错误处理器 - 与GVA的response error拦截器一致
|
||||
errorHandler: (error: any, opts: any) => {
|
||||
if (opts?.skipErrorHandler) throw error;
|
||||
if (error.response?.status === 401) {
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('userInfo');
|
||||
history.push(loginPath);
|
||||
|
||||
// 网络错误 - 与GVA一致
|
||||
if (!error.response) {
|
||||
showErrorModal('network', getErrorMessage(error));
|
||||
return;
|
||||
}
|
||||
if (error.response?.data) {
|
||||
const errorInfo: ResponseStructure | undefined = error.response?.data;
|
||||
if (errorInfo) {
|
||||
const { message, msg, reason } = errorInfo;
|
||||
toast.error((reason ? reason + ": " : "") + (message || msg || "请求失败"));
|
||||
}
|
||||
} else if (error.response) {
|
||||
toast.error(`Response status: ${error.response.status}`);
|
||||
} else if (error.request) {
|
||||
toast.error("None response! Please retry.");
|
||||
} else {
|
||||
toast.error("Request error, please retry.");
|
||||
|
||||
const status = error.response?.status;
|
||||
const errorMsg = getErrorMessage(error);
|
||||
|
||||
// 401 身份认证失败 - 与GVA一致,点击确定后清除存储并跳转
|
||||
if (status === 401) {
|
||||
showErrorModal(401, errorMsg, () => {
|
||||
clearStorageAndRedirect();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 其他HTTP错误 - 与GVA一致
|
||||
showErrorModal(status, errorMsg);
|
||||
},
|
||||
},
|
||||
|
||||
// 请求拦截器 - 与GVA的request拦截器一致
|
||||
requestInterceptors: [
|
||||
(config: RequestOptions) => {
|
||||
// 添加token到请求头
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers = {
|
||||
...config.headers,
|
||||
'x-token': token,
|
||||
};
|
||||
const token = localStorage.getItem('token') || '';
|
||||
let userId = '';
|
||||
|
||||
try {
|
||||
const userInfo = localStorage.getItem('userInfo');
|
||||
if (userInfo) {
|
||||
const user = JSON.parse(userInfo);
|
||||
userId = String(user.ID || user.id || '');
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore parse error
|
||||
}
|
||||
|
||||
// 设置请求头 - 与GVA完全一致
|
||||
config.headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'x-token': token,
|
||||
'x-user-id': userId,
|
||||
...config.headers,
|
||||
};
|
||||
|
||||
return config;
|
||||
},
|
||||
],
|
||||
|
||||
// 响应拦截器 - 与GVA的response拦截器一致
|
||||
responseInterceptors: [
|
||||
(response) => {
|
||||
if (response.status !== 200) {
|
||||
toast.error(`Request error: ${response.status}`);
|
||||
(response: any) => {
|
||||
// 处理新token - 与GVA一致
|
||||
const newToken = response.headers?.['new-token'];
|
||||
if (newToken) {
|
||||
localStorage.setItem('token', newToken);
|
||||
}
|
||||
return response;
|
||||
|
||||
const { data } = response;
|
||||
|
||||
// 如果没有code字段,直接返回 - 与GVA一致
|
||||
if (typeof data?.code === 'undefined') {
|
||||
return response;
|
||||
}
|
||||
|
||||
// code === 0 或 headers.success === 'true' 表示成功 - 与GVA完全一致
|
||||
if (data.code === 0 || response.headers?.success === 'true') {
|
||||
// 处理header中的msg - 与GVA一致
|
||||
if (response.headers?.msg) {
|
||||
data.msg = decodeURI(response.headers.msg);
|
||||
}
|
||||
// 返回 response.data 而不是整个 response - 与GVA一致
|
||||
return { ...response, data };
|
||||
}
|
||||
|
||||
// 业务错误,显示错误消息 - 与GVA一致
|
||||
message.error(data.msg || decodeURI(response.headers?.msg || '') || '请求失败');
|
||||
return { ...response, data };
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ export async function getUserList(data: API.PageParams) {
|
||||
}
|
||||
|
||||
/** 设置用户权限 POST /user/setUserAuthority */
|
||||
export async function setUserAuthority(data: { uuid: string; authorityId: number }) {
|
||||
export async function setUserAuthority(data: { authorityId: number }) {
|
||||
return request<API.BaseResult>('/api/user/setUserAuthority', {
|
||||
method: 'POST',
|
||||
data,
|
||||
|
||||
Reference in New Issue
Block a user