diff --git a/src/workbench/browser/src/app/pages/api/api-tab.service.ts b/src/workbench/browser/src/app/pages/api/api-tab.service.ts index c60dd258e..8ee05b599 100644 --- a/src/workbench/browser/src/app/pages/api/api-tab.service.ts +++ b/src/workbench/browser/src/app/pages/api/api-tab.service.ts @@ -15,9 +15,14 @@ export class ApiTabService { return Object.values(this.BASIC_TBAS).find((val) => this.router.url.includes(val.pathname))?.type || 'preview'; } private changeContent$: Subject = new Subject(); - BASIC_TBAS = { - test: { pathname: '/home/api/test', type: 'edit', title: $localize`New Request`, extends: { method: 'POST' } }, - edit: { pathname: '/home/api/edit', type: 'edit', title: $localize`New API` }, + BASIC_TBAS: { [key: string]: Partial } = { + test: { + pathname: '/home/api/test', + type: 'edit', + title: $localize`New Request`, + extends: { method: 'POST' }, + }, + edit: { pathname: '/home/api/edit', isFixed: true, type: 'edit', title: $localize`New API` }, detail: { pathname: '/home/api/detail', type: 'preview', title: $localize`Preview` }, overview: { pathname: '/home/api/overview', type: 'preview', title: $localize`:@@API Index:Index`, icon: 'home' }, mock: { pathname: '/home/api/mock', type: 'preview', title: 'Mock' }, @@ -115,7 +120,7 @@ export class ApiTabService { } //?Why should use getCurrentTab()? //Because maybe current tab has't finish init - const currentTab = this.apiTabComponent.getTabByUrl(url); + const currentTab = this.apiTabComponent.getExistTabByUrl(url); const contentID = this.getContentID(url); //Get tab cache this.componentRef.model = currentTab?.content?.[contentID] || null; @@ -124,9 +129,11 @@ export class ApiTabService { } updateTab(currentTab, inData) { const model = inData.model; + const contentID = this.getContentID(currentTab.pathname); //Set tabItem const replaceTab: Partial = { + hasChanged: currentTab.hasChanged, isLoading: false, extends: {}, }; @@ -148,7 +155,6 @@ export class ApiTabService { } //Only hasChanged edit page storage data if (currentTab.type === 'edit') { - const contentID = this.getContentID(currentTab.pathname); //Set hasChange if (!this.componentRef?.isFormChange) { throw new Error( @@ -157,7 +163,10 @@ export class ApiTabService { } switch (inData.when) { case 'editing': { - replaceTab.hasChanged = this.componentRef.isFormChange(); + // Saved APIs do not need to verify changes + if (!currentTab.params.uuid || currentTab.params.uuid.includes('history')) { + replaceTab.hasChanged = this.componentRef.isFormChange(); + } break; } case 'saved': { @@ -170,15 +179,6 @@ export class ApiTabService { replaceTab.hasChanged = currentTab.extends?.hasChanged?.[contentID === 'edit' ? 'test' : 'edit'] || replaceTab.hasChanged; - //Set isFixed - if (replaceTab.hasChanged) { - replaceTab.isFixed = true; - } - //Has tested set fixed - if (currentTab.pathname === '/home/api/test' && model.testStartTime !== undefined) { - replaceTab.isFixed = true; - } - // Set storage //Set baseContent if (['init', 'saved'].includes(inData.when)) { @@ -190,6 +190,15 @@ export class ApiTabService { replaceTab.content = inData.when === 'saved' ? {} : currentTab.content || {}; replaceTab.content[contentID] = model && !isEmptyObj(model) ? model : null; } + + //Set isFixed + if (replaceTab.hasChanged) { + replaceTab.isFixed = true; + } + //Has tested/exsix api set fixed + if (currentTab.pathname === '/home/api/test' && (model.testStartTime !== undefined || currentTab.params.uuid)) { + replaceTab.isFixed = true; + } } // console.log('updatePartialTab',inData.url,currentTab, replaceTab); this.apiTabComponent.updatePartialTab(inData.url, replaceTab); @@ -205,7 +214,7 @@ export class ApiTabService { console.warn(`EO_WARNING:apiTabComponent hasn't init yet!`); return; } - const currentTab = this.apiTabComponent.getTabByUrl(inData.url); + const currentTab = this.apiTabComponent.getExistTabByUrl(inData.url); if (!currentTab) { console.warn(`has't find the tab fit child component ,url:${inData.url}`); return; diff --git a/src/workbench/browser/src/app/pages/api/tab/api-tab-operate.service.ts b/src/workbench/browser/src/app/pages/api/tab/api-tab-operate.service.ts index 6e57464b6..d21abc7ed 100644 --- a/src/workbench/browser/src/app/pages/api/tab/api-tab-operate.service.ts +++ b/src/workbench/browser/src/app/pages/api/tab/api-tab-operate.service.ts @@ -139,22 +139,26 @@ export class ApiTabOperateService { /** * Get exist tab index * - * @param type sameTab means has same {params.uuid} + * @param type sameTab means has same pageID and same {params.uuid} * @param tab * @returns */ - getSameContentTabIndex(tab: TabItem): number { - let result = -1; - const sameTabIDTab = this.tabStorage.tabsByID.get(tab.uuid); - if (sameTabIDTab && !sameTabIDTab.params.uuid && sameTabIDTab.pathname === tab.pathname) { - return this.tabStorage.tabOrder.findIndex((uuid) => uuid === sameTabIDTab.uuid); + getSameContentTab(tab: Partial): TabItem | null { + let result = null; + if (!tab.params.uuid) { + const sameTabIDTab = this.tabStorage.tabsByID.get(tab.uuid); + if (sameTabIDTab && sameTabIDTab.pathname === tab.pathname) { + return sameTabIDTab; + } + return result; } + //Get exist params.uuid content tab,same pathname and uuid match const mapObj = Object.fromEntries(this.tabStorage.tabsByID); for (const key in mapObj) { if (Object.prototype.hasOwnProperty.call(mapObj, key)) { const tabInfo = mapObj[key]; - if (tabInfo.params.uuid && tabInfo.params.uuid === tab.params.uuid && tabInfo.pathname === tab.pathname) { - result = this.tabStorage.tabOrder.findIndex((uuid) => uuid === tabInfo.uuid); + if (tabInfo.params.uuid === tab.params.uuid && tabInfo.pathname === tab.pathname) { + result = tabInfo; break; } } @@ -167,10 +171,9 @@ export class ApiTabOperateService { * @param url * @returns tabInfo */ - getBaiscTabFromUrl(url): TabItem { + getTabInfoFromUrl(url): { uuid: number; pathname: string; params: any } { const urlArr = url.split('?'); const params: any = {}; - const timestamp = Date.now(); const basicTab = Object.values(this.BASIC_TABS).find((val) => urlArr[0].includes(val.pathname)); if (!basicTab) { throw new Error(`EO_ERROR: Please check this router has added in BASIC_TABS,current route:${url}`); @@ -183,19 +186,30 @@ export class ApiTabOperateService { } params[key] = value; }); - params.pageID = params.pageID || timestamp; const result = { - //If data need load from ajax/indexeddb,add loading uuid: params.pageID, - isLoading: params.uuid ? true : false, pathname: basicTab.pathname, params, }; - ['title', 'icon', 'type', 'extends'].forEach((keyName) => { - result[keyName] = basicTab[keyName]; - }); + return result; + } + /** + * Get basic tab info from url + * + * @param url + * @returns tabInfo + */ + getBaiscTabFromUrl(url): TabItem { + const result = this.getTabInfoFromUrl(url); + const basicTab = Object.values(this.BASIC_TABS).find((val) => result.pathname === val.pathname); + if (!basicTab) { + throw new Error(`EO_ERROR: Please check this router has added in BASIC_TABS,current route:${url}`); + } + result.params.pageID = result.params.pageID || Date.now(); + Object.assign(result, basicTab); return result as TabItem; } + /** * Operate tab after router change,router triggle tab change * Such as new tab,pick tab,close tab... @@ -203,89 +217,68 @@ export class ApiTabOperateService { * @param res.url location.pathname+location.search */ operateTabAfterRouteChange(res: { url: string }) { - const tmpTabItem = this.getBaiscTabFromUrl(res.url); - const sameContentIndex = this.getSameContentTabIndex(tmpTabItem); - const existTab = this.getTabByIndex(sameContentIndex); - console.log('operateTabAfterRouteChange', existTab, tmpTabItem); - //If page lack pageID - //Jump to exist tab item to keep same pageID and so on - if (!res.url.includes('pageID')) { + const pureTab = this.getTabInfoFromUrl(res.url); + const nextTab = this.getBaiscTabFromUrl(res.url); + const existTab = this.getSameContentTab(pureTab); + //!Every tab must has pageID + //If lack pageID,Jump to exist tab item to keep same pageID and so on + if (!pureTab.uuid) { if (existTab) { - tmpTabItem.uuid = tmpTabItem.params.pageID = existTab.uuid; + pureTab.uuid = pureTab.params.pageID = existTab.uuid; } - this.navigateTabRoute(tmpTabItem); + this.navigateTabRoute(nextTab); return; } if (this.tabStorage.tabOrder.length === 0) { - this.tabStorage.addTab(tmpTabItem); + this.tabStorage.addTab(nextTab); this.updateChildView(); return; } //same tab content,selected it if (existTab) { - this.selectedIndex = sameContentIndex; + this.selectedIndex = this.tabStorage.tabOrder.findIndex((uuid) => uuid === existTab.uuid); this.updateChildView(); return; } - //If has same content tab (same {params.uuid}),replace it and merge data - const mapObj = Object.fromEntries(this.tabStorage.tabsByID); - for (const key in mapObj) { - if (Object.prototype.hasOwnProperty.call(mapObj, key)) { - const tab = mapObj[key]; - if (tab.params.uuid && tab.params.uuid === tmpTabItem.params.uuid) { - const mergeTab = this.preventBlankTab(tab, tmpTabItem); - mergeTab.content = tab.content; - mergeTab.baseContent = tab.baseContent; - mergeTab.extends = Object.assign(mergeTab.extends || {}, tab.extends); - this.selectedIndex = this.tabStorage.tabOrder.findIndex((uuid) => uuid === tab.uuid); - this.tabStorage.updateTab(this.selectedIndex, mergeTab); - this.updateChildView(); - return; - } + //!Same params.uuid can only open one Tab + //If has same subTab (same {params.uuid}), merge data and replace it + if (nextTab.params?.uuid) { + const hasFind = this.jumpToSameSubTab(nextTab); + if (hasFind) { + return; } } - //If has same tab (same uuid),replace it - const samePageID = this.tabStorage.tabsByID.has(tmpTabItem.uuid); - if (samePageID) { - this.selectedIndex = this.tabStorage.tabOrder.findIndex((uuid) => uuid === tmpTabItem.uuid); - this.tabStorage.updateTab(this.selectedIndex, tmpTabItem); + //Determine whether to replace the current Tab + let canbeReplaceTab = null; + if (this.tabStorage.tabsByID.has(pureTab.uuid)) { + //If the same tab exists, directly replace + canbeReplaceTab = nextTab; + } else { + canbeReplaceTab = this.findTabCanbeReplace(); } - - //Find other tab to be replace - const currentTab = this.getCurrentTab(); - //* Replace current tab first - const canbeReplaceTab = - currentTab && this.canbeReplace(currentTab) - ? currentTab - : Object.values(mapObj).find((val) => this.canbeReplace(val)); if (canbeReplaceTab) { this.selectedIndex = this.tabStorage.tabOrder.findIndex((uuid) => uuid === canbeReplaceTab.uuid); - this.tabStorage.updateTab(this.selectedIndex, tmpTabItem); + this.tabStorage.updateTab(this.selectedIndex, nextTab); this.updateChildView(); return; } //No one can be replace,add tab - this.tabStorage.addTab(tmpTabItem); + this.tabStorage.addTab(nextTab); this.selectedIndex = this.tabStorage.tabOrder.length - 1; this.updateChildView(); } //*Prevent toggling splash screen with empty tab title - preventBlankTab(origin, target) { + private preventBlankTab(origin, target) { const result = target; /** * Keyname effect show tab */ - ['title', 'hasChanged', 'isFixed', 'isLoading'].forEach((keyName) => { - //Dont't replace is loading tab content - if (result[keyName] && !result.isLoading) { - return; - } + ['title', 'hasChanged', 'isLoading'].forEach((keyName) => { result[keyName] = origin[keyName]; }); - result.isLoading = false; return result; } canbeReplace(tabItem: TabItem) { @@ -304,15 +297,46 @@ export class ApiTabOperateService { getCurrentTab() { return this.getTabByIndex(this.selectedIndex); } - private getUrlByTab(tab: TabItem) { - return ( - tab.pathname + - '?' + - Object.keys(tab.params) - .sort() - .map((keyName) => `${keyName}=${tab.params[keyName]}`) - .join('&') - ); + /** + * Same sub means tab has same {params.uuid} + * + * @param inTab + * @returns hasFind + */ + private jumpToSameSubTab(inTab): boolean { + const mapObj = Object.fromEntries(this.tabStorage.tabsByID); + for (const key in mapObj) { + if (Object.prototype.hasOwnProperty.call(mapObj, key)) { + const tab = mapObj[key]; + if (tab.params.uuid && tab.params.uuid === inTab.params.uuid) { + const mergeTab = this.preventBlankTab(tab, inTab); + mergeTab.content = tab.content; + mergeTab.baseContent = tab.baseContent; + mergeTab.extends = Object.assign(mergeTab.extends || {}, tab.extends); + this.selectedIndex = this.tabStorage.tabOrder.findIndex((uuid) => uuid === tab.uuid); + this.tabStorage.updateTab(this.selectedIndex, mergeTab); + this.updateChildView(); + return true; + } + } + } + return false; + } + + /** + * Find can be replace tab + * + * @returns + */ + private findTabCanbeReplace(): TabItem { + const mapObj = Object.fromEntries(this.tabStorage.tabsByID); + const currentTab = this.getCurrentTab(); + //* Replace current tab first + const result = + currentTab && this.canbeReplace(currentTab) + ? currentTab + : Object.values(mapObj).find((val) => this.canbeReplace(val)); + return result; } private updateChildView() { this.messageService.send({ type: 'tabContentInit', data: {} }); diff --git a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html index 91b6840ba..99780c1d5 100644 --- a/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html +++ b/src/workbench/browser/src/app/pages/api/tab/api-tab.component.html @@ -1,4 +1,3 @@ - {{ tabStorage.tabsByID.get(uuid).title }} -
+