@skopon-cool/form-sdk 0.4.5 → 0.4.7
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.map +1 -1
- package/dist/adapter/formSchema.d.ts +3 -0
- package/dist/adapter/formSchema.d.ts.map +1 -1
- package/dist/components/AskUserFormCard.d.ts.map +1 -1
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1353 -1169
- package/dist/submit/intersectPayloadBlocksWithForm.d.ts +4 -2
- package/dist/submit/intersectPayloadBlocksWithForm.d.ts.map +1 -1
- package/dist/types/index.d.ts +18 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapter/a2uiAdapter.test.ts +25 -0
- package/src/adapter/a2uiAdapter.ts +16 -1
- package/src/adapter/formSchema.test.ts +91 -10
- package/src/adapter/formSchema.ts +209 -17
- package/src/components/AskUserFormCard.tsx +25 -13
- package/src/index.ts +18 -3
- package/src/submit/intersectPayloadBlocksWithForm.ts +27 -4
- package/src/submit/submit.test.ts +54 -0
- package/src/types/index.ts +34 -0
|
@@ -25,8 +25,10 @@ export declare function intersectPayloadBlocksWithForm(payloadDef: PayloadBlocks
|
|
|
25
25
|
export declare function extractExtraBlockValues(extraBlocks: FormBlock[]): Record<string, unknown>;
|
|
26
26
|
/** payload 是否含至少一个具 name 的 input 块(可用于卡片 fallback) */
|
|
27
27
|
export declare function payloadHasInputBlocks(payloadDef: PayloadBlocksDefinition): boolean;
|
|
28
|
-
/** 交集为空时 fallback:payload 中可上屏的 blocks(layout + input) */
|
|
28
|
+
/** 交集为空时 fallback:payload 中可上屏的 blocks(layout + 非 hidden input) */
|
|
29
29
|
export declare function getPayloadRenderableBlocks(payloadDef: PayloadBlocksDefinition): FormBlock[];
|
|
30
|
-
/** payload 中所有 input 块 field name(fallback
|
|
30
|
+
/** payload 中所有 input 块 field name(fallback 提交用,含 hidden) */
|
|
31
31
|
export declare function getPayloadInputFieldNames(payloadDef: PayloadBlocksDefinition): string[];
|
|
32
|
+
/** payload 中 hidden text 的默认提交值(fallback 不上屏时仍需写入 answer) */
|
|
33
|
+
export declare function getPayloadHiddenBlockValues(payloadDef: PayloadBlocksDefinition): Record<string, unknown>;
|
|
32
34
|
//# sourceMappingURL=intersectPayloadBlocksWithForm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intersectPayloadBlocksWithForm.d.ts","sourceRoot":"","sources":["../../src/submit/intersectPayloadBlocksWithForm.ts"],"names":[],"mappings":"AACA,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;IAC3F;;;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,0DAA0D;IAC1D,YAAY,EAAE,SAAS,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;
|
|
1
|
+
{"version":3,"file":"intersectPayloadBlocksWithForm.d.ts","sourceRoot":"","sources":["../../src/submit/intersectPayloadBlocksWithForm.ts"],"names":[],"mappings":"AACA,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;IAC3F;;;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,0DAA0D;IAC1D,YAAY,EAAE,SAAS,EAAE,CAAA;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;CACpB;AA8ED,uEAAuE;AACvE,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,uBAAuB,GAAG,IAAI,CAmBvF;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,UAAU,EAAE,uBAAuB,EACnC,cAAc,EAAE,UAAU,GAAG,SAAS,GACrC,yBAAyB,CA+B3B;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,CAIlF;AAED,mEAAmE;AACnE,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,uBAAuB,GAAG,SAAS,EAAE,CAO3F;AAED,4DAA4D;AAC5D,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,uBAAuB,GAAG,MAAM,EAAE,CAIvF;AAED,6DAA6D;AAC7D,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,uBAAuB,GAClC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -31,9 +31,17 @@ export interface FormBlockOption {
|
|
|
31
31
|
value: string;
|
|
32
32
|
label: string;
|
|
33
33
|
}
|
|
34
|
+
export type FormConditionOperator = 'eq' | 'ne' | 'empty' | 'notEmpty';
|
|
35
|
+
export interface FormBlockCondition {
|
|
36
|
+
fieldName: string;
|
|
37
|
+
operator: FormConditionOperator;
|
|
38
|
+
value?: string;
|
|
39
|
+
}
|
|
34
40
|
/** 简历多选组件:渲染时用于 /univ/resume/search 的筛选条件 */
|
|
35
41
|
export interface FormResumeFilter {
|
|
36
42
|
names?: string[];
|
|
43
|
+
/** Agent 类型(编辑器辅助,运行时不用) */
|
|
44
|
+
agentKind?: string;
|
|
37
45
|
agentUniqueIds?: string[];
|
|
38
46
|
resumeUniqueIds?: string[];
|
|
39
47
|
pageSize?: number;
|
|
@@ -70,6 +78,12 @@ export interface FormBlock {
|
|
|
70
78
|
fileMinCount?: number;
|
|
71
79
|
fileMaxCount?: number;
|
|
72
80
|
defaultValue?: string | string[] | boolean;
|
|
81
|
+
/** 派生 JSON Schema 时写入 property.$comment */
|
|
82
|
+
jsonSchemaDescription?: string;
|
|
83
|
+
/** 是否隐藏(仅 text 块;隐藏时仍提交) */
|
|
84
|
+
hidden?: boolean;
|
|
85
|
+
/** 是否需要 LLM 处理(持久化进 block json) */
|
|
86
|
+
needLLM?: boolean;
|
|
73
87
|
/** 简历多选:是否显示「换一批」 */
|
|
74
88
|
resumeEnableRefresh?: boolean;
|
|
75
89
|
/** 简历多选:最少选中数量,配置值 ≥ 1 */
|
|
@@ -86,6 +100,7 @@ export interface FormBlock {
|
|
|
86
100
|
caseMaxCount?: number;
|
|
87
101
|
/** 案例单选:列表筛选条件(持久化进 block json) */
|
|
88
102
|
caseFilter?: FormCaseFilter;
|
|
103
|
+
condition?: FormBlockCondition;
|
|
89
104
|
}
|
|
90
105
|
export interface FormSchema {
|
|
91
106
|
title?: string;
|
|
@@ -96,6 +111,9 @@ export interface FormSchema {
|
|
|
96
111
|
export declare function isMediaBlockType(type: FormBlockType): boolean;
|
|
97
112
|
export declare function isLayoutBlockType(type: FormBlockType): boolean;
|
|
98
113
|
export declare function isInputBlockType(type: FormBlockType): boolean;
|
|
114
|
+
export declare function isDisplayBlockType(type: FormBlockType): boolean;
|
|
115
|
+
export declare function isOptionsBlockType(type: FormBlockType): boolean;
|
|
116
|
+
export declare function supportsDefaultValue(type: FormBlockType): boolean;
|
|
99
117
|
export interface FormDefinitionPayload {
|
|
100
118
|
title?: string;
|
|
101
119
|
description?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -88,6 +88,31 @@ describe('a2uiAdapter blocks <-> surface', () => {
|
|
|
88
88
|
expect(imgNode?.mediaSize).toBe('large')
|
|
89
89
|
})
|
|
90
90
|
|
|
91
|
+
it('blocksToA2ui omits hidden text controls but keeps dataModel defaults', () => {
|
|
92
|
+
const doc = blocksToA2ui(
|
|
93
|
+
{
|
|
94
|
+
title: '',
|
|
95
|
+
description: '',
|
|
96
|
+
blocks: [
|
|
97
|
+
{ id: 'b-title', type: 'text', name: 'title', label: '标题', defaultValue: 'shown' },
|
|
98
|
+
{
|
|
99
|
+
id: 'b-hint',
|
|
100
|
+
type: 'text',
|
|
101
|
+
name: 'agent_hint',
|
|
102
|
+
label: '提示',
|
|
103
|
+
defaultValue: 'secret',
|
|
104
|
+
hidden: true,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
{ includeHeader: false },
|
|
109
|
+
)
|
|
110
|
+
expect(doc.components.some((c) => c.id === 'b-hint')).toBe(false)
|
|
111
|
+
expect(doc.components.some((c) => c.id === 'b-title')).toBe(true)
|
|
112
|
+
expect(doc.dataModel?.agent_hint).toBe('secret')
|
|
113
|
+
expect(doc.dataModel?.title).toBe('shown')
|
|
114
|
+
})
|
|
115
|
+
|
|
91
116
|
it('blocksToA2ui preserves select vs multiselect picker types', () => {
|
|
92
117
|
const doc = blocksToA2ui(
|
|
93
118
|
{
|
|
@@ -147,6 +147,20 @@ function blockToComponent(block: FormBlock): {
|
|
|
147
147
|
return { node: { id, component: 'Text', text: label, variant: 'body' } }
|
|
148
148
|
|
|
149
149
|
case 'text':
|
|
150
|
+
// 隐藏 text:不生成可见控件,仍写入 dataModel 默认值供提交
|
|
151
|
+
if (block.hidden) {
|
|
152
|
+
return name
|
|
153
|
+
? { node: null, dataKey: name, dataValue: defaultValueForBlock(block) }
|
|
154
|
+
: { node: null }
|
|
155
|
+
}
|
|
156
|
+
return withData({
|
|
157
|
+
id,
|
|
158
|
+
component: 'TextField',
|
|
159
|
+
label,
|
|
160
|
+
variant: 'shortText',
|
|
161
|
+
...(block.placeholder ? { placeholder: block.placeholder } : {}),
|
|
162
|
+
value: { path },
|
|
163
|
+
})
|
|
150
164
|
case 'tel':
|
|
151
165
|
case 'url':
|
|
152
166
|
return withData({
|
|
@@ -312,10 +326,11 @@ export function blocksToA2ui(
|
|
|
312
326
|
|
|
313
327
|
for (const block of definition.blocks ?? []) {
|
|
314
328
|
const { node, dataKey, dataValue } = blockToComponent(block)
|
|
329
|
+
// hidden 等无节点块仍可写入 dataModel,保证「不展示仍提交」
|
|
330
|
+
if (dataKey) dataModel[dataKey] = dataValue
|
|
315
331
|
if (!node) continue
|
|
316
332
|
components.push(node)
|
|
317
333
|
childIds.push(node.id)
|
|
318
|
-
if (dataKey) dataModel[dataKey] = dataValue
|
|
319
334
|
}
|
|
320
335
|
|
|
321
336
|
components.unshift({ id: ROOT_ID, component: 'Column', children: childIds })
|
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
* @vitest-environment node
|
|
3
3
|
*/
|
|
4
4
|
import { describe, it, expect } from 'vitest'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
coerceToggleValue,
|
|
7
|
+
normalizeFormDefinition,
|
|
8
|
+
blocksToJsonSchema,
|
|
9
|
+
} from './formSchema'
|
|
6
10
|
import { mapFormDefinitionFromDto } from './resolveSurface'
|
|
7
11
|
|
|
8
12
|
describe('coerceToggleValue', () => {
|
|
@@ -38,27 +42,104 @@ describe('normalizeFormDefinition toggle default', () => {
|
|
|
38
42
|
expect(def.blocks[0]?.defaultValue).toBe(true)
|
|
39
43
|
})
|
|
40
44
|
|
|
41
|
-
it('
|
|
42
|
-
const
|
|
45
|
+
it('regenerates jsonSchema from blocks', () => {
|
|
46
|
+
const def = normalizeFormDefinition({
|
|
47
|
+
title: 'Title',
|
|
48
|
+
description: 'Desc',
|
|
49
|
+
blocks: [
|
|
50
|
+
{
|
|
51
|
+
id: 'b1',
|
|
52
|
+
type: 'text',
|
|
53
|
+
name: 'title',
|
|
54
|
+
label: '标题',
|
|
55
|
+
required: true,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
jsonSchema: { type: 'object', properties: { stale: { type: 'number' } } },
|
|
59
|
+
})
|
|
60
|
+
expect(def.jsonSchema).toEqual(
|
|
61
|
+
blocksToJsonSchema(def.blocks, 'Title', 'Desc'),
|
|
62
|
+
)
|
|
63
|
+
expect(def.jsonSchema).toMatchObject({
|
|
64
|
+
type: 'object',
|
|
65
|
+
properties: { title: { type: 'string' } },
|
|
66
|
+
required: ['title'],
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('normalizeFormDefinition preserves needLLM and editor fields', () => {
|
|
72
|
+
it('keeps needLLM, hidden, condition, jsonSchemaDescription after normalize', () => {
|
|
73
|
+
const def = normalizeFormDefinition({
|
|
74
|
+
title: '',
|
|
75
|
+
description: '',
|
|
76
|
+
blocks: [
|
|
77
|
+
{
|
|
78
|
+
id: 'b1',
|
|
79
|
+
type: 'text',
|
|
80
|
+
name: 'prompt',
|
|
81
|
+
label: 'Prompt',
|
|
82
|
+
needLLM: true,
|
|
83
|
+
hidden: true,
|
|
84
|
+
jsonSchemaDescription: 'for agent',
|
|
85
|
+
condition: { fieldName: 'other', operator: 'eq', value: '1' },
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: 'b0',
|
|
89
|
+
type: 'text',
|
|
90
|
+
name: 'other',
|
|
91
|
+
label: 'Other',
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
jsonSchema: {},
|
|
95
|
+
})
|
|
96
|
+
const block = def.blocks.find((item) => item.name === 'prompt')
|
|
97
|
+
expect(block?.needLLM).toBe(true)
|
|
98
|
+
expect(block?.hidden).toBe(true)
|
|
99
|
+
expect(block?.jsonSchemaDescription).toBe('for agent')
|
|
100
|
+
expect(block?.condition).toEqual({
|
|
101
|
+
fieldName: 'other',
|
|
102
|
+
operator: 'eq',
|
|
103
|
+
value: '1',
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
it('keeps resumeFilter.agentKind', () => {
|
|
43
108
|
const def = normalizeFormDefinition({
|
|
44
109
|
title: '',
|
|
45
110
|
description: '',
|
|
46
|
-
blocks: [
|
|
47
|
-
|
|
111
|
+
blocks: [
|
|
112
|
+
{
|
|
113
|
+
id: 'b1',
|
|
114
|
+
type: 'resume_multiselect',
|
|
115
|
+
name: 'partners',
|
|
116
|
+
resumeFilter: {
|
|
117
|
+
agentKind: 'management',
|
|
118
|
+
names: [],
|
|
119
|
+
agentUniqueIds: [],
|
|
120
|
+
resumeUniqueIds: [],
|
|
121
|
+
pageSize: 20,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
jsonSchema: {},
|
|
48
126
|
})
|
|
49
|
-
expect(def.
|
|
127
|
+
expect(def.blocks[0]?.resumeFilter?.agentKind).toBe('management')
|
|
50
128
|
})
|
|
51
129
|
})
|
|
52
130
|
|
|
53
131
|
describe('mapFormDefinitionFromDto jsonSchema', () => {
|
|
54
|
-
it('
|
|
55
|
-
const schema = { type: 'object' }
|
|
132
|
+
it('regenerates jsonSchema from DTO blocks', () => {
|
|
56
133
|
const def = mapFormDefinitionFromDto({
|
|
57
134
|
title: 'T',
|
|
58
135
|
blocks: [{ id: 'b1', type: 'text', name: 'x', label: 'X' }],
|
|
59
|
-
json_schema:
|
|
136
|
+
json_schema: { type: 'object', properties: { stale: { type: 'number' } } },
|
|
137
|
+
})
|
|
138
|
+
expect(def?.jsonSchema).toMatchObject({
|
|
139
|
+
type: 'object',
|
|
140
|
+
title: 'T',
|
|
141
|
+
properties: { x: { type: 'string' } },
|
|
60
142
|
})
|
|
61
|
-
expect(def?.jsonSchema).toEqual(schema)
|
|
62
143
|
})
|
|
63
144
|
})
|
|
64
145
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
FormBlock,
|
|
3
|
+
FormBlockType,
|
|
4
|
+
FormCaseFilter,
|
|
5
|
+
FormResumeFilter,
|
|
6
|
+
FormJsonSchema,
|
|
7
|
+
FormSchema,
|
|
8
|
+
} from '../types/index'
|
|
9
|
+
import { isInputBlockType, isMediaBlockType, isOptionsBlockType } from '../types/index'
|
|
3
10
|
import { generateId } from './id'
|
|
4
11
|
import {
|
|
5
12
|
filterFileAcceptExtensionsForTypes,
|
|
@@ -32,7 +39,7 @@ function normalizeFileUploadCount(raw: unknown, fallback = 1): number {
|
|
|
32
39
|
return Math.max(0, Math.floor(num))
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
function normalizeMultiselectCount(raw: unknown, fallback = 1): number {
|
|
42
|
+
export function normalizeMultiselectCount(raw: unknown, fallback = 1): number {
|
|
36
43
|
const num = typeof raw === 'number' ? raw : Number(raw)
|
|
37
44
|
if (!Number.isFinite(num)) return fallback
|
|
38
45
|
return Math.max(1, Math.floor(num))
|
|
@@ -42,16 +49,27 @@ function normalizeDefaultValue(
|
|
|
42
49
|
type: FormBlockType,
|
|
43
50
|
raw: FormBlock['defaultValue'],
|
|
44
51
|
): FormBlock['defaultValue'] {
|
|
45
|
-
if (raw === undefined) return undefined
|
|
46
|
-
if (type === 'toggle')
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
if (raw === undefined || raw === null) return undefined
|
|
53
|
+
if (type === 'toggle') {
|
|
54
|
+
return coerceToggleValue(raw)
|
|
55
|
+
}
|
|
56
|
+
if (
|
|
57
|
+
type === 'multiselect' ||
|
|
58
|
+
type === 'checkbox' ||
|
|
59
|
+
type === 'resume_multiselect' ||
|
|
60
|
+
type === 'case_multiselect'
|
|
61
|
+
) {
|
|
62
|
+
if (Array.isArray(raw)) {
|
|
63
|
+
const values = raw.map(String).map((item) => item.trim()).filter(Boolean)
|
|
64
|
+
return values
|
|
65
|
+
}
|
|
66
|
+
if (typeof raw === 'string' && raw.trim()) return [raw.trim()]
|
|
50
67
|
return []
|
|
51
68
|
}
|
|
52
69
|
if (typeof raw === 'boolean') return raw ? 'true' : 'false'
|
|
53
70
|
if (Array.isArray(raw)) return raw.map(String)
|
|
54
|
-
|
|
71
|
+
const trimmed = String(raw).trim()
|
|
72
|
+
return trimmed || undefined
|
|
55
73
|
}
|
|
56
74
|
|
|
57
75
|
/** n8n 等可能把 flowUniqueIds 发成 JSON 数组字符串;解析失败则 []。 */
|
|
@@ -100,8 +118,13 @@ function normalizeResumeFilter(raw: FormResumeFilter | undefined): FormResumeFil
|
|
|
100
118
|
Number.isFinite(pageSizeRaw) && pageSizeRaw >= 1 && pageSizeRaw <= 100
|
|
101
119
|
? Math.floor(pageSizeRaw)
|
|
102
120
|
: 20
|
|
121
|
+
const agentKind =
|
|
122
|
+
typeof raw?.agentKind === 'string' && raw.agentKind.trim()
|
|
123
|
+
? raw.agentKind.trim()
|
|
124
|
+
: undefined
|
|
103
125
|
return {
|
|
104
126
|
names: Array.isArray(raw?.names) ? raw!.names.map(String).filter(Boolean) : [],
|
|
127
|
+
agentKind,
|
|
105
128
|
agentUniqueIds: Array.isArray(raw?.agentUniqueIds)
|
|
106
129
|
? raw!.agentUniqueIds.map(String).filter(Boolean)
|
|
107
130
|
: [],
|
|
@@ -148,12 +171,22 @@ function normalizeBlock(raw: FormBlock): FormBlock {
|
|
|
148
171
|
fileMinCount: type === 'file' ? normalizeFileUploadCount(migrated.fileMinCount, 1) : undefined,
|
|
149
172
|
fileMaxCount: type === 'file' ? normalizeFileUploadCount(migrated.fileMaxCount, 1) : undefined,
|
|
150
173
|
defaultValue: normalizeDefaultValue(type, migrated.defaultValue),
|
|
174
|
+
jsonSchemaDescription: migrated.jsonSchemaDescription?.trim() ?? '',
|
|
175
|
+
hidden: type === 'text' ? migrated.hidden === true : undefined,
|
|
176
|
+
needLLM: migrated.needLLM === true ? true : undefined,
|
|
151
177
|
options: Array.isArray(migrated.options)
|
|
152
178
|
? migrated.options.map((opt, index) => ({
|
|
153
179
|
value: opt.value?.trim() || `option_${index + 1}`,
|
|
154
180
|
label: opt.label?.trim() || `选项 ${index + 1}`,
|
|
155
181
|
}))
|
|
156
182
|
: undefined,
|
|
183
|
+
condition: migrated.condition?.fieldName
|
|
184
|
+
? {
|
|
185
|
+
fieldName: migrated.condition.fieldName.trim(),
|
|
186
|
+
operator: migrated.condition.operator ?? 'eq',
|
|
187
|
+
value: migrated.condition.value,
|
|
188
|
+
}
|
|
189
|
+
: undefined,
|
|
157
190
|
resumeEnableRefresh:
|
|
158
191
|
type === 'resume_multiselect' ? migrated.resumeEnableRefresh !== false : undefined,
|
|
159
192
|
resumeMinCount:
|
|
@@ -181,12 +214,175 @@ function normalizeBlock(raw: FormBlock): FormBlock {
|
|
|
181
214
|
}
|
|
182
215
|
}
|
|
183
216
|
|
|
217
|
+
function getOptionValueSet(block: FormBlock): Set<string> {
|
|
218
|
+
return new Set((block.options ?? []).map((opt) => opt.value))
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function buildMultiselectCountJsonSchema(
|
|
222
|
+
minCount: number | undefined,
|
|
223
|
+
maxCount: number | undefined,
|
|
224
|
+
): Record<string, unknown> {
|
|
225
|
+
const schema: Record<string, unknown> = {
|
|
226
|
+
type: 'array',
|
|
227
|
+
items: { type: 'string' },
|
|
228
|
+
}
|
|
229
|
+
if (minCount !== undefined) {
|
|
230
|
+
schema.minItems = normalizeMultiselectCount(minCount, 1)
|
|
231
|
+
}
|
|
232
|
+
if (maxCount !== undefined) {
|
|
233
|
+
schema.maxItems = normalizeMultiselectCount(maxCount, 1)
|
|
234
|
+
}
|
|
235
|
+
return schema
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function resolveSchemaDefaultValue(block: FormBlock): unknown | undefined {
|
|
239
|
+
const normalized = normalizeDefaultValue(block.type, block.defaultValue)
|
|
240
|
+
if (normalized === undefined) return undefined
|
|
241
|
+
|
|
242
|
+
const optionValues = getOptionValueSet(block)
|
|
243
|
+
|
|
244
|
+
switch (block.type) {
|
|
245
|
+
case 'toggle':
|
|
246
|
+
return normalized as boolean
|
|
247
|
+
case 'number': {
|
|
248
|
+
const num = Number(normalized as string)
|
|
249
|
+
return Number.isNaN(num) ? undefined : num
|
|
250
|
+
}
|
|
251
|
+
case 'multiselect':
|
|
252
|
+
case 'checkbox': {
|
|
253
|
+
const values = (normalized as string[]).filter((item) => optionValues.has(item))
|
|
254
|
+
return values.length > 0 ? values : undefined
|
|
255
|
+
}
|
|
256
|
+
case 'select':
|
|
257
|
+
case 'radio': {
|
|
258
|
+
const value = normalized as string
|
|
259
|
+
return optionValues.has(value) ? value : undefined
|
|
260
|
+
}
|
|
261
|
+
case 'resume_multiselect':
|
|
262
|
+
case 'case_multiselect': {
|
|
263
|
+
const values = Array.isArray(normalized)
|
|
264
|
+
? (normalized as string[]).map(String).filter(Boolean)
|
|
265
|
+
: []
|
|
266
|
+
return values.length > 0 ? values : undefined
|
|
267
|
+
}
|
|
268
|
+
default:
|
|
269
|
+
return normalized
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function blockToJsonSchemaProperty(block: FormBlock): Record<string, unknown> | null {
|
|
274
|
+
const name = block.name?.trim()
|
|
275
|
+
if (!name || !isInputBlockType(block.type)) return null
|
|
276
|
+
|
|
277
|
+
let property: Record<string, unknown>
|
|
278
|
+
|
|
279
|
+
switch (block.type) {
|
|
280
|
+
case 'number':
|
|
281
|
+
property = { type: 'number' }
|
|
282
|
+
break
|
|
283
|
+
case 'toggle':
|
|
284
|
+
property = { type: 'boolean' }
|
|
285
|
+
break
|
|
286
|
+
case 'select':
|
|
287
|
+
case 'radio': {
|
|
288
|
+
property = { type: 'string' }
|
|
289
|
+
const enumValues = (block.options ?? []).map((opt) => opt.value).filter(Boolean)
|
|
290
|
+
if (enumValues.length > 0) property.enum = enumValues
|
|
291
|
+
break
|
|
292
|
+
}
|
|
293
|
+
case 'multiselect':
|
|
294
|
+
case 'checkbox': {
|
|
295
|
+
const enumValues = (block.options ?? []).map((opt) => opt.value).filter(Boolean)
|
|
296
|
+
property = {
|
|
297
|
+
type: 'array',
|
|
298
|
+
items:
|
|
299
|
+
enumValues.length > 0
|
|
300
|
+
? { type: 'string', enum: enumValues }
|
|
301
|
+
: { type: 'string' },
|
|
302
|
+
}
|
|
303
|
+
break
|
|
304
|
+
}
|
|
305
|
+
case 'file': {
|
|
306
|
+
const minCount = normalizeFileUploadCount(block.fileMinCount, 1)
|
|
307
|
+
const maxCount = normalizeFileUploadCount(block.fileMaxCount, 1)
|
|
308
|
+
if (maxCount <= 1) {
|
|
309
|
+
property = { type: 'string' }
|
|
310
|
+
} else {
|
|
311
|
+
property = {
|
|
312
|
+
type: 'array',
|
|
313
|
+
items: { type: 'string' },
|
|
314
|
+
minItems: minCount,
|
|
315
|
+
maxItems: maxCount,
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
break
|
|
319
|
+
}
|
|
320
|
+
case 'resume_multiselect':
|
|
321
|
+
property = buildMultiselectCountJsonSchema(block.resumeMinCount, block.resumeMaxCount)
|
|
322
|
+
break
|
|
323
|
+
case 'case_multiselect':
|
|
324
|
+
property = buildMultiselectCountJsonSchema(block.caseMinCount, block.caseMaxCount)
|
|
325
|
+
break
|
|
326
|
+
case 'text':
|
|
327
|
+
property = { type: 'string' }
|
|
328
|
+
if (block.hidden) property['x-hidden'] = true
|
|
329
|
+
break
|
|
330
|
+
default:
|
|
331
|
+
property = { type: 'string' }
|
|
332
|
+
break
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const comment = block.jsonSchemaDescription?.trim()
|
|
336
|
+
if (comment) property.$comment = comment
|
|
337
|
+
|
|
338
|
+
const defaultValue = resolveSchemaDefaultValue(block)
|
|
339
|
+
if (defaultValue !== undefined) property.default = defaultValue
|
|
340
|
+
|
|
341
|
+
return property
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function blocksToJsonSchema(
|
|
345
|
+
blocks: FormBlock[],
|
|
346
|
+
title?: string,
|
|
347
|
+
description?: string,
|
|
348
|
+
): FormJsonSchema {
|
|
349
|
+
const properties: Record<string, Record<string, unknown>> = {}
|
|
350
|
+
const required: string[] = []
|
|
351
|
+
|
|
352
|
+
for (const block of blocks) {
|
|
353
|
+
const name = block.name?.trim()
|
|
354
|
+
if (!name || !isInputBlockType(block.type)) continue
|
|
355
|
+
|
|
356
|
+
const property = blockToJsonSchemaProperty(block)
|
|
357
|
+
if (!property) continue
|
|
358
|
+
|
|
359
|
+
properties[name] = property
|
|
360
|
+
if (block.required) required.push(name)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const schema: FormJsonSchema = {
|
|
364
|
+
type: 'object',
|
|
365
|
+
properties,
|
|
366
|
+
required,
|
|
367
|
+
additionalProperties: false,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const trimmedTitle = title?.trim()
|
|
371
|
+
const trimmedDescription = description?.trim()
|
|
372
|
+
if (trimmedTitle) schema.title = trimmedTitle
|
|
373
|
+
if (trimmedDescription) schema.description = trimmedDescription
|
|
374
|
+
|
|
375
|
+
return schema
|
|
376
|
+
}
|
|
377
|
+
|
|
184
378
|
export function syncFormDefinition(definition: FormSchema): FormSchema {
|
|
379
|
+
const title = definition.title ?? ''
|
|
380
|
+
const description = definition.description ?? ''
|
|
185
381
|
return {
|
|
186
|
-
title
|
|
187
|
-
description
|
|
382
|
+
title,
|
|
383
|
+
description,
|
|
188
384
|
blocks: definition.blocks,
|
|
189
|
-
jsonSchema: definition.
|
|
385
|
+
jsonSchema: blocksToJsonSchema(definition.blocks, title, description),
|
|
190
386
|
}
|
|
191
387
|
}
|
|
192
388
|
|
|
@@ -199,7 +395,7 @@ export function normalizeFormDefinition(raw: FormSchema | undefined): FormSchema
|
|
|
199
395
|
title: typeof raw.title === 'string' ? raw.title : '',
|
|
200
396
|
description: typeof raw.description === 'string' ? raw.description : '',
|
|
201
397
|
blocks,
|
|
202
|
-
jsonSchema:
|
|
398
|
+
jsonSchema: {},
|
|
203
399
|
})
|
|
204
400
|
}
|
|
205
401
|
|
|
@@ -267,7 +463,3 @@ export function createEmptyBlock(type: FormBlockType): FormBlock {
|
|
|
267
463
|
|
|
268
464
|
return block
|
|
269
465
|
}
|
|
270
|
-
|
|
271
|
-
function isOptionsBlockType(type: FormBlockType): boolean {
|
|
272
|
-
return type === 'select' || type === 'multiselect' || type === 'radio' || type === 'checkbox'
|
|
273
|
-
}
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '../submit/buildAnswerAgentBody'
|
|
14
14
|
import {
|
|
15
15
|
extractExtraBlockValues,
|
|
16
|
-
|
|
16
|
+
getPayloadHiddenBlockValues,
|
|
17
17
|
getPayloadRenderableBlocks,
|
|
18
18
|
intersectPayloadBlocksWithForm,
|
|
19
19
|
parsePayloadBlocksJson,
|
|
@@ -195,18 +195,29 @@ function AskUserFormCardBody({
|
|
|
195
195
|
return []
|
|
196
196
|
}, [intersection, usePayloadFallback, payloadDef])
|
|
197
197
|
|
|
198
|
-
const extraValues = useMemo(
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
const extraValues = useMemo(() => {
|
|
199
|
+
if (usePayloadFallback && payloadDef) {
|
|
200
|
+
// fallback:hidden 不上屏,提交仍带默认值
|
|
201
|
+
return getPayloadHiddenBlockValues(payloadDef)
|
|
202
|
+
}
|
|
203
|
+
const fromExtra = extractExtraBlockValues(intersection?.extraBlocks ?? [])
|
|
204
|
+
// 交集命中但 surface 可能为空(仅 hidden)时,兜底写入默认值
|
|
205
|
+
const fromHiddenMatched = extractExtraBlockValues(
|
|
206
|
+
(intersection?.matchedBlocks ?? []).filter(
|
|
207
|
+
(block) => block.type === 'text' && block.hidden === true,
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
return { ...fromHiddenMatched, ...fromExtra }
|
|
211
|
+
}, [usePayloadFallback, payloadDef, intersection])
|
|
212
|
+
|
|
213
|
+
const fieldNames = useMemo(
|
|
214
|
+
() =>
|
|
215
|
+
renderBlocks
|
|
216
|
+
.filter((block) => block.name?.trim())
|
|
217
|
+
.map((block) => block.name!.trim()),
|
|
218
|
+
[renderBlocks],
|
|
201
219
|
)
|
|
202
220
|
|
|
203
|
-
const fieldNames = useMemo(() => {
|
|
204
|
-
if (usePayloadFallback && payloadDef) return getPayloadInputFieldNames(payloadDef)
|
|
205
|
-
return renderBlocks
|
|
206
|
-
.filter((block) => block.name?.trim())
|
|
207
|
-
.map((block) => block.name!.trim())
|
|
208
|
-
}, [usePayloadFallback, payloadDef, renderBlocks])
|
|
209
|
-
|
|
210
221
|
const surfaceTitle = intersection?.title ?? payloadDef?.title ?? ''
|
|
211
222
|
const surfaceDescription = intersection?.description ?? payloadDef?.description ?? ''
|
|
212
223
|
|
|
@@ -313,12 +324,13 @@ function AskUserFormCardBody({
|
|
|
313
324
|
setSubmitting(true)
|
|
314
325
|
try {
|
|
315
326
|
const cardValues = rendererRef.current?.getValues(fieldNames) ?? {}
|
|
316
|
-
|
|
327
|
+
// extra/hidden 后合并:getValues 对未上屏字段可能为 undefined,不能覆盖默认值
|
|
328
|
+
const answer = { ...cardValues, ...extraValues }
|
|
329
|
+
const validationError = validateFormSubmitValues(renderBlocks, answer)
|
|
317
330
|
if (validationError) {
|
|
318
331
|
onNotifyRef.current?.('error', validationError.message)
|
|
319
332
|
return
|
|
320
333
|
}
|
|
321
|
-
const answer = { ...cardValues, ...extraValues }
|
|
322
334
|
|
|
323
335
|
if (submitMode === 'post') {
|
|
324
336
|
const url = (callbackUrl ?? '').trim()
|