cabloy 5.1.207 → 5.1.209
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cabloy-version +1 -1
- package/CHANGELOG.md +13 -0
- package/README.md +1 -0
- package/package.json +1 -1
- package/repo-agent-governance/managed-assets.json +9 -9
- package/repo-agent-governance/policies/repository.md +1 -1
- package/repo-agent-governance/skills/cabloy-workflow/SKILL.md +4 -2
- package/repo-agent-governance/skills/cabloy-workflow/evals/evals.json +13 -1
- package/repo-docs/backend/unit-testing.md +1 -1
- package/repo-docs/frontend/form-guide.md +17 -1
- package/repo-docs/frontend/model-architecture.md +4 -0
- package/repo-docs/frontend/model-state-guide.md +23 -2
- package/repo-docs/frontend/resource-entry-page-deep-dive.md +119 -0
- package/repo-docs/frontend/scripts.md +3 -3
- package/repo-docs/frontend/zova-form-under-the-hood.md +5 -0
- package/repo-docs/fullstack/parallel-worktree-environment.md +3 -3
- package/repo-docs/fullstack/quickstart.md +11 -1
- package/repo-docs/reference/repo-scripts.md +19 -5
- package/repo-e2e/specs/cabloy-basic.spec.ts +1577 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/src/suite/a-home/modules/home-user/src/controller/passport.ts +1 -0
- package/vona/src/suite/a-home/modules/home-user/src/dto/passport.ts +4 -0
- package/vona/src/suite/a-home/modules/home-user/test/passport.test.ts +172 -0
- package/vona/src/suite/a-home/modules/home-user/test/role.test.ts +1 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-openapi/src/types/rest.ts +1 -0
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/packages-cli/cli/package.json +2 -2
- package/zova/packages-cli/cli-set-front/cli/templates/rest/render.ts +2 -0
- package/zova/packages-cli/cli-set-front/cli/templates/rest/rest.ts +9 -0
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/src/suite/a-home/modules/home-api/src/.metadata/index.ts +17 -17
- package/zova/src/suite/a-home/modules/home-api/src/api/openapi/schemas.ts +68 -52
- package/zova/src/suite/a-home/modules/home-api/src/api/openapi/types.ts +524 -343
- package/zova/src/suite/a-home/modules/home-passport/src/model/passport.ts +45 -11
- package/zova/src/suite/a-home/modules/home-passport/test/lib/persistenceIdentity.test.ts +229 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/package.json +9 -1
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/.metadata/index.ts +81 -5
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/bean/bean.draftCoordinator.ts +158 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockForm/controller.tsx +11 -3
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +438 -8
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/config/config.ts +11 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/config/locale/en-us.ts +9 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/config/locale/zh-cn.ts +9 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/lib/draftSchema.ts +649 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/model/draft.ts +59 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/monkey.ts +11 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/types/draft.ts +49 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/test/lib/draftDate.test.ts +262 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/test/lib/draftLifecycle.test.ts +1073 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/test/lib/draftLoader.mjs +13 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/test/lib/draftModel.test.ts +572 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/test/lib/draftSchema.test.ts +427 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/test/lib/draftSchemaSafety.test.ts +200 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsx +6 -1
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-form/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/form/controller.tsx +86 -1
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/component/formField/controller.tsx +88 -13
- package/zova/src/suite-vendor/a-zova/modules/a-form/src/lib/schemaRefresh.ts +751 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/test/lib/schemaRefresh.test.ts +637 -0
- package/zova/src/suite-vendor/a-zova/modules/a-form/test/lib/schemaRefreshLifecycle.test.ts +994 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.persister.ts +331 -87
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.query.ts +11 -6
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.useQuery.ts +3 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/bean/bean.model/bean.model.useState.ts +17 -1
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/modelPersistence.ts +204 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/lib/persistenceQueue.ts +50 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/monkey.ts +5 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/service/storage.ts +47 -2
- package/zova/src/suite-vendor/a-zova/modules/a-model/src/types/query.ts +3 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/test/lib/modelLoader.mjs +34 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/test/lib/modelPersistence.test.ts +727 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/test/lib/persistenceQueue.test.ts +137 -0
- package/zova/src/suite-vendor/a-zova/modules/a-model/test/lib/persistenceTransition.test.ts +129 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/monkey.ts +7 -5
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +2 -0
- package/zova/src/suite-vendor/a-zova/modules/a-router/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/bean/bean.router.ts +14 -0
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/lib/routeDiscard.ts +26 -0
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/service/routerGuards.ts +6 -3
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-router/src/types/routeDiscard.ts +24 -0
- package/zova/src/suite-vendor/a-zova/modules/a-router/test/lib/routeDiscard.test.ts +105 -0
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts +202 -30
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/test/lib/tabs.test.ts +524 -0
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/test/lib/tabsLoader.mjs +35 -0
- package/zova/src/suite-vendor/a-zova/package.json +6 -6
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
IDecoratorModelOptions,
|
|
3
|
+
ModelPersistenceToken,
|
|
4
|
+
ServiceStorage,
|
|
5
|
+
UseQueryOptions,
|
|
6
|
+
} from 'zova-module-a-model';
|
|
2
7
|
|
|
3
8
|
import {
|
|
4
9
|
RouteLocationNormalizedLoaded,
|
|
5
10
|
RouteLocationNormalizedLoadedGeneric,
|
|
6
11
|
} from '@cabloy/vue-router';
|
|
7
12
|
import { mutate } from 'mutate-on-copy';
|
|
13
|
+
import { shallowReactive } from 'vue';
|
|
8
14
|
import { deepEqual, deepExtend, TypeEventOff } from 'zova';
|
|
9
15
|
import { BeanModelBase, Model } from 'zova-module-a-model';
|
|
10
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
IPageMeta,
|
|
18
|
+
IRouteViewRouteItem,
|
|
19
|
+
IRouteViewRouteMeta,
|
|
20
|
+
TypeRouteDiscardReason,
|
|
21
|
+
} from 'zova-module-a-router';
|
|
11
22
|
|
|
12
23
|
import {
|
|
13
24
|
ModelTabsOptions,
|
|
@@ -19,6 +30,15 @@ import {
|
|
|
19
30
|
|
|
20
31
|
export interface IModelOptionsTabs extends IDecoratorModelOptions, ModelTabsOptionsBase {}
|
|
21
32
|
|
|
33
|
+
interface TabsReadiness {
|
|
34
|
+
ctx: { disposed: boolean };
|
|
35
|
+
persistence: ServiceStorage;
|
|
36
|
+
token: ModelPersistenceToken;
|
|
37
|
+
ready: boolean;
|
|
38
|
+
promise?: Promise<void>;
|
|
39
|
+
work: (() => void)[];
|
|
40
|
+
}
|
|
41
|
+
|
|
22
42
|
@Model<IModelOptionsTabs>({
|
|
23
43
|
enableSelector: true,
|
|
24
44
|
max: -1,
|
|
@@ -27,7 +47,29 @@ export interface IModelOptionsTabs extends IDecoratorModelOptions, ModelTabsOpti
|
|
|
27
47
|
})
|
|
28
48
|
export class ModelTabs extends BeanModelBase {
|
|
29
49
|
tabsOptions: ModelTabsOptions;
|
|
30
|
-
|
|
50
|
+
private _tabsState: RouteTab[];
|
|
51
|
+
private _tabsReadiness?: TabsReadiness;
|
|
52
|
+
private _tabsDisposed = false;
|
|
53
|
+
private _offPersistence?: () => void;
|
|
54
|
+
|
|
55
|
+
get tabs(): RouteTab[] {
|
|
56
|
+
if (this._tabsDisposed || this.ctx.disposed) return [];
|
|
57
|
+
if (!this.tabsOptions?.cache) return this._tabsState ?? [];
|
|
58
|
+
const readiness = this._getTabsReadiness();
|
|
59
|
+
if (!readiness.ready) {
|
|
60
|
+
this._startTabsRestore(readiness);
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
return this._tabsState;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
set tabs(value: RouteTab[]) {
|
|
67
|
+
if (this._tabsDisposed || this.ctx.disposed) return;
|
|
68
|
+
// The pending array is only a projection, never a write/default for storage.
|
|
69
|
+
if (this.tabsOptions.cache && !this._getTabsReadiness().ready) return;
|
|
70
|
+
this._tabsState = value;
|
|
71
|
+
}
|
|
72
|
+
|
|
31
73
|
componentKeyCurrent?: string;
|
|
32
74
|
tabKeyCurrent?: string;
|
|
33
75
|
tabCurrentIndex: number;
|
|
@@ -36,7 +78,9 @@ export class ModelTabs extends BeanModelBase {
|
|
|
36
78
|
private _eventSsrHmrReload: TypeEventOff;
|
|
37
79
|
|
|
38
80
|
protected async __init__(scene: string, options?: ModelTabsOptions) {
|
|
81
|
+
const ctx = this.ctx;
|
|
39
82
|
await super.__init__(scene);
|
|
83
|
+
if (this._tabsDisposed || ctx.disposed) return;
|
|
40
84
|
this.bean._setBean('$$modelTabs', this);
|
|
41
85
|
// options
|
|
42
86
|
this.tabsOptions = deepExtend({}, this.$onionOptions, options);
|
|
@@ -71,25 +115,30 @@ export class ModelTabs extends BeanModelBase {
|
|
|
71
115
|
const queryOptionsTabs: UseQueryOptions<RouteTab[]> = {
|
|
72
116
|
queryKey: ['tabs'],
|
|
73
117
|
meta: {
|
|
74
|
-
|
|
118
|
+
// Memory query metadata is dehydrated; only DB state can retain a factory.
|
|
119
|
+
defaultData: this.tabsOptions.cache ? () => this._getInitialTabs() : this._getInitialTabs(),
|
|
75
120
|
},
|
|
76
121
|
};
|
|
77
122
|
if (this.tabsOptions.cache) {
|
|
78
|
-
this.
|
|
123
|
+
this._tabsState = this.$useStateLocalAsync(queryOptionsTabs);
|
|
124
|
+
const persistence = this.$modelPersistence;
|
|
125
|
+
this._offPersistence = persistence.subscribe(() => {
|
|
126
|
+
if (!this._tabsDisposed && !this.ctx.disposed) {
|
|
127
|
+
this._startTabsRestore(this._getTabsReadiness());
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// Keep initial ordering, but do not initialize a route from an obsolete
|
|
131
|
+
// restore if identity changes while the model itself is being loaded.
|
|
132
|
+
while (!this._tabsDisposed && !this.ctx.disposed) {
|
|
133
|
+
const readiness = this._getTabsReadiness();
|
|
134
|
+
await this._startTabsRestore(readiness);
|
|
135
|
+
if (this._isTabsReadinessCurrent(readiness)) break;
|
|
136
|
+
}
|
|
79
137
|
} else {
|
|
80
|
-
this.
|
|
81
|
-
|
|
82
|
-
// load cache
|
|
83
|
-
if (this.tabsOptions.cache) {
|
|
84
|
-
// await this.$ensureStateLocalAsync(this.tabKeyCurrent);
|
|
85
|
-
await this.$ensureStateLocalAsync(this.tabs);
|
|
86
|
-
}
|
|
87
|
-
// reset pageDirty
|
|
88
|
-
this._resetAllPageDirty();
|
|
89
|
-
// first route
|
|
90
|
-
if (this.$currentRoute) {
|
|
91
|
-
this.forwardRoute(this.$currentRoute);
|
|
138
|
+
this._tabsState = this.$useStateMem(queryOptionsTabs);
|
|
139
|
+
this._initializeTabs();
|
|
92
140
|
}
|
|
141
|
+
if (this._tabsDisposed || this.ctx.disposed) return;
|
|
93
142
|
// event
|
|
94
143
|
if (process.env.CLIENT && this.sys.config.ssr.hmr) {
|
|
95
144
|
this._eventSsrHmrReload = this.sys.meta.event.on('a-ssrhmr:reload', async (_data, next) => {
|
|
@@ -109,17 +158,90 @@ export class ModelTabs extends BeanModelBase {
|
|
|
109
158
|
}
|
|
110
159
|
|
|
111
160
|
protected __dispose__() {
|
|
161
|
+
this._tabsDisposed = true;
|
|
162
|
+
this._offPersistence?.();
|
|
163
|
+
this._tabsReadiness?.work.splice(0);
|
|
112
164
|
if (this._eventSsrHmrReload) {
|
|
113
165
|
this._eventSsrHmrReload();
|
|
166
|
+
this._eventSsrHmrReload = undefined!;
|
|
114
167
|
}
|
|
115
168
|
}
|
|
116
169
|
|
|
170
|
+
private _getTabsReadiness(): TabsReadiness {
|
|
171
|
+
const persistence = this.$modelPersistence;
|
|
172
|
+
let readiness = this._tabsReadiness;
|
|
173
|
+
if (!readiness || !persistence.current(readiness.token)) {
|
|
174
|
+
readiness?.work.splice(0);
|
|
175
|
+
readiness = shallowReactive({
|
|
176
|
+
ctx: this.ctx,
|
|
177
|
+
persistence,
|
|
178
|
+
token: persistence.capture(),
|
|
179
|
+
ready: false,
|
|
180
|
+
work: [],
|
|
181
|
+
});
|
|
182
|
+
this._tabsReadiness = readiness;
|
|
183
|
+
}
|
|
184
|
+
return readiness;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private _isTabsReadinessCurrent(readiness: TabsReadiness) {
|
|
188
|
+
return (
|
|
189
|
+
!this._tabsDisposed &&
|
|
190
|
+
!readiness.ctx.disposed &&
|
|
191
|
+
this._tabsReadiness === readiness &&
|
|
192
|
+
readiness.persistence.current(readiness.token)
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
private _startTabsRestore(readiness: TabsReadiness): Promise<void> {
|
|
197
|
+
if (!readiness.promise) {
|
|
198
|
+
// One actual backing read per epoch: repeated render/watch reads share it.
|
|
199
|
+
const state = this._tabsState;
|
|
200
|
+
readiness.promise = this.$ensureStateLocalAsync(state).then(() => {
|
|
201
|
+
if (!this._isTabsReadinessCurrent(readiness)) return;
|
|
202
|
+
readiness.ready = true;
|
|
203
|
+
this._initializeTabs();
|
|
204
|
+
for (const work of readiness.work.splice(0)) {
|
|
205
|
+
if (!this._isTabsReadinessCurrent(readiness)) break;
|
|
206
|
+
work();
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
void readiness.promise.catch(error => {
|
|
210
|
+
if (this._isTabsReadinessCurrent(readiness)) this.$errorHandler(error);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return readiness.promise;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private _initializeTabs() {
|
|
217
|
+
this._resetAllPageDirty();
|
|
218
|
+
if (this.$currentRoute) this.forwardRoute(this.$currentRoute);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private _deferTabsWork(work: () => void): boolean {
|
|
222
|
+
if (this._tabsDisposed || this.ctx.disposed) return true;
|
|
223
|
+
if (!this.tabsOptions.cache) return false;
|
|
224
|
+
const readiness = this._getTabsReadiness();
|
|
225
|
+
if (readiness.ready) return false;
|
|
226
|
+
readiness.work.push(work);
|
|
227
|
+
this._startTabsRestore(readiness);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private _captureTabsOperation() {
|
|
232
|
+
const ctx = this.ctx;
|
|
233
|
+
const persistence = this.$modelPersistence;
|
|
234
|
+
const token = persistence.capture();
|
|
235
|
+
return () => !this._tabsDisposed && !ctx.disposed && persistence.current(token);
|
|
236
|
+
}
|
|
237
|
+
|
|
117
238
|
get cache() {
|
|
118
239
|
return this.tabsOptions.cache;
|
|
119
240
|
}
|
|
120
241
|
|
|
121
242
|
// need not async
|
|
122
243
|
addTab(tab: RouteTabTransient, affix?: boolean): boolean {
|
|
244
|
+
if (this._deferTabsWork(() => this.addTab(tab, affix))) return false;
|
|
123
245
|
const res = this._addTab(tab, affix);
|
|
124
246
|
// current
|
|
125
247
|
this.tabKeyCurrent = res ? tab.tabKey : undefined;
|
|
@@ -129,6 +251,7 @@ export class ModelTabs extends BeanModelBase {
|
|
|
129
251
|
|
|
130
252
|
// need not async
|
|
131
253
|
_addTab(tab: Partial<RouteTabTransient>, affix?: boolean): boolean {
|
|
254
|
+
if (this._deferTabsWork(() => this._addTab(tab, affix))) return false;
|
|
132
255
|
const tabKey = tab.tabKey;
|
|
133
256
|
if (!tabKey) return false;
|
|
134
257
|
// tab
|
|
@@ -179,10 +302,12 @@ export class ModelTabs extends BeanModelBase {
|
|
|
179
302
|
}
|
|
180
303
|
|
|
181
304
|
resetAllTabInfos() {
|
|
305
|
+
if (this._deferTabsWork(() => this.resetAllTabInfos())) return;
|
|
182
306
|
this.tabs = this._getInitialTabs() as RouteTab[];
|
|
183
307
|
}
|
|
184
308
|
|
|
185
309
|
updateAllTabInfos(tabInitials?: RouteTabInitial[]) {
|
|
310
|
+
if (this._deferTabsWork(() => this.updateAllTabInfos(tabInitials))) return;
|
|
186
311
|
for (const tab of this.tabs) {
|
|
187
312
|
const tabInitial = tabInitials?.find(item => item.tabKey === tab.tabKey);
|
|
188
313
|
this.updateTabInfo(tab.tabKey, tabInitial);
|
|
@@ -190,6 +315,7 @@ export class ModelTabs extends BeanModelBase {
|
|
|
190
315
|
}
|
|
191
316
|
|
|
192
317
|
updateTabInfo(tabKey?: string, tabInitial?: RouteTabInitial) {
|
|
318
|
+
if (this._deferTabsWork(() => this.updateTabInfo(tabKey, tabInitial))) return;
|
|
193
319
|
if (!tabKey) return;
|
|
194
320
|
// tabs
|
|
195
321
|
const [index, tabOld] = this.findTab(tabKey);
|
|
@@ -205,6 +331,9 @@ export class ModelTabs extends BeanModelBase {
|
|
|
205
331
|
}
|
|
206
332
|
|
|
207
333
|
updateTabItemPageMeta(tabKey?: string, componentKey?: string, pageMeta?: IPageMeta) {
|
|
334
|
+
if (this._deferTabsWork(() => this.updateTabItemPageMeta(tabKey, componentKey, pageMeta))) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
208
337
|
if (!tabKey || !componentKey) return false;
|
|
209
338
|
// tab
|
|
210
339
|
const [index, tab] = this.findTab(tabKey);
|
|
@@ -227,11 +356,27 @@ export class ModelTabs extends BeanModelBase {
|
|
|
227
356
|
return true;
|
|
228
357
|
}
|
|
229
358
|
|
|
230
|
-
async deleteTab(
|
|
231
|
-
|
|
359
|
+
async deleteTab(
|
|
360
|
+
tabKey?: string,
|
|
361
|
+
noActiveNext?: boolean,
|
|
362
|
+
reason: TypeRouteDiscardReason | false = 'workspaceClose',
|
|
363
|
+
) {
|
|
364
|
+
if (this._tabsDisposed || this.ctx.disposed || !tabKey) return;
|
|
365
|
+
const current = this._captureTabsOperation();
|
|
232
366
|
// tabs
|
|
233
|
-
const [index] = this.findTab(tabKey);
|
|
234
|
-
if (index === -1) return;
|
|
367
|
+
const [index, tab] = this.findTab(tabKey);
|
|
368
|
+
if (index === -1 || !tab) return;
|
|
369
|
+
const cleanup = reason
|
|
370
|
+
? this.$router.notifyRouteDiscard(
|
|
371
|
+
reason,
|
|
372
|
+
(tab.items ?? []).map(item => ({
|
|
373
|
+
fullPath: item.fullPath,
|
|
374
|
+
componentKey: item.componentKey,
|
|
375
|
+
tabKey,
|
|
376
|
+
})),
|
|
377
|
+
this.selector,
|
|
378
|
+
)
|
|
379
|
+
: undefined;
|
|
235
380
|
// active next
|
|
236
381
|
let tabKeyActiveNext;
|
|
237
382
|
if (!noActiveNext && index === this.tabCurrentIndex) {
|
|
@@ -246,14 +391,22 @@ export class ModelTabs extends BeanModelBase {
|
|
|
246
391
|
this.tabs = mutate(this.tabs, copyState => {
|
|
247
392
|
copyState.splice(index, 1);
|
|
248
393
|
});
|
|
394
|
+
await cleanup;
|
|
395
|
+
if (!current()) return;
|
|
249
396
|
// active next
|
|
250
397
|
if (tabKeyActiveNext) {
|
|
251
398
|
await this.activeTab(tabKeyActiveNext);
|
|
252
399
|
}
|
|
253
400
|
}
|
|
254
401
|
|
|
255
|
-
async deleteTabItem(
|
|
256
|
-
|
|
402
|
+
async deleteTabItem(
|
|
403
|
+
tabKey?: string,
|
|
404
|
+
componentKey?: string,
|
|
405
|
+
noActiveNext?: boolean,
|
|
406
|
+
reason: TypeRouteDiscardReason | false = 'taskClose',
|
|
407
|
+
) {
|
|
408
|
+
if (this._tabsDisposed || this.ctx.disposed || !tabKey || !componentKey) return false;
|
|
409
|
+
const current = this._captureTabsOperation();
|
|
257
410
|
if (tabKey === componentKey) return false; // not delete first tabItem
|
|
258
411
|
// tab
|
|
259
412
|
const [index, tab] = this.findTab(tabKey);
|
|
@@ -265,9 +418,16 @@ export class ModelTabs extends BeanModelBase {
|
|
|
265
418
|
// deleteTab if length===1
|
|
266
419
|
if (tab.items.length === 1 && !tab.affix) {
|
|
267
420
|
// delete tab
|
|
268
|
-
await this.deleteTab(tabKey, noActiveNext);
|
|
421
|
+
await this.deleteTab(tabKey, noActiveNext, reason);
|
|
269
422
|
return true;
|
|
270
423
|
}
|
|
424
|
+
const cleanup = reason
|
|
425
|
+
? this.$router.notifyRouteDiscard(
|
|
426
|
+
reason,
|
|
427
|
+
[{ fullPath: tab.items[indexItem].fullPath, componentKey, tabKey }],
|
|
428
|
+
this.selector,
|
|
429
|
+
)
|
|
430
|
+
: undefined;
|
|
271
431
|
// active next
|
|
272
432
|
let componentKeyActiveNext;
|
|
273
433
|
if (!noActiveNext && componentKey === this.componentKeyCurrent) {
|
|
@@ -286,6 +446,8 @@ export class ModelTabs extends BeanModelBase {
|
|
|
286
446
|
this.tabs = mutate(this.tabs, copyState => {
|
|
287
447
|
copyState.splice(index, 1, tabNew);
|
|
288
448
|
});
|
|
449
|
+
await cleanup;
|
|
450
|
+
if (!current()) return false;
|
|
289
451
|
// active next
|
|
290
452
|
if (componentKeyActiveNext) {
|
|
291
453
|
await this.activeTabItem(tabKey, componentKeyActiveNext);
|
|
@@ -306,6 +468,7 @@ export class ModelTabs extends BeanModelBase {
|
|
|
306
468
|
}
|
|
307
469
|
|
|
308
470
|
updateTab(tab: Partial<RouteTabTransient>) {
|
|
471
|
+
if (this._deferTabsWork(() => this.updateTab(tab))) return;
|
|
309
472
|
const tabKey = tab.tabKey!;
|
|
310
473
|
const [index, tabOld] = this.findTab(tabKey);
|
|
311
474
|
if (index === -1 || !tabOld) return;
|
|
@@ -342,7 +505,7 @@ export class ModelTabs extends BeanModelBase {
|
|
|
342
505
|
}
|
|
343
506
|
|
|
344
507
|
async activeTab(tabKey?: string) {
|
|
345
|
-
if (!tabKey) return;
|
|
508
|
+
if (this._tabsDisposed || this.ctx.disposed || !tabKey) return;
|
|
346
509
|
const [_, tab] = this.findTab(tabKey);
|
|
347
510
|
if (!tab) return;
|
|
348
511
|
// should not updateTab here
|
|
@@ -355,7 +518,7 @@ export class ModelTabs extends BeanModelBase {
|
|
|
355
518
|
}
|
|
356
519
|
|
|
357
520
|
async activeTabItem(tabKey?: string, componentKey?: string) {
|
|
358
|
-
if (!tabKey || !componentKey) return;
|
|
521
|
+
if (this._tabsDisposed || this.ctx.disposed || !tabKey || !componentKey) return;
|
|
359
522
|
const [_, tab] = this.findTab(tabKey);
|
|
360
523
|
if (!tab) return;
|
|
361
524
|
const tabItem = tab.items?.find(item => item.componentKey === componentKey);
|
|
@@ -422,6 +585,8 @@ export class ModelTabs extends BeanModelBase {
|
|
|
422
585
|
}
|
|
423
586
|
|
|
424
587
|
async pruneTabs() {
|
|
588
|
+
if (this._tabsDisposed || this.ctx.disposed) return;
|
|
589
|
+
const current = this._captureTabsOperation();
|
|
425
590
|
let max = this.tabsOptions.max;
|
|
426
591
|
if (max === undefined || max === -1) return;
|
|
427
592
|
if (max < 1) max = 1;
|
|
@@ -437,12 +602,14 @@ export class ModelTabs extends BeanModelBase {
|
|
|
437
602
|
}
|
|
438
603
|
}
|
|
439
604
|
if (!tabKey) break;
|
|
440
|
-
await this.deleteTab(tabKey, true);
|
|
605
|
+
await this.deleteTab(tabKey, true, false);
|
|
606
|
+
if (!current()) return;
|
|
441
607
|
}
|
|
442
608
|
}
|
|
443
609
|
|
|
444
610
|
async pruneTabItems(tabKey?: string) {
|
|
445
|
-
if (!tabKey) return;
|
|
611
|
+
if (this._tabsDisposed || this.ctx.disposed || !tabKey) return;
|
|
612
|
+
const current = this._captureTabsOperation();
|
|
446
613
|
let maxItems = this.tabsOptions.maxItems;
|
|
447
614
|
if (maxItems === undefined || maxItems === -1) return;
|
|
448
615
|
if (maxItems < 1) maxItems = 1;
|
|
@@ -460,7 +627,8 @@ export class ModelTabs extends BeanModelBase {
|
|
|
460
627
|
}
|
|
461
628
|
}
|
|
462
629
|
if (!componentKey) break;
|
|
463
|
-
await this.deleteTabItem(tabKey, componentKey, true);
|
|
630
|
+
await this.deleteTabItem(tabKey, componentKey, true, false);
|
|
631
|
+
if (!current()) return;
|
|
464
632
|
}
|
|
465
633
|
}
|
|
466
634
|
|
|
@@ -518,16 +686,20 @@ export class ModelTabs extends BeanModelBase {
|
|
|
518
686
|
}
|
|
519
687
|
|
|
520
688
|
backRoute(route: RouteLocationNormalizedLoadedGeneric) {
|
|
689
|
+
if (this._deferTabsWork(() => this.backRoute(route))) return;
|
|
521
690
|
const [tabKey, componentKey] = this.findTabItemByFullPath(route.fullPath);
|
|
522
|
-
|
|
691
|
+
// The router emits the Back notification once; replace/pruning are not explicit discards.
|
|
692
|
+
void this.deleteTabItem(tabKey, componentKey, true, false);
|
|
523
693
|
}
|
|
524
694
|
|
|
525
695
|
forwardRoute(route: RouteLocationNormalizedLoadedGeneric) {
|
|
696
|
+
if (this._deferTabsWork(() => this.forwardRoute(route))) return;
|
|
526
697
|
const routeMeta = this.prepareRouteMeta(route);
|
|
527
698
|
this.addTab(routeMeta);
|
|
528
699
|
}
|
|
529
700
|
|
|
530
701
|
public setPageMeta(route: RouteLocationNormalizedLoadedGeneric, pageMeta: IPageMeta) {
|
|
702
|
+
if (this._deferTabsWork(() => this.setPageMeta(route, pageMeta))) return;
|
|
531
703
|
const [tabKey, componentKey] = this.findTabItemByFullPath(route.fullPath);
|
|
532
704
|
this.updateTabItemPageMeta(tabKey, componentKey, pageMeta);
|
|
533
705
|
}
|