init:first commit of plus-ui

This commit is contained in:
Teo
2025-05-21 11:24:53 +08:00
commit 95e38df6a5
2219 changed files with 2478311 additions and 0 deletions

3
src/store/index.ts Normal file
View File

@ -0,0 +1,3 @@
const store = createPinia();
export default store;

72
src/store/modules/app.ts Normal file
View File

@ -0,0 +1,72 @@
import zhCN from 'element-plus/es/locale/lang/zh-cn';
import enUS from 'element-plus/es/locale/lang/en';
export const useAppStore = defineStore('app', () => {
const sidebarStatus = useStorage('sidebarStatus', '1');
const sidebar = reactive({
opened: sidebarStatus.value ? !!+sidebarStatus.value : true,
withoutAnimation: false,
hide: false
});
const device = ref<string>('desktop');
const size = useStorage<'large' | 'default' | 'small'>('size', 'default');
// 语言
const language = useStorage('language', 'zh_CN');
const languageObj: any = {
en_US: enUS,
zh_CN: zhCN
};
const locale = computed(() => {
return languageObj[language.value];
});
const toggleSideBar = (withoutAnimation: boolean) => {
if (sidebar.hide) {
return false;
}
sidebar.opened = !sidebar.opened;
sidebar.withoutAnimation = withoutAnimation;
if (sidebar.opened) {
sidebarStatus.value = '1';
} else {
sidebarStatus.value = '0';
}
};
const closeSideBar = ({ withoutAnimation }: any): void => {
sidebarStatus.value = '0';
sidebar.opened = false;
sidebar.withoutAnimation = withoutAnimation;
};
const toggleDevice = (d: string): void => {
device.value = d;
};
const setSize = (s: 'large' | 'default' | 'small'): void => {
size.value = s;
};
const toggleSideBarHide = (status: boolean): void => {
sidebar.hide = status;
};
const changeLanguage = (val: string): void => {
language.value = val;
};
return {
device,
sidebar,
language,
locale,
size,
changeLanguage,
toggleSideBar,
closeSideBar,
toggleDevice,
setSize,
toggleSideBarHide
};
});
export default useAppStore;

View File

@ -0,0 +1,12 @@
import { defineStore } from 'pinia';
export const bigUpload = defineStore('bigUpload', {
state: (): { panelShow: boolean } => ({
panelShow: false
}),
actions: {
async setPanelShow(bool: boolean) {
this.panelShow = bool;
}
}
});

65
src/store/modules/dict.ts Normal file
View File

@ -0,0 +1,65 @@
export const useDictStore = defineStore('dict', () => {
const dict = ref<Map<string, DictDataOption[]>>(new Map());
/**
* 获取字典
* @param _key 字典key
*/
const getDict = (_key: string): DictDataOption[] | null => {
if (!_key) {
return null;
}
return dict.value.get(_key) || null;
};
/**
* 设置字典
* @param _key 字典key
* @param _value 字典value
*/
const setDict = (_key: string, _value: DictDataOption[]) => {
if (!_key) {
return false;
}
try {
dict.value.set(_key, _value);
return true;
} catch (e) {
console.error('Error in setDict:', e);
return false;
}
};
/**
* 删除字典
* @param _key
*/
const removeDict = (_key: string): boolean => {
if (!_key) {
return false;
}
try {
return dict.value.delete(_key);
} catch (e) {
console.error('Error in removeDict:', e);
return false;
}
};
/**
* 清空字典
*/
const cleanDict = (): void => {
dict.value.clear();
};
return {
dict,
getDict,
setDict,
removeDict,
cleanDict
};
});
export default useDictStore;

View File

