openxiangda-cli 2.0.0-alpha.85 → 2.0.0-alpha.89
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/package.json +4 -4
- package/template/AGENTS.md +4 -4
- package/template/README.md +4 -4
- package/template/apps/server/package.json +1 -1
- package/template/apps/server/src/app.module.ts +14 -1
- package/template/apps/server/test/smoke.test.ts +3 -1
- package/template/apps/web/data-api-live.e2e.html +15 -0
- package/template/apps/web/e2e/data-api-live-fixture.tsx +141 -0
- package/template/apps/web/e2e/data-api-live.spec.ts +138 -0
- package/template/apps/web/e2e/field-protocol-fixture.tsx +280 -0
- package/template/apps/web/e2e/field-protocol.spec.ts +100 -0
- package/template/apps/web/e2e/resources.spec.ts +258 -20
- package/template/apps/web/field-protocol.e2e.html +12 -0
- package/template/apps/web/package.json +1 -1
- package/template/apps/web/playwright.config.ts +3 -1
- package/template/apps/web/scripts/check.mjs +8 -5
- package/template/apps/web/scripts/verify-build.mjs +1 -1
- package/template/apps/web/src/AuthoritativeSelector.tsx +165 -132
- package/template/apps/web/src/FilePreviewPage.tsx +30 -9
- package/template/apps/web/src/RoleSubjectSelect.tsx +128 -0
- package/template/apps/web/src/Shell.tsx +93 -6
- package/template/apps/web/src/components/platform-fields/AddressField.tsx +338 -0
- package/template/apps/web/src/components/platform-fields/AttachmentFileList.tsx +25 -6
- package/template/apps/web/src/components/platform-fields/CascadeField.tsx +49 -0
- package/template/apps/web/src/components/platform-fields/DateTimeField.tsx +148 -0
- package/template/apps/web/src/components/platform-fields/JsonField.tsx +77 -0
- package/template/apps/web/src/components/platform-fields/LocationField.tsx +164 -0
- package/template/apps/web/src/components/platform-fields/PlatformDirectoryPicker.tsx +19 -44
- package/template/apps/web/src/components/platform-fields/ResourceReferenceField.tsx +67 -0
- package/template/apps/web/src/components/platform-fields/RichTextField.tsx +196 -0
- package/template/apps/web/src/components/platform-fields/SignatureField.tsx +315 -0
- package/template/apps/web/src/components/platform-fields/SubtableField.tsx +553 -0
- package/template/apps/web/src/components/platform-fields/address-value.ts +80 -0
- package/template/apps/web/src/components/platform-fields/cascade-value.ts +66 -0
- package/template/apps/web/src/components/platform-fields/directory-value.ts +53 -0
- package/template/apps/web/src/components/platform-fields/field-form-codec.ts +98 -0
- package/template/apps/web/src/components/platform-fields/location-value.ts +87 -0
- package/template/apps/web/src/components/platform-fields/resource-query.ts +131 -0
- package/template/apps/web/src/components/platform-fields/rich-text-value.ts +59 -0
- package/template/apps/web/src/components/platform-fields/subtable-value.ts +187 -0
- package/template/apps/web/src/components/resource/GeneratedResourceCrud.tsx +194 -108
- package/template/apps/web/src/components/resource/ResourceBatchActions.tsx +8 -1
- package/template/apps/web/src/components/resource/SurfaceFields.tsx +385 -79
- package/template/apps/web/src/components/resource/generated-resource-definition.ts +18 -0
- package/template/apps/web/src/components/resource/resource-import.ts +4 -4
- package/template/apps/web/src/data-provider.ts +59 -34
- package/template/apps/web/src/platform-client.ts +341 -136
- package/template/apps/web/src/runtime.tsx +156 -17
- package/template/apps/web/src/styles.css +264 -0
- package/template/apps/web/test/address-field.test.ts +66 -0
- package/template/apps/web/test/cascade-value.test.ts +68 -0
- package/template/apps/web/test/contracts.test.ts +29 -5
- package/template/apps/web/test/directory-value.test.ts +55 -0
- package/template/apps/web/test/field-bounds.test.ts +17 -0
- package/template/apps/web/test/field-form-codec.test.ts +85 -0
- package/template/apps/web/test/location-field.test.ts +83 -0
- package/template/apps/web/test/resource-import.test.ts +8 -7
- package/template/apps/web/test/resource-query.test.ts +144 -0
- package/template/apps/web/test/rich-json-field.test.ts +53 -0
- package/template/apps/web/test/signature-serial-field.test.ts +40 -0
- package/template/apps/web/test/subtable-value.test.ts +144 -0
- package/template/openxiangda.config.ts +1 -1
- package/template/package.json +3 -3
- package/template/packages/contracts/src/generated.ts +95 -0
- package/template/scripts/verify-template-budget.mjs +11 -4
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DataResourceSurface } from 'openxiangda-contracts/browser';
|
|
2
|
+
|
|
3
|
+
export interface GeneratedResourceDefinition {
|
|
4
|
+
code: string;
|
|
5
|
+
name: string;
|
|
6
|
+
capabilities: {
|
|
7
|
+
read: string;
|
|
8
|
+
create: string;
|
|
9
|
+
update: string;
|
|
10
|
+
delete: string;
|
|
11
|
+
};
|
|
12
|
+
surface: DataResourceSurface;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type GeneratedResourceDefinitions = Record<
|
|
16
|
+
string,
|
|
17
|
+
GeneratedResourceDefinition
|
|
18
|
+
>;
|
|
@@ -82,7 +82,7 @@ export function parseResourceImportMatrix(
|
|
|
82
82
|
if (!code) return null;
|
|
83
83
|
const field = surface.fields[code];
|
|
84
84
|
if (!field) return null;
|
|
85
|
-
if (
|
|
85
|
+
if (['file', 'image', 'signature'].includes(field.type)) {
|
|
86
86
|
errors.push(`文件字段“${field.label}”必须通过平台上传组件录入`);
|
|
87
87
|
return null;
|
|
88
88
|
}
|
|
@@ -153,7 +153,7 @@ function importValue(field: DataResourceSurface['fields'][string], input: unknow
|
|
|
153
153
|
if (!Number.isFinite(value)) throw new Error('必须是数字');
|
|
154
154
|
return value;
|
|
155
155
|
}
|
|
156
|
-
if (field.
|
|
156
|
+
if (field.type === 'boolean') {
|
|
157
157
|
if (input === true || input === 1 || /^(?:是|true|yes|1)$/i.test(String(input).trim())) return true;
|
|
158
158
|
if (input === false || input === 0 || /^(?:否|false|no|0)$/i.test(String(input).trim())) return false;
|
|
159
159
|
throw new Error('请填写是或否');
|
|
@@ -185,7 +185,7 @@ function importValue(field: DataResourceSurface['fields'][string], input: unknow
|
|
|
185
185
|
throw new Error('必须是有效 JSON');
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
|
-
const multiple = field.
|
|
188
|
+
const multiple = field.type.endsWith('.multiple');
|
|
189
189
|
const values = multiple
|
|
190
190
|
? (Array.isArray(input) ? input : String(input).split(/[、,,;;\n]+/)).map((value) => String(value).trim()).filter(Boolean)
|
|
191
191
|
: [String(input).trim()];
|
|
@@ -193,7 +193,7 @@ function importValue(field: DataResourceSurface['fields'][string], input: unknow
|
|
|
193
193
|
const resolved = values.map((value) => {
|
|
194
194
|
const option = field.options?.find((item) => item.value === value || item.label === value);
|
|
195
195
|
if (!option) throw new Error(`“${value}”不在可选项中`);
|
|
196
|
-
return option
|
|
196
|
+
return { ...option };
|
|
197
197
|
});
|
|
198
198
|
return multiple ? resolved : resolved[0];
|
|
199
199
|
}
|
|
@@ -2,46 +2,71 @@ import type { DataProvider } from '@refinedev/core';
|
|
|
2
2
|
import { createNativeResourceClient, type GenericResourceQuery } from './platform-client';
|
|
3
3
|
import { applicationCode } from './runtime-meta';
|
|
4
4
|
import { resourceDefinitions } from '../../../packages/contracts/src/generated.js';
|
|
5
|
+
import type { GeneratedResourceDefinitions } from './components/resource/generated-resource-definition';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
code: string;
|
|
8
|
-
name: string;
|
|
9
|
-
capabilities: { read: string; create: string; update: string; delete: string };
|
|
10
|
-
surface: import('openxiangda-contracts/browser').DataResourceSurface;
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const definitions = resourceDefinitions as unknown as Record<string, GeneratedDefinition>;
|
|
7
|
+
const definitions = resourceDefinitions as unknown as GeneratedResourceDefinitions;
|
|
14
8
|
type ListParams = Parameters<NonNullable<DataProvider['getList']>>[0];
|
|
15
9
|
type OneParams = Parameters<NonNullable<DataProvider['getOne']>>[0];
|
|
16
10
|
type CreateParams = Parameters<NonNullable<DataProvider['create']>>[0];
|
|
17
11
|
type UpdateParams = Parameters<NonNullable<DataProvider['update']>>[0];
|
|
18
12
|
type DeleteParams = Parameters<NonNullable<DataProvider['deleteOne']>>[0];
|
|
19
13
|
|
|
20
|
-
function
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
export function createApplicationProvider(
|
|
15
|
+
declaredDefinitions: GeneratedResourceDefinitions
|
|
16
|
+
) {
|
|
17
|
+
const clientFor = (resource: string) => {
|
|
18
|
+
const definition = declaredDefinitions[resource];
|
|
19
|
+
if (!definition) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`OPENXIANGDA_GENERATED_RESOURCE_NOT_DECLARED:${resource}`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return createNativeResourceClient(resource, definition.surface);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
getApiUrl: () =>
|
|
29
|
+
`/service/openxiangda-api/v2/applications/${applicationCode()}/native/data`,
|
|
30
|
+
getList: async ({ resource, pagination, sorters, meta }: ListParams) => {
|
|
31
|
+
const query = (meta?.query || {}) as Partial<GenericResourceQuery>;
|
|
32
|
+
const sorter = sorters?.[0];
|
|
33
|
+
const result = await clientFor(resource).list({
|
|
34
|
+
page: pagination?.currentPage || 1,
|
|
35
|
+
pageSize: pagination?.pageSize || 20,
|
|
36
|
+
...query,
|
|
37
|
+
...(sorter
|
|
38
|
+
? {
|
|
39
|
+
sort: {
|
|
40
|
+
field: String(sorter.field),
|
|
41
|
+
order: (sorter.order || 'asc') as 'asc' | 'desc',
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
: {}),
|
|
45
|
+
});
|
|
46
|
+
return { data: result.rows, total: result.total };
|
|
47
|
+
},
|
|
48
|
+
getOne: async ({ resource, id }: OneParams) => ({
|
|
49
|
+
data: await clientFor(resource).get(String(id)),
|
|
50
|
+
}),
|
|
51
|
+
create: async ({ resource, variables }: CreateParams) => ({
|
|
52
|
+
data: await clientFor(resource).create(
|
|
53
|
+
variables as Record<string, unknown>
|
|
54
|
+
),
|
|
55
|
+
}),
|
|
56
|
+
update: async ({ resource, id, variables, meta }: UpdateParams) => ({
|
|
57
|
+
data: await clientFor(resource).update(
|
|
58
|
+
String(id),
|
|
59
|
+
Number(meta?.expectedRevision),
|
|
60
|
+
variables as Record<string, unknown>
|
|
61
|
+
),
|
|
62
|
+
}),
|
|
63
|
+
deleteOne: async ({ resource, id, meta }: DeleteParams) => ({
|
|
64
|
+
data: await clientFor(resource).remove(
|
|
65
|
+
String(id),
|
|
66
|
+
Number(meta?.expectedRevision)
|
|
67
|
+
),
|
|
68
|
+
}),
|
|
69
|
+
} as unknown as DataProvider;
|
|
24
70
|
}
|
|
25
71
|
|
|
26
|
-
export const applicationProvider =
|
|
27
|
-
getApiUrl: () => `/service/openxiangda-api/v2/applications/${applicationCode()}/native/data`,
|
|
28
|
-
getList: async ({ resource, pagination, sorters, meta }: ListParams) => {
|
|
29
|
-
const query = (meta?.query || {}) as Partial<GenericResourceQuery>;
|
|
30
|
-
const sorter = sorters?.[0];
|
|
31
|
-
const result = await clientFor(resource).list({
|
|
32
|
-
page: pagination?.currentPage || 1,
|
|
33
|
-
pageSize: pagination?.pageSize || 20,
|
|
34
|
-
...query,
|
|
35
|
-
...(sorter ? { sort: { field: String(sorter.field), order: (sorter.order || 'asc') as 'asc' | 'desc' } } : {}),
|
|
36
|
-
});
|
|
37
|
-
return { data: result.rows, total: result.total };
|
|
38
|
-
},
|
|
39
|
-
getOne: async ({ resource, id }: OneParams) => ({ data: await clientFor(resource).get(String(id)) }),
|
|
40
|
-
create: async ({ resource, variables }: CreateParams) => ({ data: await clientFor(resource).create(variables as Record<string, unknown>) }),
|
|
41
|
-
update: async ({ resource, id, variables, meta }: UpdateParams) => ({
|
|
42
|
-
data: await clientFor(resource).update(String(id), Number(meta?.expectedRevision), variables as Record<string, unknown>),
|
|
43
|
-
}),
|
|
44
|
-
deleteOne: async ({ resource, id, meta }: DeleteParams) => ({
|
|
45
|
-
data: await clientFor(resource).remove(String(id), Number(meta?.expectedRevision)),
|
|
46
|
-
}),
|
|
47
|
-
} as unknown as DataProvider;
|
|
72
|
+
export const applicationProvider = createApplicationProvider(definitions);
|