diff --git a/src/http/axios/service.ts b/src/http/axios/service.ts
new file mode 100644
index 0000000..7bcbb39
--- /dev/null
+++ b/src/http/axios/service.ts
@@ -0,0 +1,67 @@
+import axios from 'axios';
+
+import { globalError } from '@/utils/antd-extract';
+import { useUserToken } from '@/store/userStore';
+
+// 刷新token的API调用
+// const refreshTokenApi = async () => {
+// // 调用API
+// const res = await service.get('auth/refresh', {
+// params: {
+// refreshToken: localStorage.getItem('refresh_token'),
+// },
+// });
+// if (res) {
+// // 更新token数据
+// FetcherStore.setState((state) => {
+// state.token = res.data.accessToken;
+// });
+// localStorage.setItem('refresh_token', res.data.refreshToken);
+// }
+// return res;
+// };
+// 设置axios的额外配置
+export const service = axios.create({
+ baseURL: import.meta.env.VITE_APP_BASE_API,
+});
+// 拦截请求处理
+service.interceptors.request.use(async (params) => {
+ // 添加token
+ const userToken = useUserToken();
+ if (userToken.accessToken) {
+ params.headers.set('Authorization', `Bearer ${userToken.accessToken}`);
+ }
+ return params;
+});
+// 拦截响应处理
+service.interceptors.response.use(
+ async (response) => {
+ return response;
+ },
+ async (error) => {
+ // if (import.meta.env.DEV) console.log('respError', error);
+ if (!error.response)
+ switch (error.response.status) {
+ case 401: {
+ // 如果响应401就把原本的FetcherStore数据设置为空,好让页面跳至登录页
+ // FetcherStore.setState((state) => {
+ // state.token = null;
+ // });
+ // // 响应401且不是刷新token的请求时,去主动调用刷新token请求
+ // if (!error.response.config.url.includes('auth/refresh')) {
+ // const res = await refreshTokenApi();
+ // if (res.status === 200) {
+ // return axios(error.response.config);
+ // }
+ // message.error('登录过期,请重新登录');
+ // return Promise.reject(res.data);
+ // }
+ break;
+ }
+ default:
+ globalError(error);
+ break;
+ }
+ return Promise.reject(error);
+ },
+);
diff --git a/src/http/tanstack/react-query.ts b/src/http/tanstack/react-query.ts
new file mode 100644
index 0000000..9a6d15c
--- /dev/null
+++ b/src/http/tanstack/react-query.ts
@@ -0,0 +1,16 @@
+import { QueryClient } from '@tanstack/react-query';
+
+// 创建一个 client
+export const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ // 设置HTTP状态码为403的时候不重试,其他情况取默认值
+ retry: (_failureCount, error: any) => error.response?.status !== 403,
+ cacheTime: 300_000, // 缓存有效期 5m
+ staleTime: 10_1000, // 数据变得 "陈旧"(stale)的时间 10s
+ refetchOnWindowFocus: false, // 禁止窗口聚焦时重新获取数据
+ refetchOnReconnect: false, // 禁止重新连接时重新获取数据
+ refetchOnMount: false, // 禁止组件挂载时重新获取数据
+ },
+ },
+});
diff --git a/src/main.tsx b/src/main.tsx
index c214e05..d06d6c3 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,5 +1,5 @@
// react-query
-import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
// react
import { Suspense } from 'react';
@@ -10,6 +10,7 @@ import { HelmetProvider } from 'react-helmet-async';
import 'virtual:svg-icons-register';
import App from '@/App';
+import { queryClient } from '@/http/tanstack/react-query';
// i18n
import './locales/i18n';
@@ -26,31 +27,17 @@ const charAt = `
`;
console.info(`%c${charAt}`, 'color: #5BE49B');
-// 创建一个 client
-const queryClient = new QueryClient({
- defaultOptions: {
- queries: {
- retry: 3, // 失败重试次数
- cacheTime: 300_000, // 缓存有效期 5m
- staleTime: 10_1000, // 数据变得 "陈旧"(stale)的时间 10s
- refetchOnWindowFocus: false, // 禁止窗口聚焦时重新获取数据
- refetchOnReconnect: false, // 禁止重新连接时重新获取数据
- refetchOnMount: false, // 禁止组件挂载时重新获取数据
- },
- },
-});
-
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
-
-
-
-
-
-
-
- ,
+
+
+
+
+
+
+
+ ,
);
// 不再使用 mock 数据,调用 nest 后端获取
diff --git a/src/services/menu.ts b/src/services/menu.ts
new file mode 100644
index 0000000..3b97426
--- /dev/null
+++ b/src/services/menu.ts
@@ -0,0 +1,60 @@
+import { useMutation, useQuery } from '@tanstack/react-query';
+
+import { InputType, OutputType } from '@/pages/setting/menus/list.page';
+import { globalSuccess } from '@/utils/antd-extract';
+import { service } from '@/http/axios/service';
+import { queryClient } from '@/http/tanstack/react-query';
+
+/**
+ * 树结构查询
+ */
+export const useListMenuTree = () => {
+ return useQuery(['listMenuTree'], async () =>
+ service.get('menu/tree').then((res) => res.data),
+ );
+};
+
+/**
+ * 树结构查询:给角色模块提供,用于数据隔离
+ */
+export const useListMenuTreeForRole = () => {
+ return useQuery(['listMenuTreeForRole'], async () =>
+ service.get('menu/tree').then((res) => res.data),
+ );
+};
+
+/**
+ * 更新
+ */
+export const useUpdateMenu = () => {
+ return useMutation(async (params: InputType) => service.patch('/menu', { ...params }), {
+ onSuccess: () => {
+ globalSuccess();
+ queryClient.invalidateQueries(['listMenuTree']);
+ },
+ });
+};
+
+/**
+ * 新建
+ */
+export const useCreateMenu = () => {
+ return useMutation(async (params: InputType) => service.post('/menu', { ...params }), {
+ onSuccess: () => {
+ globalSuccess();
+ queryClient.invalidateQueries(['listMenuTree']);
+ },
+ });
+};
+
+/**
+ * 删除
+ */
+export const useDeleteMenu = () => {
+ return useMutation(async (ids: number[]) => service.delete('/menu', { data: { ids } }), {
+ onSuccess: () => {
+ globalSuccess();
+ queryClient.invalidateQueries(['listMenuTree']);
+ },
+ });
+};
diff --git a/src/utils/antd-extract.ts b/src/utils/antd-extract.ts
new file mode 100644
index 0000000..d3d3525
--- /dev/null
+++ b/src/utils/antd-extract.ts
@@ -0,0 +1,9 @@
+import { message } from 'antd';
+import { AxiosError } from 'axios';
+
+import { Result } from '#/api';
+
+export const globalSuccess = () => message.success('success', 2);
+// export const globalError = (error: AxiosError) => message.error(error.message);
+export const globalError = (error: AxiosError) =>
+ message.error(error.response?.data.message ?? error.message);