@ -0,0 +1,42 @@
// stores/useAirStore.js
import { getLocal } from '@/utils';
import { defineStore } from 'pinia';
export const useAirStore = defineStore('air', {
state: () => ({
debugValue: false, // 远程调试模式
HatchCoverValue: false, // 舱盖
AerocraftValue: false, // 飞行器
gateWay: JSON.parse(getLocal('airGateway')) || null, //
queryParams: {
target_height: 100, //目标点高度
rth_altitude: 100, //返航高度
commander_flight_height: 100, //指点飞行高度
rc_lost_action: 2, //遥控器失控动作
rth_mode: 1, //返航模式设置值
commander_mode_lost_action: 1, //指点飞行失控动作
commander_flight_mode: 1, //指点飞行模式设置值
max_speed: 12, //一键起飞的飞行过程中能达到的最大速度
security_takeoff_height: 100, //安全起飞高度
target_latitude: 0, //目标点纬度
target_longitude: 0, //目标点经度
height: 100
}
}),
actions: {
UP_debugValue(payload) {
localStorage.setItem('debugValue', JSON.stringify(payload));
},
UP_HatchCoverValue(payload) {
localStorage.setItem('HatchCoverValue', JSON.stringify(payload));
},
UP_AerocraftValue(payload) {
localStorage.setItem('AerocraftValue', JSON.stringify(payload));
},
SET_GATEWAY(gateWay) {
this.gateWay = gateWay;
localStorage.setItem('airGateway', JSON.stringify(gateWay));
}
}
});

View File

@ -0,0 +1,42 @@
import { defineStore } from 'pinia';
interface NoticeItem {
title?: string;
read: boolean;
message: any;
time: string;
}
export const useNoticeStore = defineStore('notice', () => {
const state = reactive({
notices: [] as NoticeItem[]
});
const addNotice = (notice: NoticeItem) => {
state.notices.push(notice);
};
const removeNotice = (notice: NoticeItem) => {
state.notices.splice(state.notices.indexOf(notice), 1);
};
//实现全部已读
const readAll = () => {
state.notices.forEach((item: any) => {
item.read = true;
});
};
const clearNotice = () => {
state.notices = [];
};
return {
state,
addNotice,
removeNotice,
readAll,
clearNotice
};
});
export default useNoticeStore;

View File

