@skopon-cool/form-sdk 0.6.2 → 0.6.4
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/dist/adapter/a2uiAdapter.d.ts +1 -1
- package/dist/adapter/a2uiAdapter.d.ts.map +1 -1
- package/dist/adapter/formAssetOptions.d.ts +3 -0
- package/dist/adapter/formAssetOptions.d.ts.map +1 -0
- package/dist/adapter/formSchema.d.ts.map +1 -1
- package/dist/adapter/resolveSurface.d.ts +1 -1
- package/dist/adapter/resolveSurface.d.ts.map +1 -1
- package/dist/catalog/a2uiCustomCatalog.d.ts.map +1 -1
- package/dist/components/AskUserFormCard.d.ts +2 -0
- package/dist/components/AskUserFormCard.d.ts.map +1 -1
- package/dist/form-sdk.css +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2018 -1797
- package/dist/submit/buildAnswerAgentBody.d.ts.map +1 -1
- package/dist/submit/intersectPayloadBlocksWithForm.d.ts +11 -2
- package/dist/submit/intersectPayloadBlocksWithForm.d.ts.map +1 -1
- package/dist/types/index.d.ts +39 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapter/a2uiAdapter.test.ts +16 -0
- package/src/adapter/a2uiAdapter.ts +66 -2
- package/src/adapter/formAssetOptions.ts +19 -0
- package/src/adapter/formSchema.ts +60 -0
- package/src/adapter/resolveSurface.ts +4 -1
- package/src/catalog/a2uiCustomCatalog.tsx +216 -13
- package/src/catalog/fileUploadPreview.test.tsx +73 -0
- package/src/catalog/moduleTabs.test.tsx +59 -0
- package/src/components/AskUserFormCard.tsx +28 -9
- package/src/components/AskUserFormCard.upload.test.tsx +66 -0
- package/src/icons/FilePlaceholderIcon.tsx +2 -2
- package/src/index.ts +2 -0
- package/src/styles/index.css +295 -0
- package/src/submit/buildAnswerAgentBody.ts +2 -0
- package/src/submit/intersectPayloadBlocksWithForm.ts +52 -3
- package/src/submit/submit.test.ts +54 -0
- package/src/types/index.ts +41 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildAnswerAgentBody.d.ts","sourceRoot":"","sources":["../../src/submit/buildAnswerAgentBody.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,kCAAkC,CAAA;AAEzC,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;
|
|
1
|
+
{"version":3,"file":"buildAnswerAgentBody.d.ts","sourceRoot":"","sources":["../../src/submit/buildAnswerAgentBody.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,kCAAkC,CAAA;AAEzC,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAqBD,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,yBAAyB,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,CAAC,EAAE,uBAAuB,GAAG,UAAU,GAAG,IAAI,CAAA;IAClD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAAC,EACnC,OAAO,EACP,OAAO,EACP,IAAI,EACJ,MAAM,GACP,EAAE,2BAA2B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAevD;AAED,iDAAiD;AACjD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,IAAI,CAqBpF;AAED,iCAAiC;AACjC,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,yBAAyB,GACjC,MAAM,GAAG,IAAI,CAQf"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FormBlock, FormSchema } from '../types/index';
|
|
2
|
-
export type PayloadBlocksDefinition = Pick<FormSchema, 'title' | 'description' | 'blocks'> & {
|
|
2
|
+
export type PayloadBlocksDefinition = Pick<FormSchema, 'title' | 'description' | 'modules' | 'blocks'> & {
|
|
3
|
+
schema?: 'skopon.material-form/v2';
|
|
3
4
|
/**
|
|
4
5
|
* ask 原始 blocks(normalize 前)。交集合并按此键集覆盖 form,
|
|
5
6
|
* 避免 normalize 填入的空默认值误盖表单样式。
|
|
@@ -13,6 +14,14 @@ export interface PayloadBlocksIntersection {
|
|
|
13
14
|
renderBlocks: FormBlock[];
|
|
14
15
|
title: string;
|
|
15
16
|
description: string;
|
|
17
|
+
modules?: FormSchema['modules'];
|
|
18
|
+
}
|
|
19
|
+
export interface IntersectPayloadBlocksOptions {
|
|
20
|
+
/**
|
|
21
|
+
* snapshot:消息快照中的显式配置优先(兼容旧行为);
|
|
22
|
+
* latest:最新表单负责布局/文案/校验,只保留快照里的填写与审核运行态。
|
|
23
|
+
*/
|
|
24
|
+
definitionPolicy?: 'snapshot' | 'latest';
|
|
16
25
|
}
|
|
17
26
|
/** 解析 ask_user payload 为 blocksJson 形(title / description / blocks) */
|
|
18
27
|
export declare function parsePayloadBlocksJson(payload: unknown): PayloadBlocksDefinition | null;
|
|
@@ -20,7 +29,7 @@ export declare function parsePayloadBlocksJson(payload: unknown): PayloadBlocksD
|
|
|
20
29
|
* payload.blocks 与 vt_forms 按 input 块 name 取交集用于渲染;
|
|
21
30
|
* payload 中 form 不存在的块归入 extraBlocks(不渲染,提交时写入 curl)。
|
|
22
31
|
*/
|
|
23
|
-
export declare function intersectPayloadBlocksWithForm(payloadDef: PayloadBlocksDefinition, formDefinition: FormSchema | undefined): PayloadBlocksIntersection;
|
|
32
|
+
export declare function intersectPayloadBlocksWithForm(payloadDef: PayloadBlocksDefinition, formDefinition: FormSchema | undefined, options?: IntersectPayloadBlocksOptions): PayloadBlocksIntersection;
|
|
24
33
|
/** 从 extraBlocks 提取提交值(优先 defaultValue) */
|
|
25
34
|
export declare function extractExtraBlockValues(extraBlocks: FormBlock[]): Record<string, unknown>;
|
|
26
35
|
/** payload 是否含至少一个具 name 的 input 块(可用于卡片 fallback) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intersectPayloadBlocksWithForm.d.ts","sourceRoot":"","sources":["../../src/submit/intersectPayloadBlocksWithForm.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3D,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,CAAC,GAAG;
|
|
1
|
+
{"version":3,"file":"intersectPayloadBlocksWithForm.d.ts","sourceRoot":"","sources":["../../src/submit/intersectPayloadBlocksWithForm.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3D,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC,GAAG;IACvG,MAAM,CAAC,EAAE,yBAAyB,CAAA;IAClC;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,EAAE,CAAA;CACxB,CAAA;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,EAAE,SAAS,EAAE,CAAA;IAC1B,WAAW,EAAE,SAAS,EAAE,CAAA;IACxB,gEAAgE;IAChE,YAAY,EAAE,SAAS,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;CAChC;AAUD,MAAM,WAAW,6BAA6B;IAC5C;;;OAGG;IACH,gBAAgB,CAAC,EAAE,UAAU,GAAG,QAAQ,CAAA;CACzC;AAiGD,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI,CAsBvF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,uBAAuB,EACnC,cAAc,EAAE,UAAU,GAAG,SAAS,EACtC,OAAO,CAAC,EAAE,6BAA6B,GACtC,yBAAyB,CAwC3B;AA+BD,2CAA2C;AAC3C,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzF;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,uBAAuB,GAAG,OAAO,CAOlF;AAED,yEAAyE;AACzE,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,uBAAuB,GAAG,SAAS,EAAE,CAQ3F;AAED,4DAA4D;AAC5D,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,uBAAuB,GAAG,MAAM,EAAE,CASvF;AAED,6DAA6D;AAC7D,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,uBAAuB,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,7 +4,15 @@
|
|
|
4
4
|
export type A2uiBinding<T> = T | {
|
|
5
5
|
path: string;
|
|
6
6
|
};
|
|
7
|
-
export type A2uiComponentName = 'Column' | 'Row' | 'Text' | 'Image' | 'Video' | 'AudioPlayer' | 'TextField' | 'CheckBox' | 'ChoicePicker' | 'DateTimeInput' | 'FileUpload' | 'SkoponMedia' | 'SkoponWeb' | 'SkoponSelect' | 'SkoponResumeSelect' | 'SkoponCaseSelect';
|
|
7
|
+
export type A2uiComponentName = 'Column' | 'Row' | 'Text' | 'Image' | 'Video' | 'AudioPlayer' | 'TextField' | 'CheckBox' | 'ChoicePicker' | 'DateTimeInput' | 'FileUpload' | 'SkoponMedia' | 'SkoponWeb' | 'SkoponSelect' | 'SkoponResumeSelect' | 'SkoponCaseSelect' | 'SkoponModulePanel';
|
|
8
|
+
/** 表单分组模块。运行时以 Tab 展示,要求与示例统一放在模块底部。 */
|
|
9
|
+
export interface FormModule {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
reviewCriteria?: string;
|
|
14
|
+
exampleUrls?: string[];
|
|
15
|
+
}
|
|
8
16
|
export interface A2uiComponentNode {
|
|
9
17
|
id: string;
|
|
10
18
|
component: A2uiComponentName | string;
|
|
@@ -31,6 +39,13 @@ export interface FormBlockOption {
|
|
|
31
39
|
value: string;
|
|
32
40
|
label: string;
|
|
33
41
|
}
|
|
42
|
+
export interface FormAssetOption {
|
|
43
|
+
assetId: string;
|
|
44
|
+
url: string;
|
|
45
|
+
previewUrl?: string;
|
|
46
|
+
label: string;
|
|
47
|
+
assetType?: string;
|
|
48
|
+
}
|
|
34
49
|
export type FormConditionOperator = 'eq' | 'ne' | 'empty' | 'notEmpty';
|
|
35
50
|
export interface FormBlockCondition {
|
|
36
51
|
fieldName: string;
|
|
@@ -99,6 +114,25 @@ export interface FormBlock {
|
|
|
99
114
|
filePlaceholderIcon?: FormFilePlaceholderIcon;
|
|
100
115
|
fileMinCount?: number;
|
|
101
116
|
fileMaxCount?: number;
|
|
117
|
+
/** 上传素材的示例图,仅展示,不参与提交。 */
|
|
118
|
+
fileExampleUrls?: string[];
|
|
119
|
+
/** 制作模板的逐字段审核标准;由工作流读取,不能使用提交值覆盖。 */
|
|
120
|
+
fileReviewCriteria?: string;
|
|
121
|
+
/** 是否需要由工作流审核上传内容;素材库已审核素材可以关闭。 */
|
|
122
|
+
fileReviewRequired?: boolean;
|
|
123
|
+
/** 物料采集来源模式,由 skopon-admin 环节配置。 */
|
|
124
|
+
fileSourceMode?: 'upload_required' | 'upload_or_asset' | 'asset_only';
|
|
125
|
+
/** 通用收集流从 Footage 为当前素材位查询的候选;仅提交选中的文件 URL。 */
|
|
126
|
+
fileAssetOptions?: FormAssetOption[];
|
|
127
|
+
/** 对应 Footage 同步素材位的限定 key。 */
|
|
128
|
+
assetSlotKey?: string;
|
|
129
|
+
assetType?: 'image' | 'video' | 'audio' | 'text';
|
|
130
|
+
/** 运行时素材审核状态;用于返工表单回显,不作为用户提交值。 */
|
|
131
|
+
fileReviewStatus?: 'passed' | 'failed';
|
|
132
|
+
/** 运行时素材审核说明;failed 时通常为不合格原因。 */
|
|
133
|
+
fileReviewMessage?: string;
|
|
134
|
+
/** 审核未通过的历史文件,仅作错误态回显,不计入有效提交。 */
|
|
135
|
+
fileRejectedUrls?: string[];
|
|
102
136
|
defaultValue?: string | string[] | boolean;
|
|
103
137
|
/** 派生 JSON Schema 时写入 property.$comment */
|
|
104
138
|
jsonSchemaDescription?: string;
|
|
@@ -129,10 +163,13 @@ export interface FormBlock {
|
|
|
129
163
|
/** 案例单选:列表筛选条件(持久化进 block json) */
|
|
130
164
|
caseFilter?: FormCaseFilter;
|
|
131
165
|
condition?: FormBlockCondition;
|
|
166
|
+
/** 所属模块;命中 FormSchema.modules.id 时在对应 Tab 中展示。 */
|
|
167
|
+
moduleId?: string;
|
|
132
168
|
}
|
|
133
169
|
export interface FormSchema {
|
|
134
170
|
title?: string;
|
|
135
171
|
description?: string;
|
|
172
|
+
modules?: FormModule[];
|
|
136
173
|
blocks: FormBlock[];
|
|
137
174
|
jsonSchema: FormJsonSchema;
|
|
138
175
|
}
|
|
@@ -146,6 +183,7 @@ export declare function supportsDefaultValue(type: FormBlockType): boolean;
|
|
|
146
183
|
export interface FormDefinitionPayload {
|
|
147
184
|
title?: string;
|
|
148
185
|
description?: string;
|
|
186
|
+
modules?: FormModule[];
|
|
149
187
|
blocks?: FormBlock[];
|
|
150
188
|
a2ui?: A2uiSurfaceDoc;
|
|
151
189
|
json_schema?: FormJsonSchema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEjD,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,KAAK,GACL,MAAM,GACN,OAAO,GACP,OAAO,GACP,aAAa,GACb,WAAW,GACX,UAAU,GACV,cAAc,GACd,eAAe,GACf,YAAY,GACZ,aAAa,GACb,WAAW,GACX,cAAc,GACd,oBAAoB,GACpB,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -44,6 +44,22 @@ function makeDefinition(): Pick<FormSchema, 'title' | 'description' | 'blocks'>
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
describe('a2uiAdapter blocks <-> surface', () => {
|
|
47
|
+
it('preserves material upload examples and review criteria through normalization and A2UI', () => {
|
|
48
|
+
const definition = normalizeFormDefinition({ blocks: [{
|
|
49
|
+
id: 'material', type: 'file', name: 'image01', help: '上传人物',
|
|
50
|
+
fileExampleUrls: [' https://cdn.test/example.jpg ', 'javascript:alert(1)', 'https://cdn.test/example.jpg'],
|
|
51
|
+
fileReviewCriteria: ' 五官清晰 ',
|
|
52
|
+
fileReviewStatus: 'failed', fileReviewMessage: ' 面部模糊 ',
|
|
53
|
+
fileRejectedUrls: ['https://cdn.test/rejected.jpg'],
|
|
54
|
+
}] })
|
|
55
|
+
const back = a2uiToBlocks(blocksToA2ui(definition))
|
|
56
|
+
expect(back.blocks[0]).toMatchObject({
|
|
57
|
+
name: 'image01', help: '上传人物', fileReviewCriteria: '五官清晰',
|
|
58
|
+
fileExampleUrls: ['https://cdn.test/example.jpg'],
|
|
59
|
+
fileReviewStatus: 'failed', fileReviewMessage: '面部模糊',
|
|
60
|
+
fileRejectedUrls: ['https://cdn.test/rejected.jpg'],
|
|
61
|
+
})
|
|
62
|
+
})
|
|
47
63
|
it('blocksToA2ui builds a Column root referencing all components', () => {
|
|
48
64
|
const doc = blocksToA2ui(makeDefinition(), { styleId: 'theme-a' })
|
|
49
65
|
const root = doc.components.find((c) => c.id === 'root')
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeAssetOptions } from './formAssetOptions'
|
|
1
2
|
import type {
|
|
2
3
|
A2uiComponentNode,
|
|
3
4
|
A2uiSurfaceDoc,
|
|
@@ -276,6 +277,17 @@ function blockToComponent(block: FormBlock): {
|
|
|
276
277
|
id,
|
|
277
278
|
component: 'FileUpload',
|
|
278
279
|
label,
|
|
280
|
+
help: block.help ?? '',
|
|
281
|
+
fileExampleUrls: block.fileExampleUrls ?? [],
|
|
282
|
+
fileReviewCriteria: block.fileReviewCriteria ?? '',
|
|
283
|
+
fileReviewRequired: block.fileReviewRequired !== false,
|
|
284
|
+
fileSourceMode: block.fileSourceMode,
|
|
285
|
+
fileAssetOptions: block.fileAssetOptions ?? [],
|
|
286
|
+
assetSlotKey: block.assetSlotKey,
|
|
287
|
+
assetType: block.assetType,
|
|
288
|
+
fileReviewStatus: block.fileReviewStatus,
|
|
289
|
+
fileReviewMessage: block.fileReviewMessage ?? '',
|
|
290
|
+
fileRejectedUrls: block.fileRejectedUrls ?? [],
|
|
279
291
|
accept: (block.fileAcceptExtensions ?? []).join(','),
|
|
280
292
|
fileAcceptTypes: block.fileAcceptTypes ?? [],
|
|
281
293
|
filePlaceholderIcon: block.filePlaceholderIcon ?? 'document',
|
|
@@ -307,12 +319,15 @@ export function isA2uiSurfaceEmpty(doc: A2uiSurfaceDoc | null | undefined): bool
|
|
|
307
319
|
|
|
308
320
|
/** FormBlock[] -> A2UI surface 文档 */
|
|
309
321
|
export function blocksToA2ui(
|
|
310
|
-
definition: Pick<FormSchema, 'title' | 'description' | 'blocks'>,
|
|
322
|
+
definition: Pick<FormSchema, 'title' | 'description' | 'modules' | 'blocks'>,
|
|
311
323
|
options?: BlocksToA2uiOptions,
|
|
312
324
|
): A2uiSurfaceDoc {
|
|
313
325
|
const components: A2uiComponentNode[] = []
|
|
314
326
|
const childIds: string[] = []
|
|
315
327
|
const dataModel: Record<string, unknown> = {}
|
|
328
|
+
const modules = definition.modules ?? []
|
|
329
|
+
const moduleById = new Map(modules.map((module) => [module.id, module]))
|
|
330
|
+
const moduleChildIds = new Map(modules.map((module) => [module.id, [] as string[]]))
|
|
316
331
|
|
|
317
332
|
const includeHeader = options?.includeHeader !== false
|
|
318
333
|
const title = (definition.title ?? '').trim()
|
|
@@ -332,8 +347,40 @@ export function blocksToA2ui(
|
|
|
332
347
|
// hidden 等无节点块仍可写入 dataModel,保证「不展示仍提交」
|
|
333
348
|
if (dataKey) dataModel[dataKey] = dataValue
|
|
334
349
|
if (!node) continue
|
|
350
|
+
const moduleId = block.moduleId?.trim()
|
|
351
|
+
if (moduleId && moduleById.has(moduleId) && node.component === 'FileUpload') {
|
|
352
|
+
// 分组表单把公共说明集中放在 Tab 底部,避免每个上传位重复拉长页面。
|
|
353
|
+
node.fileExampleUrls = []
|
|
354
|
+
node.fileReviewCriteria = ''
|
|
355
|
+
node.compact = true
|
|
356
|
+
}
|
|
335
357
|
components.push(node)
|
|
336
|
-
|
|
358
|
+
if (moduleId && moduleChildIds.has(moduleId)) {
|
|
359
|
+
moduleChildIds.get(moduleId)!.push(node.id)
|
|
360
|
+
} else {
|
|
361
|
+
childIds.push(node.id)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const tabs = modules.flatMap((module) => {
|
|
366
|
+
const children = moduleChildIds.get(module.id) ?? []
|
|
367
|
+
if (children.length === 0) return []
|
|
368
|
+
const panelId = `__module_panel_${module.id}`
|
|
369
|
+
components.push({
|
|
370
|
+
id: panelId,
|
|
371
|
+
component: 'SkoponModulePanel',
|
|
372
|
+
children,
|
|
373
|
+
single: children.length === 1,
|
|
374
|
+
description: module.description ?? '',
|
|
375
|
+
requirementsTitle: `${module.label.replace(/(.*?)|\(.*?\)/g, '').trim()}素材要求`,
|
|
376
|
+
reviewCriteria: module.reviewCriteria ?? '',
|
|
377
|
+
exampleUrls: module.exampleUrls ?? [],
|
|
378
|
+
})
|
|
379
|
+
return [{ title: module.label, child: panelId }]
|
|
380
|
+
})
|
|
381
|
+
if (tabs.length > 0) {
|
|
382
|
+
components.push({ id: '__modules__', component: 'Tabs', tabs })
|
|
383
|
+
childIds.push('__modules__')
|
|
337
384
|
}
|
|
338
385
|
|
|
339
386
|
components.unshift({ id: ROOT_ID, component: 'Column', children: childIds })
|
|
@@ -448,6 +495,23 @@ function componentToBlock(node: A2uiComponentNode): FormBlock | null {
|
|
|
448
495
|
case 'FileUpload': {
|
|
449
496
|
const accept = asLiteral(node.accept) ?? ''
|
|
450
497
|
const block: FormBlock = { ...base('file'), name }
|
|
498
|
+
block.help = asLiteral(node.help)
|
|
499
|
+
block.fileReviewCriteria = asLiteral(node.fileReviewCriteria)
|
|
500
|
+
block.fileReviewRequired = node.fileReviewRequired !== false
|
|
501
|
+
if (node.fileSourceMode === 'upload_required' || node.fileSourceMode === 'upload_or_asset' || node.fileSourceMode === 'asset_only') block.fileSourceMode = node.fileSourceMode
|
|
502
|
+
block.fileAssetOptions = normalizeAssetOptions(node.fileAssetOptions)
|
|
503
|
+
block.assetSlotKey = asLiteral(node.assetSlotKey)
|
|
504
|
+
if (node.assetType === 'image' || node.assetType === 'video' || node.assetType === 'audio' || node.assetType === 'text') block.assetType = node.assetType
|
|
505
|
+
if (node.fileReviewStatus === 'passed' || node.fileReviewStatus === 'failed') {
|
|
506
|
+
block.fileReviewStatus = node.fileReviewStatus
|
|
507
|
+
}
|
|
508
|
+
block.fileReviewMessage = asLiteral(node.fileReviewMessage)
|
|
509
|
+
if (Array.isArray(node.fileRejectedUrls)) {
|
|
510
|
+
block.fileRejectedUrls = node.fileRejectedUrls.filter((url): url is string => typeof url === 'string')
|
|
511
|
+
}
|
|
512
|
+
if (Array.isArray(node.fileExampleUrls)) {
|
|
513
|
+
block.fileExampleUrls = node.fileExampleUrls.filter((url): url is string => typeof url === 'string')
|
|
514
|
+
}
|
|
451
515
|
if (accept) block.fileAcceptExtensions = accept.split(',').map((s) => s.trim()).filter(Boolean)
|
|
452
516
|
if (Array.isArray(node.fileAcceptTypes)) {
|
|
453
517
|
block.fileAcceptTypes = node.fileAcceptTypes.map(String)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FormAssetOption } from '../types/index'
|
|
2
|
+
|
|
3
|
+
export function normalizeAssetOptions(raw: unknown): FormAssetOption[] {
|
|
4
|
+
if (!Array.isArray(raw)) return []
|
|
5
|
+
const seen = new Set<string>()
|
|
6
|
+
return raw.slice(0, 100).flatMap((item) => {
|
|
7
|
+
if (!item || typeof item !== 'object') return []
|
|
8
|
+
const assetId = String(item.assetId ?? '').trim()
|
|
9
|
+
const url = typeof item.url === 'string' ? item.url.trim() : ''
|
|
10
|
+
if (!assetId || seen.has(assetId) || !/^https?:\/\//i.test(url)) return []
|
|
11
|
+
seen.add(assetId)
|
|
12
|
+
return [{
|
|
13
|
+
assetId, url,
|
|
14
|
+
label: typeof item.label === 'string' ? item.label : `素材 ${assetId}`,
|
|
15
|
+
previewUrl: typeof item.previewUrl === 'string' && /^https?:\/\//i.test(item.previewUrl) ? item.previewUrl : undefined,
|
|
16
|
+
assetType: typeof item.assetType === 'string' ? item.assetType : undefined,
|
|
17
|
+
}]
|
|
18
|
+
})
|
|
19
|
+
}
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
FormContentFilter,
|
|
6
6
|
FormResumeFilter,
|
|
7
7
|
FormJsonSchema,
|
|
8
|
+
FormModule,
|
|
8
9
|
FormSchema,
|
|
9
10
|
} from '../types/index'
|
|
10
11
|
import {
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
} from './formMedia'
|
|
26
27
|
import { FORM_WEB_DEFAULT_HEIGHT, normalizeWebHeight } from './formWeb'
|
|
27
28
|
import { hasRenderableFormOptions, normalizeFormOptions } from './formOptions'
|
|
29
|
+
import { normalizeAssetOptions } from './formAssetOptions'
|
|
28
30
|
|
|
29
31
|
const DEFAULT_MEDIA_URL_MAX = 10
|
|
30
32
|
|
|
@@ -194,6 +196,7 @@ function normalizeBlock(raw: FormBlock): FormBlock {
|
|
|
194
196
|
return {
|
|
195
197
|
id: migrated.id?.trim() || generateId('block-'),
|
|
196
198
|
type,
|
|
199
|
+
moduleId: migrated.moduleId?.trim() || undefined,
|
|
197
200
|
name: migrated.name?.trim() || undefined,
|
|
198
201
|
label: migrated.label ?? '',
|
|
199
202
|
placeholder: migrated.placeholder ?? '',
|
|
@@ -213,6 +216,30 @@ function normalizeBlock(raw: FormBlock): FormBlock {
|
|
|
213
216
|
filePlaceholderIcon: normalizeFilePlaceholderIcon(migrated.filePlaceholderIcon),
|
|
214
217
|
fileMinCount: type === 'file' ? normalizeFileUploadCount(migrated.fileMinCount, 1) : undefined,
|
|
215
218
|
fileMaxCount: type === 'file' ? normalizeFileUploadCount(migrated.fileMaxCount, 1) : undefined,
|
|
219
|
+
fileExampleUrls: type === 'file' && Array.isArray(migrated.fileExampleUrls)
|
|
220
|
+
? [...new Set(migrated.fileExampleUrls.filter((url): url is string =>
|
|
221
|
+
typeof url === 'string' && /^https?:\/\//i.test(url.trim()),
|
|
222
|
+
).map((url) => url.trim()))]
|
|
223
|
+
: undefined,
|
|
224
|
+
fileReviewCriteria: type === 'file' ? migrated.fileReviewCriteria?.trim() ?? '' : undefined,
|
|
225
|
+
fileReviewRequired: type === 'file' ? migrated.fileReviewRequired !== false : undefined,
|
|
226
|
+
fileSourceMode: type === 'file' && ['upload_required', 'upload_or_asset', 'asset_only'].includes(String(migrated.fileSourceMode))
|
|
227
|
+
? migrated.fileSourceMode
|
|
228
|
+
: undefined,
|
|
229
|
+
fileAssetOptions: type === 'file' ? normalizeAssetOptions(migrated.fileAssetOptions) : undefined,
|
|
230
|
+
assetSlotKey: type === 'file' ? migrated.assetSlotKey?.trim() || undefined : undefined,
|
|
231
|
+
assetType: type === 'file' && ['image', 'video', 'audio', 'text'].includes(String(migrated.assetType))
|
|
232
|
+
? migrated.assetType
|
|
233
|
+
: undefined,
|
|
234
|
+
fileReviewStatus: type === 'file' && (migrated.fileReviewStatus === 'passed' || migrated.fileReviewStatus === 'failed')
|
|
235
|
+
? migrated.fileReviewStatus
|
|
236
|
+
: undefined,
|
|
237
|
+
fileReviewMessage: type === 'file' ? migrated.fileReviewMessage?.trim() ?? '' : undefined,
|
|
238
|
+
fileRejectedUrls: type === 'file' && Array.isArray(migrated.fileRejectedUrls)
|
|
239
|
+
? [...new Set(migrated.fileRejectedUrls.filter((url): url is string =>
|
|
240
|
+
typeof url === 'string' && /^https?:\/\//i.test(url.trim()),
|
|
241
|
+
).map((url) => url.trim()))]
|
|
242
|
+
: undefined,
|
|
216
243
|
defaultValue: normalizeDefaultValue(type, migrated.defaultValue),
|
|
217
244
|
jsonSchemaDescription: migrated.jsonSchemaDescription?.trim() ?? '',
|
|
218
245
|
hidden: type === 'text' ? migrated.hidden === true : undefined,
|
|
@@ -432,19 +459,52 @@ export function syncFormDefinition(definition: FormSchema): FormSchema {
|
|
|
432
459
|
return {
|
|
433
460
|
title,
|
|
434
461
|
description,
|
|
462
|
+
modules: definition.modules,
|
|
435
463
|
blocks: definition.blocks,
|
|
436
464
|
jsonSchema: blocksToJsonSchema(definition.blocks, title, description),
|
|
437
465
|
}
|
|
438
466
|
}
|
|
439
467
|
|
|
468
|
+
function normalizeModules(raw: FormModule[] | undefined): FormModule[] | undefined {
|
|
469
|
+
if (!Array.isArray(raw)) return undefined
|
|
470
|
+
const ids = new Set<string>()
|
|
471
|
+
const modules: FormModule[] = []
|
|
472
|
+
for (const item of raw) {
|
|
473
|
+
if (!item || typeof item !== 'object') continue
|
|
474
|
+
const id = item.id?.trim()
|
|
475
|
+
const label = item.label?.trim()
|
|
476
|
+
if (!id || !label || ids.has(id)) continue
|
|
477
|
+
ids.add(id)
|
|
478
|
+
const exampleUrls = Array.isArray(item.exampleUrls)
|
|
479
|
+
? [...new Set(item.exampleUrls
|
|
480
|
+
.filter((url): url is string => typeof url === 'string' && /^https?:\/\//i.test(url.trim()))
|
|
481
|
+
.map((url) => url.trim()))]
|
|
482
|
+
: undefined
|
|
483
|
+
modules.push({
|
|
484
|
+
id,
|
|
485
|
+
label,
|
|
486
|
+
description: item.description?.trim() || undefined,
|
|
487
|
+
reviewCriteria: item.reviewCriteria?.trim() || undefined,
|
|
488
|
+
exampleUrls,
|
|
489
|
+
})
|
|
490
|
+
}
|
|
491
|
+
return modules.length > 0 ? modules : undefined
|
|
492
|
+
}
|
|
493
|
+
|
|
440
494
|
export function normalizeFormDefinition(raw: FormSchema | undefined): FormSchema {
|
|
441
495
|
if (!raw || !Array.isArray(raw.blocks)) {
|
|
442
496
|
return syncFormDefinition({ title: '', description: '', blocks: [], jsonSchema: {} })
|
|
443
497
|
}
|
|
444
498
|
const blocks = raw.blocks.map(normalizeBlock)
|
|
499
|
+
const modules = normalizeModules(raw.modules)
|
|
500
|
+
const moduleIds = new Set(modules?.map((item) => item.id) ?? [])
|
|
501
|
+
for (const block of blocks) {
|
|
502
|
+
if (block.moduleId && !moduleIds.has(block.moduleId)) block.moduleId = undefined
|
|
503
|
+
}
|
|
445
504
|
return syncFormDefinition({
|
|
446
505
|
title: typeof raw.title === 'string' ? raw.title : '',
|
|
447
506
|
description: typeof raw.description === 'string' ? raw.description : '',
|
|
507
|
+
modules,
|
|
448
508
|
blocks,
|
|
449
509
|
jsonSchema: {},
|
|
450
510
|
})
|
|
@@ -26,6 +26,7 @@ export function resolveSurfaceFromFormDefinition(
|
|
|
26
26
|
const normalized = normalizeFormDefinition({
|
|
27
27
|
title: typeof payload.title === 'string' ? payload.title : '',
|
|
28
28
|
description: typeof payload.description === 'string' ? payload.description : '',
|
|
29
|
+
modules: Array.isArray(payload.modules) ? payload.modules : undefined,
|
|
29
30
|
blocks,
|
|
30
31
|
jsonSchema: normalizeJsonSchema(payload.jsonSchema ?? payload.json_schema),
|
|
31
32
|
})
|
|
@@ -34,7 +35,7 @@ export function resolveSurfaceFromFormDefinition(
|
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export function buildAskUserSurface(
|
|
37
|
-
formDef: Pick<FormSchema, 'title' | 'description'>,
|
|
38
|
+
formDef: Pick<FormSchema, 'title' | 'description' | 'modules'>,
|
|
38
39
|
matchedBlocks: FormBlock[],
|
|
39
40
|
options?: BlocksToA2uiOptions,
|
|
40
41
|
): A2uiSurfaceDoc {
|
|
@@ -42,6 +43,7 @@ export function buildAskUserSurface(
|
|
|
42
43
|
{
|
|
43
44
|
title: formDef.title,
|
|
44
45
|
description: formDef.description,
|
|
46
|
+
modules: formDef.modules,
|
|
45
47
|
blocks: matchedBlocks,
|
|
46
48
|
},
|
|
47
49
|
options,
|
|
@@ -60,6 +62,7 @@ export function mapFormDefinitionFromDto(raw: unknown): FormSchema | undefined {
|
|
|
60
62
|
return normalizeFormDefinition({
|
|
61
63
|
title: typeof obj.title === 'string' ? obj.title : '',
|
|
62
64
|
description: typeof obj.description === 'string' ? obj.description : '',
|
|
65
|
+
modules: Array.isArray(obj.modules) ? obj.modules as FormSchema['modules'] : undefined,
|
|
63
66
|
blocks: Array.isArray(obj.blocks) ? (obj.blocks as FormSchema['blocks']) : [],
|
|
64
67
|
jsonSchema: normalizeJsonSchema(obj.jsonSchema ?? obj.json_schema),
|
|
65
68
|
})
|