@tmagic/form 1.8.0-beta.22 → 1.8.0-beta.24

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 CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0-beta.22",
2
+ "version": "1.8.0-beta.24",
3
3
  "name": "@tmagic/form",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -40,21 +40,21 @@
40
40
  "dependencies": {
41
41
  "@element-plus/icons-vue": "^2.3.2",
42
42
  "@popperjs/core": "^2.11.8",
43
- "dayjs": "^1.11.19",
44
- "lodash-es": "^4.17.21",
45
- "sortablejs": "^1.15.6"
43
+ "dayjs": "^1.11.21",
44
+ "lodash-es": "^4.18.1",
45
+ "sortablejs": "^1.15.7"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/lodash-es": "^4.17.4",
48
+ "@types/lodash-es": "^4.17.12",
49
49
  "@types/sortablejs": "^1.15.9",
50
- "@vue/test-utils": "^2.4.6"
50
+ "@vue/test-utils": "^2.4.11"
51
51
  },
52
52
  "peerDependencies": {
53
- "vue": "^3.5.40",
54
53
  "typescript": "^6.0.3",
55
- "@tmagic/utils": "1.8.0-beta.22",
56
- "@tmagic/design": "1.8.0-beta.22",
57
- "@tmagic/form-schema": "1.8.0-beta.22"
54
+ "vue": "^3.5.41",
55
+ "@tmagic/design": "1.8.0-beta.24",
56
+ "@tmagic/utils": "1.8.0-beta.24",
57
+ "@tmagic/form-schema": "1.8.0-beta.24"
58
58
  },
59
59
  "peerDependenciesMeta": {
60
60
  "typescript": {
@@ -27,7 +27,15 @@
27
27
  <template v-else-if="type && display && !showDiff">
28
28
  <TMagicFormItem v-bind="formItemProps" :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }">
29
29
  <template #label>
30
- <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
30
+ <slot
31
+ v-if="shouldRenderLeafField"
32
+ name="label"
33
+ :config="config"
34
+ :type="type"
35
+ :text="text"
36
+ :prop="itemProp"
37
+ :disabled="disabled"
38
+ >
31
39
  <FormLabel
32
40
  :tip="config.tip"
33
41
  :type="type"
@@ -41,8 +49,25 @@
41
49
  </slot>
42
50
  </template>
43
51
 
44
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
52
+ <!-- 静默校验时只保留 FormItem:校验依赖其 prop / rules 与 model 值,与字段组件实例无关 -->
53
+ <template v-if="shouldRenderLeafField">
54
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
55
+ <component
56
+ v-bind="fieldsProps"
57
+ :is="tagName"
58
+ :model="model"
59
+ :last-values="lastValues"
60
+ :is-compare="isCompare"
61
+ @change="onChangeHandler"
62
+ @addDiffCount="onAddDiffCount"
63
+ ></component>
64
+ <template #content>
65
+ <div v-html="tooltip.text"></div>
66
+ </template>
67
+ </TMagicTooltip>
68
+
45
69
  <component
70
+ v-else
46
71
  v-bind="fieldsProps"
47
72
  :is="tagName"
48
73
  :model="model"
@@ -51,24 +76,13 @@
51
76
  @change="onChangeHandler"
52
77
  @addDiffCount="onAddDiffCount"
53
78
  ></component>
54
- <template #content>
55
- <div v-html="tooltip.text"></div>
56
- </template>
57
- </TMagicTooltip>
58
-
59
- <component
60
- v-else
61
- v-bind="fieldsProps"
62
- :is="tagName"
63
- :model="model"
64
- :last-values="lastValues"
65
- :is-compare="isCompare"
66
- @change="onChangeHandler"
67
- @addDiffCount="onAddDiffCount"
68
- ></component>
79
+ </template>
69
80
  </TMagicFormItem>
70
81
 
71
- <TMagicTooltip v-if="config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel" placement="top">
82
+ <TMagicTooltip
83
+ v-if="shouldRenderLeafField && config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel"
84
+ placement="top"
85
+ >
72
86
  <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
73
87
  <template #content>
74
88
  <div v-html="config.tip"></div>
@@ -88,7 +102,15 @@
88
102
  }"
89
103
  >
90
104
  <template #label>
91
- <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
105
+ <slot
106
+ v-if="shouldRenderLeafField"
107
+ name="label"
108
+ :config="config"
109
+ :type="type"
110
+ :text="text"
111
+ :prop="itemProp"
112
+ :disabled="disabled"
113
+ >
92
114
  <FormLabel
93
115
  :tip="config.tip"
94
116
  :type="type"
@@ -98,8 +120,23 @@
98
120
  ></FormLabel>
99
121
  </slot>
100
122
  </template>