@ -0,0 +1,221 @@
import { defineStore } from 'pinia';
import router, { constantRoutes, dynamicRoutes } from '@/router';
import store from '@/store';
import { getRouters } from '@/api/menu';
import auth from '@/plugins/auth';
import { RouteRecordRaw } from 'vue-router';
import Layout from '@/layout/index.vue';
import ParentView from '@/components/ParentView/index.vue';
import InnerLink from '@/layout/components/InnerLink/index.vue';
import { createCustomNameComponent } from '@/utils/createCustomNameComponent';
// 匹配views里面所有的.vue文件
const modules = import.meta.glob('./../../views/**/*.vue');
export const usePermissionStore = defineStore('permission', () => {
const routes = ref<RouteRecordRaw[]>([]);
const addRoutes = ref<RouteRecordRaw[]>([]);
const defaultRoutes = ref<RouteRecordRaw[]>([]);
const topbarRouters = ref<RouteRecordRaw[]>([]);
const sidebarRouters = ref<RouteRecordRaw[]>([]);
const getRoutes = (): RouteRecordRaw[] => {
return routes.value as RouteRecordRaw[];
};
const getSidebarRoutes = (): RouteRecordRaw[] => {
return sidebarRouters.value as RouteRecordRaw[];
};
const getTopbarRoutes = (): RouteRecordRaw[] => {
return topbarRouters.value as RouteRecordRaw[];
};
const setRoutes = (newRoutes: RouteRecordRaw[]): void => {
addRoutes.value = newRoutes;
routes.value = constantRoutes.concat(newRoutes);
};
const setDefaultRoutes = (routes: RouteRecordRaw[]): void => {
defaultRoutes.value = constantRoutes.concat(routes);
};
const setTopbarRoutes = (routes: RouteRecordRaw[]): void => {
topbarRouters.value = routes;
};
const setSidebarRouters = (routes: RouteRecordRaw[]): void => {
sidebarRouters.value = routes;
};
const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
const res = await getRouters();
const { data } = res;
const sdata = JSON.parse(JSON.stringify(data));
const rdata = JSON.parse(JSON.stringify(data));
const defaultData = JSON.parse(JSON.stringify(data));
const sidebarRoutes = filterAsyncRouter(sdata);
const rewriteRoutes = filterAsyncRouter(rdata, undefined, true);
const defaultRoutes = filterAsyncRouter(defaultData);
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
asyncRoutes.forEach((route) => {
router.addRoute(route);
});
setRoutes(rewriteRoutes);
setSidebarRouters(constantRoutes.concat(sidebarRoutes));
setDefaultRoutes(sidebarRoutes);
setTopbarRoutes(defaultRoutes);
// 路由name重复检查
duplicateRouteChecker(asyncRoutes, sidebarRoutes);
return new Promise<RouteRecordRaw[]>((resolve) => resolve(rewriteRoutes));
};
/**
* 遍历后台传来的路由字符串,转换为组件对象
* @param asyncRouterMap 后台传来的路由字符串
* @param lastRouter 上一级路由
* @param type 是否是重写路由
*/
const filterAsyncRouter = (asyncRouterMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw, type = false): RouteRecordRaw[] => {
return asyncRouterMap.filter((route) => {
if (type && route.children) {
route.children = filterChildren(route.children, undefined);
}
// Layout ParentView 组件特殊处理
if (route.component?.toString() === 'Layout') {
route.component = Layout;
} else if (route.component?.toString() === 'ParentView') {
route.component = ParentView;
} else if (route.component?.toString() === 'InnerLink') {
route.component = InnerLink;
} else {
route.component = loadView(route.component, route.name as string);
}
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type);
} else {
delete route.children;
delete route.redirect;
}
return true;
});
};
const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => {
let children: RouteRecordRaw[] = [];
childrenMap.forEach((el) => {
if (el.children && el.children.length) {
if (el.component?.toString() === 'ParentView' && !lastRouter) {
el.children.forEach((c) => {
c.path = el.path + '/' + c.path;
if (c.children && c.children.length) {
children = children.concat(filterChildren(c.children, c));
return;
}
children.push(c);
});
return;
}
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path;
if (el.children && el.children.length) {
children = children.concat(filterChildren(el.children, el));
return;
}
}
children = children.concat(el);
});
return children;
};
return {
routes,
topbarRouters,
sidebarRouters,
defaultRoutes,
getRoutes,
getSidebarRoutes,
getTopbarRoutes,
setRoutes,
generateRoutes,
setSidebarRouters
};
});
// 动态路由遍历,验证是否具备权限
export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => {
const res: RouteRecordRaw[] = [];
routes.forEach((route) => {
if (route.permissions) {
if (auth.hasPermiOr(route.permissions)) {
res.push(route);
}
} else if (route.roles) {
if (auth.hasRoleOr(route.roles)) {
res.push(route);
}
}
});
return res;
};
export const loadView = (view: any, name: string) => {
let res;
for (const path in modules) {
const viewsIndex = path.indexOf('/views/');
let dir = path.substring(viewsIndex + 7);
dir = dir.substring(0, dir.lastIndexOf('.vue'));
if (dir === view) {
res = createCustomNameComponent(modules[path], { name });
return res;
}
}
return res;
};
// 非setup
export const usePermissionStoreHook = () => {
return usePermissionStore(store);
};
interface Route {
name?: string | symbol;
path: string;
children?: Route[];
}
/**
* 检查路由name是否重复
* @param localRoutes 本地路由
* @param routes 动态路由
*/
function duplicateRouteChecker(localRoutes: Route[], routes: Route[]) {
// 展平
function flatRoutes(routes: Route[]) {
const res: Route[] = [];
routes.forEach((route) => {
if (route.children) {
res.push(...flatRoutes(route.children));
} else {
res.push(route);
}
});
return res;
}
const allRoutes = flatRoutes([...localRoutes, ...routes]);
const nameList: string[] = [];
allRoutes.forEach((route) => {
const name = route.name.toString();
if (name && nameList.includes(name)) {
const message = `路由名称: [${name}] 重复, 会造成 404`;
console.error(message);
ElNotification({
title: '路由名称重复',
message,
type: 'error'
});
return;
}
nameList.push(route.name.toString());
});
}
export default usePermissionStore;

View File

