cabloy 5.1.158 → 5.1.159
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/.claude/skills/cabloy-contract-loop/SKILL.md +24 -7
- package/.claude/skills/cabloy-contract-loop/evals/evals.json +12 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +28 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +19 -2
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +18 -0
- package/.claude/skills/cabloy-zova-source-reading/SKILL.md +14 -0
- package/.claude/skills/cabloy-zova-source-reading/references/core-reading-paths.md +10 -0
- package/.github/workflows/docs-pages.yml +18 -2
- package/CHANGELOG.md +19 -0
- package/package.json +2 -1
- package/repo-docs/.vitepress/config.mjs +34 -4
- package/repo-docs/.vitepress/theme/components/GitHubRepositoriesNav.vue +255 -0
- package/repo-docs/.vitepress/theme/custom.css +21 -0
- package/repo-docs/.vitepress/theme/index.js +7 -1
- package/repo-docs/backend/resource-field-update.md +48 -1
- package/repo-docs/blogs/index.md +2 -0
- package/repo-docs/frontend/form-layout-guide.md +6 -6
- package/repo-docs/frontend/page-guide.md +12 -0
- package/repo-docs/frontend/page-meta-guide.md +27 -0
- package/repo-docs/frontend/ssr-architecture-overview.md +4 -0
- package/repo-docs/frontend/ssr-build-deploy-guide.md +1 -1
- package/repo-docs/frontend/zova-form-source-reading-map.md +2 -2
- package/repo-docs/frontend/zova-form-under-the-hood.md +1 -1
- package/repo-docs/frontend/zova-reactivity-under-the-hood.md +65 -9
- package/repo-docs/fullstack/contract-loop-playbook.md +2 -0
- package/repo-docs/fullstack/ssr-site-and-flavor-setup.md +198 -0
- package/repo-docs/fullstack/vona-zova-integration.md +2 -0
- package/repo-e2e/docs/playwright.config.ts +37 -0
- package/repo-e2e/docs/specs/blogs-index.spec.ts +180 -0
- package/repo-e2e/docs/specs/github-repositories-nav.spec.ts +69 -0
- package/repo-e2e/docs/test-results/.last-run.json +4 -0
- package/repo-e2e/specs/cabloy-basic.spec.ts +136 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/entity/{{resourceName}}.tsx_ +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +21 -80
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productCreate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productUpdate.tsx +2 -0
- package/vona/src/suite/a-commerce/modules/commerce-catalog/src/dto/productView.tsx +2 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordSelectReq.tsx +3 -7
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/dto/recordView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-record/src/entity/record.tsx +8 -0
- package/vona/src/suite/a-training/modules/training-record/test/record.test.ts +51 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +3 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentCreate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentUpdate.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/dto/studentView.tsx +1 -0
- package/vona/src/suite/a-training/modules/training-student/src/entity/student.tsx +10 -1
- package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +99 -1
- 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-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-web/src/bean/pipe.filter.ts +85 -57
- 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 +13 -3
- package/zova/packages-cli/cli-set-front/package.json +1 -1
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +31 -11
- package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +3 -0
- package/zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx +1 -1
- package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +68 -8
- 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/model/resource.ts +4 -0
- 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/lib/formLayout.ts +0 -6
- package/zova/src/suite-vendor/a-zova/modules/a-form/test/lib/formLayout.test.ts +16 -9
- 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/model/sdk.ts +4 -0
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/rest.ts +13 -2
- package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/schema.ts +1 -0
- 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 +50 -0
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx +60 -7
- package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/table.ts +6 -4
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
|
@@ -25,6 +25,7 @@ import type {
|
|
|
25
25
|
export type TypePipeFilterData = ITableQuery;
|
|
26
26
|
|
|
27
27
|
export type TypePipeFilterResult = TypeQueryParamsPatch;
|
|
28
|
+
type TypeQueryJoin = NonNullable<TypeQueryParamsPatch['joins']>[number];
|
|
28
29
|
|
|
29
30
|
export interface IPipeOptionsFilter
|
|
30
31
|
extends IDecoratorPipeOptions, IDecoratorPipeOptionsArgument, ValidatorOptions {}
|
|
@@ -68,10 +69,13 @@ export class PipeFilter
|
|
|
68
69
|
private async _transform(value: TypePipeFilterData, options: IPipeOptionsFilter) {
|
|
69
70
|
// 1. system: columns/where/orders/pageNo/pageSize
|
|
70
71
|
const params = this._transformSystem(value);
|
|
72
|
+
const rootOpenapi = ZodMetadata.getOpenapiMetadata(options.schema!) as
|
|
73
|
+
| ISchemaObjectExtensionField
|
|
74
|
+
| undefined;
|
|
71
75
|
// 2. fields
|
|
72
|
-
await this._transformFields(params, value, options);
|
|
76
|
+
await this._transformFields(params, value, options, rootOpenapi);
|
|
73
77
|
// 3. system: orders
|
|
74
|
-
this._transformOrders(params, options);
|
|
78
|
+
this._transformOrders(params, options, rootOpenapi);
|
|
75
79
|
// ok
|
|
76
80
|
return params;
|
|
77
81
|
}
|
|
@@ -104,32 +108,75 @@ export class PipeFilter
|
|
|
104
108
|
return params;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
private _transformOrders(
|
|
111
|
+
private _transformOrders(
|
|
112
|
+
params: TypeQueryParamsPatch,
|
|
113
|
+
options: IPipeOptionsFilter,
|
|
114
|
+
rootOpenapi: ISchemaObjectExtensionField | undefined,
|
|
115
|
+
) {
|
|
108
116
|
if (!params.orders) return;
|
|
109
|
-
// openapi
|
|
110
|
-
const openapi: ISchemaObjectExtensionField | undefined = ZodMetadata.getOpenapiMetadata(
|
|
111
|
-
options.schema!,
|
|
112
|
-
) as ISchemaObjectExtensionField | undefined;
|
|
113
|
-
const table = openapi?.filter?.table;
|
|
114
|
-
// loop
|
|
115
117
|
for (const order of params.orders) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
tableCurrent = openapi?.filter?.table;
|
|
127
|
-
}
|
|
128
|
-
if (openapi?.filter?.originalName) {
|
|
129
|
-
fieldCurrent = openapi?.filter?.originalName;
|
|
130
|
-
}
|
|
118
|
+
if (!Array.isArray(order) || order.length !== 2) {
|
|
119
|
+
throw new Error('invalid order');
|
|
120
|
+
}
|
|
121
|
+
const [key, direction] = order;
|
|
122
|
+
if (typeof key !== 'string' || (direction !== 'asc' && direction !== 'desc')) {
|
|
123
|
+
throw new Error('invalid order');
|
|
124
|
+
}
|
|
125
|
+
const info = this._resolveField(key, options, rootOpenapi);
|
|
126
|
+
if (!info || info.openapi?.filter?.capabilities?.order === false) {
|
|
127
|
+
throw new Error(`invalid order field: ${key}`);
|
|
131
128
|
}
|
|
132
|
-
cast(order)[0] =
|
|
129
|
+
cast(order)[0] = info.orderFullName;
|
|
130
|
+
this._addJoin(params, info.joinInfo);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private _resolveField(
|
|
135
|
+
key: string,
|
|
136
|
+
options: IPipeOptionsFilter,
|
|
137
|
+
rootOpenapi: ISchemaObjectExtensionField | undefined,
|
|
138
|
+
) {
|
|
139
|
+
const fieldSchema = ZodMetadata.getFieldSchema(options.schema!, key);
|
|
140
|
+
if (!fieldSchema) {
|
|
141
|
+
if (!rootOpenapi?.filter?.table) {
|
|
142
|
+
return { orderFullName: key };
|
|
143
|
+
}
|
|
144
|
+
const keyParts = key.split('.');
|
|
145
|
+
const [table, field] = keyParts;
|
|
146
|
+
if (!table || !field || keyParts.length !== 2) return;
|
|
147
|
+
const resolved = this._resolveField(field, options, rootOpenapi);
|
|
148
|
+
if (!resolved || resolved.orderFullName !== key) return;
|
|
149
|
+
return resolved;
|
|
150
|
+
}
|
|
151
|
+
const fieldSchemaInner = ZodMetadata.unwrapChained(fieldSchema);
|
|
152
|
+
const openapi: ISchemaObjectExtensionField | undefined = ZodMetadata.getOpenapiMetadata(
|
|
153
|
+
fieldSchema,
|
|
154
|
+
) as ISchemaObjectExtensionField | undefined;
|
|
155
|
+
const originalName = openapi?.filter?.originalName ?? key;
|
|
156
|
+
const table = openapi?.filter?.table ?? rootOpenapi?.filter?.table;
|
|
157
|
+
const joinInfo = openapi?.filter?.joinOn
|
|
158
|
+
? ([
|
|
159
|
+
openapi.filter.joinType ?? 'innerJoin',
|
|
160
|
+
openapi.filter.table,
|
|
161
|
+
openapi.filter.joinOn,
|
|
162
|
+
] as TypeQueryJoin)
|
|
163
|
+
: undefined;
|
|
164
|
+
return {
|
|
165
|
+
fieldSchema,
|
|
166
|
+
fieldSchemaInner,
|
|
167
|
+
openapi,
|
|
168
|
+
originalName,
|
|
169
|
+
fullName: joinInfo ? `${openapi?.filter?.table}.${originalName}` : originalName,
|
|
170
|
+
orderFullName: table ? `${table}.${originalName}` : originalName,
|
|
171
|
+
joinInfo,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private _addJoin(params: TypeQueryParamsPatch, joinInfo: TypeQueryJoin | undefined) {
|
|
176
|
+
if (!joinInfo) return;
|
|
177
|
+
if (!params.joins) params.joins = [];
|
|
178
|
+
if (params.joins.findIndex(item => item[1] === joinInfo[1]) === -1) {
|
|
179
|
+
params.joins.push(joinInfo);
|
|
133
180
|
}
|
|
134
181
|
}
|
|
135
182
|
|
|
@@ -139,36 +186,21 @@ export class PipeFilter
|
|
|
139
186
|
params: TypeQueryParamsPatch,
|
|
140
187
|
value: any,
|
|
141
188
|
options: IPipeOptionsFilter,
|
|
189
|
+
rootOpenapi: ISchemaObjectExtensionField | undefined,
|
|
142
190
|
) {
|
|
143
191
|
if (__FieldsSystem.includes(key)) return;
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
146
|
-
const fieldSchemaInner =
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const [transformName] = openapi?.filter?.transform ?? ['a-web:base', undefined];
|
|
192
|
+
const info = this._resolveField(key, options, rootOpenapi);
|
|
193
|
+
if (!info) return;
|
|
194
|
+
const { fieldSchema, fieldSchemaInner, openapi, originalName, fullName, joinInfo } = info;
|
|
195
|
+
const [transformName, transformOptions] = openapi?.filter?.transform ?? [
|
|
196
|
+
'a-web:base',
|
|
197
|
+
undefined,
|
|
198
|
+
];
|
|
152
199
|
const fieldNullable = fieldValue === null && isNullableSchema(fieldSchema);
|
|
153
200
|
if (isNilOrEmptyString(fieldValue) && !fieldNullable) return;
|
|
154
|
-
// name
|
|
155
|
-
const originalName = openapi?.filter?.originalName ?? key;
|
|
156
|
-
let fullName: string;
|
|
157
|
-
// joins
|
|
158
|
-
let joinInfo;
|
|
159
|
-
if (openapi?.filter?.joinOn) {
|
|
160
|
-
const joinType = openapi.filter.joinType ?? 'innerJoin';
|
|
161
|
-
const joinTable = openapi.filter.table;
|
|
162
|
-
const joinOn = openapi.filter.joinOn;
|
|
163
|
-
joinInfo = [joinType, joinTable, joinOn];
|
|
164
|
-
fullName = `${joinTable}.${originalName}`;
|
|
165
|
-
} else {
|
|
166
|
-
fullName = originalName;
|
|
167
|
-
}
|
|
168
201
|
// check where
|
|
169
202
|
if (Object.prototype.hasOwnProperty.call(params.where, fullName)) return;
|
|
170
203
|
// filter transform
|
|
171
|
-
const [, transformOptions] = openapi?.filter?.transform ?? ['a-web:base', undefined];
|
|
172
204
|
const transformOptions2 = this.bean.onion.filterTransform.getOnionOptionsDynamic(
|
|
173
205
|
transformName as keyof IFilterTransformRecord,
|
|
174
206
|
transformOptions,
|
|
@@ -182,7 +214,7 @@ export class PipeFilter
|
|
|
182
214
|
if (!beanInstance.where) {
|
|
183
215
|
throw new Error(`filterTransform.where not found: ${beanFullName}`);
|
|
184
216
|
}
|
|
185
|
-
const
|
|
217
|
+
const transformInfo = {
|
|
186
218
|
params,
|
|
187
219
|
query: value,
|
|
188
220
|
options,
|
|
@@ -194,17 +226,12 @@ export class PipeFilter
|
|
|
194
226
|
schema: fieldSchema,
|
|
195
227
|
openapi,
|
|
196
228
|
};
|
|
197
|
-
const resTransform = await beanInstance.where(
|
|
229
|
+
const resTransform = await beanInstance.where(transformInfo, transformOptions2);
|
|
198
230
|
if (resTransform !== undefined) {
|
|
199
231
|
// where
|
|
200
232
|
params.where[fullName] = resTransform;
|
|
201
233
|
// join
|
|
202
|
-
|
|
203
|
-
if (!params.joins) params.joins = [];
|
|
204
|
-
if (params.joins.findIndex(item => item[1] === joinInfo.joinTable) === -1) {
|
|
205
|
-
params.joins.push(joinInfo);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
234
|
+
this._addJoin(params, joinInfo);
|
|
208
235
|
}
|
|
209
236
|
}
|
|
210
237
|
|
|
@@ -212,10 +239,11 @@ export class PipeFilter
|
|
|
212
239
|
params: TypeQueryParamsPatch,
|
|
213
240
|
value: any,
|
|
214
241
|
options: IPipeOptionsFilter,
|
|
242
|
+
rootOpenapi: ISchemaObjectExtensionField | undefined,
|
|
215
243
|
) {
|
|
216
244
|
// loop
|
|
217
245
|
for (const key in value) {
|
|
218
|
-
await this._transformField(key, value[key], params, value, options);
|
|
246
|
+
await this._transformField(key, value[key], params, value, options, rootOpenapi);
|
|
219
247
|
}
|
|
220
248
|
}
|
|
221
249
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-cli",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.106",
|
|
4
4
|
"gitHead": "6f675a8cc46d596142c591c28a40cc4d82fcc6cc",
|
|
5
5
|
"description": "zova cli",
|
|
6
6
|
"keywords": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@cabloy/process-helper": "^3.1.8",
|
|
45
45
|
"fs-extra": "^11.3.5",
|
|
46
46
|
"semver": "^7.6.2",
|
|
47
|
-
"zova-cli-set-front": "^1.2.
|
|
47
|
+
"zova-cli-set-front": "^1.2.104"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"clean-package": "^2.2.0",
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
schemaRenderTableActionRowJsx,
|
|
18
18
|
} from './component.ts';
|
|
19
19
|
import {
|
|
20
|
+
schemaRenderColumn,
|
|
20
21
|
schemaRenderDisableNotifyChanged,
|
|
21
22
|
schemaRenderFieldSource,
|
|
22
23
|
schemaRenderLayout,
|
|
@@ -35,6 +36,7 @@ export const ZovaRender = {
|
|
|
35
36
|
disableNotifyChanged: schemaRenderDisableNotifyChanged,
|
|
36
37
|
onEffect: schemaRenderOnEffect,
|
|
37
38
|
fieldSource: schemaRenderFieldSource,
|
|
39
|
+
column: schemaRenderColumn,
|
|
38
40
|
// component
|
|
39
41
|
field: schemaRenderField,
|
|
40
42
|
fieldJsx: schemaRenderFieldJsx,
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import type z from 'zod';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import type {
|
|
4
|
+
IResourceFormFieldLayoutOptions,
|
|
5
|
+
ITableColumnOptions,
|
|
6
|
+
TypeFormFieldOnEffect,
|
|
7
|
+
TypeFormSchemaScene,
|
|
8
|
+
TypeSchemaScene,
|
|
9
|
+
} from 'zova-module-a-openapi';
|
|
6
10
|
import type { TypeSchemaOrderLevel } from 'zova-module-a-openapi';
|
|
7
11
|
|
|
8
12
|
import { _generalSchemaRest, _order } from './inner.ts';
|
|
@@ -78,3 +82,9 @@ export function schemaRenderOrder<T extends z.ZodType>(
|
|
|
78
82
|
return _generalSchemaRest(schema, options, scene);
|
|
79
83
|
};
|
|
80
84
|
}
|
|
85
|
+
|
|
86
|
+
export function schemaRenderColumn<T extends z.ZodType>(options: ITableColumnOptions) {
|
|
87
|
+
return function (schema: T): T {
|
|
88
|
+
return schema.openapi({ rest: { table: options } });
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.156",
|
|
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.88",
|
|
49
|
-
"zova-suite-a-zova": "^5.1.
|
|
49
|
+
"zova-suite-a-zova": "^5.1.155"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"clean-package": "^2.2.0",
|
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SortingState } from '@tanstack/vue-table';
|
|
1
2
|
import type { IComponentOptions } from 'zova';
|
|
2
3
|
import type {
|
|
3
4
|
IJsxRenderContextPage,
|
|
@@ -10,6 +11,7 @@ import type {
|
|
|
10
11
|
} from 'zova-module-a-openapi';
|
|
11
12
|
|
|
12
13
|
import { celEnvBase } from '@cabloy/utils';
|
|
14
|
+
import { functionalUpdate } from '@tanstack/vue-table';
|
|
13
15
|
import { VNode } from 'vue';
|
|
14
16
|
import { BeanControllerBase, deepEqual } from 'zova';
|
|
15
17
|
import { ZovaJsx } from 'zova-jsx';
|
|
@@ -49,8 +51,9 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
49
51
|
|
|
50
52
|
queryFixedData: ITableQuery;
|
|
51
53
|
queryFilterData: ITableQuery;
|
|
54
|
+
querySortingData: ITableQuery;
|
|
55
|
+
sorting: SortingState;
|
|
52
56
|
queryPaged: ITablePaged;
|
|
53
|
-
query: ITableQuery;
|
|
54
57
|
|
|
55
58
|
$$modelResource: ModelResource<TData>;
|
|
56
59
|
|
|
@@ -65,6 +68,8 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
65
68
|
// query
|
|
66
69
|
this.queryFixedData = this.$props.queryFixed ?? {};
|
|
67
70
|
this.queryFilterData = {};
|
|
71
|
+
this.querySortingData = {};
|
|
72
|
+
this.sorting = [];
|
|
68
73
|
this.$watch(
|
|
69
74
|
() => this.$props.queryFixed,
|
|
70
75
|
queryFixed => {
|
|
@@ -72,16 +77,6 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
72
77
|
},
|
|
73
78
|
);
|
|
74
79
|
this.queryPaged = { pageNo: 1, pageSize: this.$props.pageSize };
|
|
75
|
-
this.query = this.$computed(() => {
|
|
76
|
-
const { where: whereFixed, ...queryFixed } = this.queryFixedData;
|
|
77
|
-
const { where: whereFilter, ...queryFilter } = this.queryFilterData;
|
|
78
|
-
return {
|
|
79
|
-
...queryFixed,
|
|
80
|
-
...queryFilter,
|
|
81
|
-
where: { ...whereFilter, ...whereFixed },
|
|
82
|
-
...this.queryPaged,
|
|
83
|
-
};
|
|
84
|
-
});
|
|
85
80
|
// load schema/data
|
|
86
81
|
await $QueriesEnsureLoaded(
|
|
87
82
|
() => this.$$modelResource.apiSchemasSelect.sdk,
|
|
@@ -101,6 +96,18 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
101
96
|
return this.$props.resource;
|
|
102
97
|
}
|
|
103
98
|
|
|
99
|
+
get query() {
|
|
100
|
+
const { where: whereFixed, ...queryFixed } = this.queryFixedData;
|
|
101
|
+
const { where: whereFilter, ...queryFilter } = this.queryFilterData;
|
|
102
|
+
return {
|
|
103
|
+
...queryFixed,
|
|
104
|
+
...queryFilter,
|
|
105
|
+
...this.querySortingData,
|
|
106
|
+
where: { ...whereFilter, ...whereFixed },
|
|
107
|
+
...this.queryPaged,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
104
111
|
get queryData() {
|
|
105
112
|
return this.$$modelResource.select(this.query);
|
|
106
113
|
}
|
|
@@ -117,6 +124,10 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
117
124
|
return this.$$modelResource.schemaFilter;
|
|
118
125
|
}
|
|
119
126
|
|
|
127
|
+
get schemaOrder() {
|
|
128
|
+
return this.$$modelResource.schemaOrder;
|
|
129
|
+
}
|
|
130
|
+
|
|
120
131
|
get schemaRow() {
|
|
121
132
|
return this.$$modelResource.schemaRow;
|
|
122
133
|
}
|
|
@@ -147,6 +158,15 @@ export class ControllerBlockPage<TData extends {} = {}> extends BeanControllerBa
|
|
|
147
158
|
this.queryPaged.pageNo = 1;
|
|
148
159
|
}
|
|
149
160
|
|
|
161
|
+
onSortingChange(updater: SortingState | ((old: SortingState) => SortingState)) {
|
|
162
|
+
this.sorting = functionalUpdate(updater, this.sorting).slice(0, 1);
|
|
163
|
+
const sorting = this.sorting[0];
|
|
164
|
+
this.querySortingData = sorting
|
|
165
|
+
? { orders: [[sorting.id, sorting.desc ? 'desc' : 'asc']] }
|
|
166
|
+
: {};
|
|
167
|
+
this.queryPaged.pageNo = 1;
|
|
168
|
+
}
|
|
169
|
+
|
|
150
170
|
public setTableRef(tableRef: BeanControllerTableBase<TData> | undefined) {
|
|
151
171
|
this.tableRef = tableRef;
|
|
152
172
|
if (!tableRef || !this._tableMetaRefreshPending) return;
|
package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx
CHANGED
|
@@ -44,6 +44,9 @@ export class ControllerBlockTable<TData extends {} = {}> extends BeanControllerB
|
|
|
44
44
|
}}
|
|
45
45
|
data={$$page.data as unknown as TData[]}
|
|
46
46
|
schema={$$page.schemaRow}
|
|
47
|
+
schemaOrder={$$page.schemaOrder}
|
|
48
|
+
sorting={$$page.sorting}
|
|
49
|
+
onSortingChange={updater => $$page.onSortingChange(updater)}
|
|
47
50
|
tableScope={$$page.jsxCelScope}
|
|
48
51
|
></ZTable>
|
|
49
52
|
</div>
|
|
@@ -167,7 +167,7 @@ export class ControllerBlockPageEntry<TData extends {} = {}> extends BeanControl
|
|
|
167
167
|
|
|
168
168
|
setPageMeta(data: any | undefined, pageDirty?: boolean) {
|
|
169
169
|
if (!this.$pageRoute) return;
|
|
170
|
-
const pageTitle = data?.[this.$props.pageTitleKey];
|
|
170
|
+
const pageTitle = data?.[this.$props.pageTitleKey] || this.formSchema?.title;
|
|
171
171
|
this.$router.setPageMeta(this.$pageRoute, { pageTitle, pageDirty, formMeta: this.formMeta });
|
|
172
172
|
}
|
|
173
173
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AriaAttributes, CSSProperties } from 'vue';
|
|
2
|
+
|
|
1
3
|
import { FlexRender } from '@tanstack/vue-table';
|
|
2
4
|
import { BeanRenderBase } from 'zova';
|
|
3
5
|
import { Render } from 'zova-module-a-bean';
|
|
@@ -8,7 +10,14 @@ export class RenderTable<TData extends {} = {}> extends BeanRenderBase {
|
|
|
8
10
|
public render() {
|
|
9
11
|
return (
|
|
10
12
|
<ZTable
|
|
11
|
-
{
|
|
13
|
+
data={this.$props.data}
|
|
14
|
+
schema={this.$props.schema}
|
|
15
|
+
schemaOrder={this.$props.schemaOrder}
|
|
16
|
+
sorting={this.$props.sorting}
|
|
17
|
+
onSortingChange={this.$props.onSortingChange}
|
|
18
|
+
tableScope={this.$props.tableScope}
|
|
19
|
+
getColumns={this.$props.getColumns}
|
|
20
|
+
getRowId={this.$props.getRowId}
|
|
12
21
|
controllerRef={ref => {
|
|
13
22
|
this.$props?.tableRef?.(ref);
|
|
14
23
|
}}
|
|
@@ -19,6 +28,21 @@ export class RenderTable<TData extends {} = {}> extends BeanRenderBase {
|
|
|
19
28
|
);
|
|
20
29
|
}
|
|
21
30
|
|
|
31
|
+
private _getColumnStyle(column: any): CSSProperties {
|
|
32
|
+
const rest = (column.columnDef.meta as any)?.rest;
|
|
33
|
+
const fixed = column.getIsPinned();
|
|
34
|
+
const width = rest?.width ? `${rest.width}px` : undefined;
|
|
35
|
+
return {
|
|
36
|
+
width,
|
|
37
|
+
minWidth: width,
|
|
38
|
+
textAlign: rest?.align,
|
|
39
|
+
position: fixed ? ('sticky' as const) : undefined,
|
|
40
|
+
left: fixed === 'left' ? `${column.getStart('left')}px` : undefined,
|
|
41
|
+
right: fixed === 'right' ? `${column.getAfter('right')}px` : undefined,
|
|
42
|
+
zIndex: fixed ? 1 : undefined,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
22
46
|
public _renderTable($$table: ControllerTable<TData>) {
|
|
23
47
|
const table = $$table.table;
|
|
24
48
|
return (
|
|
@@ -26,12 +50,43 @@ export class RenderTable<TData extends {} = {}> extends BeanRenderBase {
|
|
|
26
50
|
<thead>
|
|
27
51
|
<tr>
|
|
28
52
|
{table.getFlatHeaders().map(header => {
|
|
53
|
+
const column = header.column;
|
|
54
|
+
const canSort = column.getCanSort();
|
|
55
|
+
const sorted = column.getIsSorted();
|
|
56
|
+
const fixed = column.getIsPinned();
|
|
57
|
+
const headerDefinition = column.columnDef.header;
|
|
58
|
+
const headerContent = (
|
|
59
|
+
<FlexRender render={headerDefinition} props={header.getContext()}></FlexRender>
|
|
60
|
+
);
|
|
61
|
+
let sortAria: AriaAttributes['aria-sort'] = 'none';
|
|
62
|
+
let sortIcon: '▲' | '▼' | '↕' = '↕';
|
|
63
|
+
if (sorted === 'asc') {
|
|
64
|
+
sortAria = 'ascending';
|
|
65
|
+
sortIcon = '▲';
|
|
66
|
+
} else if (sorted === 'desc') {
|
|
67
|
+
sortAria = 'descending';
|
|
68
|
+
sortIcon = '▼';
|
|
69
|
+
}
|
|
29
70
|
return (
|
|
30
|
-
<th
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
71
|
+
<th
|
|
72
|
+
key={header.id}
|
|
73
|
+
class={fixed ? 'bg-base-100' : undefined}
|
|
74
|
+
style={this._getColumnStyle(column)}
|
|
75
|
+
aria-sort={sortAria}
|
|
76
|
+
>
|
|
77
|
+
{canSort ? (
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
class="inline-flex w-full items-center gap-1"
|
|
81
|
+
onClick={column.getToggleSortingHandler()}
|
|
82
|
+
aria-label={`Sort by ${column.id}`}
|
|
83
|
+
>
|
|
84
|
+
{headerContent}
|
|
85
|
+
<span aria-hidden="true">{sortIcon}</span>
|
|
86
|
+
</button>
|
|
87
|
+
) : (
|
|
88
|
+
headerContent
|
|
89
|
+
)}
|
|
35
90
|
</th>
|
|
36
91
|
);
|
|
37
92
|
})}
|
|
@@ -42,10 +97,15 @@ export class RenderTable<TData extends {} = {}> extends BeanRenderBase {
|
|
|
42
97
|
return (
|
|
43
98
|
<tr key={row.id}>
|
|
44
99
|
{row.getVisibleCells().map(cell => {
|
|
100
|
+
const column = cell.column;
|
|
45
101
|
return (
|
|
46
|
-
<td
|
|
102
|
+
<td
|
|
103
|
+
key={cell.id}
|
|
104
|
+
class={column.getIsPinned() ? 'bg-base-100' : undefined}
|
|
105
|
+
style={this._getColumnStyle(column)}
|
|
106
|
+
>
|
|
47
107
|
<FlexRender
|
|
48
|
-
render={
|
|
108
|
+
render={column.columnDef.cell}
|
|
49
109
|
props={cell.getContext()}
|
|
50
110
|
></FlexRender>
|
|
51
111
|
</td>
|
|
@@ -57,6 +57,7 @@ export class ModelResource<
|
|
|
57
57
|
public schemaCreate?: ISchemaObjectExtensionField;
|
|
58
58
|
public schemaUpdate?: ISchemaObjectExtensionField;
|
|
59
59
|
public schemaFilter?: ISchemaObjectExtensionField;
|
|
60
|
+
public schemaOrder?: ISchemaObjectExtensionField;
|
|
60
61
|
public schemaRow?: ISchemaObjectExtensionField;
|
|
61
62
|
public schemaPages?: ISchemaObjectExtensionField;
|
|
62
63
|
|
|
@@ -87,6 +88,9 @@ export class ModelResource<
|
|
|
87
88
|
this.schemaFilter = this.$computed(() => {
|
|
88
89
|
return this.apiSchemasSelect.filter;
|
|
89
90
|
});
|
|
91
|
+
this.schemaOrder = this.$computed(() => {
|
|
92
|
+
return this.apiSchemasSelect.order;
|
|
93
|
+
});
|
|
90
94
|
this.schemaRow = this.$computed(() => {
|
|
91
95
|
return this.apiSchemasSelect.row;
|
|
92
96
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zova-suite-a-cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.51",
|
|
4
4
|
"gitHead": "4dece229b6d3f4735930e833afcb4b95ec51bf86",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"zova-module-a-rbac": "^5.0.1",
|
|
11
|
-
"zova-module-rest-resource": "^5.1.
|
|
11
|
+
"zova-module-rest-resource": "^5.1.50"
|
|
12
12
|
},
|
|
13
13
|
"title": "a-cabloy"
|
|
14
14
|
}
|
|
@@ -58,12 +58,6 @@ export function resolveFormLayout(
|
|
|
58
58
|
),
|
|
59
59
|
)
|
|
60
60
|
.filter(Boolean) as IResolvedFormLayoutNode[];
|
|
61
|
-
for (const property of properties ?? []) {
|
|
62
|
-
const name = property.key;
|
|
63
|
-
if (!name || !propertyNames.has(name) || fieldNames.has(name)) continue;
|
|
64
|
-
fieldNames.add(name);
|
|
65
|
-
children.push({ type: 'field', name });
|
|
66
|
-
}
|
|
67
61
|
return { children, fieldTabPaths, diagnostics };
|
|
68
62
|
}
|
|
69
63
|
|
|
@@ -76,6 +76,19 @@ test('Form Layout accepts every preserved alias for the same canonical source',
|
|
|
76
76
|
assert.deepEqual(resolved.diagnostics, []);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
|
+
test('Form Layout renders only explicitly declared visible fields', () => {
|
|
80
|
+
const resolved = resolveFormLayout(
|
|
81
|
+
createTabsLayout('name'),
|
|
82
|
+
createProperties({ key: 'name', type: 'string' }, { key: 'level', type: 'string' }),
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
assert.deepEqual(getTabFieldNames(resolved), ['name']);
|
|
86
|
+
assert.deepEqual(resolved.fieldTabPaths, {
|
|
87
|
+
name: [{ tabsId: 'tabs-0', tabId: 'tab-0-0' }],
|
|
88
|
+
});
|
|
89
|
+
assert.deepEqual(resolved.diagnostics, []);
|
|
90
|
+
});
|
|
91
|
+
|
|
79
92
|
test('Form Layout prefers an exact canonical field over a colliding alias', () => {
|
|
80
93
|
const resolved = resolveFormLayout(
|
|
81
94
|
{ children: [{ type: 'field', name: 'content' }] },
|
|
@@ -89,10 +102,7 @@ test('Form Layout prefers an exact canonical field over a colliding alias', () =
|
|
|
89
102
|
),
|
|
90
103
|
);
|
|
91
104
|
|
|
92
|
-
assert.deepEqual(resolved.children, [
|
|
93
|
-
{ type: 'field', name: 'content' },
|
|
94
|
-
{ type: 'field', name: 'content.descriptionMarkdown' },
|
|
95
|
-
]);
|
|
105
|
+
assert.deepEqual(resolved.children, [{ type: 'field', name: 'content' }]);
|
|
96
106
|
assert.deepEqual(resolved.diagnostics, []);
|
|
97
107
|
});
|
|
98
108
|
|
|
@@ -131,17 +141,14 @@ test('Form Layout keeps ambiguous aliases and relation prefixes unknown', () =>
|
|
|
131
141
|
),
|
|
132
142
|
);
|
|
133
143
|
|
|
134
|
-
assert.deepEqual(resolved.children, [
|
|
135
|
-
{ type: 'field', name: 'content.descriptionMarkdown' },
|
|
136
|
-
{ type: 'field', name: 'content.descriptionHtml' },
|
|
137
|
-
]);
|
|
144
|
+
assert.deepEqual(resolved.children, []);
|
|
138
145
|
assert.deepEqual(resolved.diagnostics, [
|
|
139
146
|
{ type: 'unknownField', value: '_content' },
|
|
140
147
|
{ type: 'unknownField', value: 'content' },
|
|
141
148
|
]);
|
|
142
149
|
});
|
|
143
150
|
|
|
144
|
-
test('Form Layout does not resolve
|
|
151
|
+
test('Form Layout does not resolve invisible aliases', () => {
|
|
145
152
|
const resolved = resolveFormLayout(
|
|
146
153
|
{ children: [{ type: 'field', name: '_descriptionMarkdown' }] },
|
|
147
154
|
createProperties({
|
|
@@ -183,6 +183,10 @@ export class ModelSdk extends BeanModelBase {
|
|
|
183
183
|
const operationObject = sdk.data?.operationObject;
|
|
184
184
|
return getSchemaOfRequestQueryFilter(operationObject, { where: true });
|
|
185
185
|
},
|
|
186
|
+
get order() {
|
|
187
|
+
const operationObject = sdk.data?.operationObject;
|
|
188
|
+
return getSchemaOfRequestQueryFilter(operationObject, { order: true });
|
|
189
|
+
},
|
|
186
190
|
get requestBody() {
|
|
187
191
|
const operationObject = sdk.data?.operationObject;
|
|
188
192
|
const schemaBody = getSchemaOfRequestBody(operationObject);
|