mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2025-12-08 17:12:28 +08:00
前端和后端源码,合并到一个git仓库中,方便用户下载,避免前后端不匹配的问题
This commit is contained in:
9
jeecgboot-vue3/src/store/index.ts
Normal file
9
jeecgboot-vue3/src/store/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import type { App } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
const store = createPinia();
|
||||
|
||||
export function setupStore(app: App<Element>) {
|
||||
app.use(store);
|
||||
}
|
||||
|
||||
export { store };
|
||||
114
jeecgboot-vue3/src/store/modules/app.ts
Normal file
114
jeecgboot-vue3/src/store/modules/app.ts
Normal file
@ -0,0 +1,114 @@
|
||||
import type { ProjectConfig, HeaderSetting, MenuSetting, TransitionSetting, MultiTabsSetting } from '/#/config';
|
||||
import type { BeforeMiniState } from '/#/store';
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '/@/store';
|
||||
|
||||
import { ThemeEnum } from '/@/enums/appEnum';
|
||||
import { APP_DARK_MODE_KEY_, PROJ_CFG_KEY } from '/@/enums/cacheEnum';
|
||||
import { Persistent } from '/@/utils/cache/persistent';
|
||||
import { darkMode } from '/@/settings/designSetting';
|
||||
import { resetRouter } from '/@/router';
|
||||
import { deepMerge } from '/@/utils';
|
||||
|
||||
interface AppState {
|
||||
darkMode?: ThemeEnum;
|
||||
// Page loading status
|
||||
pageLoading: boolean;
|
||||
// project config
|
||||
projectConfig: ProjectConfig | null;
|
||||
// When the window shrinks, remember some states, and restore these states when the window is restored
|
||||
beforeMiniInfo: BeforeMiniState;
|
||||
// 页面跳转临时参数存储
|
||||
messageHrefParams: any
|
||||
}
|
||||
let timeId: TimeoutHandle;
|
||||
export const useAppStore = defineStore({
|
||||
id: 'app',
|
||||
state: (): AppState => ({
|
||||
darkMode: undefined,
|
||||
pageLoading: false,
|
||||
projectConfig: Persistent.getLocal(PROJ_CFG_KEY),
|
||||
beforeMiniInfo: {},
|
||||
messageHrefParams: {}
|
||||
}),
|
||||
getters: {
|
||||
getPageLoading(): boolean {
|
||||
return this.pageLoading;
|
||||
},
|
||||
getDarkMode(): 'light' | 'dark' | string {
|
||||
return this.darkMode || localStorage.getItem(APP_DARK_MODE_KEY_) || darkMode;
|
||||
},
|
||||
|
||||
getBeforeMiniInfo(): BeforeMiniState {
|
||||
return this.beforeMiniInfo;
|
||||
},
|
||||
|
||||
getProjectConfig(): ProjectConfig {
|
||||
return this.projectConfig || ({} as ProjectConfig);
|
||||
},
|
||||
|
||||
getHeaderSetting(): HeaderSetting {
|
||||
return this.getProjectConfig.headerSetting;
|
||||
},
|
||||
getMenuSetting(): MenuSetting {
|
||||
return this.getProjectConfig.menuSetting;
|
||||
},
|
||||
getTransitionSetting(): TransitionSetting {
|
||||
return this.getProjectConfig.transitionSetting;
|
||||
},
|
||||
getMultiTabsSetting(): MultiTabsSetting {
|
||||
return this.getProjectConfig.multiTabsSetting;
|
||||
},
|
||||
getMessageHrefParams():any{
|
||||
return this.messageHrefParams;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
setPageLoading(loading: boolean): void {
|
||||
this.pageLoading = loading;
|
||||
},
|
||||
|
||||
setDarkMode(mode: ThemeEnum): void {
|
||||
this.darkMode = mode;
|
||||
localStorage.setItem(APP_DARK_MODE_KEY_, mode);
|
||||
},
|
||||
|
||||
setBeforeMiniInfo(state: BeforeMiniState): void {
|
||||
this.beforeMiniInfo = state;
|
||||
},
|
||||
|
||||
setProjectConfig(config: DeepPartial<ProjectConfig>): void {
|
||||
this.projectConfig = deepMerge(this.projectConfig || {}, config);
|
||||
// update-begin--author:liaozhiyang---date:20240408---for:【QQYUN-8922】设置导航栏模式没存本地,刷新就还原了
|
||||
Persistent.setLocal(PROJ_CFG_KEY, this.projectConfig, true);
|
||||
// update-end--author:liaozhiyang---date:20240408---for:【QQYUN-8922】设置导航栏模式没存本地,刷新就还原了
|
||||
},
|
||||
|
||||
async resetAllState() {
|
||||
resetRouter();
|
||||
Persistent.clearAll();
|
||||
},
|
||||
async setPageLoadingAction(loading: boolean): Promise<void> {
|
||||
if (loading) {
|
||||
clearTimeout(timeId);
|
||||
// Prevent flicker
|
||||
timeId = setTimeout(() => {
|
||||
this.setPageLoading(loading);
|
||||
}, 50);
|
||||
} else {
|
||||
this.setPageLoading(loading);
|
||||
clearTimeout(timeId);
|
||||
}
|
||||
},
|
||||
setMessageHrefParams(params: any): void {
|
||||
this.messageHrefParams = params;
|
||||
},
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useAppStoreWithOut() {
|
||||
return useAppStore(store);
|
||||
}
|
||||
75
jeecgboot-vue3/src/store/modules/defIndex.ts
Normal file
75
jeecgboot-vue3/src/store/modules/defIndex.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import {store} from '/@/store';
|
||||
import {defineStore} from 'pinia';
|
||||
import {defHttp} from "@/utils/http/axios";
|
||||
|
||||
interface DefIndexState {
|
||||
// 首页url
|
||||
url: string,
|
||||
// 首页组件
|
||||
component: string
|
||||
}
|
||||
|
||||
export const useDefIndexStore = defineStore({
|
||||
id: 'defIndex',
|
||||
state: (): DefIndexState => ({
|
||||
url: '',
|
||||
component: '',
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
/**
|
||||
* 查询默认主页配置
|
||||
*/
|
||||
async query() {
|
||||
const config = await defIndexApi.query();
|
||||
this.url = config.url;
|
||||
this.component = config.component;
|
||||
},
|
||||
/**
|
||||
* 更新默认主页配置
|
||||
* @param url 首页url
|
||||
* @param component 首页组件
|
||||
* @param isRoute 是否是路由
|
||||
*/
|
||||
async update(url: string, component: string, isRoute: boolean) {
|
||||
await defIndexApi.update(url, component, isRoute);
|
||||
await this.query()
|
||||
},
|
||||
|
||||
check(url: string) {
|
||||
return url === this.url;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useDefIndexStoreWithOut() {
|
||||
return useDefIndexStore(store);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认首页配置API
|
||||
*/
|
||||
export const defIndexApi = {
|
||||
/**
|
||||
* 查询默认首页配置
|
||||
*/
|
||||
async query() {
|
||||
const url = '/sys/sysRoleIndex/queryDefIndex'
|
||||
return await defHttp.get({url});
|
||||
},
|
||||
/**
|
||||
* 更新默认首页配置
|
||||
* @param url 首页url
|
||||
* @param component 首页组件
|
||||
* @param isRoute 是否是路由
|
||||
*/
|
||||
async update(url: string, component: string, isRoute: boolean) {
|
||||
let apiUrl = '/sys/sysRoleIndex/updateDefIndex'
|
||||
apiUrl += '?url=' + url
|
||||
apiUrl += '&component=' + component
|
||||
apiUrl += '&isRoute=' + isRoute
|
||||
return await defHttp.put({url: apiUrl});
|
||||
},
|
||||
|
||||
}
|
||||
74
jeecgboot-vue3/src/store/modules/errorLog.ts
Normal file
74
jeecgboot-vue3/src/store/modules/errorLog.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import type { ErrorLogInfo } from '/#/store';
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '/@/store';
|
||||
|
||||
import { formatToDateTime } from '/@/utils/dateUtil';
|
||||
import projectSetting from '/@/settings/projectSetting';
|
||||
|
||||
import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
|
||||
|
||||
export interface ErrorLogState {
|
||||
errorLogInfoList: Nullable<ErrorLogInfo[]>;
|
||||
errorLogListCount: number;
|
||||
}
|
||||
|
||||
export const useErrorLogStore = defineStore({
|
||||
id: 'app-error-log',
|
||||
state: (): ErrorLogState => ({
|
||||
errorLogInfoList: null,
|
||||
errorLogListCount: 0,
|
||||
}),
|
||||
getters: {
|
||||
getErrorLogInfoList(): ErrorLogInfo[] {
|
||||
return this.errorLogInfoList || [];
|
||||
},
|
||||
getErrorLogListCount(): number {
|
||||
return this.errorLogListCount;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
addErrorLogInfo(info: ErrorLogInfo) {
|
||||
const item = {
|
||||
...info,
|
||||
time: formatToDateTime(new Date()),
|
||||
};
|
||||
this.errorLogInfoList = [item, ...(this.errorLogInfoList || [])];
|
||||
this.errorLogListCount += 1;
|
||||
},
|
||||
|
||||
setErrorLogListCount(count: number): void {
|
||||
this.errorLogListCount = count;
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggered after ajax request error
|
||||
* @param error
|
||||
* @returns
|
||||
*/
|
||||
addAjaxErrorInfo(error) {
|
||||
const { useErrorHandle } = projectSetting;
|
||||
if (!useErrorHandle) {
|
||||
return;
|
||||
}
|
||||
const errInfo: Partial<ErrorLogInfo> = {
|
||||
message: error.message,
|
||||
type: ErrorTypeEnum.AJAX,
|
||||
};
|
||||
if (error.response) {
|
||||
const { config: { url = '', data: params = '', method = 'get', headers = {} } = {}, data = {} } = error.response;
|
||||
errInfo.url = url;
|
||||
errInfo.name = 'Ajax Error!';
|
||||
errInfo.file = '-';
|
||||
errInfo.stack = JSON.stringify(data);
|
||||
errInfo.detail = JSON.stringify({ params, method, headers });
|
||||
}
|
||||
this.addErrorLogInfo(errInfo as ErrorLogInfo);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useErrorLogStoreWithOut() {
|
||||
return useErrorLogStore(store);
|
||||
}
|
||||
85
jeecgboot-vue3/src/store/modules/locale.ts
Normal file
85
jeecgboot-vue3/src/store/modules/locale.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import type { LocaleSetting, LocaleType } from '/#/config';
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '/@/store';
|
||||
|
||||
import { LOCALE_KEY } from '/@/enums/cacheEnum';
|
||||
import { createLocalStorage } from '/@/utils/cache';
|
||||
import { localeSetting } from '/@/settings/localeSetting';
|
||||
|
||||
const ls = createLocalStorage();
|
||||
|
||||
const lsLocaleSetting = (ls.get(LOCALE_KEY) || localeSetting) as LocaleSetting;
|
||||
|
||||
interface LocaleState {
|
||||
localInfo: LocaleSetting;
|
||||
pathTitleMap: object;
|
||||
// myapps主题色(低代码应用列表首页)
|
||||
appIndexTheme: string
|
||||
// myapps - 跳转前路由地址
|
||||
appMainPth: string
|
||||
}
|
||||
|
||||
export const useLocaleStore = defineStore({
|
||||
id: 'app-locale',
|
||||
state: (): LocaleState => ({
|
||||
localInfo: lsLocaleSetting,
|
||||
pathTitleMap: {},
|
||||
appIndexTheme: '',
|
||||
appMainPth: ''
|
||||
}),
|
||||
getters: {
|
||||
getShowPicker(): boolean {
|
||||
return !!this.localInfo?.showPicker;
|
||||
},
|
||||
getLocale(): LocaleType {
|
||||
return this.localInfo?.locale ?? 'zh_CN';
|
||||
},
|
||||
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
getPathTitle: (state) => {
|
||||
return (path) => state.pathTitleMap[path];
|
||||
},
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
getAppIndexTheme(): string {
|
||||
return this.appIndexTheme;
|
||||
},
|
||||
getAppMainPth(): string {
|
||||
return this.appMainPth;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* Set up multilingual information and cache
|
||||
* @param info multilingual info
|
||||
*/
|
||||
setLocaleInfo(info: Partial<LocaleSetting>) {
|
||||
this.localInfo = { ...this.localInfo, ...info };
|
||||
ls.set(LOCALE_KEY, this.localInfo);
|
||||
},
|
||||
/**
|
||||
* Initialize multilingual information and load the existing configuration from the local cache
|
||||
*/
|
||||
initLocale() {
|
||||
this.setLocaleInfo({
|
||||
...localeSetting,
|
||||
...this.localInfo,
|
||||
});
|
||||
},
|
||||
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
setPathTitle(path, title) {
|
||||
this.pathTitleMap[path] = title;
|
||||
},
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1144 online 配置成菜单后,打开菜单,显示名称未展示为菜单名称
|
||||
setAppIndexTheme(theme) {
|
||||
this.appIndexTheme = theme;
|
||||
},
|
||||
setAppMainPth(path) {
|
||||
this.appMainPth = path;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useLocaleStoreWithOut() {
|
||||
return useLocaleStore(store);
|
||||
}
|
||||
40
jeecgboot-vue3/src/store/modules/lock.ts
Normal file
40
jeecgboot-vue3/src/store/modules/lock.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import type { LockInfo } from '/#/store';
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
import { LOCK_INFO_KEY } from '/@/enums/cacheEnum';
|
||||
import { Persistent } from '/@/utils/cache/persistent';
|
||||
import { useUserStore } from './user';
|
||||
|
||||
interface LockState {
|
||||
lockInfo: Nullable<LockInfo>;
|
||||
}
|
||||
|
||||
export const useLockStore = defineStore({
|
||||
id: 'app-lock',
|
||||
state: (): LockState => ({
|
||||
lockInfo: Persistent.getLocal(LOCK_INFO_KEY),
|
||||
}),
|
||||
getters: {
|
||||
getLockInfo(): Nullable<LockInfo> {
|
||||
return this.lockInfo;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setLockInfo(info: LockInfo) {
|
||||
this.lockInfo = Object.assign({}, this.lockInfo, info);
|
||||
Persistent.setLocal(LOCK_INFO_KEY, this.lockInfo, true);
|
||||
},
|
||||
resetLockInfo() {
|
||||
Persistent.removeLocal(LOCK_INFO_KEY, true);
|
||||
this.lockInfo = null;
|
||||
},
|
||||
// Unlock
|
||||
async unLock(password?: string) {
|
||||
if (this.lockInfo?.pwd === password) {
|
||||
this.resetLockInfo();
|
||||
return true;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
419
jeecgboot-vue3/src/store/modules/multipleTab.ts
Normal file
419
jeecgboot-vue3/src/store/modules/multipleTab.ts
Normal file
@ -0,0 +1,419 @@
|
||||
import type { RouteLocationNormalized, RouteLocationRaw, Router } from 'vue-router';
|
||||
|
||||
import { toRaw, unref } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '/@/store';
|
||||
|
||||
import { useGo, useRedo } from '/@/hooks/web/usePage';
|
||||
import { Persistent } from '/@/utils/cache/persistent';
|
||||
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
|
||||
import { getRawRoute } from '/@/utils';
|
||||
import { MULTIPLE_TABS_KEY } from '/@/enums/cacheEnum';
|
||||
|
||||
import projectSetting from '/@/settings/projectSetting';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import type { LocationQueryRaw, RouteParamsRaw } from 'vue-router';
|
||||
|
||||
export interface MultipleTabState {
|
||||
cacheTabList: Set<string>;
|
||||
tabList: RouteLocationNormalized[];
|
||||
lastDragEndIndex: number;
|
||||
redirectPageParam: null | redirectPageParamType;
|
||||
}
|
||||
|
||||
interface redirectPageParamType {
|
||||
redirect_type: string;
|
||||
name?: string;
|
||||
path?: string;
|
||||
query: LocationQueryRaw;
|
||||
params?: RouteParamsRaw;
|
||||
}
|
||||
|
||||
function handleGotoPage(router: Router, path?) {
|
||||
const go = useGo(router);
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
go(path || unref(router.currentRoute).path, true);
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
}
|
||||
const getToTarget = (tabItem: RouteLocationNormalized) => {
|
||||
const { params, path, query } = tabItem;
|
||||
return {
|
||||
params: params || {},
|
||||
path,
|
||||
query: query || {},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 2024-06-05
|
||||
* liaozhiyang
|
||||
* 关闭的tab中是否包含当前页面
|
||||
*/
|
||||
const closeTabContainCurrentRoute = (router, pathList) => {
|
||||
const { currentRoute } = router;
|
||||
const getCurrentTab = () => {
|
||||
const route = unref(currentRoute);
|
||||
const tabStore = useMultipleTabStore();
|
||||
return tabStore.getTabList.find((item) => item.path === route.path)!;
|
||||
};
|
||||
const currentTab = getCurrentTab();
|
||||
if (currentTab) {
|
||||
return pathList.includes(currentTab.path);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const cacheTab = projectSetting.multiTabsSetting.cache;
|
||||
|
||||
export const useMultipleTabStore = defineStore({
|
||||
id: 'app-multiple-tab',
|
||||
state: (): MultipleTabState => ({
|
||||
// Tabs that need to be cached
|
||||
cacheTabList: new Set(),
|
||||
// multiple tab list
|
||||
tabList: cacheTab ? Persistent.getLocal(MULTIPLE_TABS_KEY) || [] : [],
|
||||
// Index of the last moved tab
|
||||
lastDragEndIndex: 0,
|
||||
// 重定向时存储的路由参数
|
||||
redirectPageParam: null,
|
||||
}),
|
||||
getters: {
|
||||
getTabList(): RouteLocationNormalized[] {
|
||||
return this.tabList;
|
||||
},
|
||||
getCachedTabList(): string[] {
|
||||
return Array.from(this.cacheTabList);
|
||||
},
|
||||
getLastDragEndIndex(): number {
|
||||
return this.lastDragEndIndex;
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/**
|
||||
* Update the cache according to the currently opened tabs
|
||||
*/
|
||||
async updateCacheTab() {
|
||||
const cacheMap: Set<string> = new Set();
|
||||
|
||||
for (const tab of this.tabList) {
|
||||
const item = getRawRoute(tab);
|
||||
// Ignore the cache
|
||||
const needCache = !item.meta?.ignoreKeepAlive;
|
||||
if (!needCache) {
|
||||
continue;
|
||||
}
|
||||
const name = item.name as string;
|
||||
cacheMap.add(name);
|
||||
}
|
||||
this.cacheTabList = cacheMap;
|
||||
},
|
||||
|
||||
/**
|
||||
* Refresh tabs
|
||||
*/
|
||||
async refreshPage(router: Router) {
|
||||
const { currentRoute } = router;
|
||||
const route = unref(currentRoute);
|
||||
const name = route.name;
|
||||
|
||||
const findTab = this.getCachedTabList.find((item) => item === name);
|
||||
if (findTab) {
|
||||
this.cacheTabList.delete(findTab);
|
||||
}
|
||||
const redo = useRedo(router);
|
||||
await redo();
|
||||
},
|
||||
clearCacheTabs(): void {
|
||||
this.cacheTabList = new Set();
|
||||
},
|
||||
resetState(): void {
|
||||
this.tabList = [];
|
||||
this.clearCacheTabs();
|
||||
},
|
||||
goToPage(router: Router) {
|
||||
const go = useGo(router);
|
||||
const len = this.tabList.length;
|
||||
const { path } = unref(router.currentRoute);
|
||||
|
||||
let toPath: PageEnum | string = PageEnum.BASE_HOME;
|
||||
|
||||
if (len > 0) {
|
||||
const page = this.tabList[len - 1];
|
||||
const p = page.fullPath || page.path;
|
||||
if (p) {
|
||||
toPath = p;
|
||||
}
|
||||
}
|
||||
// Jump to the current page and report an error
|
||||
path !== toPath && go(toPath as PageEnum, true);
|
||||
},
|
||||
|
||||
async addTab(route: RouteLocationNormalized) {
|
||||
const { path, name, fullPath, params, query, meta } = getRawRoute(route);
|
||||
// 404 The page does not need to add a tab
|
||||
if (
|
||||
path === PageEnum.ERROR_PAGE ||
|
||||
path === PageEnum.BASE_LOGIN ||
|
||||
!name ||
|
||||
[REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let updateIndex = -1;
|
||||
// Existing pages, do not add tabs repeatedly
|
||||
const tabHasExits = this.tabList.some((tab, index) => {
|
||||
updateIndex = index;
|
||||
return (tab.fullPath || tab.path) === (fullPath || path);
|
||||
});
|
||||
|
||||
// If the tab already exists, perform the update operation
|
||||
if (tabHasExits) {
|
||||
const curTab = toRaw(this.tabList)[updateIndex];
|
||||
if (!curTab) {
|
||||
return;
|
||||
}
|
||||
curTab.params = params || curTab.params;
|
||||
curTab.query = query || curTab.query;
|
||||
curTab.fullPath = fullPath || curTab.fullPath;
|
||||
this.tabList.splice(updateIndex, 1, curTab);
|
||||
} else {
|
||||
// Add tab
|
||||
// 获取动态路由打开数,超过 0 即代表需要控制打开数
|
||||
const dynamicLevel = meta?.dynamicLevel ?? -1;
|
||||
if (dynamicLevel > 0) {
|
||||
// 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了
|
||||
// 首先获取到真实的路由,使用配置方式减少计算开销.
|
||||
// const realName: string = path.match(/(\S*)\//)![1];
|
||||
const realPath = meta?.realPath ?? '';
|
||||
// 获取到已经打开的动态路由数, 判断是否大于某一个值
|
||||
if (this.tabList.filter((e) => e.meta?.realPath ?? '' === realPath).length >= dynamicLevel) {
|
||||
// 关闭第一个
|
||||
const index = this.tabList.findIndex((item) => item.meta.realPath === realPath);
|
||||
index !== -1 && this.tabList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
this.tabList.push(route);
|
||||
}
|
||||
this.updateCacheTab();
|
||||
cacheTab && Persistent.setLocal(MULTIPLE_TABS_KEY, this.tabList);
|
||||
},
|
||||
|
||||
async closeTab(tab: RouteLocationNormalized, router: Router) {
|
||||
const close = (route: RouteLocationNormalized) => {
|
||||
const { fullPath, meta: { affix } = {} } = route;
|
||||
if (affix) {
|
||||
return;
|
||||
}
|
||||
const index = this.tabList.findIndex((item) => item.fullPath === fullPath);
|
||||
index !== -1 && this.tabList.splice(index, 1);
|
||||
};
|
||||
|
||||
const { currentRoute, replace } = router;
|
||||
|
||||
const { path } = unref(currentRoute);
|
||||
if (path !== tab.path) {
|
||||
// Closed is not the activation tab
|
||||
close(tab);
|
||||
this.updateCacheTab();
|
||||
return;
|
||||
}
|
||||
|
||||
// Closed is activated atb
|
||||
let toTarget: RouteLocationRaw = {};
|
||||
|
||||
const index = this.tabList.findIndex((item) => item.path === path);
|
||||
|
||||
// If the current is the leftmost tab
|
||||
if (index === 0) {
|
||||
// There is only one tab, then jump to the homepage, otherwise jump to the right tab
|
||||
if (this.tabList.length === 1) {
|
||||
const userStore = useUserStore();
|
||||
toTarget = userStore.getUserInfo.homePath || PageEnum.BASE_HOME;
|
||||
} else {
|
||||
// Jump to the right tab
|
||||
const page = this.tabList[index + 1];
|
||||
toTarget = getToTarget(page);
|
||||
}
|
||||
} else {
|
||||
// Close the current tab
|
||||
const page = this.tabList[index - 1];
|
||||
toTarget = getToTarget(page);
|
||||
}
|
||||
close(currentRoute.value);
|
||||
await replace(toTarget);
|
||||
},
|
||||
|
||||
// Close according to key
|
||||
async closeTabByKey(key: string, router: Router) {
|
||||
const index = this.tabList.findIndex((item) => (item.fullPath || item.path) === key);
|
||||
if (index !== -1) {
|
||||
await this.closeTab(this.tabList[index], router);
|
||||
const { currentRoute, replace } = router;
|
||||
// 检查当前路由是否存在于tabList中
|
||||
const isActivated = this.tabList.findIndex((item) => {
|
||||
return item.fullPath === currentRoute.value.fullPath;
|
||||
});
|
||||
// 如果当前路由不存在于TabList中,尝试切换到其它路由
|
||||
if (isActivated === -1) {
|
||||
let pageIndex;
|
||||
if (index > 0) {
|
||||
pageIndex = index - 1;
|
||||
} else if (index < this.tabList.length - 1) {
|
||||
pageIndex = index + 1;
|
||||
} else {
|
||||
pageIndex = -1;
|
||||
}
|
||||
if (pageIndex >= 0) {
|
||||
const page = this.tabList[index - 1];
|
||||
const toTarget = getToTarget(page);
|
||||
await replace(toTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Sort the tabs
|
||||
async sortTabs(oldIndex: number, newIndex: number) {
|
||||
const currentTab = this.tabList[oldIndex];
|
||||
this.tabList.splice(oldIndex, 1);
|
||||
this.tabList.splice(newIndex, 0, currentTab);
|
||||
this.lastDragEndIndex = this.lastDragEndIndex + 1;
|
||||
},
|
||||
|
||||
// Close the tab on the right and jump
|
||||
async closeLeftTabs(route: RouteLocationNormalized, router: Router) {
|
||||
const index = this.tabList.findIndex((item) => item.path === route.path);
|
||||
let isCloseCurrentTab = false;
|
||||
if (index > 0) {
|
||||
const leftTabs = this.tabList.slice(0, index);
|
||||
const pathList: string[] = [];
|
||||
for (const item of leftTabs) {
|
||||
const affix = item?.meta?.affix ?? false;
|
||||
if (!affix) {
|
||||
pathList.push(item.fullPath);
|
||||
}
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
isCloseCurrentTab = closeTabContainCurrentRoute(router, pathList);
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
this.bulkCloseTabs(pathList);
|
||||
}
|
||||
this.updateCacheTab();
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
if (isCloseCurrentTab) {
|
||||
handleGotoPage(router, route.path);
|
||||
} else {
|
||||
handleGotoPage(router);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
},
|
||||
|
||||
// Close the tab on the left and jump
|
||||
async closeRightTabs(route: RouteLocationNormalized, router: Router) {
|
||||
const index = this.tabList.findIndex((item) => item.fullPath === route.fullPath);
|
||||
let isCloseCurrentTab = false;
|
||||
if (index >= 0 && index < this.tabList.length - 1) {
|
||||
const rightTabs = this.tabList.slice(index + 1, this.tabList.length);
|
||||
|
||||
const pathList: string[] = [];
|
||||
for (const item of rightTabs) {
|
||||
const affix = item?.meta?.affix ?? false;
|
||||
if (!affix) {
|
||||
pathList.push(item.fullPath);
|
||||
}
|
||||
}
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
isCloseCurrentTab = closeTabContainCurrentRoute(router, pathList);
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
this.bulkCloseTabs(pathList);
|
||||
}
|
||||
this.updateCacheTab();
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
if (isCloseCurrentTab) {
|
||||
handleGotoPage(router, route.path);
|
||||
} else {
|
||||
handleGotoPage(router);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
},
|
||||
|
||||
async closeAllTab(router: Router) {
|
||||
this.tabList = this.tabList.filter((item) => item?.meta?.affix ?? false);
|
||||
this.clearCacheTabs();
|
||||
this.goToPage(router);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Close other tabs
|
||||
*/
|
||||
async closeOtherTabs(route: RouteLocationNormalized, router: Router) {
|
||||
const closePathList = this.tabList.map((item) => item.fullPath);
|
||||
let isCloseCurrentTab = false;
|
||||
const pathList: string[] = [];
|
||||
|
||||
for (const path of closePathList) {
|
||||
if (path !== route.fullPath) {
|
||||
const closeItem = this.tabList.find((item) => item.path === path);
|
||||
if (!closeItem) {
|
||||
continue;
|
||||
}
|
||||
const affix = closeItem?.meta?.affix ?? false;
|
||||
if (!affix) {
|
||||
pathList.push(closeItem.fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
isCloseCurrentTab = closeTabContainCurrentRoute(router, pathList);
|
||||
this.bulkCloseTabs(pathList);
|
||||
this.updateCacheTab();
|
||||
// update-begin--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
if (isCloseCurrentTab) {
|
||||
handleGotoPage(router, route.path);
|
||||
} else {
|
||||
handleGotoPage(router);
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240605---for:【TV360X-732】非当前页右键关闭左侧、关闭右侧、关闭其它功能正常使用
|
||||
},
|
||||
|
||||
/**
|
||||
* Close tabs in bulk
|
||||
*/
|
||||
async bulkCloseTabs(pathList: string[]) {
|
||||
this.tabList = this.tabList.filter((item) => !pathList.includes(item.fullPath));
|
||||
},
|
||||
|
||||
/**
|
||||
* Set tab's title
|
||||
*/
|
||||
async setTabTitle(title: string, route: RouteLocationNormalized) {
|
||||
const findTab = this.getTabList.find((item) => item === route);
|
||||
if (findTab) {
|
||||
findTab.meta.title = title;
|
||||
await this.updateCacheTab();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* replace tab's path
|
||||
* **/
|
||||
async updateTabPath(fullPath: string, route: RouteLocationNormalized) {
|
||||
const findTab = this.getTabList.find((item) => item === route);
|
||||
if (findTab) {
|
||||
findTab.fullPath = fullPath;
|
||||
findTab.path = fullPath;
|
||||
await this.updateCacheTab();
|
||||
}
|
||||
},
|
||||
setRedirectPageParam(data) {
|
||||
this.redirectPageParam = data;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useMultipleTabWithOutStore() {
|
||||
return useMultipleTabStore(store);
|
||||
}
|
||||
314
jeecgboot-vue3/src/store/modules/permission.ts
Normal file
314
jeecgboot-vue3/src/store/modules/permission.ts
Normal file
@ -0,0 +1,314 @@
|
||||
import type { AppRouteRecordRaw, Menu } from '/@/router/types';
|
||||
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '/@/store';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useUserStore } from './user';
|
||||
import { useAppStoreWithOut } from './app';
|
||||
import { toRaw } from 'vue';
|
||||
import { transformObjToRoute, flatMultiLevelRoutes, addSlashToRouteComponent } from '/@/router/helper/routeHelper';
|
||||
import { transformRouteToMenu } from '/@/router/helper/menuHelper';
|
||||
|
||||
import projectSetting from '/@/settings/projectSetting';
|
||||
|
||||
import { PermissionModeEnum } from '/@/enums/appEnum';
|
||||
|
||||
import { asyncRoutes } from '/@/router/routes';
|
||||
import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
|
||||
import { staticRoutesList } from '../../router/routes/staticRouter';
|
||||
|
||||
import { filter } from '/@/utils/helper/treeHelper';
|
||||
|
||||
import { getBackMenuAndPerms } from '/@/api/sys/menu';
|
||||
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
|
||||
// 系统权限
|
||||
interface AuthItem {
|
||||
// 菜单权限编码,例如:“sys:schedule:list,sys:schedule:info”,多个逗号隔开
|
||||
action: string;
|
||||
// 权限策略1显示2禁用
|
||||
type: string | number;
|
||||
// 权限状态(0无效1有效)
|
||||
status: string | number;
|
||||
// 权限名称
|
||||
describe?: string;
|
||||
isAuth?: boolean;
|
||||
}
|
||||
|
||||
interface PermissionState {
|
||||
// Permission code list
|
||||
permCodeList: string[] | number[];
|
||||
// Whether the route has been dynamically added
|
||||
isDynamicAddedRoute: boolean;
|
||||
// To trigger a menu update
|
||||
lastBuildMenuTime: number;
|
||||
// Backstage menu list
|
||||
backMenuList: Menu[];
|
||||
frontMenuList: Menu[];
|
||||
// 用户所拥有的权限
|
||||
authList: AuthItem[];
|
||||
// 全部权限配置
|
||||
allAuthList: AuthItem[];
|
||||
// 系统安全模式
|
||||
sysSafeMode: boolean;
|
||||
// online子表按钮权限
|
||||
onlineSubTableAuthMap: object;
|
||||
}
|
||||
export const usePermissionStore = defineStore({
|
||||
id: 'app-permission',
|
||||
state: (): PermissionState => ({
|
||||
permCodeList: [],
|
||||
// Whether the route has been dynamically added
|
||||
isDynamicAddedRoute: false,
|
||||
// To trigger a menu update
|
||||
lastBuildMenuTime: 0,
|
||||
// Backstage menu list
|
||||
backMenuList: [],
|
||||
// menu List
|
||||
frontMenuList: [],
|
||||
authList: [],
|
||||
allAuthList: [],
|
||||
sysSafeMode: false,
|
||||
onlineSubTableAuthMap: {},
|
||||
}),
|
||||
getters: {
|
||||
getPermCodeList(): string[] | number[] {
|
||||
return this.permCodeList;
|
||||
},
|
||||
getBackMenuList(): Menu[] {
|
||||
return this.backMenuList;
|
||||
},
|
||||
getFrontMenuList(): Menu[] {
|
||||
return this.frontMenuList;
|
||||
},
|
||||
getLastBuildMenuTime(): number {
|
||||
return this.lastBuildMenuTime;
|
||||
},
|
||||
getIsDynamicAddedRoute(): boolean {
|
||||
return this.isDynamicAddedRoute;
|
||||
},
|
||||
|
||||
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
|
||||
getOnlineSubTableAuth: (state) => {
|
||||
return (code) => state.onlineSubTableAuthMap[code];
|
||||
},
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
|
||||
},
|
||||
actions: {
|
||||
setPermCodeList(codeList: string[]) {
|
||||
this.permCodeList = codeList;
|
||||
},
|
||||
|
||||
setBackMenuList(list: Menu[]) {
|
||||
this.backMenuList = list;
|
||||
list?.length > 0 && this.setLastBuildMenuTime();
|
||||
},
|
||||
|
||||
setFrontMenuList(list: Menu[]) {
|
||||
this.frontMenuList = list;
|
||||
},
|
||||
|
||||
setLastBuildMenuTime() {
|
||||
this.lastBuildMenuTime = new Date().getTime();
|
||||
},
|
||||
|
||||
setDynamicAddedRoute(added: boolean) {
|
||||
this.isDynamicAddedRoute = added;
|
||||
},
|
||||
resetState(): void {
|
||||
this.isDynamicAddedRoute = false;
|
||||
this.permCodeList = [];
|
||||
this.backMenuList = [];
|
||||
this.lastBuildMenuTime = 0;
|
||||
},
|
||||
async changePermissionCode() {
|
||||
const systemPermission = await getBackMenuAndPerms();
|
||||
const codeList = systemPermission.codeList;
|
||||
this.setPermCodeList(codeList);
|
||||
this.setAuthData(systemPermission);
|
||||
|
||||
//菜单路由
|
||||
const routeList = systemPermission.menu;
|
||||
return routeList;
|
||||
},
|
||||
async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const appStore = useAppStoreWithOut();
|
||||
|
||||
let routes: AppRouteRecordRaw[] = [];
|
||||
const roleList = toRaw(userStore.getRoleList) || [];
|
||||
const { permissionMode = projectSetting.permissionMode } = appStore.getProjectConfig;
|
||||
|
||||
const routeFilter = (route: AppRouteRecordRaw) => {
|
||||
const { meta } = route;
|
||||
const { roles } = meta || {};
|
||||
if (!roles) return true;
|
||||
return roleList.some((role) => roles.includes(role));
|
||||
};
|
||||
|
||||
const routeRemoveIgnoreFilter = (route: AppRouteRecordRaw) => {
|
||||
const { meta } = route;
|
||||
const { ignoreRoute } = meta || {};
|
||||
return !ignoreRoute;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 根据设置的首页path,修正routes中的affix标记(固定首页)
|
||||
* */
|
||||
const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
|
||||
if (!routes || routes.length === 0) return;
|
||||
let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME;
|
||||
function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
|
||||
if (parentPath) parentPath = parentPath + '/';
|
||||
routes.forEach((route: AppRouteRecordRaw) => {
|
||||
const { path, children, redirect } = route;
|
||||
const currentPath = path.startsWith('/') ? path : parentPath + path;
|
||||
if (currentPath === homePath) {
|
||||
if (redirect) {
|
||||
homePath = route.redirect! as string;
|
||||
} else {
|
||||
route.meta = Object.assign({}, route.meta, { affix: true });
|
||||
throw new Error('end');
|
||||
}
|
||||
}
|
||||
children && children.length > 0 && patcher(children, currentPath);
|
||||
});
|
||||
}
|
||||
try {
|
||||
patcher(routes);
|
||||
} catch (e) {
|
||||
// 已处理完毕跳出循环
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
switch (permissionMode) {
|
||||
case PermissionModeEnum.ROLE:
|
||||
routes = filter(asyncRoutes, routeFilter);
|
||||
routes = routes.filter(routeFilter);
|
||||
// 将多级路由转换为二级
|
||||
routes = flatMultiLevelRoutes(routes);
|
||||
break;
|
||||
|
||||
case PermissionModeEnum.ROUTE_MAPPING:
|
||||
routes = filter(asyncRoutes, routeFilter);
|
||||
routes = routes.filter(routeFilter);
|
||||
const menuList = transformRouteToMenu(routes, true);
|
||||
routes = filter(routes, routeRemoveIgnoreFilter);
|
||||
routes = routes.filter(routeRemoveIgnoreFilter);
|
||||
menuList.sort((a, b) => {
|
||||
return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0);
|
||||
});
|
||||
|
||||
this.setFrontMenuList(menuList);
|
||||
// 将多级路由转换为二级
|
||||
routes = flatMultiLevelRoutes(routes);
|
||||
break;
|
||||
|
||||
// 后台菜单构建
|
||||
case PermissionModeEnum.BACK:
|
||||
const { createMessage, createWarningModal } = useMessage();
|
||||
console.log(" --- 构建后台路由菜单 --- ")
|
||||
// 菜单加载提示
|
||||
// createMessage.loading({
|
||||
// content: t('sys.app.menuLoading'),
|
||||
// duration: 1,
|
||||
// });
|
||||
|
||||
// 从后台获取权限码,
|
||||
// 这个函数可能只需要执行一次,并且实际的项目可以在正确的时间被放置
|
||||
let routeList: AppRouteRecordRaw[] = [];
|
||||
try {
|
||||
routeList = await this.changePermissionCode();
|
||||
//routeList = (await getMenuList()) as AppRouteRecordRaw[];
|
||||
// update-begin--author:liaozhiyang---date:20240313---for:【QQYUN-8487】注释掉判断菜单是否vue2版本逻辑代码
|
||||
// update-begin----author:sunjianlei---date:20220315------for: 判断是否是 vue3 版本的菜单 ---
|
||||
// let hasIndex: boolean = false;
|
||||
// let hasIcon: boolean = false;
|
||||
// for (let menuItem of routeList) {
|
||||
// // 条件1:判断组件是否是 layouts/default/index
|
||||
// if (!hasIndex) {
|
||||
// hasIndex = menuItem.component === 'layouts/default/index';
|
||||
// }
|
||||
// // 条件2:判断图标是否带有 冒号
|
||||
// if (!hasIcon) {
|
||||
// hasIcon = !!menuItem.meta?.icon?.includes(':');
|
||||
// }
|
||||
// // 满足任何一个条件都直接跳出循环
|
||||
// if (hasIcon || hasIndex) {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// // 两个条件都不满足,就弹出提示框
|
||||
// if (!hasIcon && !hasIndex) {
|
||||
// // 延迟1.5秒之后再出现提示,否则提示框出不来
|
||||
// setTimeout(
|
||||
// () =>
|
||||
// createWarningModal({
|
||||
// title: '检测提示',
|
||||
// content:
|
||||
// '当前菜单表是 <b>Vue2版本</b>,导致菜单加载异常!<br>点击确认,切换到Vue3版菜单!',
|
||||
// onOk:function () {
|
||||
// switchVue3Menu();
|
||||
// location.reload();
|
||||
// }
|
||||
// }),
|
||||
// 100
|
||||
// );
|
||||
// }
|
||||
// update-end----author:sunjianlei---date:20220315------for: 判断是否是 vue3 版本的菜单 ---
|
||||
// update-end--author:liaozhiyang---date:20240313---for:【QQYUN-8487】注释掉判断菜单是否vue2版本逻辑代码
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
// 组件地址前加斜杠处理 author: lsq date:2021-09-08
|
||||
routeList = addSlashToRouteComponent(routeList);
|
||||
// 动态引入组件
|
||||
routeList = transformObjToRoute(routeList);
|
||||
|
||||
// 构建后台路由菜单
|
||||
const backMenuList = transformRouteToMenu(routeList);
|
||||
this.setBackMenuList(backMenuList);
|
||||
|
||||
// 删除meta.ignoreRoute项
|
||||
routeList = filter(routeList, routeRemoveIgnoreFilter);
|
||||
routeList = routeList.filter(routeRemoveIgnoreFilter);
|
||||
|
||||
routeList = flatMultiLevelRoutes(routeList);
|
||||
// update-begin--author:liaozhiyang---date:20240529---for:【TV360X-522】ai助手路由写死在前端
|
||||
routes = [PAGE_NOT_FOUND_ROUTE, ...routeList, ...staticRoutesList];
|
||||
// update-end--author:liaozhiyang---date:20240529---for:【TV360X-522】ai助手路由写死在前端
|
||||
break;
|
||||
}
|
||||
|
||||
routes.push(ERROR_LOG_ROUTE);
|
||||
patchHomeAffix(routes);
|
||||
return routes;
|
||||
},
|
||||
setAuthData(systemPermission) {
|
||||
this.authList = systemPermission.auth;
|
||||
this.allAuthList = systemPermission.allAuth;
|
||||
this.sysSafeMode = systemPermission.sysSafeMode;
|
||||
},
|
||||
setAuthList(authList: AuthItem[]) {
|
||||
this.authList = authList;
|
||||
},
|
||||
setAllAuthList(authList: AuthItem[]) {
|
||||
this.allAuthList = authList;
|
||||
},
|
||||
|
||||
//update-begin-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
|
||||
setOnlineSubTableAuth(code, hideBtnList) {
|
||||
this.onlineSubTableAuthMap[code] = hideBtnList;
|
||||
},
|
||||
//update-end-author:taoyan date:2022-6-1 for: VUEN-1162 子表按钮没控制
|
||||
},
|
||||
});
|
||||
|
||||
// 需要在设置之外使用
|
||||
export function usePermissionStoreWithOut() {
|
||||
return usePermissionStore(store);
|
||||
}
|
||||
389
jeecgboot-vue3/src/store/modules/user.ts
Normal file
389
jeecgboot-vue3/src/store/modules/user.ts
Normal file
@ -0,0 +1,389 @@
|
||||
import type { UserInfo, LoginInfo } from '/#/store';
|
||||
import type { ErrorMessageMode } from '/#/axios';
|
||||
import { defineStore } from 'pinia';
|
||||
import { store } from '/@/store';
|
||||
import { RoleEnum } from '/@/enums/roleEnum';
|
||||
import { PageEnum } from '/@/enums/pageEnum';
|
||||
import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY, LOGIN_INFO_KEY, DB_DICT_DATA_KEY, TENANT_ID, OAUTH2_THIRD_LOGIN_TENANT_ID } from '/@/enums/cacheEnum';
|
||||
import { getAuthCache, setAuthCache, removeAuthCache } from '/@/utils/auth';
|
||||
import { GetUserInfoModel, LoginParams, ThirdLoginParams } from '/@/api/sys/model/userModel';
|
||||
import { doLogout, getUserInfo, loginApi, phoneLoginApi, thirdLogin } from '/@/api/sys/user';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { router } from '/@/router';
|
||||
import { usePermissionStore } from '/@/store/modules/permission';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic';
|
||||
import { isArray } from '/@/utils/is';
|
||||
import { useGlobSetting } from '/@/hooks/setting';
|
||||
import { JDragConfigEnum } from '/@/enums/jeecgEnum';
|
||||
import { useSso } from '/@/hooks/web/useSso';
|
||||
import { isOAuth2AppEnv } from "/@/views/sys/login/useLogin";
|
||||
import { getUrlParam } from "@/utils";
|
||||
interface dictType {
|
||||
[key: string]: any;
|
||||
}
|
||||
interface UserState {
|
||||
userInfo: Nullable<UserInfo>;
|
||||
token?: string;
|
||||
roleList: RoleEnum[];
|
||||
dictItems?: dictType | null;
|
||||
sessionTimeout?: boolean;
|
||||
lastUpdateTime: number;
|
||||
tenantid?: string | number;
|
||||
shareTenantId?: Nullable<string | number>;
|
||||
loginInfo?: Nullable<LoginInfo>;
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore({
|
||||
id: 'app-user',
|
||||
state: (): UserState => ({
|
||||
// 用户信息
|
||||
userInfo: null,
|
||||
// token
|
||||
token: undefined,
|
||||
// 角色列表
|
||||
roleList: [],
|
||||
// 字典
|
||||
dictItems: null,
|
||||
// session过期时间
|
||||
sessionTimeout: false,
|
||||
// Last fetch time
|
||||
lastUpdateTime: 0,
|
||||
//租户id
|
||||
tenantid: '',
|
||||
// 分享租户ID
|
||||
// 用于分享页面所属租户与当前用户登录租户不一致的情况
|
||||
shareTenantId: null,
|
||||
//登录返回信息
|
||||
loginInfo: null,
|
||||
}),
|
||||
getters: {
|
||||
getUserInfo(): UserInfo {
|
||||
if(this.userInfo == null){
|
||||
this.userInfo = getAuthCache<UserInfo>(USER_INFO_KEY)!=null ? getAuthCache<UserInfo>(USER_INFO_KEY) : null;
|
||||
}
|
||||
return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {};
|
||||
},
|
||||
getLoginInfo(): LoginInfo {
|
||||
return this.loginInfo || getAuthCache<LoginInfo>(LOGIN_INFO_KEY) || {};
|
||||
},
|
||||
getToken(): string {
|
||||
return this.token || getAuthCache<string>(TOKEN_KEY);
|
||||
},
|
||||
getAllDictItems(): [] {
|
||||
return this.dictItems || getAuthCache(DB_DICT_DATA_KEY);
|
||||
},
|
||||
getRoleList(): RoleEnum[] {
|
||||
return this.roleList.length > 0 ? this.roleList : getAuthCache<RoleEnum[]>(ROLES_KEY);
|
||||
},
|
||||
getSessionTimeout(): boolean {
|
||||
return !!this.sessionTimeout;
|
||||
},
|
||||
getLastUpdateTime(): number {
|
||||
return this.lastUpdateTime;
|
||||
},
|
||||
getTenant(): string | number {
|
||||
return this.tenantid || getAuthCache<string | number>(TENANT_ID);
|
||||
},
|
||||
// 是否有分享租户id
|
||||
hasShareTenantId(): boolean {
|
||||
return this.shareTenantId != null && this.shareTenantId !== '';
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
setToken(info: string | undefined) {
|
||||
this.token = info ? info : ''; // for null or undefined value
|
||||
setAuthCache(TOKEN_KEY, info);
|
||||
},
|
||||
setRoleList(roleList: RoleEnum[]) {
|
||||
this.roleList = roleList;
|
||||
setAuthCache(ROLES_KEY, roleList);
|
||||
},
|
||||
setUserInfo(info: UserInfo | null) {
|
||||
this.userInfo = info;
|
||||
this.lastUpdateTime = new Date().getTime();
|
||||
setAuthCache(USER_INFO_KEY, info);
|
||||
},
|
||||
setLoginInfo(info: LoginInfo | null) {
|
||||
this.loginInfo = info;
|
||||
setAuthCache(LOGIN_INFO_KEY, info);
|
||||
},
|
||||
setAllDictItems(dictItems) {
|
||||
this.dictItems = dictItems;
|
||||
setAuthCache(DB_DICT_DATA_KEY, dictItems);
|
||||
},
|
||||
setAllDictItemsByLocal() {
|
||||
// update-begin--author:liaozhiyang---date:20240321---for:【QQYUN-8572】表格行选择卡顿问题(customRender中字典引起的)
|
||||
if (!this.dictItems) {
|
||||
const allDictItems = getAuthCache(DB_DICT_DATA_KEY);
|
||||
if (allDictItems) {
|
||||
this.dictItems = allDictItems;
|
||||
}
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240321---for:【QQYUN-8572】表格行选择卡顿问题(customRender中字典引起的)
|
||||
},
|
||||
setTenant(id) {
|
||||
this.tenantid = id;
|
||||
setAuthCache(TENANT_ID, id);
|
||||
},
|
||||
setShareTenantId(id: NonNullable<typeof this.shareTenantId>) {
|
||||
this.shareTenantId = id;
|
||||
},
|
||||
setSessionTimeout(flag: boolean) {
|
||||
this.sessionTimeout = flag;
|
||||
},
|
||||
resetState() {
|
||||
this.userInfo = null;
|
||||
this.dictItems = null;
|
||||
this.token = '';
|
||||
this.roleList = [];
|
||||
this.sessionTimeout = false;
|
||||
},
|
||||
/**
|
||||
* 登录事件
|
||||
*/
|
||||
async login(
|
||||
params: LoginParams & {
|
||||
goHome?: boolean;
|
||||
mode?: ErrorMessageMode;
|
||||
}
|
||||
): Promise<GetUserInfoModel | null> {
|
||||
try {
|
||||
const { goHome = true, mode, ...loginParams } = params;
|
||||
const data = await loginApi(loginParams, mode);
|
||||
const { token, userInfo } = data;
|
||||
// save token
|
||||
this.setToken(token);
|
||||
this.setTenant(userInfo.loginTenantId);
|
||||
return this.afterLoginAction(goHome, data);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 扫码登录事件
|
||||
*/
|
||||
async qrCodeLogin(token): Promise<GetUserInfoModel | null> {
|
||||
try {
|
||||
// save token
|
||||
this.setToken(token);
|
||||
return this.afterLoginAction(true, {});
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 登录完成处理
|
||||
* @param goHome
|
||||
*/
|
||||
async afterLoginAction(goHome?: boolean, data?: any): Promise<any | null> {
|
||||
if (!this.getToken) return null;
|
||||
//获取用户信息
|
||||
const userInfo = await this.getUserInfoAction();
|
||||
const sessionTimeout = this.sessionTimeout;
|
||||
if (sessionTimeout) {
|
||||
this.setSessionTimeout(false);
|
||||
} else {
|
||||
//update-begin---author:scott ---date::2024-02-21 for:【QQYUN-8326】登录不需要构建路由,进入首页有构建---
|
||||
// // 构建后台菜单路由
|
||||
// const permissionStore = usePermissionStore();
|
||||
// if (!permissionStore.isDynamicAddedRoute) {
|
||||
// const routes = await permissionStore.buildRoutesAction();
|
||||
// routes.forEach((route) => {
|
||||
// router.addRoute(route as unknown as RouteRecordRaw);
|
||||
// });
|
||||
// router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);
|
||||
// permissionStore.setDynamicAddedRoute(true);
|
||||
// }
|
||||
//update-end---author:scott ---date::2024-02-21 for:【QQYUN-8326】登录不需要构建路由,进入首页有构建---
|
||||
|
||||
await this.setLoginInfo({ ...data, isLogin: true });
|
||||
//update-begin-author:liusq date:2022-5-5 for:登录成功后缓存拖拽模块的接口前缀
|
||||
localStorage.setItem(JDragConfigEnum.DRAG_BASE_URL, useGlobSetting().domainUrl);
|
||||
//update-end-author:liusq date:2022-5-5 for: 登录成功后缓存拖拽模块的接口前缀
|
||||
|
||||
// update-begin-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
|
||||
let redirect = router.currentRoute.value?.query?.redirect as string;
|
||||
// 判断是否有 redirect 重定向地址
|
||||
//update-begin---author:wangshuai ---date:20230424 for:【QQYUN-5195】登录之后直接刷新页面导致没有进入创建组织页面------------
|
||||
if (redirect && goHome) {
|
||||
//update-end---author:wangshuai ---date:20230424 for:【QQYUN-5195】登录之后直接刷新页面导致没有进入创建组织页面------------
|
||||
// update-begin--author:liaozhiyang---date:20240104---for:【QQYUN-7804】部署生产环境,登录跳转404问题
|
||||
let publicPath = import.meta.env.VITE_PUBLIC_PATH;
|
||||
if (publicPath && publicPath != '/') {
|
||||
// update-begin--author:liaozhiyang---date:20240509---for:【issues/1147】登录跳转时去掉发布路径的最后一个/以解决404问题
|
||||
if (publicPath.endsWith('/')) {
|
||||
publicPath = publicPath.slice(0, -1);
|
||||
}
|
||||
redirect = publicPath + redirect;
|
||||
}
|
||||
// update-end--author:liaozhiyang---date:20240509---for:【issues/1147】登录跳转时去掉发布路径的最后一个/以解决404问题
|
||||
// 当前页面打开
|
||||
window.open(redirect, '_self')
|
||||
return data;
|
||||
}
|
||||
// update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
|
||||
|
||||
//update-begin---author:wangshuai---date:2024-04-03---for:【issues/1102】设置单点登录后页面,进入首页提示404,也没有绘制侧边栏 #1102---
|
||||
let ticket = getUrlParam('ticket');
|
||||
if(ticket){
|
||||
goHome && (window.location.replace((userInfo && userInfo.homePath) || PageEnum.BASE_HOME));
|
||||
}else{
|
||||
goHome && (await router.replace((userInfo && userInfo.homePath) || PageEnum.BASE_HOME));
|
||||
}
|
||||
//update-end---author:wangshuai---date:2024-04-03---for:【issues/1102】设置单点登录后页面,进入首页提示404,也没有绘制侧边栏 #1102---
|
||||
}
|
||||
return data;
|
||||
},
|
||||
/**
|
||||
* 手机号登录
|
||||
* @param params
|
||||
*/
|
||||
async phoneLogin(
|
||||
params: LoginParams & {
|
||||
goHome?: boolean;
|
||||
mode?: ErrorMessageMode;
|
||||
}
|
||||
): Promise<GetUserInfoModel | null> {
|
||||
try {
|
||||
const { goHome = true, mode, ...loginParams } = params;
|
||||
const data = await phoneLoginApi(loginParams, mode);
|
||||
const { token } = data;
|
||||
// save token
|
||||
this.setToken(token);
|
||||
return this.afterLoginAction(goHome, data);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
async getUserInfoAction(): Promise<UserInfo | null> {
|
||||
if (!this.getToken) {
|
||||
return null;
|
||||
}
|
||||
const { userInfo, sysAllDictItems } = await getUserInfo();
|
||||
if (userInfo) {
|
||||
const { roles = [] } = userInfo;
|
||||
if (isArray(roles)) {
|
||||
const roleList = roles.map((item) => item.value) as RoleEnum[];
|
||||
this.setRoleList(roleList);
|
||||
} else {
|
||||
userInfo.roles = [];
|
||||
this.setRoleList([]);
|
||||
}
|
||||
this.setUserInfo(userInfo);
|
||||
}
|
||||
/**
|
||||
* 添加字典信息到缓存
|
||||
* @updateBy:lsq
|
||||
* @updateDate:2021-09-08
|
||||
*/
|
||||
if (sysAllDictItems) {
|
||||
this.setAllDictItems(sysAllDictItems);
|
||||
}
|
||||
return userInfo;
|
||||
},
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
async logout(goLogin = false) {
|
||||
if (this.getToken) {
|
||||
try {
|
||||
await doLogout();
|
||||
} catch {
|
||||
console.log('注销Token失败');
|
||||
}
|
||||
}
|
||||
|
||||
// //update-begin-author:taoyan date:2022-5-5 for: src/layouts/default/header/index.vue showLoginSelect方法 获取tenantId 退出登录后再次登录依然能获取到值,没有清空
|
||||
// let username:any = this.userInfo && this.userInfo.username;
|
||||
// if(username){
|
||||
// removeAuthCache(username)
|
||||
// }
|
||||
// //update-end-author:taoyan date:2022-5-5 for: src/layouts/default/header/index.vue showLoginSelect方法 获取tenantId 退出登录后再次登录依然能获取到值,没有清空
|
||||
|
||||
this.setToken('');
|
||||
setAuthCache(TOKEN_KEY, null);
|
||||
this.setSessionTimeout(false);
|
||||
this.setUserInfo(null);
|
||||
this.setLoginInfo(null);
|
||||
this.setTenant(null);
|
||||
// update-begin--author:liaozhiyang---date:20240517---for:【TV360X-23】退出登录后会提示「Token时效,请重新登录」
|
||||
setTimeout(() => {
|
||||
this.setAllDictItems(null);
|
||||
}, 1e3);
|
||||
// update-end--author:liaozhiyang---date:20240517---for:【TV360X-23】退出登录后会提示「Token时效,请重新登录」
|
||||
//update-begin-author:liusq date:2022-5-5 for:退出登录后清除拖拽模块的接口前缀
|
||||
localStorage.removeItem(JDragConfigEnum.DRAG_BASE_URL);
|
||||
//update-end-author:liusq date:2022-5-5 for: 退出登录后清除拖拽模块的接口前缀
|
||||
|
||||
//如果开启单点登录,则跳转到单点统一登录中心
|
||||
const openSso = useGlobSetting().openSso;
|
||||
if (openSso == 'true') {
|
||||
await useSso().ssoLoginOut();
|
||||
}
|
||||
//update-begin---author:wangshuai ---date:20230224 for:[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
|
||||
//退出登录的时候需要用的应用id
|
||||
if(isOAuth2AppEnv()){
|
||||
let tenantId = getAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
|
||||
removeAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
|
||||
goLogin && await router.push({ name:"Login",query:{ tenantId:tenantId }})
|
||||
}else{
|
||||
// update-begin-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
|
||||
goLogin && (await router.push({
|
||||
path: PageEnum.BASE_LOGIN,
|
||||
query: {
|
||||
// 传入当前的路由,登录成功后跳转到当前路由
|
||||
redirect: router.currentRoute.value.fullPath,
|
||||
}
|
||||
}));
|
||||
// update-end-author:sunjianlei date:20230306 for: 修复登录成功后,没有正确重定向的问题
|
||||
|
||||
}
|
||||
//update-end---author:wangshuai ---date:20230224 for:[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
|
||||
},
|
||||
/**
|
||||
* 登录事件
|
||||
*/
|
||||
async ThirdLogin(
|
||||
params: ThirdLoginParams & {
|
||||
goHome?: boolean;
|
||||
mode?: ErrorMessageMode;
|
||||
}
|
||||
): Promise<any | null> {
|
||||
try {
|
||||
const { goHome = true, mode, ...ThirdLoginParams } = params;
|
||||
const data = await thirdLogin(ThirdLoginParams, mode);
|
||||
const { token } = data;
|
||||
// save token
|
||||
this.setToken(token);
|
||||
return this.afterLoginAction(goHome, data);
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 退出询问
|
||||
*/
|
||||
confirmLoginOut() {
|
||||
const { createConfirm } = useMessage();
|
||||
const { t } = useI18n();
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('sys.app.logoutTip'),
|
||||
content: t('sys.app.logoutMessage'),
|
||||
onOk: async () => {
|
||||
await this.logout(true);
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Need to be used outside the setup
|
||||
export function useUserStoreWithOut() {
|
||||
return useUserStore(store);
|
||||
}
|
||||
Reference in New Issue
Block a user