cabloy 5.1.168 → 5.1.170
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 +15 -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 +83 -106
- 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 +21 -6
- package/zova/src/suite/a-home/modules/home-base/src/service/ssrLayout.ts +20 -10
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/component/routedDialog/controller.tsx +2 -0
- package/zova/src/suite/cabloy-basic/modules/basic-app/src/types/appModal.ts +3 -0
- 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-resource/src/component/formFieldResourcePicker/controller.tsx +5 -0
- package/zova/src/suite/cabloy-basic/modules/basic-resource/src/lib/resourcePickerPageHost.ts +64 -0
- package/zova/src/suite/cabloy-basic/modules/basic-resource/test/lib/resourcePickerPageHost.test.ts +94 -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/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/lib/resourcePicker.ts +42 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resourcePicker/controller.tsx +20 -79
- package/zova/src/suite-vendor/a-cabloy/package.json +3 -2
- package/zova/src/suite-vendor/a-zova/modules/a-bean/package.json +1 -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-router/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-routerstack/package.json +1 -1
- 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 +7 -7
|
@@ -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
|
@@ -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
|
|
@@ -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.160
|
|
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.0
|
|
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
|
|
@@ -1097,10 +1112,10 @@ importers:
|
|
|
1097
1112
|
specifier: ^5.1.53
|
|
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
|
|
@@ -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.42
|
|
1122
1137
|
version: link:modules/a-table
|
|
1123
1138
|
zova-module-a-zod:
|
|
1124
1139
|
specifier: ^5.1.37
|
|
@@ -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;
|
package/zova/src/suite/cabloy-basic/modules/basic-app/src/component/routedDialog/controller.tsx
CHANGED
|
@@ -41,8 +41,10 @@ export class ControllerRoutedDialog extends BeanControllerBase {
|
|
|
41
41
|
if (!this.$$modelStack) return <div class="loading loading-spinner"></div>;
|
|
42
42
|
const router = item.state.router;
|
|
43
43
|
if (!router) return <div class="text-error">Router unavailable</div>;
|
|
44
|
+
const pageHostProviders = item.options.createPageHostProviders?.(item.state.context);
|
|
44
45
|
const vnode = h(ZRouterViewStack, {
|
|
45
46
|
pageHostProviders: {
|
|
47
|
+
...pageHostProviders,
|
|
46
48
|
[routedDialogContextKey]: item.state.context,
|
|
47
49
|
},
|
|
48
50
|
});
|
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RowSelectionState, SortingState } from '@tanstack/vue-table';
|
|
2
2
|
import type { TableIdentity } from 'table-identity';
|
|
3
3
|
import type { IComponentOptions } from 'zova';
|
|
4
|
+
import type { ModelLayoutProfile } from 'zova-module-a-layoutprofile';
|
|
4
5
|
import type {
|
|
5
6
|
IJsxRenderContextPage,
|
|
6
7
|
IPageScope,
|
|
@@ -19,7 +20,7 @@ import { BeanControllerBase, deepEqual } from 'zova';
|
|
|
19
20
|
import { ZovaJsx } from 'zova-jsx';
|
|
20
21
|
import { Controller } from 'zova-module-a-bean';
|
|
21
22
|
import { $QueriesEnsureLoaded } from 'zova-module-a-model';
|
|
22
|
-
import { BeanControllerTableBase } from 'zova-module-a-table';
|
|
23
|
+
import { BeanControllerTableBase, ITableLayout } from 'zova-module-a-table';
|
|
23
24
|
import { ModelResource } from 'zova-module-rest-resource';
|
|
24
25
|
|
|
25
26
|
import {
|
|
@@ -62,6 +63,7 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
62
63
|
tableRef: BeanControllerTableBase<TData> | undefined;
|
|
63
64
|
private _tableMetaRefreshPending = false;
|
|
64
65
|
private _tableMetaRefreshTask: Promise<void> | undefined;
|
|
66
|
+
$$modelLayoutProfile: ModelLayoutProfile | undefined;
|
|
65
67
|
|
|
66
68
|
jsxZova: ZovaJsx;
|
|
67
69
|
jsxCelScope: IPageScope;
|
|
@@ -91,6 +93,14 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
91
93
|
true,
|
|
92
94
|
this.resource,
|
|
93
95
|
);
|
|
96
|
+
const layoutKey = this.layoutKey;
|
|
97
|
+
if (layoutKey) {
|
|
98
|
+
this.$$modelLayoutProfile = await this.bean._getBeanSelector(
|
|
99
|
+
'a-layoutprofile.model.layoutProfile',
|
|
100
|
+
true,
|
|
101
|
+
layoutKey,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
94
104
|
// jsx
|
|
95
105
|
this._prepareJsx();
|
|
96
106
|
// query
|
|
@@ -120,6 +130,7 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
120
130
|
await $QueriesEnsureLoaded(
|
|
121
131
|
() => this.$$modelResource.apiSchemasSelect.sdk,
|
|
122
132
|
() => this.queryData,
|
|
133
|
+
() => this.queryLayoutProfile,
|
|
123
134
|
);
|
|
124
135
|
this._refreshSelectedRows((this.data ?? []) as unknown as Record<string, unknown>[]);
|
|
125
136
|
this._notifySelectionChange();
|
|
@@ -144,6 +155,38 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
144
155
|
return this.$props.resource;
|
|
145
156
|
}
|
|
146
157
|
|
|
158
|
+
get queryLayoutProfile() {
|
|
159
|
+
return this.$$modelLayoutProfile?.load();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
get tableLayout(): ITableLayout | undefined {
|
|
163
|
+
return this.queryLayoutProfile?.data as ITableLayout | undefined;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get layoutKey() {
|
|
167
|
+
return this.$pageRoute?.path;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get columnConfigSeed() {
|
|
171
|
+
const layout = this.tableRef?.layout;
|
|
172
|
+
const properties = this.tableRef?.layoutProperties;
|
|
173
|
+
if (!layout || !properties || !this.$$modelLayoutProfile || !this.$passport.isAuthenticated)
|
|
174
|
+
return;
|
|
175
|
+
return { layout, properties };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async saveColumnConfig(layout: ITableLayout): Promise<void> {
|
|
179
|
+
if (!this.$$modelLayoutProfile) return;
|
|
180
|
+
await this.$$modelLayoutProfile.save().mutateAsync(layout as any);
|
|
181
|
+
await this._requestTableMetaRefresh();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async resetColumnConfig(): Promise<void> {
|
|
185
|
+
if (!this.$$modelLayoutProfile) return;
|
|
186
|
+
await this.$$modelLayoutProfile.reset().mutateAsync();
|
|
187
|
+
await this._requestTableMetaRefresh();
|
|
188
|
+
}
|
|
189
|
+
|
|
147
190
|
private _normalizeSelectedIds(ids: readonly TableIdentity[]) {
|
|
148
191
|
const selectedIds: TableIdentity[] = [];
|
|
149
192
|
const keys = new Set<string>();
|
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx
CHANGED
|
@@ -50,6 +50,7 @@ export class ControllerBlockTable<TData extends {} = {}> extends BeanControllerB
|
|
|
50
50
|
rowSelection={$$page.rowSelection}
|
|
51
51
|
onRowSelectionChange={updater => $$page.onRowSelectionChange(updater)}
|
|
52
52
|
tableScope={$$page.jsxCelScope}
|
|
53
|
+
layout={$$page.tableLayout}
|
|
53
54
|
></ZTable>
|
|
54
55
|
</div>
|
|
55
56
|
);
|
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockToolbarBulk/controller.tsx
CHANGED
|
@@ -9,7 +9,10 @@ import { VNode } from 'vue';
|
|
|
9
9
|
import { BeanControllerBase, Use } from 'zova';
|
|
10
10
|
import { Controller } from 'zova-module-a-bean';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
groupTableActionBulkActions,
|
|
14
|
+
resolveTableActionBulkDynamicProps,
|
|
15
|
+
} from '../../lib/selection.js';
|
|
13
16
|
|
|
14
17
|
declare module 'zova-module-a-openapi' {
|
|
15
18
|
export interface IResourceBlockRecord {
|
|
@@ -65,35 +68,48 @@ export class ControllerBlockToolbarBulk extends BeanControllerBase {
|
|
|
65
68
|
const { $$page } = this.$$renderContext;
|
|
66
69
|
const domActions = this._renderActions();
|
|
67
70
|
const selectionAvailable = $$page.selectionAvailable;
|
|
68
|
-
if (
|
|
71
|
+
if (domActions.start.length === 0 && domActions.end.length === 0 && !selectionAvailable) return;
|
|
69
72
|
return (
|
|
70
73
|
<div class={this.$props.class}>
|
|
71
|
-
<div
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
<div
|
|
75
|
+
class="flex flex-wrap items-center gap-3"
|
|
76
|
+
role="toolbar"
|
|
77
|
+
aria-label={this.scope.locale.BulkActions()}
|
|
78
|
+
>
|
|
79
|
+
{(selectionAvailable || domActions.start.length > 0) && (
|
|
80
|
+
<div class="join">
|
|
81
|
+
{$$page.selectionToggleAvailable && (
|
|
82
|
+
<button
|
|
83
|
+
type="button"
|
|
84
|
+
class="btn join-item"
|
|
85
|
+
onClick={() => $$page.toggleSelection()}
|
|
86
|
+
>
|
|
87
|
+
{$$page.selectionVisible ? this.scope.locale.Done() : this.scope.locale.Select()}
|
|
88
|
+
</button>
|
|
89
|
+
)}
|
|
90
|
+
{$$page.selectionEnabled && (
|
|
91
|
+
<span
|
|
92
|
+
class="join-item border-base-300 bg-base-200 px-3 py-2"
|
|
93
|
+
role="status"
|
|
94
|
+
aria-live="polite"
|
|
95
|
+
>
|
|
96
|
+
{this.scope.locale.SelectedItems($$page.selection.count)}
|
|
97
|
+
</span>
|
|
98
|
+
)}
|
|
99
|
+
{domActions.start}
|
|
100
|
+
</div>
|
|
76
101
|
)}
|
|
77
|
-
{
|
|
78
|
-
<span
|
|
79
|
-
class="join-item border-base-300 bg-base-200 px-3 py-2"
|
|
80
|
-
role="status"
|
|
81
|
-
aria-live="polite"
|
|
82
|
-
>
|
|
83
|
-
{this.scope.locale.SelectedItems($$page.selection.count)}
|
|
84
|
-
</span>
|
|
85
|
-
)}
|
|
86
|
-
{domActions}
|
|
102
|
+
{domActions.end.length > 0 && <div class="ml-auto join">{domActions.end}</div>}
|
|
87
103
|
</div>
|
|
88
104
|
</div>
|
|
89
105
|
);
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
private _renderActions() {
|
|
108
|
+
private _renderActions(): Record<'start' | 'end', VNode[]> {
|
|
93
109
|
const { $jsx, $celScope } = this.$$renderContext;
|
|
94
110
|
const actions = this.$props.actions;
|
|
95
|
-
if (!actions || actions.length === 0) return;
|
|
96
|
-
const
|
|
111
|
+
if (!actions || actions.length === 0) return { start: [], end: [] };
|
|
112
|
+
const items: { item: VNode; placement?: unknown }[] = [];
|
|
97
113
|
actions.forEach((action, index) => {
|
|
98
114
|
const actionName = action.name;
|
|
99
115
|
const permissionHint = action.options?.permission;
|
|
@@ -127,12 +143,11 @@ export class ControllerBlockToolbarBulk extends BeanControllerBase {
|
|
|
127
143
|
});
|
|
128
144
|
const domAction = $jsx.render(action.render!, options, $celScope, this.$$renderContext);
|
|
129
145
|
if (!domAction) return;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
domActions.push(domAction);
|
|
146
|
+
const renderedActions = Array.isArray(domAction) ? domAction : [domAction];
|
|
147
|
+
for (const item of renderedActions) {
|
|
148
|
+
items.push({ item, placement: action.options?.placement });
|
|
134
149
|
}
|
|
135
150
|
});
|
|
136
|
-
return
|
|
151
|
+
return groupTableActionBulkActions(items);
|
|
137
152
|
}
|
|
138
153
|
}
|