@ -0,0 +1,47 @@
import { defineStore } from 'pinia';
import defaultSettings from '@/settings';
import { useDynamicTitle } from '@/utils/dynamicTitle';
export const useSettingsStore = defineStore('setting', () => {
const storageSetting = useStorage<LayoutSetting>('layout-setting', {
topNav: defaultSettings.topNav,
tagsView: defaultSettings.tagsView,
fixedHeader: defaultSettings.fixedHeader,
sidebarLogo: defaultSettings.sidebarLogo,
dynamicTitle: defaultSettings.dynamicTitle,
sideTheme: defaultSettings.sideTheme,
theme: defaultSettings.theme
});
const title = ref<string>(defaultSettings.title);
const theme = ref<string>(storageSetting.value.theme);
const sideTheme = ref<string>(storageSetting.value.sideTheme);
const showSettings = ref<boolean>(defaultSettings.showSettings);
const topNav = ref<boolean>(storageSetting.value.topNav);
const tagsView = ref<boolean>(storageSetting.value.tagsView);
const fixedHeader = ref<boolean>(storageSetting.value.fixedHeader);
const sidebarLogo = ref<boolean>(storageSetting.value.sidebarLogo);
const dynamicTitle = ref<boolean>(storageSetting.value.dynamicTitle);
const animationEnable = ref<boolean>(defaultSettings.animationEnable);
const dark = ref<boolean>(defaultSettings.dark);
const setTitle = (value: string) => {
title.value = value;
useDynamicTitle();
};
return {
title,
theme,
sideTheme,
showSettings,
topNav,
tagsView,
fixedHeader,
sidebarLogo,
dynamicTitle,
animationEnable,
dark,
setTitle
};
});
export default useSettingsStore;

View File

