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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export default {
|
|
2
2
|
Available: '可用库存',
|
|
3
3
|
Category: '分类',
|
|
4
|
+
CreateProduct: '新建商品',
|
|
4
5
|
Description: '描述',
|
|
5
6
|
DescriptionHtml: '商品描述(HTML)',
|
|
6
7
|
DescriptionMarkdown: '商品描述(Markdown)',
|
|
@@ -15,6 +16,8 @@ export default {
|
|
|
15
16
|
Unpublished: '未发布',
|
|
16
17
|
Sku: 'SKU',
|
|
17
18
|
SkuAttributeName: 'SKU 属性名',
|
|
19
|
+
UpdateProduct: '编辑商品',
|
|
20
|
+
ViewProduct: '商品详情',
|
|
18
21
|
SkuAttributeValue: 'SKU 属性值',
|
|
19
22
|
SkuAttributes: 'SKU 属性',
|
|
20
23
|
SkuCode: 'SKU 编码',
|
|
@@ -11,6 +11,7 @@ import { ModelProduct } from '../model/product.ts';
|
|
|
11
11
|
export interface IDtoOptionsProductCreate extends IDecoratorDtoOptions<'productContentForm'> {}
|
|
12
12
|
|
|
13
13
|
@Dto<IDtoOptionsProductCreate>({
|
|
14
|
+
openapi: { title: $locale('CreateProduct') },
|
|
14
15
|
fields: {
|
|
15
16
|
productContentForm: $makeMetadata(
|
|
16
17
|
ZovaRender.fieldSource('productContentForm.descriptionMarkdown'),
|
|
@@ -19,6 +20,7 @@ export interface IDtoOptionsProductCreate extends IDecoratorDtoOptions<'productC
|
|
|
19
20
|
},
|
|
20
21
|
blocks: [
|
|
21
22
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
23
|
+
pageTitleKey: 'title',
|
|
22
24
|
blocks: [
|
|
23
25
|
ZovaRender.block('basic-pageentry:blockForm', {
|
|
24
26
|
blocks: [
|
|
@@ -11,6 +11,7 @@ import { ModelProduct } from '../model/product.ts';
|
|
|
11
11
|
export interface IDtoOptionsProductUpdate extends IDecoratorDtoOptions<'productContentForm'> {}
|
|
12
12
|
|
|
13
13
|
@Dto<IDtoOptionsProductUpdate>({
|
|
14
|
+
openapi: { title: $locale('UpdateProduct') },
|
|
14
15
|
fields: {
|
|
15
16
|
productContentForm: $makeMetadata(
|
|
16
17
|
ZovaRender.fieldSource('productContentForm.descriptionMarkdown'),
|
|
@@ -19,6 +20,7 @@ export interface IDtoOptionsProductUpdate extends IDecoratorDtoOptions<'productC
|
|
|
19
20
|
},
|
|
20
21
|
blocks: [
|
|
21
22
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
23
|
+
pageTitleKey: 'title',
|
|
22
24
|
blocks: [
|
|
23
25
|
ZovaRender.block('basic-pageentry:blockForm', {
|
|
24
26
|
blocks: [
|
|
@@ -11,6 +11,7 @@ import { ModelProduct } from '../model/product.ts';
|
|
|
11
11
|
export interface IDtoOptionsProductView extends IDecoratorDtoOptions<'productContentForm'> {}
|
|
12
12
|
|
|
13
13
|
@Dto<IDtoOptionsProductView>({
|
|
14
|
+
openapi: { title: $locale('ViewProduct') },
|
|
14
15
|
fields: {
|
|
15
16
|
productContentForm: $makeMetadata(
|
|
16
17
|
ZovaRender.fieldSource('productContentForm.descriptionMarkdown'),
|
|
@@ -19,6 +20,7 @@ export interface IDtoOptionsProductView extends IDecoratorDtoOptions<'productCon
|
|
|
19
20
|
},
|
|
20
21
|
blocks: [
|
|
21
22
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
23
|
+
pageTitleKey: 'title',
|
|
22
24
|
blocks: [
|
|
23
25
|
ZovaRender.block('basic-pageentry:blockForm', {
|
|
24
26
|
blocks: [
|
|
@@ -14,6 +14,7 @@ import { DtoDetailRecordSubjectResItem } from './detailRecordSubjectResItem.tsx'
|
|
|
14
14
|
export interface IDtoOptionsRecordCreate extends IDecoratorDtoOptions {}
|
|
15
15
|
|
|
16
16
|
@Dto<IDtoOptionsRecordCreate>({
|
|
17
|
+
openapi: { title: $locale('AddTrainingRecord') },
|
|
17
18
|
blocks: [
|
|
18
19
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
19
20
|
blocks: [
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { IDecoratorDtoOptions } from 'vona-module-a-web';
|
|
2
2
|
|
|
3
|
-
import { $makeSchema,
|
|
3
|
+
import { $makeSchema, v } from 'vona-module-a-openapiutils';
|
|
4
4
|
import { $Dto } from 'vona-module-a-orm';
|
|
5
5
|
import { Dto } from 'vona-module-a-web';
|
|
6
6
|
import z from 'zod';
|
|
7
7
|
import { ZovaRender } from 'zova-rest-cabloy-basic-admin';
|
|
8
8
|
|
|
9
|
-
import { $locale } from '../.metadata/locales.ts';
|
|
10
9
|
import { EntityRecord } from '../entity/record.tsx';
|
|
11
10
|
|
|
12
11
|
export interface IDtoOptionsRecordSelectReq extends IDecoratorDtoOptions {}
|
|
@@ -16,12 +15,9 @@ export interface IDtoOptionsRecordSelectReq extends IDecoratorDtoOptions {}
|
|
|
16
15
|
fields: {
|
|
17
16
|
name: $makeSchema(v.optional(), z.string()),
|
|
18
17
|
studentId: $makeSchema(
|
|
19
|
-
|
|
20
|
-
ZovaRender.field('basic-resource:formFieldResourcePicker', {
|
|
21
|
-
resource: $resourceName('training-student:student'),
|
|
22
|
-
}),
|
|
18
|
+
ZovaRender.field('basic-input:formFieldInput'),
|
|
23
19
|
v.optional(),
|
|
24
|
-
z.
|
|
20
|
+
z.string(),
|
|
25
21
|
),
|
|
26
22
|
createdAt: $makeSchema(
|
|
27
23
|
ZovaRender.field('basic-date:formFieldDateRange'),
|
|
@@ -14,6 +14,7 @@ import { DtoDetailRecordSubjectResItem } from './detailRecordSubjectResItem.tsx'
|
|
|
14
14
|
export interface IDtoOptionsRecordUpdate extends IDecoratorDtoOptions {}
|
|
15
15
|
|
|
16
16
|
@Dto<IDtoOptionsRecordUpdate>({
|
|
17
|
+
openapi: { title: $locale('EditTrainingRecord') },
|
|
17
18
|
blocks: [
|
|
18
19
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
19
20
|
blocks: [
|
|
@@ -15,6 +15,7 @@ import { DtoDetailRecordSubjectView } from './detailRecordSubjectView.tsx';
|
|
|
15
15
|
export interface IDtoOptionsRecordView extends IDecoratorDtoOptions {}
|
|
16
16
|
|
|
17
17
|
@Dto<IDtoOptionsRecordView>({
|
|
18
|
+
openapi: { title: $locale('ViewTrainingRecord') },
|
|
18
19
|
blocks: [
|
|
19
20
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
20
21
|
blocks: [
|
|
@@ -42,7 +42,15 @@ export class EntityRecord extends EntityBase {
|
|
|
42
42
|
@Api.field(
|
|
43
43
|
v.title($locale('Student')),
|
|
44
44
|
v.required(),
|
|
45
|
+
v.filter({
|
|
46
|
+
table: 'trainingStudent',
|
|
47
|
+
joinType: 'innerJoin',
|
|
48
|
+
joinOn: ['studentId', 'trainingStudent.id'],
|
|
49
|
+
originalName: 'name',
|
|
50
|
+
op: '_includesI_',
|
|
51
|
+
}),
|
|
45
52
|
ZovaRender.order(2),
|
|
53
|
+
ZovaRender.column({ enableSorting: true }),
|
|
46
54
|
ZovaRender.field('basic-resource:formFieldResourcePicker', {
|
|
47
55
|
resource: $resourceName('training-student:student'),
|
|
48
56
|
}),
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import type { DtoRecordCreate, DtoRecordUpdate } from 'vona-module-training-record';
|
|
2
2
|
import type { DtoStudentCreate } from 'vona-module-training-student';
|
|
3
3
|
|
|
4
|
+
import { ZodMetadata } from '@cabloy/zod-openapi';
|
|
4
5
|
import fse from 'fs-extra';
|
|
5
6
|
import assert from 'node:assert';
|
|
6
7
|
import os from 'node:os';
|
|
7
8
|
import path from 'node:path';
|
|
8
9
|
import { describe, it } from 'node:test';
|
|
9
10
|
import { app } from 'vona-mock';
|
|
11
|
+
import { getTargetDecoratorRules } from 'vona-module-a-openapiutils';
|
|
10
12
|
|
|
11
13
|
import { DtoDetailRecordSubjectResItem } from '../src/dto/detailRecordSubjectResItem.tsx';
|
|
14
|
+
import { DtoRecordSelectReq } from '../src/dto/recordSelectReq.tsx';
|
|
12
15
|
import { DtoRecordSelectResItem } from '../src/dto/recordSelectResItem.tsx';
|
|
13
16
|
import { DtoRecordView } from '../src/dto/recordView.tsx';
|
|
14
17
|
|
|
@@ -27,6 +30,38 @@ describe('record.test.ts', () => {
|
|
|
27
30
|
assert.ok(subjectComponent?.properties?._lineNumber);
|
|
28
31
|
assert.equal(subjectComponent.required?.includes('_lineNumber'), false);
|
|
29
32
|
|
|
33
|
+
const selectReqRules = getTargetDecoratorRules(DtoRecordSelectReq.prototype);
|
|
34
|
+
const studentIdMetadata = ZodMetadata.getOpenapiMetadata(selectReqRules.studentId!);
|
|
35
|
+
assert.equal(selectReqRules.studentId?.type, 'optional');
|
|
36
|
+
assert.deepEqual(studentIdMetadata?.filter, {
|
|
37
|
+
table: 'trainingStudent',
|
|
38
|
+
joinType: 'innerJoin',
|
|
39
|
+
joinOn: ['studentId', 'trainingStudent.id'],
|
|
40
|
+
originalName: 'name',
|
|
41
|
+
op: '_includesI_',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const controller = app.bean.onion.controller
|
|
45
|
+
.getOnionsEnabledCached()
|
|
46
|
+
.find(item => item.beanOptions.beanFullName === 'training-record.controller.record')
|
|
47
|
+
?.beanOptions.beanClass;
|
|
48
|
+
if (!controller) throw new Error('training-record.controller.record not found');
|
|
49
|
+
const controllerApiJson = await app.bean.openapi.generateJsonOfControllerAction(
|
|
50
|
+
controller,
|
|
51
|
+
'select',
|
|
52
|
+
'V31',
|
|
53
|
+
);
|
|
54
|
+
const parameters = controllerApiJson.paths?.['/api/training/record']?.get?.parameters ?? [];
|
|
55
|
+
const studentIdParameter = parameters.find(parameter => parameter.name === 'studentId');
|
|
56
|
+
assert.ok(studentIdParameter);
|
|
57
|
+
assert.equal(studentIdParameter.in, 'query');
|
|
58
|
+
assert.equal(studentIdParameter.required, false);
|
|
59
|
+
assert.deepEqual(studentIdParameter.schema?.type, ['string', 'null']);
|
|
60
|
+
assert.equal(
|
|
61
|
+
parameters.some(parameter => parameter.name === 'studentName'),
|
|
62
|
+
false,
|
|
63
|
+
);
|
|
64
|
+
|
|
30
65
|
for (const [DtoClass, hasTrainingRecordSubjects] of [
|
|
31
66
|
[DtoRecordSelectResItem, false],
|
|
32
67
|
[DtoRecordView, true],
|
|
@@ -49,6 +84,7 @@ describe('record.test.ts', () => {
|
|
|
49
84
|
'id',
|
|
50
85
|
'name',
|
|
51
86
|
]);
|
|
87
|
+
assert.equal(component.properties.studentId.rest.table.enableSorting, true);
|
|
52
88
|
}
|
|
53
89
|
});
|
|
54
90
|
});
|
|
@@ -123,6 +159,21 @@ describe('record.test.ts', () => {
|
|
|
123
159
|
assert.equal(String(recordItem!.studentId), String(studentId));
|
|
124
160
|
assert.equal(String(recordItem!.student?.id), String(studentId));
|
|
125
161
|
assert.equal(recordItem!.student?.name, studentData.name);
|
|
162
|
+
|
|
163
|
+
const filteredSelectRes: any = await app.bean.executor.performAction(
|
|
164
|
+
'get',
|
|
165
|
+
'/training/record',
|
|
166
|
+
{ query: { studentId: 'tude' } },
|
|
167
|
+
);
|
|
168
|
+
assert.equal(
|
|
169
|
+
filteredSelectRes.list.some((item: any) => String(item.id) === String(recordId)),
|
|
170
|
+
true,
|
|
171
|
+
);
|
|
172
|
+
assert.equal(
|
|
173
|
+
filteredSelectRes.list.every((item: any) => item.student?.name.includes('tude')),
|
|
174
|
+
true,
|
|
175
|
+
);
|
|
176
|
+
|
|
126
177
|
assert.equal(recordItem!.dossierFiles?.length, 1);
|
|
127
178
|
assert.equal(recordItem!.dossierFiles?.[0]?.filename, 'attendance.txt');
|
|
128
179
|
|
|
@@ -5,6 +5,9 @@ export default {
|
|
|
5
5
|
StudentContent: 'Student Content',
|
|
6
6
|
Level: 'Training Stage',
|
|
7
7
|
BasicInformation: 'Basic Information',
|
|
8
|
+
AddStudent: 'Add Student',
|
|
9
|
+
EditStudent: 'Edit Student',
|
|
10
|
+
ViewStudent: 'View Student',
|
|
8
11
|
StudentProfile: 'Student Profile',
|
|
9
12
|
LevelAdvanced: 'Advanced Workshop',
|
|
10
13
|
LevelBeginner: 'Foundation Track',
|
|
@@ -18,6 +18,7 @@ const studentContentField = $makeMetadata(
|
|
|
18
18
|
export interface IDtoOptionsStudentCreate extends IDecoratorDtoOptions<'content'> {}
|
|
19
19
|
|
|
20
20
|
@Dto<IDtoOptionsStudentCreate>({
|
|
21
|
+
openapi: { title: $locale('AddStudent') },
|
|
21
22
|
blocks: [
|
|
22
23
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
23
24
|
blocks: [
|
|
@@ -63,6 +63,7 @@ export class DtoStudentSelectResItem extends $Dto.get(() => ModelStudent) {
|
|
|
63
63
|
@Api.field(
|
|
64
64
|
v.title($locale('Operations')),
|
|
65
65
|
ZovaRender.order(1, 'max'),
|
|
66
|
+
ZovaRender.column({ align: 'center', width: 360, fixed: 'right' }),
|
|
66
67
|
ZovaRender.cell('basic-table:actionOperationsRow', {
|
|
67
68
|
actions: [
|
|
68
69
|
ZovaRender.tableActionRow('training-student:actionSummary', {
|
|
@@ -18,6 +18,7 @@ const studentContentField = $makeMetadata(
|
|
|
18
18
|
export interface IDtoOptionsStudentUpdate extends IDecoratorDtoOptions<'content'> {}
|
|
19
19
|
|
|
20
20
|
@Dto<IDtoOptionsStudentUpdate>({
|
|
21
|
+
openapi: { title: $locale('EditStudent') },
|
|
21
22
|
blocks: [
|
|
22
23
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
23
24
|
blocks: [
|
|
@@ -22,6 +22,7 @@ const studentContentField = $makeSchema(
|
|
|
22
22
|
export interface IDtoOptionsStudentView extends IDecoratorDtoOptions<'_descriptionMarkdown'> {}
|
|
23
23
|
|
|
24
24
|
@Dto<IDtoOptionsStudentView>({
|
|
25
|
+
openapi: { title: $locale('ViewStudent') },
|
|
25
26
|
blocks: [
|
|
26
27
|
ZovaRender.block('basic-pageentry:blockPageEntry', {
|
|
27
28
|
blocks: [
|
|
@@ -19,11 +19,12 @@ export interface IEntityOptionsStudent extends IDecoratorEntityOptions {}
|
|
|
19
19
|
@Entity<IEntityOptionsStudent>('trainingStudent', {
|
|
20
20
|
openapi: { title: $locale('Student') },
|
|
21
21
|
fields: {
|
|
22
|
-
id: $makeMetadata(ZovaRender.order(1, 'core')),
|
|
22
|
+
id: $makeMetadata(ZovaRender.order(1, 'core'), ZovaRender.column({ width: 50, fixed: 'left' })),
|
|
23
23
|
iid: $makeMetadata(ZovaRender.visible(false)),
|
|
24
24
|
deleted: $makeMetadata(ZovaRender.visible(false)),
|
|
25
25
|
createdAt: $makeMetadata(
|
|
26
26
|
ZovaRender.order(-2, 'max'),
|
|
27
|
+
ZovaRender.column({ align: 'center', width: 180, enableSorting: true }),
|
|
27
28
|
ZovaRender.field('basic-date:formFieldDate'),
|
|
28
29
|
ZovaRender.cell('basic-date:date'),
|
|
29
30
|
),
|
|
@@ -40,6 +41,13 @@ export class EntityStudent extends EntityBase {
|
|
|
40
41
|
v.required(),
|
|
41
42
|
v.min(2),
|
|
42
43
|
ZovaRender.order(1),
|
|
44
|
+
ZovaRender.column({
|
|
45
|
+
align: 'left',
|
|
46
|
+
width: 240,
|
|
47
|
+
fixed: 'left',
|
|
48
|
+
enableSorting: true,
|
|
49
|
+
sortDescFirst: true,
|
|
50
|
+
}),
|
|
43
51
|
ZovaRender.cell('basic-table:actionView'),
|
|
44
52
|
)
|
|
45
53
|
name: string;
|
|
@@ -82,6 +90,7 @@ export class EntityStudent extends EntityBase {
|
|
|
82
90
|
v.title($locale('Level')),
|
|
83
91
|
v.required(),
|
|
84
92
|
ZovaRender.order(5),
|
|
93
|
+
ZovaRender.column({ align: 'center', width: 140, enableSorting: true }),
|
|
85
94
|
// Tutorial 3 built-in form renderer example. Keep it commented here for side-by-side comparison.
|
|
86
95
|
// ZovaRender.field('basic-select:formFieldSelect', {
|
|
87
96
|
// items: studentLevelItems,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import { DtoDetailRecordResItem } from '../src/dto/detailRecordResItem.tsx';
|
|
14
14
|
import { DtoStudentSummary } from '../src/dto/studentSummary.tsx';
|
|
15
15
|
|
|
16
|
-
describe('student.test.ts', () => {
|
|
16
|
+
describe('student.test.ts', { concurrency: false }, () => {
|
|
17
17
|
it('action:student:formLayoutMetadata', async () => {
|
|
18
18
|
await app.bean.executor.mockCtx(async () => {
|
|
19
19
|
for (const DtoClass of [DtoStudentCreate, DtoStudentUpdate, DtoStudentView]) {
|
|
@@ -95,6 +95,104 @@ describe('student.test.ts', () => {
|
|
|
95
95
|
});
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
+
it('action:student:tableColumnMetadata', async () => {
|
|
99
|
+
await app.bean.executor.mockCtx(async () => {
|
|
100
|
+
const apiJson = await app.bean.openapi.generateJsonOfClass(DtoStudentSelectResItem);
|
|
101
|
+
const component = Object.values(apiJson.components!.schemas as any).find(item => {
|
|
102
|
+
return (item as any).properties?._operationsRow;
|
|
103
|
+
}) as any;
|
|
104
|
+
assert.deepEqual(
|
|
105
|
+
{
|
|
106
|
+
align: component.properties.name.rest.table.align,
|
|
107
|
+
width: component.properties.name.rest.table.width,
|
|
108
|
+
fixed: component.properties.name.rest.table.fixed,
|
|
109
|
+
enableSorting: component.properties.name.rest.table.enableSorting,
|
|
110
|
+
},
|
|
111
|
+
{ align: 'left', width: 240, fixed: 'left', enableSorting: true },
|
|
112
|
+
);
|
|
113
|
+
assert.deepEqual(
|
|
114
|
+
{
|
|
115
|
+
align: component.properties.level.rest.table.align,
|
|
116
|
+
width: component.properties.level.rest.table.width,
|
|
117
|
+
enableSorting: component.properties.level.rest.table.enableSorting,
|
|
118
|
+
},
|
|
119
|
+
{ align: 'center', width: 140, enableSorting: true },
|
|
120
|
+
);
|
|
121
|
+
assert.deepEqual(
|
|
122
|
+
{
|
|
123
|
+
align: component.properties.createdAt.rest.table.align,
|
|
124
|
+
width: component.properties.createdAt.rest.table.width,
|
|
125
|
+
enableSorting: component.properties.createdAt.rest.table.enableSorting,
|
|
126
|
+
},
|
|
127
|
+
{ align: 'center', width: 180, enableSorting: true },
|
|
128
|
+
);
|
|
129
|
+
assert.deepEqual(
|
|
130
|
+
{
|
|
131
|
+
align: component.properties._operationsRow.rest.table.align,
|
|
132
|
+
width: component.properties._operationsRow.rest.table.width,
|
|
133
|
+
fixed: component.properties._operationsRow.rest.table.fixed,
|
|
134
|
+
},
|
|
135
|
+
{ align: 'center', width: 360, fixed: 'right' },
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('action:student:serverSorting', async () => {
|
|
141
|
+
await app.bean.executor.mockCtx(async () => {
|
|
142
|
+
await app.bean.passport.signinMock();
|
|
143
|
+
const studentIds: string[] = [];
|
|
144
|
+
const names = ['__StudentSortA__', '__StudentSortZ__'];
|
|
145
|
+
try {
|
|
146
|
+
for (const [index, name] of names.entries()) {
|
|
147
|
+
const studentId = await app.bean.executor.performAction('post', '/training/student', {
|
|
148
|
+
body: {
|
|
149
|
+
name,
|
|
150
|
+
mobile: `13812345${678 + index}`,
|
|
151
|
+
level: 1,
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
studentIds.push(studentId);
|
|
155
|
+
}
|
|
156
|
+
const ascending: DtoStudentSelectRes = await app.bean.executor.performAction(
|
|
157
|
+
'get',
|
|
158
|
+
'/training/student',
|
|
159
|
+
{ query: { orders: [['name', 'asc']] } },
|
|
160
|
+
);
|
|
161
|
+
const descending: DtoStudentSelectRes = await app.bean.executor.performAction(
|
|
162
|
+
'get',
|
|
163
|
+
'/training/student',
|
|
164
|
+
{ query: { orders: [['name', 'desc']] } },
|
|
165
|
+
);
|
|
166
|
+
const ascendingIndexes = names.map(name =>
|
|
167
|
+
ascending.list.findIndex(item => item.name === name),
|
|
168
|
+
);
|
|
169
|
+
const descendingIndexes = names.map(name =>
|
|
170
|
+
descending.list.findIndex(item => item.name === name),
|
|
171
|
+
);
|
|
172
|
+
assert.equal(
|
|
173
|
+
ascendingIndexes.every(index => index >= 0),
|
|
174
|
+
true,
|
|
175
|
+
);
|
|
176
|
+
assert.equal(
|
|
177
|
+
descendingIndexes.every(index => index >= 0),
|
|
178
|
+
true,
|
|
179
|
+
);
|
|
180
|
+
assert.equal(ascendingIndexes[0] < ascendingIndexes[1], true);
|
|
181
|
+
assert.equal(descendingIndexes[0] > descendingIndexes[1], true);
|
|
182
|
+
} finally {
|
|
183
|
+
for (const studentId of studentIds.reverse()) {
|
|
184
|
+
await app
|
|
185
|
+
.scope('training-student')
|
|
186
|
+
.model.studentContent.delete({ studentId }, { disableDeleted: true });
|
|
187
|
+
await app
|
|
188
|
+
.scope('training-student')
|
|
189
|
+
.model.student.deleteById(studentId, { disableDeleted: true });
|
|
190
|
+
}
|
|
191
|
+
await app.bean.passport.signout();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
98
196
|
it('action:student:emittedDtoSchemas', async () => {
|
|
99
197
|
await app.bean.executor.mockCtx(async () => {
|
|
100
198
|
const summaryApiJson = await app.bean.openapi.generateJsonOfClass(DtoStudentSummary);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vona-suite-a-cabloy",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.51",
|
|
4
4
|
"gitHead": "a79189b882c17af5911573896a781bbb0046d37d",
|
|
5
5
|
"description": "",
|
|
6
6
|
"author": "",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"vona-module-a-cabloy": "workspace:^",
|
|
11
11
|
"vona-module-a-markdown": "^5.0.3",
|
|
12
|
-
"vona-module-a-rbac": "^5.0.
|
|
12
|
+
"vona-module-a-rbac": "^5.0.5",
|
|
13
13
|
"vona-module-a-socket": "workspace:^",
|
|
14
14
|
"vona-module-a-ssr": "workspace:^",
|
|
15
15
|
"vona-module-a-ssrhmr": "workspace:^"
|