101
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
123
+ <template v-if="shouldRenderLeafField">
124
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
125
+ <component
126
+ v-bind="fieldsProps"
127
+ :is="tagName"
128
+ :model="model"
129
+ :last-values="lastValues"
130
+ :is-compare="isCompare"
131
+ @change="onChangeHandler"
132
+ ></component>
133
+ <template #content>
134
+ <div v-html="tooltip.text"></div>
135
+ </template>
136
+ </TMagicTooltip>
137
+
102
138
  <component
139
+ v-else
103
140
  v-bind="fieldsProps"
104
141
  :is="tagName"
105
142
  :model="model"
@@ -107,20 +144,7 @@
107
144
  :is-compare="isCompare"
108
145
  @change="onChangeHandler"
109
146
  ></component>
110
- <template #content>
111
- <div v-html="tooltip.text"></div>
112
- </template>
113
- </TMagicTooltip>
114
-
115
- <component
116
- v-else
117
- v-bind="fieldsProps"
118
- :is="tagName"
119
- :model="model"
120
- :last-values="lastValues"
121
- :is-compare="isCompare"
122
- @change="onChangeHandler"
123
- ></component>
147
+ </template>
124
148
  </TMagicFormItem>
125
149
 
126
150
  <!-- 普通字段:渲染前后两份独立的组件用于对比 -->
@@ -131,7 +155,15 @@
131
155
  :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-before-diff': true }"
132
156
  >
133
157
  <template #label>
134
- <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
158
+ <slot
159
+ v-if="shouldRenderLeafField"
160
+ name="label"
161
+ :config="config"
162
+ :type="type"
163
+ :text="text"
164
+ :prop="itemProp"
165
+ :disabled="disabled"
166
+ >
135
167
  <FormLabel
136
168
  :tip="config.tip"
137
169
  :type="type"
@@ -141,23 +173,28 @@
141
173
  ></FormLabel>
142
174
  </slot>
143
175
  </template>
144
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
145
- <component v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
146
- <template #content>
147
- <div v-html="tooltip.text"></div>
148
- </template>
149
- </TMagicTooltip>
150
-
151
- <component
152
- v-else
153
- v-bind="fieldsProps"
154
- :is="tagName"
155
- :model="lastValues"
156
- @change="onChangeHandler"
157
- ></component>
176
+ <template v-if="shouldRenderLeafField">
177
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
178
+ <component v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
179
+ <template #content>
180
+ <div v-html="tooltip.text"></div>
181
+ </template>
182
+ </TMagicTooltip>
183
+
184
+ <component
185
+ v-else
186
+ v-bind="fieldsProps"
187
+ :is="tagName"
188
+ :model="lastValues"
189
+ @change="onChangeHandler"
190
+ ></component>
191
+ </template>
158
192
  </TMagicFormItem>
159
193
 
160
- <TMagicTooltip v-if="config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel" placement="top">
194
+ <TMagicTooltip
195
+ v-if="shouldRenderLeafField && config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel"
196
+ placement="top"
197
+ >
161
198
  <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
162
199
  <template #content>
163
200
  <div v-html="config.tip"></div>
@@ -171,7 +208,15 @@
171
208
  :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-after-diff': true }"
172
209
  >
173
210
  <template #label>
174
- <slot name="label" :config="config" :type="type" :text="text" :prop="itemProp" :disabled="disabled">
211
+ <slot
212
+ v-if="shouldRenderLeafField"
213
+ name="label"
214
+ :config="config"
215
+ :type="type"
216
+ :text="text"
217
+ :prop="itemProp"
218
+ :disabled="disabled"
219
+ >
175
220
  <FormLabel
176
221
  :tip="config.tip"
177
222
  :type="type"
@@ -181,17 +226,22 @@
181
226
  ></FormLabel>
182
227
  </slot>
183
228
  </template>
184
- <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
185
- <component v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
186
- <template #content>
187
- <div v-html="tooltip.text"></div>
188
- </template>
189
- </TMagicTooltip>
190
-
191
- <component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
229
+ <template v-if="shouldRenderLeafField">
230
+ <TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
231
+ <component v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
232
+ <template #content>
233
+ <div v-html="tooltip.text"></div>
234
+ </template>
235
+ </TMagicTooltip>
236
+
237
+ <component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
238
+ </template>
192
239
  </TMagicFormItem>
193
240
 
194
- <TMagicTooltip v-if="config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel" placement="top">
241
+ <TMagicTooltip
242
+ v-if="shouldRenderLeafField && config.tip && type === 'checkbox' && !(config as CheckboxConfig).useLabel"
243
+ placement="top"
244
+ >
195
245
  <TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
196
246
  <template #content>
197
247
  <div v-html="config.tip"></div>
@@ -260,9 +310,10 @@ import type {
260
310
  FormValue,
261
311
  ToolTipConfigType,
262
312
  } from '../schema';