@ -0,0 +1,233 @@
import { RouteLocationNormalized } from 'vue-router';
export const useTagsViewStore = defineStore('tagsView', () => {
const visitedViews = ref<RouteLocationNormalized[]>([]);
const cachedViews = ref<string[]>([]);
const iframeViews = ref<RouteLocationNormalized[]>([]);
const getVisitedViews = (): RouteLocationNormalized[] => {
return visitedViews.value as RouteLocationNormalized[];
};
const getIframeViews = (): RouteLocationNormalized[] => {
return iframeViews.value as RouteLocationNormalized[];
};
const getCachedViews = (): string[] => {
return cachedViews.value;
};
const addView = (view: RouteLocationNormalized) => {
addVisitedView(view);
addCachedView(view);
};
const addIframeView = (view: RouteLocationNormalized): void => {
if (iframeViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return;
iframeViews.value.push(
Object.assign({}, view, {
title: view.meta?.title || 'no-name'
})
);
};
const delIframeView = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
return new Promise((resolve) => {
iframeViews.value = iframeViews.value.filter((item: RouteLocationNormalized) => item.path !== view.path);
resolve([...(iframeViews.value as RouteLocationNormalized[])]);
});
};
const addVisitedView = (view: RouteLocationNormalized): void => {
if (visitedViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return;
visitedViews.value.push(
Object.assign({}, view, {
title: view.meta?.title || 'no-name'
})
);
};
const delView = (
view: RouteLocationNormalized
): Promise<{
visitedViews: RouteLocationNormalized[];
cachedViews: string[];
}> => {
return new Promise((resolve) => {
delVisitedView(view);
if (!isDynamicRoute(view)) {
delCachedView(view);
}
resolve({
visitedViews: [...(visitedViews.value as RouteLocationNormalized[])],
cachedViews: [...cachedViews.value]
});
});
};
const delVisitedView = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
return new Promise((resolve) => {
for (const [i, v] of visitedViews.value.entries()) {
if (v.path === view.path) {
visitedViews.value.splice(i, 1);
break;
}
}
resolve([...(visitedViews.value as RouteLocationNormalized[])]);
});
};
const delCachedView = (view?: RouteLocationNormalized): Promise<string[]> => {
let viewName = '';
if (view) {
viewName = view.name as string;
}
return new Promise((resolve) => {
const index = cachedViews.value.indexOf(viewName);
index > -1 && cachedViews.value.splice(index, 1);
resolve([...cachedViews.value]);
});
};
const delOthersViews = (
view: RouteLocationNormalized
): Promise<{
visitedViews: RouteLocationNormalized[];
cachedViews: string[];
}> => {
return new Promise((resolve) => {
delOthersVisitedViews(view);
delOthersCachedViews(view);
resolve({
visitedViews: [...(visitedViews.value as RouteLocationNormalized[])],
cachedViews: [...cachedViews.value]
});
});
};
const delOthersVisitedViews = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
return new Promise((resolve) => {
visitedViews.value = visitedViews.value.filter((v: RouteLocationNormalized) => {
return v.meta?.affix || v.path === view.path;
});
resolve([...(visitedViews.value as RouteLocationNormalized[])]);
});
};
const delOthersCachedViews = (view: RouteLocationNormalized): Promise<string[]> => {
const viewName = view.name as string;
return new Promise((resolve) => {
const index = cachedViews.value.indexOf(viewName);
if (index > -1) {
cachedViews.value = cachedViews.value.slice(index, index + 1);
} else {
cachedViews.value = [];
}
resolve([...cachedViews.value]);
});
};
const delAllViews = (): Promise<{ visitedViews: RouteLocationNormalized[]; cachedViews: string[] }> => {
return new Promise((resolve) => {
delAllVisitedViews();
delAllCachedViews();
resolve({
visitedViews: [...(visitedViews.value as RouteLocationNormalized[])],
cachedViews: [...cachedViews.value]
});
});
};
const delAllVisitedViews = (): Promise<RouteLocationNormalized[]> => {
return new Promise((resolve) => {
visitedViews.value = visitedViews.value.filter((tag: RouteLocationNormalized) => tag.meta?.affix);
resolve([...(visitedViews.value as RouteLocationNormalized[])]);
});
};
const delAllCachedViews = (): Promise<string[]> => {
return new Promise((resolve) => {
cachedViews.value = [];
resolve([...cachedViews.value]);
});
};
const updateVisitedView = (view: RouteLocationNormalized): void => {
for (let v of visitedViews.value) {
if (v.path === view.path) {
v = Object.assign(v, view);
break;
}
}
};
const delRightTags = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
return new Promise((resolve) => {
const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path);
if (index === -1) {
return;
}
visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => {
if (idx <= index || (item.meta && item.meta.affix)) {
return true;
}
const i = cachedViews.value.indexOf(item.name as string);
if (i > -1) {
cachedViews.value.splice(i, 1);
}
return false;
});
resolve([...(visitedViews.value as RouteLocationNormalized[])]);
});
};
const delLeftTags = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
return new Promise((resolve) => {
const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path);
if (index === -1) {
return;
}
visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => {
if (idx >= index || (item.meta && item.meta.affix)) {
return true;
}
const i = cachedViews.value.indexOf(item.name as string);
if (i > -1) {
cachedViews.value.splice(i, 1);
}
return false;
});
resolve([...(visitedViews.value as RouteLocationNormalized[])]);
});
};
const addCachedView = (view: RouteLocationNormalized): void => {
const viewName = view.name as string;
if (!viewName) return;
if (cachedViews.value.includes(viewName)) return;
if (!view.meta?.noCache) {
cachedViews.value.push(viewName);
}
};
const isDynamicRoute = (view: RouteLocationNormalized): boolean => {
// 检查匹配的路由记录中是否有动态段
return view.matched.some((m) => m.path.includes(':'));
};
return {
visitedViews,
cachedViews,
iframeViews,
getVisitedViews,
getIframeViews,
getCachedViews,
addVisitedView,
addCachedView,
delVisitedView,
delCachedView,
updateVisitedView,
addView,
delView,
delAllViews,
delAllVisitedViews,
delAllCachedViews,
delOthersViews,
delRightTags,
delLeftTags,
addIframeView,
delIframeView
};
});
export default useTagsViewStore;

166
src/store/modules/user.ts Normal file
View File

