diff --git a/packages/editor/src/utils/type-match-rules.ts b/packages/editor/src/utils/type-match-rules.ts index ac51a6bb..a1a87c2f 100644 --- a/packages/editor/src/utils/type-match-rules.ts +++ b/packages/editor/src/utils/type-match-rules.ts @@ -20,7 +20,7 @@ import type { DataSourceFieldType, DataSourceSchema, Id } from '@tmagic/core'; import { NodeType } from '@tmagic/core'; import { appendValidateSuggestion } from '@tmagic/design'; import type { TypeMatchValidateContext, TypeMatchValidator } from '@tmagic/form'; -import { validateTypeMatch } from '@tmagic/form'; +import { MAX_SUGGESTION_OPTIONS, optionSuggestion, stringifyExampleValue, validateTypeMatch } from '@tmagic/form'; import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, DATA_SOURCE_SET_DATA_METHOD_NAME, @@ -41,35 +41,9 @@ import { numberOptions, } from '@editor/utils/props'; -/** - * 将值格式化为可读的参考示例字符串。 - */ -const stringifyExampleValue = (value: any): string => { - if (typeof value === 'string') return `"${value}"`; - if (value === null || value === undefined) return String(value); - if (typeof value === 'object') { - try { - return JSON.stringify(value); - } catch { - return String(value); - } - } - return String(value); -}; - -// 参考建议中最多展示的可选值个数,超出以「等」省略。 -const MAX_SUGGESTION_OPTIONS = 20; - -/** - * 生成「请使用以下某一个值:xxx;xxx」形式的参考建议;无可选值时返回空字符串(不追加建议)。 - */ -const listSuggestion = (values: any[]): string => { - const list = values.filter((item) => typeof item !== 'undefined' && item !== null && item !== ''); - if (!list.length) return ''; - const shown = list.slice(0, MAX_SUGGESTION_OPTIONS).map(stringifyExampleValue); - const suffix = list.length > MAX_SUGGESTION_OPTIONS ? ' 等' : ''; - return `请使用以下某一个值:${shown.join(';')}${suffix}`; -}; +/** 复用 form 的可选项建议文案;额外过滤 null / 空串,避免业务侧无效枚举污染提示。 */ +const listSuggestion = (values: any[]): string => + optionSuggestion(values.filter((item) => item !== null && item !== '')); /** * 列出当前可用数据源 id 的参考建议。 diff --git a/packages/form/src/index.ts b/packages/form/src/index.ts index f2cd6314..e749d1e7 100644 --- a/packages/form/src/index.ts +++ b/packages/form/src/index.ts @@ -67,8 +67,11 @@ export { clearTypeMatchRules, deleteTypeMatchRule, getTypeMatchRule, + MAX_SUGGESTION_OPTIONS, + optionSuggestion, registerTypeMatchRule, registerTypeMatchRules, + stringifyExampleValue, validateTypeMatch, } from './utils/typeMatch'; diff --git a/packages/form/src/utils/typeMatch.ts b/packages/form/src/utils/typeMatch.ts index 3f5a8222..44313df6 100644 --- a/packages/form/src/utils/typeMatch.ts +++ b/packages/form/src/utils/typeMatch.ts @@ -135,7 +135,7 @@ const TIMESTAMP_VALUE_FORMATS = new Set(['x', 'timestamp']); /** * 将值格式化为可读的参考示例字符串。 */ -const stringifyExampleValue = (value: any): string => { +export const stringifyExampleValue = (value: any): string => { if (typeof value === 'string') return `"${value}"`; if (value === null || value === undefined) return String(value); if (typeof value === 'object') { @@ -148,19 +148,23 @@ const stringifyExampleValue = (value: any): string => { return String(value); }; -// 参考建议中最多展示的可选值个数,超出以「等」省略。 -const MAX_SUGGESTION_OPTIONS = 20; +/** 参考建议中可选值全量展示上限;超出时仅举例 EXAMPLE_SUGGESTION_COUNT 个并标明总数。 */ +export const MAX_SUGGESTION_OPTIONS = 20; +const EXAMPLE_SUGGESTION_COUNT = 5; /** - * 生成「请使用以下某一个值:xxx;xxx」形式的参考建议;无可选值时返回空字符串(不追加建议)。 - * 可选值超过 MAX_SUGGESTION_OPTIONS 个时仅展示前若干个并以「等」省略。 + * 生成可选项参考建议;无可选值时返回空字符串(不追加建议)。 + * 未超过 MAX_SUGGESTION_OPTIONS 时全部列举;超过时仅举例前 EXAMPLE_SUGGESTION_COUNT 个并标明总数,避免 AI 误以为仅有举例值合法。 */ -const optionSuggestion = (optionValues: any[]): string => { +export const optionSuggestion = (optionValues: any[]): string => { const values = optionValues.filter((item) => typeof item !== 'undefined'); if (!values.length) return ''; - const shown = values.slice(0, MAX_SUGGESTION_OPTIONS).map(stringifyExampleValue); - const suffix = values.length > MAX_SUGGESTION_OPTIONS ? ' 等' : ''; - return `请使用以下某一个值:${shown.join(';')}${suffix}`; + const truncated = values.length > MAX_SUGGESTION_OPTIONS; + const shown = values.slice(0, truncated ? EXAMPLE_SUGGESTION_COUNT : values.length).map(stringifyExampleValue); + if (truncated) { + return `请从可选项中选用合法值(共 ${values.length} 个,例如:${shown.join(';')})`; + } + return `请使用以下某一个值:${shown.join(';')}`; }; /** diff --git a/packages/form/tests/unit/utils/typeMatch.spec.ts b/packages/form/tests/unit/utils/typeMatch.spec.ts index ee4e087c..11f0d254 100644 --- a/packages/form/tests/unit/utils/typeMatch.spec.ts +++ b/packages/form/tests/unit/utils/typeMatch.spec.ts @@ -196,14 +196,25 @@ describe('validateTypeMatch', () => { expect(validateTypeMatch(3, mForm, propsOf(config))).toBe('3 不在可选项中\n\n请使用以下某一个值:1;2'); }); - test('可选项超过 20 个时建议仅展示前 20 个并以「等」省略', () => { + test('可选项超过 20 个时建议仅举例前 5 个并标明总数', () => { const values = Array.from({ length: 21 }, (_, i) => i + 1); const config = { type: 'select', options: values.map((v) => ({ text: `${v}`, value: v })), }; expect(validateTypeMatch(99, mForm, propsOf(config))).toBe( - `99 不在可选项中\n\n请使用以下某一个值:${values.slice(0, 20).join(';')} 等`, + `99 不在可选项中\n\n请从可选项中选用合法值(共 21 个,例如:${values.slice(0, 5).join(';')})`, + ); + }); + + test('可选项未超过 20 个时建议全部列举', () => { + const values = Array.from({ length: 20 }, (_, i) => i + 1); + const config = { + type: 'select', + options: values.map((v) => ({ text: `${v}`, value: v })), + }; + expect(validateTypeMatch(99, mForm, propsOf(config))).toBe( + `99 不在可选项中\n\n请使用以下某一个值:${values.join(';')}`, ); });