import { defineStore } from "pinia"; import { KeepAliveState } from "@/stores/interface"; import { cloneDeep } from "lodash-es"; import { useTabsStore } from "@/stores/modules/tabs"; import { LockBill, UnLockBill } from "@/api/modules/common"; // 最大缓存页面数量限制 const MAX_KEEP_ALIVE_COUNT = 10; export const useKeepAliveStore = defineStore({ id: "geeker-keepAlive", state: (): KeepAliveState => ({ keepAliveName: [], keepAliveList: [], closeAliveList: [] }), getters: { getKeepAliveList(): any[] { return this.keepAliveList; } }, actions: { setKeepAliveList(keepAliveList: any) { this.keepAliveList = keepAliveList; console.log("getKeepAliveName routeItem setKeepAliveList :>> ", JSON.stringify(this.keepAliveList)); console.log("getKeepAliveName routeItem setKeepAliveList :>> ", this.keepAliveList); }, // Add KeepAliveName async addKeepAliveName(route: any) { !this.keepAliveName.includes(route.name) && this.keepAliveName.push(route.name); let routeItem = this.keepAliveList.find((item: any) => item.fullPath == route.fullPath); console.log("getKeepAliveName routeItem addKeepAliveName :>> ", JSON.stringify(this.keepAliveList)); console.log("getKeepAliveName routeItem removeKeepAliveName closeAliveList:>> ", JSON.stringify(this.closeAliveList)); if (!routeItem) { let closeItem = this.closeAliveList.find((item: any) => item.fullPath == route.fullPath); routeItem = { fullPath: route.fullPath, times: 0 }; if (closeItem) { routeItem.times = closeItem.times + 1; this.closeAliveList = this.closeAliveList.filter((item: any) => item.fullPath != route.fullPath); } this.keepAliveList.push(routeItem); // 检查缓存数量限制,如果超过限制则清理最旧的缓存 this.cleanOldKeepAliveCache(); } }, // Remove KeepAliveName removeKeepAliveName(route: any) { this.keepAliveName = this.keepAliveName.filter(item => item !== route.name); let index = this.keepAliveList.findIndex(item => item.fullPath === route.fullPath); if (index > -1) { if (this.closeAliveList.every(item => item.fullPath !== route.fullPath)) { this.closeAliveList.push(this.keepAliveList[index]); } this.keepAliveList.splice(index, 1); // this.keepAliveList[index].times = this.keepAliveList[index].times + 1; if (route.needLock && Number(route.billid)) { UnLockBill({ keyword: String(route.keyword), billid: Number(route.billid), billcode: String(route.billcode) }).catch(err => { console.warn("解锁失败:", route.keyword, route.billid, err); }); } } console.log("getKeepAliveName routeItem removeKeepAliveName :>> ", index, JSON.stringify(this.keepAliveList)); console.log("getKeepAliveName routeItem removeKeepAliveName :>> ", this.keepAliveList); console.log("getKeepAliveName routeItem keepAliveName :>> ", this.keepAliveName); }, // Set KeepAliveName async setKeepAliveName(keepAliveName: string[] = []) { this.keepAliveName = keepAliveName; }, async updateKeepAliveName(route: any) { // this.addKeepAliveName(route); console.log("updateKeepAliveName route :>> ", route, this.keepAliveList); this.keepAliveList.forEach((item: any) => { console.log("updateKeepAliveName item :>> ", item.fullPath, route.fullPath, item.fullPath == route.fullPath); if (item.fullPath == route.fullPath) { item.times++; } }); }, getKeepAliveName(name: string) { let routeItem = this.keepAliveList.find((item: any) => item.fullPath == name); if (!routeItem) { // routeItem = { // fullPath: name, // times: 0 // }; // this.keepAliveList.push(routeItem); return undefined; } return routeItem; }, // 清理过期的 keepAlive 缓存 cleanOldKeepAliveCache() { if (this.keepAliveList.length <= MAX_KEEP_ALIVE_COUNT) { return; } const tabStore = useTabsStore(); // 过滤出编辑页,跳过不清理 const editPages = this.keepAliveList.filter(item => { const tab = tabStore.tabsMenuList.find(tabItem => tabItem.path === item.fullPath); return tab && (tab.path.includes("/edit?") || tab.path.includes("/edit/")); }); // 计算可清理的缓存数量(总数减去编辑页数量) const availableToClean = this.keepAliveList.length - editPages.length; const maxCleanable = MAX_KEEP_ALIVE_COUNT - editPages.length; // 如果非编辑页数量在限制范围内,不需要清理 if (availableToClean <= maxCleanable) { console.log(`非编辑页缓存数量 (${availableToClean}) 在限制范围内,跳过清理`); return; } console.log( `keepAlive 缓存数量 (${this.keepAliveList.length}) 超过限制,编辑页: ${editPages.length},开始清理非编辑页缓存` ); // 获取非编辑页缓存进行清理 const nonEditPages = this.keepAliveList.filter(item => { const tab = tabStore.tabsMenuList.find(tabItem => tabItem.path === item.fullPath); return !tab || (!tab.path.includes("/edit?") && !tab.path.includes("/edit/")); }); // 按访问次数排序,优先清理访问次数少的缓存 const sortedCache = nonEditPages.sort((a, b) => a.times - b.times); // 需要清理的数量 const removeCount = availableToClean - maxCleanable; const toRemove = sortedCache.slice(0, removeCount); // 清理 keepAliveName 和 keepAliveList toRemove.forEach(item => { console.log(`清理 keepAlive 缓存: ${item.fullPath} (访问次数: ${item.times})`); // 从 keepAliveList 中移除 const index = this.keepAliveList.findIndex(cacheItem => cacheItem.fullPath === item.fullPath); if (index > -1) { this.keepAliveList.splice(index, 1); } // 从 keepAliveName 中移除对应的组件名 const tab = tabStore.tabsMenuList.find(tabItem => tabItem.path === item.fullPath); if (tab && tab.name) { const nameIndex = this.keepAliveName.indexOf(tab.name); if (nameIndex > -1) { this.keepAliveName.splice(nameIndex, 1); } } }); console.log(`keepAlive 缓存清理完成,当前缓存数量: ${this.keepAliveList.length}`); }, computedKeepAliveName(to, from) { this.computedKeepAliveNamePro(to, from); return; const tabStore = useTabsStore(); const tabsParams = { icon: to.meta.icon as string, title: to.meta.title as string, path: to.fullPath, fullPath: to.fullPath, name: to.name as string, close: !to.meta.isAffix }; if (to.meta.needLock && Number(to.query.id)) { tabsParams["needLock"] = to.meta.needLock; tabsParams["keyword"] = String(to.meta.billtype); tabsParams["billid"] = Number(to.query.id); tabsParams["billcode"] = String(to.query.code); } const isEditToDetail = from.fullPath.includes("/edit") && to.fullPath.includes("/detail") && from.query?.id && from.meta?.needLock; const isSameBill = from.query?.code === to.query?.code; if (isEditToDetail && isSameBill) { UnLockBill({ keyword: String(from.meta.billtype), billid: Number(from.query.id), billcode: String(from.query.code) }).catch(err => { console.warn("edit->detail 解锁失败:", err); }); } let isReplace = false; if (tabsParams.path.indexOf("/edit?") > -1 || tabsParams.path.indexOf("/edit/") > -1) { let key = "/edit?"; let name = ""; let _path = tabsParams.path.replace(key, "/detail?"); // let _path = from.fullPath; // 使用路由参数的地址,忽略查询参数后,查找相同地址 let _path2 = tabsParams.path.replace("/edit/", "/detail/"); let hasPath1 = to.fullPath.indexOf("/edit?") > -1; let hasPath2 = to.fullPath.indexOf("/edit/") > -1; let tabsMenuList = tabStore.tabsMenuList; console.log("tabsMenuList :>> ", JSON.stringify(tabsMenuList)); for (let i = 0; i < tabsMenuList.length; i++) { if ( (hasPath1 && tabsMenuList[i].path == _path) || (hasPath2 && tabsMenuList[i].path.split("?")[0] == _path2.split("?")[0]) ) { name = tabsMenuList[i].name; tabsMenuList[i] = tabsParams; isReplace = true; break; } } console.log("keilll name edit:>> ", name); tabStore.setTabs(tabsMenuList); isReplace && this.addKeepAliveName(to); isReplace && this.removeKeepAliveName(from); name && to.meta.isKeepAlive && this.updateKeepAliveName(to); // name && this.updateKeepAliveName(to); // to.meta.isKeepAlive && this.updateKeepAliveName(route); } else if (tabsParams.path.indexOf("/detail?") > -1 || tabsParams.path.indexOf("/detail/") > -1) { let key = "/detail?"; let name = ""; let _path = tabsParams.path.replace(key, "/edit?"); // let _path = from.fullPath; // 使用路由参数的地址,忽略查询参数后,查找相同地址 let _path2 = tabsParams.path.replace("/detail/", "/edit/"); let hasPath1 = to.fullPath.indexOf("/detail?") > -1; let hasPath2 = to.fullPath.indexOf("/detail/") > -1; let tabsMenuList = tabStore.tabsMenuList; console.log("tabsMenuList :>> ", JSON.stringify(tabsMenuList)); for (let i = 0; i < tabsMenuList.length; i++) { if ( (hasPath1 && (tabsMenuList[i].path == _path || (tabsMenuList[i].path == from.fullPath && from.fullPath.indexOf("/copy?") > -1))) || (hasPath2 && tabsMenuList[i].path.split("?")[0] == _path2.split("?")[0]) ) { name = tabsMenuList[i].name; tabsMenuList[i] = tabsParams; isReplace = true; break; } } tabStore.setTabs(tabsMenuList); console.log("keilll name detail:>> ", name); isReplace && this.addKeepAliveName(to); isReplace && this.removeKeepAliveName(from); name && to.meta.isKeepAlive && this.updateKeepAliveName(to); // name && this.updateKeepAliveName(to); // to.meta.isKeepAlive && this.addKeepAliveName(to); } // } !isReplace && tabStore.addTabs(tabsParams); !isReplace && to.meta.isKeepAlive && this.addKeepAliveName(to); }, async computedKeepAliveNamePro(to, from) { const tabStore = useTabsStore(); const tabsMenuList = [...tabStore.tabsMenuList]; let isReplace = false; // --- 生成 tab 参数 --- const tabsParams = { icon: to.meta.icon as string, title: to.meta.title as string, path: to.fullPath, fullPath: to.fullPath, name: to.name as string, close: !to.meta.isAffix, query: to.query }; if (to.meta.needLock && Number(to.query?.id)) { tabsParams["needLock"] = to.meta.needLock; tabsParams["keyword"] = String(to.meta.billtype); tabsParams["billid"] = Number(to.query.id); tabsParams["billcode"] = String(to.query.code); } // --- 工具函数 --- const getBillCode = route => route?.query?.code || ""; const getBillType = route => { const pathMatch = route?.fullPath?.match(/\/([a-zA-Z]+)\/(detail|edit|copy|index)/); return pathMatch ? pathMatch[1] : ""; }; // 改为 meta 管理类型更可靠 const isSameBill = (code1, type1, code2, type2) => !!code1 && !!code2 && code1 === code2 && !!type1 && !!type2 && type1 === type2; const toBillCode = getBillCode(to); const fromBillCode = getBillCode(from); const toBillType = getBillType(to); const fromBillType = getBillType(from); // --- 查找 tab 工具函数 --- const findTabIndex = (tabType: "edit" | "detail" | "copy") => { return tabsMenuList.findIndex(item => { const itemBillCode = getBillCode(item); const itemBillType = getBillType(item); const isTypeMatch = (tabType === "edit" && item.path.includes("/edit")) || (tabType === "detail" && item.path.includes("/detail")) || (tabType === "copy" && item.path.includes("/copy")); return isSameBill(itemBillCode, itemBillType, toBillCode, toBillType) && isTypeMatch; }); }; // --- 替换 tab 并管理 keep-alive --- const replaceTab = (index: number) => { const oldTab = tabsMenuList[index]; tabsMenuList[index] = tabsParams; tabStore.setTabs(tabsMenuList); if (oldTab?.name) { this.removeKeepAliveName(oldTab); // 移除旧缓存 } if (to.meta.isKeepAlive) { this.addKeepAliveName(to); this.updateKeepAliveName(to); } isReplace = true; }; // --- 解锁单据 --- const unlockBill = async route => { if (route?.meta?.needLock && route?.query?.id) { try { await UnLockBill({ keyword: String(route.meta.billtype), billid: Number(route.query.id), billcode: String(route.query.code) }); } catch (err) { console.warn("解锁单据失败:", err); } } }; // --- 逻辑判断 --- const fromName = from?.name || ""; // 情况1: 列表 -> 详情 if ((!from || fromNameIncludes(from, "/index")) && toNameIncludes(to, "/detail") && toBillCode) { const editTabIndex = findTabIndex("edit"); if (editTabIndex > -1) { await unlockBill(tabsMenuList[editTabIndex]); replaceTab(editTabIndex); } } // 情况2: 详情 -> 编辑 if ( fromName && toNameIncludes(to, "/edit") && fromNameIncludes(from, "/detail") && isSameBill(toBillCode, toBillType, fromBillCode, fromBillType) ) { const detailTabIndex = findTabIndex("detail"); if (detailTabIndex > -1) replaceTab(detailTabIndex); } // 情况3: 编辑 -> 详情 if ( fromName && fromNameIncludes(from, "/edit") && toNameIncludes(to, "/detail") && isSameBill(toBillCode, toBillType, fromBillCode, fromBillType) ) { const editTabIndex = findTabIndex("edit"); if (editTabIndex > -1) { await unlockBill(from); // 保证顺序 replaceTab(editTabIndex); } } // 情况4: 列表 -> 编辑 if ((!from || fromNameIncludes(from, "/index")) && toNameIncludes(to, "/edit") && toBillCode) { const detailTabIndex = findTabIndex("detail"); if (detailTabIndex > -1) replaceTab(detailTabIndex); } // 情况5: 复制 -> 详情 if (fromNameIncludes(from, "/copy") && toNameIncludes(to, "/detail") && to.query?.fromCopySave) { const copyTabIndex = tabsMenuList.findIndex(item => item.path === from.fullPath); if (copyTabIndex > -1) replaceTab(copyTabIndex); } // 默认添加 tab if (!isReplace) { tabStore.addTabs(tabsParams); if (to.meta.isKeepAlive) { this.addKeepAliveName(to); } } // --- 辅助函数: 判断 route.name 类型 --- function toNameIncludes(route, type: string) { return route?.fullPath?.toString().toLowerCase().includes(type); } function fromNameIncludes(route, type: string) { return route?.fullPath?.toString().toLowerCase().includes(type); } } } });