generated from kevisual/vite-react-template
update
This commit is contained in:
1
src/pages/auth/hooks/index.ts
Normal file
1
src/pages/auth/hooks/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './use-api-query';
|
||||
55
src/pages/auth/hooks/use-api-query.ts
Normal file
55
src/pages/auth/hooks/use-api-query.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { queryLogin } from '@/modules/query';
|
||||
import { toast } from 'sonner';
|
||||
import type { UserInfo } from '../store';
|
||||
|
||||
export const authQueryKeys = {
|
||||
me: ['auth', 'me'] as const,
|
||||
token: ['auth', 'token'] as const,
|
||||
} as const;
|
||||
|
||||
export const useMe = () => {
|
||||
return useQuery({
|
||||
queryKey: authQueryKeys.me,
|
||||
queryFn: async () => {
|
||||
const res = await queryLogin.getMe();
|
||||
if (res.code === 200) {
|
||||
return res.data;
|
||||
}
|
||||
throw new Error(res.message || 'Failed to fetch user info');
|
||||
},
|
||||
staleTime: 1000 * 60 * 5, // 5 minutes
|
||||
});
|
||||
};
|
||||
|
||||
export const useSwitchOrg = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (username?: string) => {
|
||||
const res = await queryLogin.switchUser(username || '');
|
||||
if (res.code === 200) {
|
||||
return res.data;
|
||||
}
|
||||
throw new Error(res.message || 'Switch failed');
|
||||
},
|
||||
onSuccess: () => {
|
||||
toast.success('切换成功');
|
||||
queryClient.invalidateQueries({ queryKey: authQueryKeys.me });
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message || '请求失败');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetToken = () => {
|
||||
return useQuery({
|
||||
queryKey: authQueryKeys.token,
|
||||
queryFn: () => queryLogin.getToken(),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
};
|
||||
@@ -6,7 +6,6 @@ export { BaseHeader } from './modules/BaseHeader'
|
||||
import { useMemo } from 'react';
|
||||
import { useLocation, useNavigate } from '@tanstack/react-router';
|
||||
|
||||
|
||||
type Props = {
|
||||
children?: React.ReactNode,
|
||||
mustLogin?: boolean,
|
||||
|
||||
@@ -9,6 +9,7 @@ export const BaseHeader = (props: { main?: React.ComponentType | null }) => {
|
||||
me: state.me,
|
||||
clearMe: state.clearMe,
|
||||
links: state.links,
|
||||
showBaseHeader: state.showBaseHeader,
|
||||
})));
|
||||
const navigate = useNavigate();
|
||||
const meInfo = useMemo(() => {
|
||||
@@ -48,6 +49,9 @@ export const BaseHeader = (props: { main?: React.ComponentType | null }) => {
|
||||
</div>
|
||||
)
|
||||
}, [store.me, store.clearMe])
|
||||
if (!store.showBaseHeader) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-2 text-lg w-full h-12 items-center justify-between bg-gray-200">
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
|
||||
import { queryLogin } from '@/modules/query';
|
||||
import { queryLogin, stackQueryClient } from '@/modules/query';
|
||||
import { create } from 'zustand';
|
||||
import { toast } from 'sonner';
|
||||
type UserInfo = {
|
||||
import { authQueryKeys } from './hooks';
|
||||
export type UserInfo = {
|
||||
id?: string;
|
||||
username?: string;
|
||||
nickname?: string | null;
|
||||
@@ -35,6 +36,11 @@ export type LayoutStore = {
|
||||
setLoginPageConfig: (config: Partial<LayoutStore['loginPageConfig']>) => void;
|
||||
links: HeaderLink[];
|
||||
setLinks: (links: HeaderLink[]) => void;
|
||||
showBaseHeader: boolean;
|
||||
setShowBaseHeader: (showBaseHeader: boolean) => void;
|
||||
serverData: Record<string, any> | null;
|
||||
setServerData: (data: Record<string, any>) => void;
|
||||
initConvex: () => Promise<void>;
|
||||
};
|
||||
type HeaderLink = {
|
||||
title?: string;
|
||||
@@ -56,16 +62,23 @@ export const useLayoutStore = create<LayoutStore>((set, get) => ({
|
||||
set({ me: undefined, isAdmin: false });
|
||||
},
|
||||
getMe: async () => {
|
||||
const res = await queryLogin.getMe();
|
||||
if (res.code === 200) {
|
||||
set({ me: res.data });
|
||||
set({ isAdmin: res.data.orgs?.includes?.('admin') || false });
|
||||
}
|
||||
const data = await stackQueryClient.fetchQuery({
|
||||
queryKey: authQueryKeys.me,
|
||||
queryFn: async () => {
|
||||
const res = await queryLogin.getMe();
|
||||
if (res.code === 200) {
|
||||
return res.data;
|
||||
}
|
||||
throw new Error(res.message || 'Failed to fetch user info');
|
||||
},
|
||||
});
|
||||
set({ me: data, isAdmin: data?.orgs?.includes?.('admin') || false });
|
||||
},
|
||||
switchOrg: async (username?: string) => {
|
||||
const res = await queryLogin.switchUser(username || '');
|
||||
if (res.code === 200) {
|
||||
toast.success('切换成功');
|
||||
stackQueryClient.invalidateQueries({ queryKey: authQueryKeys.me });
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
@@ -76,20 +89,32 @@ export const useLayoutStore = create<LayoutStore>((set, get) => ({
|
||||
isAdmin: false,
|
||||
setIsAdmin: (isAdmin) => set({ isAdmin }),
|
||||
init: async () => {
|
||||
const token = await queryLogin.getToken();
|
||||
await queryLogin.init();
|
||||
const token = await queryLogin.checkLocalToken();
|
||||
if (token) {
|
||||
set({ me: {} })
|
||||
const me = await queryLogin.getMe();
|
||||
// const user = await queryLogin.checkLocalUser() as UserInfo;
|
||||
const user = me.code === 200 ? me.data : undefined;
|
||||
if (user) {
|
||||
set({ me: user });
|
||||
set({ isAdmin: user.orgs?.includes?.('admin') || false });
|
||||
} else {
|
||||
set({ me: {} });
|
||||
try {
|
||||
// const data = await stackQueryClient.fetchQuery({
|
||||
// queryKey: authQueryKeys.me,
|
||||
// }) as UserInfo;
|
||||
const userInfo = await queryLogin.checkLocalUser();
|
||||
if (userInfo) {
|
||||
set({ me: userInfo as UserInfo, isAdmin: userInfo.orgs?.includes?.('admin') || false });
|
||||
} else {
|
||||
set({ me: undefined, isAdmin: false });
|
||||
}
|
||||
} catch {
|
||||
set({ me: undefined, isAdmin: false });
|
||||
}
|
||||
}
|
||||
// 获取服务端数据
|
||||
// @ts-ignore
|
||||
const sererData = window.__SERVER_DATA__;
|
||||
if (sererData) {
|
||||
set({ serverData: sererData });
|
||||
}
|
||||
},
|
||||
initConvex: async () => { },
|
||||
openLinkList: ['/login'],
|
||||
setOpenLinkList: (openLinkList) => set({ openLinkList }),
|
||||
loginPageConfig: {
|
||||
@@ -102,4 +127,8 @@ export const useLayoutStore = create<LayoutStore>((set, get) => ({
|
||||
})),
|
||||
links: [{ title: '', href: '/', key: 'home' }],
|
||||
setLinks: (links) => set({ links }),
|
||||
showBaseHeader: true,
|
||||
setShowBaseHeader: (showBaseHeader) => set({ showBaseHeader }),
|
||||
serverData: null,
|
||||
setServerData: (data) => set({ serverData: data }),
|
||||
}));
|
||||
|
||||
8
src/pages/demo/page.tsx
Normal file
8
src/pages/demo/page.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { useDemoStore } from './store/index'
|
||||
export const App = () => {
|
||||
const demoStore = useDemoStore()
|
||||
console.log('demo', demoStore.formData)
|
||||
return <div>App</div>
|
||||
}
|
||||
|
||||
export default App;
|
||||
95
src/pages/demo/store/index.ts
Normal file
95
src/pages/demo/store/index.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { create } from 'zustand';
|
||||
import { query } from '@/modules/query';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
interface Data {
|
||||
id: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
type State = {
|
||||
formData: Record<string, any>;
|
||||
setFormData: (data: Record<string, any>) => void;
|
||||
showEdit: boolean;
|
||||
setShowEdit: (showEdit: boolean) => void;
|
||||
loading: boolean;
|
||||
setLoading: (loading: boolean) => void;
|
||||
list: Data[];
|
||||
getItem: (id: string) => Promise<any>;
|
||||
getList: () => Promise<any>;
|
||||
updateData: (data: Data) => Promise<void>;
|
||||
deleteData: (id: string) => Promise<void>;
|
||||
}
|
||||
|
||||
export const useDemoStore = create<State>((set, get) => {
|
||||
return {
|
||||
formData: {},
|
||||
setFormData: (data) => set({ formData: data }),
|
||||
showEdit: false,
|
||||
setShowEdit: (showEdit) => set({ showEdit }),
|
||||
loading: false,
|
||||
setLoading: (loading) => set({ loading }),
|
||||
list: [],
|
||||
getItem: async (id) => {
|
||||
const { setLoading } = get();
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await query.post({
|
||||
path: 'demo',
|
||||
key: 'item',
|
||||
data: { id }
|
||||
})
|
||||
if (res.code === 200) {
|
||||
return res;
|
||||
} else {
|
||||
toast.error(res.message || '请求失败');
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
getList: async () => {
|
||||
const { setLoading } = get();
|
||||
setLoading(true);
|
||||
try {
|
||||
const res = await query.post({
|
||||
path: 'demo',
|
||||
key: 'list'
|
||||
});
|
||||
if (res.code === 200) {
|
||||
const list = res.data?.list || []
|
||||
set({ list });
|
||||
} else {
|
||||
toast.error(res.message || '请求失败');
|
||||
}
|
||||
return res;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
updateData: async (data) => {
|
||||
const res = await query.post({
|
||||
path: 'demo',
|
||||
key: 'update',
|
||||
data
|
||||
})
|
||||
if (res.code === 200) {
|
||||
get().getList()
|
||||
} else {
|
||||
toast.error(res.message || '请求失败');
|
||||
}
|
||||
},
|
||||
deleteData: async (id) => {
|
||||
const res = await query.post({
|
||||
path: 'demo',
|
||||
key: 'delete',
|
||||
data: { id }
|
||||
})
|
||||
if (res.code === 200) {
|
||||
get().getList()
|
||||
} else {
|
||||
toast.error(res.message || '请求失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,3 +1,5 @@
|
||||
import App from './repos/page'
|
||||
const Home = () => {
|
||||
return <div>Home Page</div>
|
||||
}
|
||||
|
||||
export default App;
|
||||
export default Home;
|
||||
Reference in New Issue
Block a user