263
- import { FORM_DIFF_CONFIG_KEY, FORM_TYPE_MATCH_VALID_KEY } from '../schema';
313
+ import { FORM_DIFF_CONFIG_KEY, FORM_SILENT_MODE_KEY, FORM_TYPE_MATCH_VALID_KEY } from '../schema';
264
314
  import { getField } from '../utils/config';
265
315
  import { createObjectProp, display as displayFunction, filterFunction, getRules } from '../utils/form';
316
+ import { getSilentLeafFieldTypes } from '../utils/silentLeafFieldTypes';
266
317
 
267
318
  import FormLabel from './FormLabel.vue';
268
319
 
@@ -407,6 +458,11 @@ const effectiveSelfDiffFieldTypes = computed<Set<string>>(() => {
407
458
 
408
459
  const isSelfDiffField = computed(() => effectiveSelfDiffFieldTypes.value.has(type.value));
409
460
 
461
+ // 静默模式下仅白名单叶子字段跳过渲染;扩展字段通过 registerSilentLeafFieldTypes 注册
462
+ const silentMode = inject(FORM_SILENT_MODE_KEY, false);
463
+
464
+ const shouldRenderLeafField = computed(() => !silentMode || !getSilentLeafFieldTypes().has(type.value));
465
+
410
466
  const disabled = computed(() => props.disabled || filterFunction(mForm, props.config.disabled, props));
411
467
 
412
468
  const text = computed(() => filterFunction(mForm, props.config.text, props));
package/src/index.ts CHANGED
@@ -63,12 +63,21 @@ export {
63
63
  registerField as registerFormField,
64
64
  } from './utils/config';
65
65
 
66
+ export {
67
+ clearSilentLeafFieldTypes,
68
+ getSilentLeafFieldTypes,
69
+ registerSilentLeafFieldTypes,
70
+ } from './utils/silentLeafFieldTypes';
71
+
66
72
  export {
67
73
  clearTypeMatchRules,
68
74
  deleteTypeMatchRule,
69
75
  getTypeMatchRule,
76
+ MAX_SUGGESTION_OPTIONS,
77
+ optionSuggestion,
70
78
  registerTypeMatchRule,
71
79
  registerTypeMatchRules,
80
+ stringifyExampleValue,
72
81
  validateTypeMatch,
73
82
  } from './utils/typeMatch';
74
83
 
package/src/schema.ts CHANGED
@@ -32,11 +32,12 @@ export const FORM_TYPE_MATCH_VALID_KEY: InjectionKey<ComputedRef<boolean>> = Sym
32
32
  /**
33
33
  * 静默模式标记,由 `submitForm` / `validateForm` 以隐藏方式挂载临时表单时 provide(debug 弹层模式不提供)。
34
34
  *
35
- * 重型字段组件(如 vs-code)可 inject 该标记并跳过自身渲染:字段校验由 FormItem 基于 model 值完成,
36
- * 与叶子 UI 组件实例无关,跳过可省去无谓的实例化开销(如 monaco worker / model 的创建)。
35
+ * Container 会基于该标记,并对照 `getSilentLeafFieldTypes()`(默认 `LEAF_FIELD_TYPES`)
36
+ * 跳过叶子字段的渲染,只保留 FormItem:字段校验由 FormItem 基于 model 值完成,与叶子 UI 组件实例无关。
37
+ * 重型字段组件(如 vs-code)也可 inject 该标记跳过自身渲染,省去无谓的实例化开销。
37
38
  *
38
39
  * 注意:仅「挂载无值副作用」(不在 onMounted/watch immediate 中 emit change、不依赖 DOM 计算值)的
39
- * 字段组件可以安全跳过,接入前需逐一审计。
40
+ * 字段组件可以安全跳过,接入前需逐一审计;业务扩展请使用 `registerSilentLeafFieldTypes`。
40
41
  */
41
42
  export const FORM_SILENT_MODE_KEY: InjectionKey<boolean> = Symbol('mFormSilentMode');
42
43
 
@@ -0,0 +1,71 @@
1
+ /*
2
+ * Tencent is pleased to support the open source community by making TMagicEditor available.
3
+ *
4
+ * Copyright (C) 2025 Tencent. All rights reserved.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ import { toLine } from '@tmagic/utils';
20
+
21
+ /**
22
+ * 可在静默校验模式下跳过渲染的内置叶子字段(默认集合)。
23
+ *
24
+ * 这些字段只渲染一个输入控件,内部没有嵌套 FormItem,且不会依赖组件挂载修改 model;
25
+ * 字段值由初始化逻辑处理(包括 defaultValue),因此跳过不影响校验结果。
26
+ *
27
+ * 业务侧可通过 `registerSilentLeafFieldTypes` 追加已审计的安全自定义字段。
28
+ */
29
+ export const LEAF_FIELD_TYPES: ReadonlySet<string> = new Set([
30
+ 'text',
31
+ 'textarea',
32
+ 'number',
33
+ 'time',
34
+ 'daterange',
35
+ 'timerange',
36
+ 'checkbox',
37
+ 'radio-group',
38
+ 'switch',
39
+ 'select',
40
+ 'cascader',
41
+ 'color-picker',
42
+ 'link',
43
+ ]);
44
+
45
+ /** 业务侧追加的静默叶子字段(不含内置默认集合) */
46
+ const extraSilentLeafFieldTypes = new Set<string>();
47
+
48
+ /**
49
+ * 注册可在静默校验中跳过渲染的叶子字段 type(追加到默认 `LEAF_FIELD_TYPES` 之上)。
50
+ *
51
+ * 仅应纳入「无嵌套 FormItem、无挂载值副作用」的字段;复合 / 动态子表单不可加入。
52
+ */
53
+ export const registerSilentLeafFieldTypes = (types: Iterable<string>): void => {
54
+ for (const type of types) {
55
+ if (typeof type !== 'string' || !type) continue;
56
+ extraSilentLeafFieldTypes.add(toLine(type));
57
+ }
58
+ };
59
+
60
+ /** 当前生效的静默叶子字段集合:内置默认 ∪ 已注册扩展 */
61
+ export const getSilentLeafFieldTypes = (): ReadonlySet<string> => {
62
+ if (extraSilentLeafFieldTypes.size === 0) {
63
+ return LEAF_FIELD_TYPES;
64
+ }
65
+ return new Set([...LEAF_FIELD_TYPES, ...extraSilentLeafFieldTypes]);
66
+ };
67
+
68
+ /** 清空业务侧追加的静默叶子字段(不影响内置默认集合;主要用于单测) */
69
+ export const clearSilentLeafFieldTypes = (): void => {
70
+ extraSilentLeafFieldTypes.clear();
71
+ };
@@ -135,7 +135,7 @@ const TIMESTAMP_VALUE_FORMATS = new Set(['x', 'timestamp']);
135
135
  /**
136
136
  * 将值格式化为可读的参考示例字符串。
137
137
  */
138
- const stringifyExampleValue = (value: any): string => {
138
+ export const stringifyExampleValue = (value: any): string => {
139
139
  if (typeof value === 'string') return `"${value}"`;
140
140
  if (value === null || value === undefined) return String(value);
141
141
  if (typeof value === 'object') {
@@ -148,19 +148,23 @@ const stringifyExampleValue = (value: any): string => {
148
148
  return String(value);
149
149
  };
150
150
 
151
- // 参考建议中最多展示的可选值个数,超出以「等」省略。
152
- const MAX_SUGGESTION_OPTIONS = 20;
151
+ /** 参考建议中可选值全量展示上限;超出时仅举例 EXAMPLE_SUGGESTION_COUNT 个并标明总数。 */
152
+ export const MAX_SUGGESTION_OPTIONS = 20;
153
+ const EXAMPLE_SUGGESTION_COUNT = 5;
153
154
 
154
155
  /**
155
- * 生成「请使用以下某一个值:xxx;xxx」形式的参考建议;无可选值时返回空字符串(不追加建议)。
156
- * 可选值超过 MAX_SUGGESTION_OPTIONS 个时仅展示前若干个并以「等」省略。
156
+ * 生成可选项参考建议;无可选值时返回空字符串(不追加建议)。
157
+ * 未超过 MAX_SUGGESTION_OPTIONS 时全部列举;超过时仅举例前 EXAMPLE_SUGGESTION_COUNT 个并标明总数,避免 AI 误以为仅有举例值合法。
157
158
  */
158
- const optionSuggestion = (optionValues: any[]): string => {
159
+ export const optionSuggestion = (optionValues: any[]): string => {
159
160
  const values = optionValues.filter((item) => typeof item !== 'undefined');
160
161
  if (!values.length) return '';
161
- const shown = values.slice(0, MAX_SUGGESTION_OPTIONS).map(stringifyExampleValue);
162
- const suffix = values.length > MAX_SUGGESTION_OPTIONS ? ' 等' : '';
163
- return `请使用以下某一个值:${shown.join(';')}${suffix}`;
162
+ const truncated = values.length > MAX_SUGGESTION_OPTIONS;
163
+ const shown = values.slice(0, truncated ? EXAMPLE_SUGGESTION_COUNT : values.length).map(stringifyExampleValue);
164
+ if (truncated) {
165
+ return `请从可选项中选用合法值(共 ${values.length} 个,例如:${shown.join(';')})`;
166
+ }
167
+ return `请使用以下某一个值:${shown.join(';')}`;
164
168
  };
165
169
 
166
170
  /**