cabloy 5.1.169 → 5.1.171
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 +16 -0
- package/package.json +1 -1
- package/repo-e2e/specs/cabloy-basic.spec.ts +45 -0
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +4 -0
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +4 -0
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/pnpm-lock.yaml +177 -500
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +4 -0
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +5 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/package.json +54 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/index.ts +346 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/this.ts +2 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/bean/meta.index.ts +12 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/bean/meta.redlock.ts +11 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/bean/meta.version.ts +20 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/controller/layoutprofile.ts +38 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/dto/layoutProfile.tsx +26 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/dto/layoutProfileLoad.tsx +13 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/dto/layoutProfileSave.tsx +31 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/entity/layoutprofile.ts +20 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/index.ts +2 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/model/layoutprofile.ts +10 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/service/layoutprofile.ts +64 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/types/index.ts +1 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/types/layoutprofile.ts +21 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/test/layoutprofile.test.ts +258 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.build.json +11 -0
- package/vona/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.json +7 -0
- package/vona/src/suite-vendor/a-cabloy/package.json +2 -1
- package/vona/src/suite-vendor/a-cabloy/tsconfig.json +3 -0
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +37 -22
- package/zova/src/suite/a-home/modules/home-base/src/service/ssrLayout.ts +20 -10
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +44 -1
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +1 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockToolbarBulk/controller.tsx +40 -25
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/lib/selection.ts +11 -0
- package/zova/src/suite/cabloy-basic/modules/basic-page/test/lib/selection.test.ts +23 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/component/actionColumnConfig.ts +31 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/index.ts +25 -12
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/actionColumnConfig/controller.tsx +260 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +3 -3
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/en-us.ts +10 -0
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/zh-cn.ts +10 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/cli/openapi.config.ts +9 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/package.json +53 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/index.ts +140 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/.metadata/this.ts +2 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/layoutprofile.ts +67 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/baseURL.ts +5 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/index.ts +3 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/schemas.ts +663 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/api/openapi/types.ts +10150 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/apiSchema/layoutprofile.ts +25 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/index.ts +1 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/src/model/layoutprofile.ts +76 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.build.json +13 -0
- package/zova/src/suite-vendor/a-cabloy/modules/a-layoutprofile/tsconfig.json +5 -0
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -1
- 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/types/resource/tableActionBulk.ts +3 -0
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-ssr/src/lib/bodyReadyObserver.ts +13 -11
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +64 -2
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx +2 -3
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/lib/beanControllerTableBase.ts +10 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/index.ts +1 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/tableLayout.ts +101 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/test/lib/tableLayout.test.ts +112 -0
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { TableIdentity } from 'table-identity';
|
|
2
|
+
import type { IUser } from 'vona-module-a-user';
|
|
3
|
+
|
|
4
|
+
import { BeanBase } from 'vona';
|
|
5
|
+
import { Service } from 'vona-module-a-bean';
|
|
6
|
+
import { Core } from 'vona-module-a-core';
|
|
7
|
+
|
|
8
|
+
import type { ILayoutProfile } from '../types/layoutprofile.ts';
|
|
9
|
+
|
|
10
|
+
@Service()
|
|
11
|
+
export class ServiceLayoutprofile extends BeanBase {
|
|
12
|
+
async load(user: IUser, layoutKey: string): Promise<ILayoutProfile | undefined> {
|
|
13
|
+
const entity = await this.scope.model.layoutprofile.get({ userId: user.id, layoutKey });
|
|
14
|
+
return entity?.profile as ILayoutProfile | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async save(user: IUser, layoutKey: string, profile: ILayoutProfile): Promise<ILayoutProfile> {
|
|
18
|
+
if (await this._saveExisting(user.id, layoutKey, profile)) return profile;
|
|
19
|
+
return await this.scope.redlock.lockIsolate(
|
|
20
|
+
`layoutprofileSave.${user.id}.${layoutKey}`,
|
|
21
|
+
async () => await this._saveAfterLock(user.id, layoutKey, profile),
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async reset(user: IUser, layoutKey: string): Promise<void> {
|
|
26
|
+
await this.scope.redlock.lockIsolate(
|
|
27
|
+
`layoutprofileSave.${user.id}.${layoutKey}`,
|
|
28
|
+
async () => await this._reset(user.id, layoutKey),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Core.transaction()
|
|
33
|
+
private async _saveExisting(
|
|
34
|
+
userId: TableIdentity,
|
|
35
|
+
layoutKey: string,
|
|
36
|
+
profile: ILayoutProfile,
|
|
37
|
+
): Promise<boolean> {
|
|
38
|
+
const entity = await this.scope.model.layoutprofile.getForUpdate({ userId, layoutKey });
|
|
39
|
+
if (!entity) return false;
|
|
40
|
+
await this.scope.model.layoutprofile.updateById(entity.id, { profile });
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Core.transaction()
|
|
45
|
+
private async _saveAfterLock(
|
|
46
|
+
userId: TableIdentity,
|
|
47
|
+
layoutKey: string,
|
|
48
|
+
profile: ILayoutProfile,
|
|
49
|
+
): Promise<ILayoutProfile> {
|
|
50
|
+
const entity = await this.scope.model.layoutprofile.getForUpdate({ userId, layoutKey });
|
|
51
|
+
if (entity) {
|
|
52
|
+
await this.scope.model.layoutprofile.updateById(entity.id, { profile });
|
|
53
|
+
} else {
|
|
54
|
+
await this.scope.model.layoutprofile.insert({ userId, layoutKey, profile });
|
|
55
|
+
}
|
|
56
|
+
return profile;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@Core.transaction()
|
|
60
|
+
private async _reset(userId: TableIdentity, layoutKey: string): Promise<void> {
|
|
61
|
+
const entity = await this.scope.model.layoutprofile.getForUpdate({ userId, layoutKey });
|
|
62
|
+
if (entity) await this.scope.model.layoutprofile.deleteById(entity.id);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './layoutprofile.ts';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { TableIdentity } from 'table-identity';
|
|
2
|
+
|
|
3
|
+
export type TypeLayoutProfileWidth = number | 'auto';
|
|
4
|
+
|
|
5
|
+
export interface ILayoutProfileColumn {
|
|
6
|
+
key: string;
|
|
7
|
+
visible: boolean;
|
|
8
|
+
width: TypeLayoutProfileWidth;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ILayoutProfile extends Record<string, unknown> {
|
|
12
|
+
version: 1;
|
|
13
|
+
schemaFingerprint?: string;
|
|
14
|
+
columns: ILayoutProfileColumn[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ILayoutProfileRecord {
|
|
18
|
+
id?: TableIdentity;
|
|
19
|
+
layoutKey: string;
|
|
20
|
+
profile: ILayoutProfile;
|
|
21
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import type { TableIdentity } from 'table-identity';
|
|
2
|
+
|
|
3
|
+
import { catchError } from '@cabloy/utils';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { describe, it } from 'node:test';
|
|
7
|
+
import { app } from 'vona-mock';
|
|
8
|
+
|
|
9
|
+
import type { ILayoutProfile } from '../src/types/layoutprofile.ts';
|
|
10
|
+
|
|
11
|
+
const actionPath = '/layoutprofile';
|
|
12
|
+
|
|
13
|
+
function createTestId() {
|
|
14
|
+
return randomUUID().slice(0, 12);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function createProfile(name: string): ILayoutProfile {
|
|
18
|
+
return {
|
|
19
|
+
version: 1,
|
|
20
|
+
schemaFingerprint: `schema-${name}`,
|
|
21
|
+
columns: [
|
|
22
|
+
{ key: 'name', visible: true, width: 'auto' },
|
|
23
|
+
{ key: 'level', visible: false, width: 180 },
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function performAs(accessToken: string, method: string, path: string, options = {}) {
|
|
29
|
+
return await app.bean.executor.newCtxIsolate(async () => {
|
|
30
|
+
return await (app.bean.executor.performAction as any)(method, path, {
|
|
31
|
+
...options,
|
|
32
|
+
authToken: accessToken,
|
|
33
|
+
innerAccess: false,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function registerAndSignin(name: string) {
|
|
39
|
+
const user = await app.bean.user.register({ name }, true);
|
|
40
|
+
const token = await app.bean.passport.signinMock(name as any);
|
|
41
|
+
return { token: token.accessToken, user };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function deleteOwnedProfiles(userIds: readonly TableIdentity[], layoutKey: string) {
|
|
45
|
+
for (const userId of userIds) {
|
|
46
|
+
await app.scope('a-layoutprofile').model.layoutprofile.delete({ userId, layoutKey });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function deleteOwnedUsers(userIds: readonly TableIdentity[]) {
|
|
51
|
+
for (const userId of userIds) {
|
|
52
|
+
await app.scope('home-user').model.roleUser.delete({ userId });
|
|
53
|
+
await app.bean.user.removeById(userId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function deleteOwnedTestData(userIds: readonly TableIdentity[], layoutKey: string) {
|
|
58
|
+
await deleteOwnedProfiles(userIds, layoutKey);
|
|
59
|
+
await deleteOwnedUsers(userIds);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe('layoutprofile.test.ts', { concurrency: false }, () => {
|
|
63
|
+
it('rejects anonymous profile operations', async () => {
|
|
64
|
+
await app.bean.executor.mockCtx(async () => {
|
|
65
|
+
for (const [method, path, options] of [
|
|
66
|
+
['get', `${actionPath}/load`, { query: { layoutKey: 'anonymous' } }],
|
|
67
|
+
[
|
|
68
|
+
'post',
|
|
69
|
+
`${actionPath}/save`,
|
|
70
|
+
{ body: { layoutKey: 'anonymous', profile: createProfile('anonymous') } },
|
|
71
|
+
],
|
|
72
|
+
['delete', `${actionPath}/reset`, { query: { layoutKey: 'anonymous' } }],
|
|
73
|
+
] as const) {
|
|
74
|
+
const [, error] = await catchError(() =>
|
|
75
|
+
(app.bean.executor.performAction as any)(method, path, {
|
|
76
|
+
...options,
|
|
77
|
+
innerAccess: false,
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
assert.equal(error?.code, 401);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('isolates profiles by authenticated user and supports reset', async () => {
|
|
86
|
+
await app.bean.executor.mockCtx(async () => {
|
|
87
|
+
const suffix = createTestId();
|
|
88
|
+
const layoutKey = `/training/student/${suffix}`;
|
|
89
|
+
let userA: TableIdentity | undefined;
|
|
90
|
+
let userB: TableIdentity | undefined;
|
|
91
|
+
try {
|
|
92
|
+
const customerA = await registerAndSignin(`layout-a-${suffix}`);
|
|
93
|
+
const customerB = await registerAndSignin(`layout-b-${suffix}`);
|
|
94
|
+
userA = customerA.user.id;
|
|
95
|
+
userB = customerB.user.id;
|
|
96
|
+
const profileA = createProfile('a');
|
|
97
|
+
const profileAUpdated = {
|
|
98
|
+
...createProfile('a-updated'),
|
|
99
|
+
columns: [{ key: 'name', visible: false, width: 320 }],
|
|
100
|
+
} satisfies ILayoutProfile;
|
|
101
|
+
const profileB = createProfile('b');
|
|
102
|
+
|
|
103
|
+
assert.equal(
|
|
104
|
+
await performAs(customerA.token, 'get', `${actionPath}/load`, {
|
|
105
|
+
query: { layoutKey },
|
|
106
|
+
}),
|
|
107
|
+
undefined,
|
|
108
|
+
);
|
|
109
|
+
assert.deepEqual(
|
|
110
|
+
await performAs(customerA.token, 'post', `${actionPath}/save`, {
|
|
111
|
+
body: { layoutKey, profile: profileA },
|
|
112
|
+
}),
|
|
113
|
+
profileA,
|
|
114
|
+
);
|
|
115
|
+
const persistedA = await app.scope('a-layoutprofile').model.layoutprofile.get({
|
|
116
|
+
userId: userA,
|
|
117
|
+
layoutKey,
|
|
118
|
+
});
|
|
119
|
+
assert.equal(String(persistedA?.userId), String(userA));
|
|
120
|
+
assert.deepEqual(persistedA?.profile, profileA);
|
|
121
|
+
|
|
122
|
+
assert.equal(
|
|
123
|
+
await performAs(customerB.token, 'get', `${actionPath}/load`, {
|
|
124
|
+
query: { layoutKey },
|
|
125
|
+
}),
|
|
126
|
+
undefined,
|
|
127
|
+
);
|
|
128
|
+
assert.deepEqual(
|
|
129
|
+
await performAs(customerB.token, 'post', `${actionPath}/save`, {
|
|
130
|
+
body: { layoutKey, profile: profileB },
|
|
131
|
+
}),
|
|
132
|
+
profileB,
|
|
133
|
+
);
|
|
134
|
+
assert.deepEqual(
|
|
135
|
+
await performAs(customerA.token, 'post', `${actionPath}/save`, {
|
|
136
|
+
body: { layoutKey, profile: profileAUpdated },
|
|
137
|
+
}),
|
|
138
|
+
profileAUpdated,
|
|
139
|
+
);
|
|
140
|
+
const profilesA = await app.scope('a-layoutprofile').model.layoutprofile.select({
|
|
141
|
+
where: { userId: userA, layoutKey },
|
|
142
|
+
});
|
|
143
|
+
assert.equal(profilesA.length, 1);
|
|
144
|
+
assert.deepEqual(profilesA[0].profile, profileAUpdated);
|
|
145
|
+
|
|
146
|
+
assert.deepEqual(
|
|
147
|
+
await performAs(customerB.token, 'get', `${actionPath}/load`, {
|
|
148
|
+
query: { layoutKey },
|
|
149
|
+
}),
|
|
150
|
+
profileB,
|
|
151
|
+
);
|
|
152
|
+
assert.equal(
|
|
153
|
+
await performAs(customerB.token, 'delete', `${actionPath}/reset`, {
|
|
154
|
+
query: { layoutKey },
|
|
155
|
+
}),
|
|
156
|
+
null,
|
|
157
|
+
);
|
|
158
|
+
assert.equal(
|
|
159
|
+
await performAs(customerB.token, 'get', `${actionPath}/load`, {
|
|
160
|
+
query: { layoutKey },
|
|
161
|
+
}),
|
|
162
|
+
undefined,
|
|
163
|
+
);
|
|
164
|
+
assert.deepEqual(
|
|
165
|
+
await performAs(customerA.token, 'get', `${actionPath}/load`, {
|
|
166
|
+
query: { layoutKey },
|
|
167
|
+
}),
|
|
168
|
+
profileAUpdated,
|
|
169
|
+
);
|
|
170
|
+
} finally {
|
|
171
|
+
const userIds = [userA, userB].filter((id): id is TableIdentity => id !== undefined);
|
|
172
|
+
await deleteOwnedTestData(userIds, layoutKey);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('serializes competing first saves for one user and layout key', async () => {
|
|
178
|
+
const suffix = createTestId();
|
|
179
|
+
const layoutKey = `/layout-concurrency/${suffix}`;
|
|
180
|
+
const fixture = await app.bean.executor.mockCtx(async () => {
|
|
181
|
+
const customer = await registerAndSignin(`layout-concurrency-${suffix}`);
|
|
182
|
+
return { token: customer.token, userId: customer.user.id };
|
|
183
|
+
});
|
|
184
|
+
const profiles = [createProfile('concurrency-a'), createProfile('concurrency-b')];
|
|
185
|
+
try {
|
|
186
|
+
const results = await Promise.allSettled(
|
|
187
|
+
profiles.map(profile =>
|
|
188
|
+
app.bean.executor.mockCtx(async () => {
|
|
189
|
+
return await performAs(fixture.token, 'post', `${actionPath}/save`, {
|
|
190
|
+
body: { layoutKey, profile },
|
|
191
|
+
});
|
|
192
|
+
}),
|
|
193
|
+
),
|
|
194
|
+
);
|
|
195
|
+
assert.equal(results.filter(result => result.status === 'fulfilled').length, profiles.length);
|
|
196
|
+
|
|
197
|
+
const persisted = await app.bean.executor.mockCtx(async () => {
|
|
198
|
+
return await app.scope('a-layoutprofile').model.layoutprofile.select({
|
|
199
|
+
where: { userId: fixture.userId, layoutKey },
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
assert.equal(persisted.length, 1);
|
|
203
|
+
assert.ok(
|
|
204
|
+
profiles.some(profile => JSON.stringify(profile) === JSON.stringify(persisted[0].profile)),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const loaded = await app.bean.executor.mockCtx(async () => {
|
|
208
|
+
return await performAs(fixture.token, 'get', `${actionPath}/load`, {
|
|
209
|
+
query: { layoutKey },
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
assert.deepEqual(loaded, persisted[0].profile);
|
|
213
|
+
} finally {
|
|
214
|
+
await app.bean.executor.mockCtx(async () => {
|
|
215
|
+
await deleteOwnedTestData([fixture.userId], layoutKey);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('validates the layout key and profile structure at the API boundary', async () => {
|
|
221
|
+
await app.bean.executor.mockCtx(async () => {
|
|
222
|
+
const suffix = createTestId();
|
|
223
|
+
const customer = await registerAndSignin(`layout-validation-${suffix}`);
|
|
224
|
+
const layoutKey = `/layout-validation/${suffix}`;
|
|
225
|
+
try {
|
|
226
|
+
for (const body of [
|
|
227
|
+
{ layoutKey: '', profile: createProfile('invalid-key') },
|
|
228
|
+
{ layoutKey, profile: { ...createProfile('version'), version: 2 } },
|
|
229
|
+
{
|
|
230
|
+
layoutKey,
|
|
231
|
+
profile: {
|
|
232
|
+
...createProfile('width'),
|
|
233
|
+
columns: [{ key: 'name', visible: true, width: 0 }],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
layoutKey,
|
|
238
|
+
profile: {
|
|
239
|
+
...createProfile('visible'),
|
|
240
|
+
columns: [{ key: 'name', visible: 'yes', width: 'auto' }],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
]) {
|
|
244
|
+
const [, error] = await catchError(() =>
|
|
245
|
+
performAs(customer.token, 'post', `${actionPath}/save`, { body }),
|
|
246
|
+
);
|
|
247
|
+
assert.equal(
|
|
248
|
+
error?.code,
|
|
249
|
+
422,
|
|
250
|
+
`unexpected validation result for ${JSON.stringify(body)}: ${JSON.stringify(error)}`,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
} finally {
|
|
254
|
+
await deleteOwnedTestData([customer.user.id], layoutKey);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-suite-a-cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.53",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"vona-module-a-cabloy": "workspace:^",
|
|
11
|
+
"vona-module-a-layoutprofile": "^5.0.1",
|
|
11
12
|
"vona-module-a-markdown": "^5.0.3",
|
|
12
13
|
"vona-module-a-rbac": "^5.0.6",
|
|
13
14
|
"vona-module-a-socket": "workspace:^",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.162",
|
|
4
4
|
"gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
|
|
5
5
|
"description": "A vue3 framework with ioc",
|
|
6
6
|
"keywords": [
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"zova-core": "^5.1.89",
|
|
49
|
-
"zova-suite-a-zova": "^5.1.
|
|
49
|
+
"zova-suite-a-zova": "^5.1.161"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"clean-package": "^2.2.0",
|
package/zova/pnpm-lock.yaml
CHANGED
|
@@ -77,7 +77,7 @@ importers:
|
|
|
77
77
|
version: 10.6.0(postcss@8.5.28)
|
|
78
78
|
axios:
|
|
79
79
|
specifier: ^1.18.1
|
|
80
|
-
version: 1.20.0
|
|
80
|
+
version: 1.20.0
|
|
81
81
|
compression:
|
|
82
82
|
specifier: ^1.8.1
|
|
83
83
|
version: 1.8.2
|
|
@@ -171,6 +171,9 @@ importers:
|
|
|
171
171
|
zova-module-a-interceptor:
|
|
172
172
|
specifier: workspace:^
|
|
173
173
|
version: link:src/suite-vendor/a-zova/modules/a-interceptor
|
|
174
|
+
zova-module-a-layoutprofile:
|
|
175
|
+
specifier: workspace:^
|
|
176
|
+
version: link:src/suite-vendor/a-cabloy/modules/a-layoutprofile
|
|
174
177
|
zova-module-a-logger:
|
|
175
178
|
specifier: workspace:^
|
|
176
179
|
version: link:src/suite-vendor/a-zova/modules/a-logger
|
|
@@ -363,7 +366,7 @@ importers:
|
|
|
363
366
|
devDependencies:
|
|
364
367
|
'@cabloy/cli':
|
|
365
368
|
specifier: ^3.1.29
|
|
366
|
-
version: 3.1.29(
|
|
369
|
+
version: 3.1.29(vue@3.5.42(typescript@5.9.3))
|
|
367
370
|
'@cabloy/lint':
|
|
368
371
|
specifier: ^5.1.32
|
|
369
372
|
version: 5.1.32(@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(eslint@10.10.0(jiti@2.7.0))(supports-color@10.2.2)(typescript@5.9.3))(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.70.0(typescript@5.9.3))(@typescript-eslint/utils@8.70.0(eslint@10.10.0(jiti@2.7.0))(typescript@5.9.3))(eslint@10.10.0(jiti@2.7.0))(oxfmt@0.62.0)(oxlint@1.82.0)(supports-color@10.2.2)(typescript@5.9.3)(vue-eslint-parser@10.4.1(eslint@10.10.0(jiti@2.7.0)))
|
|
@@ -438,7 +441,7 @@ importers:
|
|
|
438
441
|
dependencies:
|
|
439
442
|
'@cabloy/cli':
|
|
440
443
|
specifier: ^3.1.29
|
|
441
|
-
version: 3.1.29(
|
|
444
|
+
version: 3.1.29(vue@3.5.42(typescript@5.9.3))
|
|
442
445
|
'@cabloy/process-helper':
|
|
443
446
|
specifier: ^3.1.8
|
|
444
447
|
version: 3.1.8
|
|
@@ -472,7 +475,7 @@ importers:
|
|
|
472
475
|
version: 7.29.7(@babel/core@7.29.7)
|
|
473
476
|
'@cabloy/cli':
|
|
474
477
|
specifier: ^3.1.29
|
|
475
|
-
version: 3.1.29(
|
|
478
|
+
version: 3.1.29(vue@3.5.42(typescript@5.9.3))
|
|
476
479
|
'@cabloy/extend':
|
|
477
480
|
specifier: ^3.2.8
|
|
478
481
|
version: 3.2.8
|
|
@@ -870,7 +873,7 @@ importers:
|
|
|
870
873
|
specifier: ^5.1.89
|
|
871
874
|
version: link:../zova-core
|
|
872
875
|
zova-suite-a-zova:
|
|
873
|
-
specifier: ^5.1.
|
|
876
|
+
specifier: ^5.1.161
|
|
874
877
|
version: link:../../src/suite-vendor/a-zova
|
|
875
878
|
devDependencies:
|
|
876
879
|
clean-package:
|
|
@@ -973,13 +976,25 @@ importers:
|
|
|
973
976
|
|
|
974
977
|
src/suite-vendor/a-cabloy:
|
|
975
978
|
dependencies:
|
|
979
|
+
zova-module-a-layoutprofile:
|
|
980
|
+
specifier: ^5.0.2
|
|
981
|
+
version: link:modules/a-layoutprofile
|
|
976
982
|
zova-module-a-rbac:
|
|
977
983
|
specifier: ^5.0.1
|
|
978
984
|
version: link:modules/a-rbac
|
|
979
985
|
zova-module-rest-resource:
|
|
980
|
-
specifier: ^5.1.
|
|
986
|
+
specifier: ^5.1.54
|
|
981
987
|
version: link:modules/rest-resource
|
|
982
988
|
|
|
989
|
+
src/suite-vendor/a-cabloy/modules/a-layoutprofile:
|
|
990
|
+
devDependencies:
|
|
991
|
+
clean-package:
|
|
992
|
+
specifier: ^2.2.0
|
|
993
|
+
version: 2.2.0
|
|
994
|
+
rimraf:
|
|
995
|
+
specifier: ^6.1.3
|
|
996
|
+
version: 6.1.3
|
|
997
|
+
|
|
983
998
|
src/suite-vendor/a-cabloy/modules/a-rbac:
|
|
984
999
|
devDependencies:
|
|
985
1000
|
clean-package:
|
|
@@ -1058,7 +1073,7 @@ importers:
|
|
|
1058
1073
|
specifier: ^5.1.26
|
|
1059
1074
|
version: link:modules/a-app
|
|
1060
1075
|
zova-module-a-bean:
|
|
1061
|
-
specifier: ^5.1.
|
|
1076
|
+
specifier: ^5.1.35
|
|
1062
1077
|
version: link:modules/a-bean
|
|
1063
1078
|
zova-module-a-behavior:
|
|
1064
1079
|
specifier: ^5.1.26
|
|
@@ -1094,19 +1109,19 @@ importers:
|
|
|
1094
1109
|
specifier: ^5.1.36
|
|
1095
1110
|
version: link:modules/a-model
|
|
1096
1111
|
zova-module-a-openapi:
|
|
1097
|
-
specifier: ^5.1.
|
|
1112
|
+
specifier: ^5.1.54
|
|
1098
1113
|
version: link:modules/a-openapi
|
|
1099
1114
|
zova-module-a-router:
|
|
1100
|
-
specifier: ^5.1.
|
|
1115
|
+
specifier: ^5.1.37
|
|
1101
1116
|
version: link:modules/a-router
|
|
1102
1117
|
zova-module-a-routerstack:
|
|
1103
|
-
specifier: ^5.1.
|
|
1118
|
+
specifier: ^5.1.29
|
|
1104
1119
|
version: link:modules/a-routerstack
|
|
1105
1120
|
zova-module-a-routertabs:
|
|
1106
1121
|
specifier: ^5.1.34
|
|
1107
1122
|
version: link:modules/a-routertabs
|
|
1108
1123
|
zova-module-a-ssr:
|
|
1109
|
-
specifier: ^5.1.
|
|
1124
|
+
specifier: ^5.1.39
|
|
1110
1125
|
version: link:modules/a-ssr
|
|
1111
1126
|
zova-module-a-ssrhmr:
|
|
1112
1127
|
specifier: ^5.1.23
|
|
@@ -1118,7 +1133,7 @@ importers:
|
|
|
1118
1133
|
specifier: ^5.1.35
|
|
1119
1134
|
version: link:modules/a-style
|
|
1120
1135
|
zova-module-a-table:
|
|
1121
|
-
specifier: ^5.1.
|
|
1136
|
+
specifier: ^5.1.43
|
|
1122
1137
|
version: link:modules/a-table
|
|
1123
1138
|
zova-module-a-zod:
|
|
1124
1139
|
specifier: ^5.1.37
|
|
@@ -1198,7 +1213,7 @@ importers:
|
|
|
1198
1213
|
dependencies:
|
|
1199
1214
|
axios:
|
|
1200
1215
|
specifier: ^1.18.1
|
|
1201
|
-
version: 1.20.0
|
|
1216
|
+
version: 1.20.0
|
|
1202
1217
|
devDependencies:
|
|
1203
1218
|
clean-package:
|
|
1204
1219
|
specifier: ^2.2.0
|
|
@@ -1471,7 +1486,7 @@ importers:
|
|
|
1471
1486
|
devDependencies:
|
|
1472
1487
|
'@cabloy/cli':
|
|
1473
1488
|
specifier: ^3.1.29
|
|
1474
|
-
version: 3.1.29(
|
|
1489
|
+
version: 3.1.29(vue@3.5.42(typescript@5.9.3))
|
|
1475
1490
|
'@types/luxon':
|
|
1476
1491
|
specifier: ^3.7.1
|
|
1477
1492
|
version: 3.7.5
|
|
@@ -8814,7 +8829,7 @@ snapshots:
|
|
|
8814
8829
|
|
|
8815
8830
|
'@bufbuild/protobuf@2.15.0': {}
|
|
8816
8831
|
|
|
8817
|
-
'@cabloy/cli@3.1.29(
|
|
8832
|
+
'@cabloy/cli@3.1.29(vue@3.5.42(typescript@5.9.3))':
|
|
8818
8833
|
dependencies:
|
|
8819
8834
|
'@babel/parser': 7.29.8
|
|
8820
8835
|
'@cabloy/module-glob': 5.3.18
|
|
@@ -8823,7 +8838,7 @@ snapshots:
|
|
|
8823
8838
|
'@cabloy/utils': 2.1.27
|
|
8824
8839
|
'@cabloy/word-utils': link:packages-utils/word-utils
|
|
8825
8840
|
'@npmcli/config': 10.12.0
|
|
8826
|
-
'@zhennann/common-bin': 4.0.1
|
|
8841
|
+
'@zhennann/common-bin': 4.0.1
|
|
8827
8842
|
'@zhennann/ejs': 3.0.1
|
|
8828
8843
|
boxen: 4.2.0
|
|
8829
8844
|
chalk: 3.0.0
|
|
@@ -11011,7 +11026,7 @@ snapshots:
|
|
|
11011
11026
|
|
|
11012
11027
|
'@vue/shared@3.5.13': {}
|
|
11013
11028
|
|
|
11014
|
-
'@zhennann/common-bin@4.0.1
|
|
11029
|
+
'@zhennann/common-bin@4.0.1':
|
|
11015
11030
|
dependencies:
|
|
11016
11031
|
chalk: 4.1.2
|
|
11017
11032
|
change-case: 4.1.2
|
|
@@ -11045,7 +11060,7 @@ snapshots:
|
|
|
11045
11060
|
|
|
11046
11061
|
acorn@8.18.0: {}
|
|
11047
11062
|
|
|
11048
|
-
agent-base@6.0.2
|
|
11063
|
+
agent-base@6.0.2:
|
|
11049
11064
|
dependencies:
|
|
11050
11065
|
debug: 4.4.3(supports-color@10.2.2)
|
|
11051
11066
|
transitivePeerDependencies:
|
|
@@ -11153,11 +11168,11 @@ snapshots:
|
|
|
11153
11168
|
dependencies:
|
|
11154
11169
|
possible-typed-array-names: 1.1.0
|
|
11155
11170
|
|
|
11156
|
-
axios@1.20.0
|
|
11171
|
+
axios@1.20.0:
|
|
11157
11172
|
dependencies:
|
|
11158
11173
|
follow-redirects: 1.16.0
|
|
11159
11174
|
form-data: 4.0.6
|
|
11160
|
-
https-proxy-agent: 5.0.1
|
|
11175
|
+
https-proxy-agent: 5.0.1
|
|
11161
11176
|
proxy-from-env: 2.1.0
|
|
11162
11177
|
transitivePeerDependencies:
|
|
11163
11178
|
- debug
|
|
@@ -12552,9 +12567,9 @@ snapshots:
|
|
|
12552
12567
|
statuses: 2.0.2
|
|
12553
12568
|
toidentifier: 1.0.1
|
|
12554
12569
|
|
|
12555
|
-
https-proxy-agent@5.0.1
|
|
12570
|
+
https-proxy-agent@5.0.1:
|
|
12556
12571
|
dependencies:
|
|
12557
|
-
agent-base: 6.0.2
|
|
12572
|
+
agent-base: 6.0.2
|
|
12558
12573
|
debug: 4.4.3(supports-color@10.2.2)
|
|
12559
12574
|
transitivePeerDependencies:
|
|
12560
12575
|
- supports-color
|
|
@@ -37,17 +37,26 @@ export class ServiceSsrLayout extends BeanBase {
|
|
|
37
37
|
`<script id="__leftDrawerOpenJS">
|
|
38
38
|
${this.options?.sidebarLeftOpenPCCapability ? this._getJsHandlerSidebar() : ''}
|
|
39
39
|
${this._getJsHandlerPageContainer()}
|
|
40
|
-
window.ssr_body_ready_handler=
|
|
41
|
-
${this.options?.sidebarLeftOpenPCCapability ? 'window.ssr_body_ready_handler_sidebar();' : ''}
|
|
42
|
-
window.ssr_body_ready_handler_pageContainer();
|
|
40
|
+
window.ssr_body_ready_handler=__targets=>{
|
|
41
|
+
${this.options?.sidebarLeftOpenPCCapability ? 'window.ssr_body_ready_handler_sidebar(__targets);' : ''}
|
|
42
|
+
window.ssr_body_ready_handler_pageContainer(__targets);
|
|
43
43
|
};
|
|
44
44
|
window.ssr_body_ready_condition=()=>{
|
|
45
|
+
const __domDrawerContainer=document.querySelector('#q-app>.drawer');
|
|
45
46
|
const __domPageContainer=document.querySelector('#q-app>.drawer>.drawer-content');
|
|
46
|
-
|
|
47
|
+
${this.options?.sidebarLeftOpenPCCapability ? "const __domDrawer=document.querySelector('#q-app>.drawer>.drawer-side');" : ''}
|
|
48
|
+
return __domDrawerContainer&&__domPageContainer${this.options?.sidebarLeftOpenPCCapability ? '&&__domDrawer' : ''}?{
|
|
49
|
+
drawerContainer:__domDrawerContainer,
|
|
50
|
+
pageContainer:__domPageContainer,
|
|
51
|
+
${this.options?.sidebarLeftOpenPCCapability ? 'drawer:__domDrawer,' : ''}
|
|
52
|
+
}:undefined;
|
|
47
53
|
};
|
|
48
|
-
window.ssr_body_ready_callback=
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
window.ssr_body_ready_callback=__targets=>{
|
|
55
|
+
try{
|
|
56
|
+
window.ssr_body_ready_handler(__targets);
|
|
57
|
+
}finally{
|
|
58
|
+
document.querySelector('#__leftDrawerOpenJS')?.remove();
|
|
59
|
+
}
|
|
51
60
|
};
|
|
52
61
|
</script>`.replaceAll('\n', '') + getBodyReadyObserverScript();
|
|
53
62
|
}
|
|
@@ -61,7 +70,7 @@ export class ServiceSsrLayout extends BeanBase {
|
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
private _getJsHandlerSidebar() {
|
|
64
|
-
return `window.ssr_body_ready_handler_sidebar=
|
|
73
|
+
return `window.ssr_body_ready_handler_sidebar=__targets=>{
|
|
65
74
|
const __belowBreakpoint=document.documentElement.clientWidth <= ${this.options?.sidebarBreakpoint};
|
|
66
75
|
let __leftDrawerOpen;
|
|
67
76
|
if(__belowBreakpoint){
|
|
@@ -70,10 +79,11 @@ export class ServiceSsrLayout extends BeanBase {
|
|
|
70
79
|
const __leftDrawerOpenPC=window.ssr_load_local('sidebarLeftOpenPC');
|
|
71
80
|
__leftDrawerOpen=__leftDrawerOpenPC!==undefined?__leftDrawerOpenPC:${this.options?.sidebarLeftOpenPCFallback ?? false};
|
|
72
81
|
}
|
|
73
|
-
const __domDrawerContainer=
|
|
74
|
-
const __domDrawer=
|
|
82
|
+
const __domDrawerContainer=__targets?.drawerContainer;
|
|
83
|
+
const __domDrawer=__targets?.drawer;
|
|
75
84
|
const sidebarWidth = '${this.scope.config.layout.sidebar.width}px';
|
|
76
85
|
const navbarHeight = '${this.scope.config.layout.navbar.height}px';
|
|
86
|
+
if(!__domDrawerContainer||!__domDrawer)return;
|
|
77
87
|
if(__leftDrawerOpen){
|
|
78
88
|
__domDrawer.style.transform='translateX(0px)';
|
|
79
89
|
__domDrawer.style.width=sidebarWidth;
|