You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
139 lines
4.4 KiB
139 lines
4.4 KiB
import { useMutation } from "@tanstack/react-query";
|
|
import { useNavigate } from "react-router";
|
|
import { create } from "zustand";
|
|
import { createJSONStorage, persist } from "zustand/middleware";
|
|
|
|
import { toast } from "sonner";
|
|
import type { UserInfo, UserToken } from "#/entity";
|
|
import { StorageEnum } from "#/enum";
|
|
import { getUserInfoApi, loginApi } from "@/api/account";
|
|
import type { PasswordTokenRequestModel } from "#/account";
|
|
import { getConfigApi } from "@/api/abp-core";
|
|
import useAbpStore from "./abpCoreStore";
|
|
import { useEventBus } from "@/utils/abp/useEventBus";
|
|
import { Events } from "@/constants/abp-core";
|
|
|
|
const { VITE_APP_HOMEPAGE: HOMEPAGE } = import.meta.env;
|
|
|
|
type UserStore = {
|
|
userInfo: Partial<UserInfo>;
|
|
userToken: UserToken;
|
|
accessCodes: string[]; //权限码 TODO 之后和动态生成菜单放一块去
|
|
// 使用 actions 命名空间来存放所有的 action
|
|
actions: {
|
|
setUserInfo: (userInfo: UserInfo) => void;
|
|
setUserToken: (token: UserToken) => void;
|
|
setAccessCodes: (accessCodes: string[]) => void;
|
|
clearUserInfoAndToken: () => void;
|
|
fetchAndSetUser: () => Promise<UserInfo | null>;
|
|
};
|
|
};
|
|
|
|
const useUserStore = create<UserStore>()(
|
|
persist(
|
|
(set) => ({
|
|
userInfo: {},
|
|
userToken: {},
|
|
accessCodes: [],
|
|
actions: {
|
|
setUserInfo: (userInfo) => {
|
|
set({ userInfo });
|
|
},
|
|
setUserToken: (userToken) => {
|
|
set({ userToken });
|
|
},
|
|
setAccessCodes: (accessCodes) => {
|
|
set({ accessCodes });
|
|
},
|
|
clearUserInfoAndToken() {
|
|
const { publish } = useEventBus();
|
|
publish(Events.UserLogout);
|
|
set({ userInfo: {}, userToken: {} });
|
|
},
|
|
fetchAndSetUser: async () => {
|
|
let userInfo: ({ [key: string]: any } & UserInfo) | null = null;
|
|
|
|
try {
|
|
const userInfoRes = await getUserInfoApi();
|
|
const abpConfig = await getConfigApi();
|
|
|
|
userInfo = {
|
|
id: userInfoRes.sub, //额外加的
|
|
userId: userInfoRes.sub,
|
|
username: userInfoRes.uniqueName ?? abpConfig.currentUser.userName,
|
|
realName: userInfoRes.name ?? abpConfig.currentUser.name,
|
|
avatar: userInfoRes.avatarUrl ?? userInfoRes.picture,
|
|
desc: userInfoRes.uniqueName ?? userInfoRes.name,
|
|
email: userInfoRes.email ?? userInfoRes.email,
|
|
emailVerified: userInfoRes.emailVerified ?? abpConfig.currentUser.emailVerified,
|
|
phoneNumber: userInfoRes.phoneNumber ?? abpConfig.currentUser.phoneNumber,
|
|
phoneNumberVerified: userInfoRes.phoneNumberVerified ?? abpConfig.currentUser.phoneNumberVerified,
|
|
token: "",
|
|
roles: abpConfig.currentUser.roles,
|
|
homePath: "/",
|
|
};
|
|
|
|
// 更新一系列到 zustand store 中
|
|
set({ userInfo });
|
|
|
|
useAbpStore.getState().actions.setApplication(abpConfig);
|
|
|
|
set({ accessCodes: Object.keys(abpConfig.auth.grantedPolicies) });
|
|
} catch (err) {
|
|
console.error("Failed to fetch user info:", err);
|
|
}
|
|
|
|
return userInfo;
|
|
},
|
|
},
|
|
}),
|
|
{
|
|
name: "userStore", // name of the item in the storage (must be unique)
|
|
storage: createJSONStorage(() => localStorage), // (optional) by default, 'localStorage' is used
|
|
partialize: (state) => ({
|
|
[StorageEnum.UserInfo]: state.userInfo,
|
|
[StorageEnum.UserToken]: state.userToken,
|
|
[StorageEnum.AccessCodes]: state.accessCodes,
|
|
}),
|
|
},
|
|
),
|
|
);
|
|
|
|
export const useUserInfo = () => useUserStore((state) => state.userInfo);
|
|
export const useUserToken = () => useUserStore((state) => state.userToken);
|
|
export const useUserPermission = () => useUserStore((state) => state.userInfo.permissions);
|
|
export const useUserAccessCodes = () => useUserStore((state) => state.accessCodes);
|
|
export const useUserActions = () => useUserStore((state) => state.actions);
|
|
|
|
export const useSignIn = () => {
|
|
const navigatge = useNavigate();
|
|
const { setUserToken, fetchAndSetUser } = useUserActions();
|
|
|
|
const signInMutation = useMutation({
|
|
mutationFn: loginApi,
|
|
retry: 0,
|
|
});
|
|
|
|
const signIn = async (data: PasswordTokenRequestModel) => {
|
|
try {
|
|
const res = await signInMutation.mutateAsync(data);
|
|
|
|
const { tokenType, accessToken, refreshToken } = res;
|
|
// 如果成功获取到 accessToken
|
|
if (accessToken) {
|
|
setUserToken({ accessToken: `${tokenType} ${accessToken}`, refreshToken });
|
|
|
|
await fetchAndSetUser();
|
|
|
|
navigatge(HOMEPAGE, { replace: true });
|
|
toast.success("Sign in success!");
|
|
}
|
|
} catch (err) {
|
|
console.error(err.message);
|
|
}
|
|
};
|
|
|
|
return signIn;
|
|
};
|
|
|
|
export default useUserStore;
|
|
|