@ -0,0 +1,166 @@
import { to } from 'await-to-js';
import { getToken, removeToken, setToken } from '@/utils/auth';
import { login as loginApi, logout as logoutApi, getInfo as getUserInfo, getUserProject } from '@/api/login';
import { LoginData, UserProject } from '@/api/types';
import defAva from '@/assets/images/profile.jpg';
import store from '@/store';
import { defineStore } from 'pinia';
import { SpecialType } from '@/api/project/workWage/types';
import { getProjectTeam } from '@/utils/projectTeam';
import $cache from '@/plugins/cache';
// 添加两个函数用于操作localStorage
const saveSelectedProjectToStorage = (project) => {
// localStorage.setItem('selectedProject', JSON.stringify(project));
$cache.local.setJSON('selectedProject', project);
getProjectTeam();
};
const saveProjectTeamToStorage = (project) => {
// localStorage.setItem('ProjectTeamList', JSON.stringify(project));
$cache.local.setJSON('ProjectTeamList', project);
};
const getSelectedProjectFromStorage = () => {
// localStorage.getItem('selectedProject');
const stored = $cache.local.getJSON('selectedProject');
return stored ? stored : null;
};
const getProjectTeamListFromStorage = () => {
const stored = $cache.local.getJSON('ProjectTeamList');
return stored ? stored : null;
};
export const useUserStore = defineStore('user', () => {
const token = ref(getToken());
const name = ref('');
const nickname = ref('');
const userId = ref<string | number>('');
const tenantId = ref<string>('');
const avatar = ref('');
const roles = ref<Array<string>>([]); // 用户角色编码集合 → 判断路由权限
const permissions = ref<Array<string>>([]); // 用户权限编码集合 → 判断按钮权限
const projects = ref<Array<{ id: string; name: string }>>([]);
// 从localStorage获取缓存的项目如果没有则默认为null
const selectedProject = ref<{ id: string; name: string } | null>(getSelectedProjectFromStorage());
const ProjectTeamList = ref<SpecialType[] | null>(getProjectTeamListFromStorage());
/**
* 登录
* @param userInfo
* @returns
*/
const login = async (userInfo: LoginData): Promise<void> => {
const [err, res] = await to(loginApi(userInfo));
if (res) {
const data = res.data;
setToken(data.access_token);
token.value = data.access_token;
return Promise.resolve();
}
return Promise.reject(err);
};
// 获取用户信息
const getInfo = async (): Promise<void> => {
const [err, res] = await to(getUserInfo());
if (res) {
const data = res.data;
const user = data.user;
const profile = user.avatar == '' || user.avatar == null ? defAva : user.avatar;
if (data.roles && data.roles.length > 0) {
roles.value = data.roles;
permissions.value = data.permissions;
} else {
roles.value = ['ROLE_DEFAULT'];
}
name.value = user.userName;
nickname.value = user.nickName;
avatar.value = profile;
userId.value = user.userId;
tenantId.value = user.tenantId;
// **新增项目数据获取**
const [projectErr, projectRes] = await to(getUserProject());
if (projectRes?.data) {
const projectList = projectRes.data.map((p) => ({
id: p.projectId,
name: p.projectName || '未知项目'
}));
setProjects(projectList);
// 如果有缓存的选中项目,且该项目在当前项目列表中存在,则使用缓存的项目
const storedProject = getSelectedProjectFromStorage();
if (storedProject && projectList.some((p) => p.id === storedProject.id)) {
setSelectedProject(storedProject);
} else if (projectList.length > 0) {
// 否则默认选择第一个项目
setSelectedProject(projectList[0]);
}
}
return Promise.resolve();
}
return Promise.reject(err);
};
// 注销
const logout = async (): Promise<void> => {
await logoutApi();
token.value = '';
roles.value = [];
permissions.value = [];
removeToken();
// 清除项目缓存
$cache.local.remove('selectedProject');
$cache.local.remove('ProjectTeamList');
};
const setAvatar = (value: string) => {
avatar.value = value;
};
const setProjects = (projectList: Array<{ id: string; name: string }>) => {
projects.value = projectList;
};
const setSelectedProject = (project: { id: string; name: string }) => {
selectedProject.value = project;
saveSelectedProjectToStorage(project);
};
const setProjectTeamList = (project: SpecialType[]) => {
ProjectTeamList.value = project;
saveProjectTeamToStorage(project);
};
return {
userId,
tenantId,
token,
nickname,
avatar,
roles,
permissions,
login,
getInfo,
logout,
setAvatar,
setProjects,
setSelectedProject,
setProjectTeamList,
projects,
selectedProject,
ProjectTeamList
};
});
export default useUserStore;
// 非 setup 方式
export function useUserStoreHook() {
return useUserStore(store);
}