|
@@ -4,6 +4,8 @@ import { cloneDeep } from "lodash-es";
|
|
|
import { useTabsStore } from "@/stores/modules/tabs";
|
|
import { useTabsStore } from "@/stores/modules/tabs";
|
|
|
import { LockBill, UnLockBill } from "@/api/modules/common";
|
|
import { LockBill, UnLockBill } from "@/api/modules/common";
|
|
|
|
|
|
|
|
|
|
+// 最大缓存页面数量限制
|
|
|
|
|
+const MAX_KEEP_ALIVE_COUNT = 10;
|
|
|
export const useKeepAliveStore = defineStore({
|
|
export const useKeepAliveStore = defineStore({
|
|
|
id: "geeker-keepAlive",
|
|
id: "geeker-keepAlive",
|
|
|
state: (): KeepAliveState => ({
|
|
state: (): KeepAliveState => ({
|
|
@@ -42,6 +44,9 @@ export const useKeepAliveStore = defineStore({
|
|
|
this.closeAliveList = this.closeAliveList.filter((item: any) => item.fullPath != route.fullPath);
|
|
this.closeAliveList = this.closeAliveList.filter((item: any) => item.fullPath != route.fullPath);
|
|
|
}
|
|
}
|
|
|
this.keepAliveList.push(routeItem);
|
|
this.keepAliveList.push(routeItem);
|
|
|
|
|
+
|
|
|
|
|
+ // 检查缓存数量限制,如果超过限制则清理最旧的缓存
|
|
|
|
|
+ this.cleanOldKeepAliveCache();
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
// Remove KeepAliveName
|
|
// Remove KeepAliveName
|
|
@@ -96,7 +101,70 @@ export const useKeepAliveStore = defineStore({
|
|
|
}
|
|
}
|
|
|
return routeItem;
|
|
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) {
|
|
computedKeepAliveName(to, from) {
|
|
|
|
|
+ this.computedKeepAliveNamePro(to, from);
|
|
|
|
|
+ return;
|
|
|
const tabStore = useTabsStore();
|
|
const tabStore = useTabsStore();
|
|
|
|
|
|
|
|
const tabsParams = {
|
|
const tabsParams = {
|
|
@@ -193,6 +261,198 @@ export const useKeepAliveStore = defineStore({
|
|
|
// }
|
|
// }
|
|
|
!isReplace && tabStore.addTabs(tabsParams);
|
|
!isReplace && tabStore.addTabs(tabsParams);
|
|
|
!isReplace && to.meta.isKeepAlive && this.addKeepAliveName(to);
|
|
!isReplace && to.meta.isKeepAlive && this.addKeepAliveName(to);
|
|
|
|
|
+ },
|
|
|
|
|
+ computedKeepAliveNamePro(to, from) {
|
|
|
|
|
+ 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,
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ let isReplace = false;
|
|
|
|
|
+ const tabsMenuList = [...tabStore.tabsMenuList];
|
|
|
|
|
+
|
|
|
|
|
+ // 判断是否为同一单据的单据编码
|
|
|
|
|
+ const getBillCode = route => {
|
|
|
|
|
+ return route?.query?.code || "";
|
|
|
|
|
+ };
|
|
|
|
|
+ const toBillCode = getBillCode(to);
|
|
|
|
|
+ const fromBillCode = getBillCode(from);
|
|
|
|
|
+
|
|
|
|
|
+ // 情况1: 列表页跳转到详情页 (/list -> /detail)
|
|
|
|
|
+ const isListToDetail =
|
|
|
|
|
+ (!from || from.fullPath.includes("/index")) && (to.fullPath.includes("/detail?") || to.fullPath.includes("/detail/"));
|
|
|
|
|
+
|
|
|
|
|
+ if (isListToDetail && toBillCode) {
|
|
|
|
|
+ // 查找是否有相同单据的编辑页
|
|
|
|
|
+ const editTabIndex = tabsMenuList.findIndex(item => {
|
|
|
|
|
+ const itemBillCode = getBillCode(item);
|
|
|
|
|
+ return itemBillCode === toBillCode && (item.path.includes("/edit?") || item.path.includes("/edit/"));
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (editTabIndex > -1) {
|
|
|
|
|
+ // 保存被替换的编辑页信息,用于解锁
|
|
|
|
|
+ const oldEditTab = tabsMenuList[editTabIndex];
|
|
|
|
|
+
|
|
|
|
|
+ // 替换编辑页为详情页
|
|
|
|
|
+ console.log("列表页->详情页: 替换编辑页", oldEditTab.path, "=>", to.fullPath);
|
|
|
|
|
+ tabsMenuList[editTabIndex] = tabsParams;
|
|
|
|
|
+ tabStore.setTabs(tabsMenuList);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果被替换的编辑页需要锁定,解锁单据
|
|
|
|
|
+ if (oldEditTab.needLock && oldEditTab.billid) {
|
|
|
|
|
+ UnLockBill({
|
|
|
|
|
+ keyword: oldEditTab.keyword,
|
|
|
|
|
+ billid: oldEditTab.billid,
|
|
|
|
|
+ billcode: oldEditTab.billcode
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ console.warn("替换编辑页时解锁失败:", err);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isReplace = true;
|
|
|
|
|
+ to.meta.isKeepAlive && this.addKeepAliveName(to);
|
|
|
|
|
+ to.meta.isKeepAlive && this.updateKeepAliveName(to);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 情况2: 详情页跳转到编辑页 (/detail -> /edit)
|
|
|
|
|
+ const isDetailToEdit =
|
|
|
|
|
+ from &&
|
|
|
|
|
+ (from.fullPath.includes("/detail?") || from.fullPath.includes("/detail/")) &&
|
|
|
|
|
+ (to.fullPath.includes("/edit?") || to.fullPath.includes("/edit/")) &&
|
|
|
|
|
+ toBillCode &&
|
|
|
|
|
+ fromBillCode &&
|
|
|
|
|
+ toBillCode === fromBillCode;
|
|
|
|
|
+
|
|
|
|
|
+ if (isDetailToEdit) {
|
|
|
|
|
+ // 查找是否有相同单据的详情页
|
|
|
|
|
+ const detailTabIndex = tabsMenuList.findIndex(item => {
|
|
|
|
|
+ const itemBillCode = getBillCode(item);
|
|
|
|
|
+ const isDetailTab = item.path.includes("/detail?") || item.path.includes("/detail/");
|
|
|
|
|
+ console.log("检查tab:", {
|
|
|
|
|
+ itemPath: item.path,
|
|
|
|
|
+ itemBillCode,
|
|
|
|
|
+ toBillCode,
|
|
|
|
|
+ isDetailTab,
|
|
|
|
|
+ match: itemBillCode === toBillCode && isDetailTab
|
|
|
|
|
+ });
|
|
|
|
|
+ return itemBillCode === toBillCode && isDetailTab;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.log("找到的详情页索引:", detailTabIndex);
|
|
|
|
|
+ if (detailTabIndex > -1) {
|
|
|
|
|
+ // 替换详情页为编辑页
|
|
|
|
|
+ console.log("详情页->编辑页: 替换详情页", tabsMenuList[detailTabIndex].path, "=>", to.fullPath);
|
|
|
|
|
+ tabsMenuList[detailTabIndex] = tabsParams;
|
|
|
|
|
+ tabStore.setTabs(tabsMenuList);
|
|
|
|
|
+ isReplace = true;
|
|
|
|
|
+ to.meta.isKeepAlive && this.addKeepAliveName(to);
|
|
|
|
|
+ to.meta.isKeepAlive && this.updateKeepAliveName(to);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 情况3: 编辑页跳转到详情页 (/edit -> /detail),需要解锁单据
|
|
|
|
|
+ const isEditToDetailPro =
|
|
|
|
|
+ from &&
|
|
|
|
|
+ (from.fullPath.includes("/edit?") || from.fullPath.includes("/edit/")) &&
|
|
|
|
|
+ (to.fullPath.includes("/detail?") || to.fullPath.includes("/detail/")) &&
|
|
|
|
|
+ toBillCode &&
|
|
|
|
|
+ fromBillCode &&
|
|
|
|
|
+ toBillCode === fromBillCode;
|
|
|
|
|
+
|
|
|
|
|
+ if (isEditToDetailPro) {
|
|
|
|
|
+ console.log("编辑页->详情页: 解锁单据", fromBillCode);
|
|
|
|
|
+ // 查找是否有相同单据的编辑页
|
|
|
|
|
+ const editTabIndex = tabsMenuList.findIndex(item => {
|
|
|
|
|
+ const itemBillCode = getBillCode(item);
|
|
|
|
|
+ const isEditTab = item.path.includes("/edit?") || item.path.includes("/edit/");
|
|
|
|
|
+ console.log("检查tab:", {
|
|
|
|
|
+ itemPath: item.path,
|
|
|
|
|
+ itemBillCode,
|
|
|
|
|
+ fromBillCode,
|
|
|
|
|
+ isEditTab,
|
|
|
|
|
+ match: itemBillCode === fromBillCode && isEditTab
|
|
|
|
|
+ });
|
|
|
|
|
+ return itemBillCode === fromBillCode && isEditTab;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.log("找到的编辑页索引:", editTabIndex);
|
|
|
|
|
+ if (editTabIndex > -1) {
|
|
|
|
|
+ // 替换编辑页为详情页
|
|
|
|
|
+ console.log("编辑页->详情页: 替换编辑页", tabsMenuList[editTabIndex].path, "=>", to.fullPath);
|
|
|
|
|
+ tabsMenuList[editTabIndex] = tabsParams;
|
|
|
|
|
+ tabStore.setTabs(tabsMenuList);
|
|
|
|
|
+
|
|
|
|
|
+ // 关键修复:移除编辑页的 keepAlive 缓存,确保下次进入编辑页会触发加锁
|
|
|
|
|
+ this.removeKeepAliveName(from);
|
|
|
|
|
+
|
|
|
|
|
+ if (from.meta?.needLock && from.query?.id) {
|
|
|
|
|
+ // 解锁单据
|
|
|
|
|
+ UnLockBill({
|
|
|
|
|
+ keyword: String(from.meta.billtype),
|
|
|
|
|
+ billid: Number(from.query.id),
|
|
|
|
|
+ billcode: String(from.query.code)
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ console.warn("edit->detail 解锁失败:", err);
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ isReplace = true;
|
|
|
|
|
+ to.meta.isKeepAlive && this.addKeepAliveName(to);
|
|
|
|
|
+ to.meta.isKeepAlive && this.updateKeepAliveName(to);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 情况4: 列表页跳转到编辑页 (/list -> /edit),需要替换相同单据的详情页
|
|
|
|
|
+ const isListToEdit =
|
|
|
|
|
+ (!from || from.fullPath.includes("/index")) && (to.fullPath.includes("/edit?") || to.fullPath.includes("/edit/"));
|
|
|
|
|
+ if (isListToEdit && toBillCode) {
|
|
|
|
|
+ // 查找是否有相同单据的详情页
|
|
|
|
|
+ const detailTabIndex = tabsMenuList.findIndex(item => {
|
|
|
|
|
+ const itemBillCode = getBillCode(item);
|
|
|
|
|
+ const isDetailTab = item.path.includes("/detail?") || item.path.includes("/detail/");
|
|
|
|
|
+ console.log("检查tab:", {
|
|
|
|
|
+ itemPath: item.path,
|
|
|
|
|
+ itemBillCode,
|
|
|
|
|
+ toBillCode,
|
|
|
|
|
+ isDetailTab,
|
|
|
|
|
+ match: itemBillCode === toBillCode && isDetailTab
|
|
|
|
|
+ });
|
|
|
|
|
+ return itemBillCode === toBillCode && isDetailTab;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ console.log("找到的详情页索引:", detailTabIndex);
|
|
|
|
|
+ if (detailTabIndex > -1) {
|
|
|
|
|
+ // 替换详情页为编辑页
|
|
|
|
|
+ console.log("列表页->编辑页: 替换详情页", tabsMenuList[detailTabIndex].path, "=>", to.fullPath);
|
|
|
|
|
+ tabsMenuList[detailTabIndex] = tabsParams;
|
|
|
|
|
+ tabStore.setTabs(tabsMenuList);
|
|
|
|
|
+ isReplace = true;
|
|
|
|
|
+ to.meta.isKeepAlive && this.addKeepAliveName(to);
|
|
|
|
|
+ to.meta.isKeepAlive && this.updateKeepAliveName(to);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 如果没有发生替换,则正常添加tab
|
|
|
|
|
+ if (!isReplace) {
|
|
|
|
|
+ tabStore.addTabs(tabsParams);
|
|
|
|
|
+ to.meta.isKeepAlive && this.addKeepAliveName(to);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|