Vben
2021-04-07 5b8eb4a49a097a47caf491c44df427522ab58daa
提交 | 用户 | age
b7ce74 1 import type { RouteLocationNormalized, RouteLocationRaw } from 'vue-router';
2f6253 2
b7ce74 3 import { toRaw, unref } from 'vue';
2f6253 4 import { Action, Module, Mutation, VuexModule, getModule } from 'vuex-module-decorators';
5 import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
6
7 import { PageEnum } from '/@/enums/pageEnum';
8
9 import store from '/@/store';
10 import router from '/@/router';
e12c58 11 import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic';
V 12 import { getRawRoute } from '/@/utils';
b7ce74 13
c303ec 14 import { useGo, useRedo } from '/@/hooks/web/usePage';
a65ad9 15 import { cloneDeep } from 'lodash-es';
2f6253 16
15567e 17 const NAME = 'app-tab';
f7aa93 18
2f6253 19 hotModuleUnregisterModule(NAME);
20
c303ec 21 function isGotoPage() {
V 22   const go = useGo();
23   go(unref(router.currentRoute).path, true);
24 }
2f6253 25
26 @Module({ namespaced: true, name: NAME, dynamic: true, store })
27 class Tab extends VuexModule {
e12c58 28   cachedTabsState: Set<string> = new Set();
c303ec 29
e23336 30   // tab list
c303ec 31   tabsState: RouteLocationNormalized[] = [];
2f6253 32
c303ec 33   lastDragEndIndexState = 0;
4baf90 34
2f6253 35   get getTabsState() {
36     return this.tabsState;
37   }
38
c303ec 39   get getCurrentTab(): RouteLocationNormalized {
2f6253 40     const route = unref(router.currentRoute);
41     return this.tabsState.find((item) => item.path === route.path)!;
4baf90 42   }
V 43
e12c58 44   get getCachedTabsState(): string[] {
V 45     return Array.from(this.cachedTabsState);
c303ec 46   }
V 47
48   get getLastDragEndIndexState(): number {
49     return this.lastDragEndIndexState;
2f6253 50   }
51
52   @Mutation
53   commitClearCache(): void {
e12c58 54     this.cachedTabsState = new Set();
2f6253 55   }
56
57   @Mutation
c303ec 58   goToPage() {
V 59     const go = useGo();
60     const len = this.tabsState.length;
61     const { path } = unref(router.currentRoute);
2f6253 62
c303ec 63     let toPath: PageEnum | string = PageEnum.BASE_HOME;
2f6253 64
c303ec 65     if (len > 0) {
V 66       const page = this.tabsState[len - 1];
67       const p = page.fullPath || page.path;
68       if (p) {
69         toPath = p;
70       }
2f6253 71     }
c303ec 72     // Jump to the current page and report an error
V 73     path !== toPath && go(toPath as PageEnum, true);
74   }
75
76   @Mutation
77   commitCachedMapState(): void {
e12c58 78     const cacheMap: Set<string> = new Set();
c303ec 79
V 80     this.tabsState.forEach((tab) => {
e12c58 81       const item = getRawRoute(tab);
189914 82       const needCache = !item.meta?.ignoreKeepAlive;
b88465 83       if (!needCache) return;
e12c58 84       const name = item.name as string;
V 85       cacheMap.add(name);
c303ec 86     });
e12c58 87     this.cachedTabsState = cacheMap;
c303ec 88   }
V 89
90   @Mutation
91   commitTabRoutesState(route: RouteLocationNormalized) {
92     const { path, fullPath, params, query } = route;
31e271 93
6bffdb 94     let updateIndex = -1;
c8e84d 95     // Existing pages, do not add tabs repeatedly
6bffdb 96     const hasTab = this.tabsState.some((tab, index) => {
V 97       updateIndex = index;
98       return (tab.fullPath || tab.path) === (fullPath || path);
2f6253 99     });
6bffdb 100     if (hasTab) {
V 101       const curTab = toRaw(this.tabsState)[updateIndex];
102       if (!curTab) return;
103       curTab.params = params || curTab.params;
104       curTab.query = query || curTab.query;
105       curTab.fullPath = fullPath || curTab.fullPath;
106       this.tabsState.splice(updateIndex, 1, curTab);
107       return;
108     }
a65ad9 109     this.tabsState = cloneDeep([...this.tabsState, route]);
2f6253 110   }
111
112   /**
113    * @description: close tab
114    */
115   @Mutation
c303ec 116   commitCloseTab(route: RouteLocationNormalized): void {
V 117     const { fullPath, meta: { affix } = {} } = route;
118     if (affix) return;
119     const index = this.tabsState.findIndex((item) => item.fullPath === fullPath);
120     index !== -1 && this.tabsState.splice(index, 1);
2f6253 121   }
122
123   @Mutation
124   commitCloseAllTab(): void {
125     this.tabsState = this.tabsState.filter((item) => {
126       return item.meta && item.meta.affix;
127     });
128   }
129
130   @Mutation
131   commitResetState(): void {
132     this.tabsState = [];
e12c58 133     this.cachedTabsState = new Set();
2f6253 134   }
135
136   @Mutation
cedba3 137   commitSortTabs({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }): void {
V 138     const currentTab = this.tabsState[oldIndex];
139
140     this.tabsState.splice(oldIndex, 1);
141     this.tabsState.splice(newIndex, 0, currentTab);
c303ec 142     this.lastDragEndIndexState = this.lastDragEndIndexState + 1;
cedba3 143   }
V 144
145   @Mutation
c303ec 146   closeMultipleTab({ pathList }: { pathList: string[] }): void {
31e271 147     this.tabsState = toRaw(this.tabsState).filter((item) => !pathList.includes(item.fullPath));
2f6253 148   }
149
150   @Action
c303ec 151   addTabAction(route: RouteLocationNormalized) {
V 152     const { path, name } = route;
116a1f 153     // 404  The page does not need to add a tab
c303ec 154     if (
V 155       path === PageEnum.ERROR_PAGE ||
156       !name ||
157       [REDIRECT_ROUTE.name, PAGE_NOT_FOUND_ROUTE.name].includes(name as string)
158     ) {
159       return;
160     }
e12c58 161     this.commitTabRoutesState(getRawRoute(route));
c303ec 162
V 163     this.commitCachedMapState();
164   }
165
166   @Mutation
a65ad9 167   async commitRedoPage() {
c303ec 168     const route = router.currentRoute.value;
e12c58 169     const name = route.name;
V 170
171     const findVal = Array.from(this.cachedTabsState).find((item) => item === name);
172     if (findVal) {
173       this.cachedTabsState.delete(findVal);
174       // this.cachedTabsState.splice(index, 1);
c303ec 175     }
V 176     const redo = useRedo();
a65ad9 177     await redo();
c303ec 178   }
V 179
180   @Action
181   closeAllTabAction() {
182     this.commitCloseAllTab();
183     this.commitClearCache();
184     this.goToPage();
185   }
186
187   @Action
188   closeTabAction(tab: RouteLocationNormalized) {
189     function getObj(tabItem: RouteLocationNormalized) {
190       const { params, path, query } = tabItem;
191       return {
192         params: params || {},
193         path,
194         query: query || {},
195       };
196     }
197     const { currentRoute, replace } = router;
198
199     const { path } = unref(currentRoute);
200     if (path !== tab.path) {
201       // Closed is not the activation tab
202       this.commitCloseTab(tab);
203       return;
204     }
205
206     // Closed is activated atb
207     let toObj: RouteLocationRaw = {};
208
209     const index = this.getTabsState.findIndex((item) => item.path === path);
210
211     // If the current is the leftmost tab
212     if (index === 0) {
213       // There is only one tab, then jump to the homepage, otherwise jump to the right tab
214       if (this.getTabsState.length === 1) {
215         toObj = PageEnum.BASE_HOME;
216       } else {
217         //  Jump to the right tab
218         const page = this.getTabsState[index + 1];
219         toObj = getObj(page);
220       }
221     } else {
222       // Close the current tab
223       const page = this.getTabsState[index - 1];
224       toObj = getObj(page);
225     }
226     this.commitCloseTab(currentRoute.value);
227     replace(toObj);
228   }
229
230   @Action
231   closeTabByKeyAction(key: string) {
232     const index = this.tabsState.findIndex((item) => (item.fullPath || item.path) === key);
233     index !== -1 && this.closeTabAction(this.tabsState[index]);
234   }
235
236   @Action
237   closeLeftTabAction(route: RouteLocationNormalized): void {
2f6253 238     const index = this.tabsState.findIndex((item) => item.path === route.path);
239
240     if (index > 0) {
241       const leftTabs = this.tabsState.slice(0, index);
242       const pathList: string[] = [];
243       for (const item of leftTabs) {
244         const affix = item.meta ? item.meta.affix : false;
245         if (!affix) {
31e271 246           pathList.push(item.fullPath);
2f6253 247         }
248       }
c303ec 249       this.closeMultipleTab({ pathList });
2f6253 250     }
c303ec 251     this.commitCachedMapState();
V 252     isGotoPage();
2f6253 253   }
254
255   @Action
c303ec 256   closeRightTabAction(route: RouteLocationNormalized): void {
31e271 257     const index = this.tabsState.findIndex((item) => item.fullPath === route.fullPath);
2f6253 258
259     if (index >= 0 && index < this.tabsState.length - 1) {
260       const rightTabs = this.tabsState.slice(index + 1, this.tabsState.length);
261
262       const pathList: string[] = [];
263       for (const item of rightTabs) {
264         const affix = item.meta ? item.meta.affix : false;
265         if (!affix) {
31e271 266           pathList.push(item.fullPath);
2f6253 267         }
268       }
c303ec 269       this.closeMultipleTab({ pathList });
2f6253 270     }
c303ec 271     this.commitCachedMapState();
V 272     isGotoPage();
2f6253 273   }
274
275   @Action
c303ec 276   closeOtherTabAction(route: RouteLocationNormalized): void {
31e271 277     const closePathList = this.tabsState.map((item) => item.fullPath);
2f6253 278     const pathList: string[] = [];
279     closePathList.forEach((path) => {
31e271 280       if (path !== route.fullPath) {
2f6253 281         const closeItem = this.tabsState.find((item) => item.path === path);
282         if (!closeItem) return;
283         const affix = closeItem.meta ? closeItem.meta.affix : false;
284         if (!affix) {
31e271 285           pathList.push(closeItem.fullPath);
2f6253 286         }
287       }
288     });
c303ec 289     this.closeMultipleTab({ pathList });
V 290     this.commitCachedMapState();
291     isGotoPage();
2f6253 292   }
293 }
294 export const tabStore = getModule<Tab>(Tab);