@tmagic/form 1.8.0-beta.1 → 1.8.0-beta.2

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.
@@ -38,6 +38,9 @@
38
38
  //#endregion
39
39
  dayjs = __toESM(dayjs, 1);
40
40
  dayjs_plugin_utc = __toESM(dayjs_plugin_utc, 1);
41
+ //#region packages/form/src/schema.ts
42
+ var FORM_DIFF_CONFIG_KEY = Symbol("mFormDiffConfig");
43
+ //#endregion
41
44
  //#region node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_freeGlobal.js
42
45
  /** Detect free variable `global` from Node.js. */
43
46
  var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
@@ -2889,7 +2892,8 @@
2889
2892
  var _hoisted_5$3 = ["innerHTML"];
2890
2893
  var _hoisted_6$1 = ["innerHTML"];
2891
2894
  var _hoisted_7$1 = ["innerHTML"];
2892
- var _hoisted_8$1 = {
2895
+ var _hoisted_8$1 = ["innerHTML"];
2896
+ var _hoisted_9 = {
2893
2897
  key: 5,
2894
2898
  style: { "text-align": "center" }
2895
2899
  };
@@ -2921,11 +2925,20 @@
2921
2925
  const props = __props;
2922
2926
  const emit = __emit;
2923
2927
  const mForm = (0, vue.inject)("mForm");
2928
+ const diffConfig = (0, vue.inject)(FORM_DIFF_CONFIG_KEY, {});
2924
2929
  const expand = (0, vue.ref)(false);
2925
2930
  const name = (0, vue.computed)(() => props.config.name || "");
2926
2931
  const showDiff = (0, vue.computed)(() => {
2927
2932
  if (!props.isCompare) return false;
2928
- return !isEqual(name.value ? props.model[name.value] : props.model, name.value ? props.lastValues[name.value] : props.lastValues);
2933
+ const curValue = name.value ? props.model[name.value] : props.model;
2934
+ const lastValue = name.value ? props.lastValues[name.value] : props.lastValues;
2935
+ const customShowDiff = diffConfig.showDiff;
2936
+ if (typeof customShowDiff === "function") return Boolean(customShowDiff({
2937
+ curValue,
2938
+ lastValue,
2939
+ config: props.config
2940
+ }));
2941
+ return !isEqual(curValue, lastValue);
2929
2942
  });
2930
2943
  const items = (0, vue.computed)(() => props.config.items);
2931
2944
  const itemProp = (0, vue.computed)(() => {
@@ -2946,6 +2959,35 @@
2946
2959
  if (type.value === "component" && props.config.component) return props.config.component;
2947
2960
  return getField(type.value || "container") || `m-${items.value ? "form" : "fields"}-${type.value}`;
2948
2961
  });
2962
+ /**
2963
+ * 自接管对比的字段类型白名单。
2964
+ *
2965
+ * 这类字段在 `isCompare === true` 且存在差异时,不再由 Container 渲染前后两份独立组件来对比,
2966
+ * 而是只渲染一次组件,将 `model` / `lastValues` / `isCompare` 一并传给字段组件,
2967
+ * 由字段组件内部自行展示前后差异(典型场景:vs-code 字段使用 monaco 自带的 diff 编辑器)。
2968
+ *
2969
+ * 这样做的好处:
2970
+ * 1. 避免重型字段(如 monaco 编辑器)在对比模式下被实例化两次,节省资源;
2971
+ * 2. 提供更专业的对比视觉效果(如 monaco diff 的行级高亮、左右滚动同步等)。
2972
+ *
2973
+ * 注意:像 `event-select` / `code-select-col` 这类内部由列表 / 嵌套子表单组成的复合字段,若按默认逻辑
2974
+ * 渲染前后两份独立组件,会出现两套下拉框 + 两份参数表单(或两套「添加事件」按钮、两份完整面板),
2975
+ * 体验很差。这类字段在内部把 `is-compare`/`lastValues` 透传给子级容器,由子级逐项展示差异,
2976
+ * 因此同样归类为自接管对比字段。
2977
+ */
2978
+ const DEFAULT_SELF_DIFF_FIELD_TYPES = [
2979
+ "vs-code",
2980
+ "event-select",
2981
+ "code-select-col",
2982
+ "code-select"
2983
+ ];
2984
+ const effectiveSelfDiffFieldTypes = (0, vue.computed)(() => {
2985
+ const custom = diffConfig.selfDiffFieldTypes;
2986
+ if (typeof custom === "function") return new Set(custom([...DEFAULT_SELF_DIFF_FIELD_TYPES]));
2987
+ if (Array.isArray(custom)) return new Set([...DEFAULT_SELF_DIFF_FIELD_TYPES, ...custom]);
2988
+ return new Set(DEFAULT_SELF_DIFF_FIELD_TYPES);
2989
+ });
2990
+ const isSelfDiffField = (0, vue.computed)(() => effectiveSelfDiffFieldTypes.value.has(type.value));
2949
2991
  const disabled = (0, vue.computed)(() => props.disabled || filterFunction(mForm, props.config.disabled, props));
2950
2992
  const text = (0, vue.computed)(() => filterFunction(mForm, props.config.text, props));
2951
2993
  const tooltip = (0, vue.computed)(() => {
@@ -3114,7 +3156,13 @@
3114
3156
  "label-width",
3115
3157
  "style"
3116
3158
  ])) : type.value && display$3.value && !showDiff.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicFormItem), (0, vue.mergeProps)(formItemProps.value, { class: { "tmagic-form-hidden": `${itemLabelWidth.value}` === "0" || !text.value } }), {
3117
- label: (0, vue.withCtx)(() => [(0, vue.createVNode)(FormLabel_default, {
3159
+ label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
3160
+ config: __props.config,
3161
+ type: type.value,
3162
+ text: text.value,
3163
+ prop: itemProp.value,
3164
+ disabled: disabled.value
3165
+ }, () => [(0, vue.createVNode)(FormLabel_default, {
3118
3166
  tip: __props.config.tip,
3119
3167
  type: type.value,
3120
3168
  "use-label": __props.config.useLabel,
@@ -3126,7 +3174,7 @@
3126
3174
  "use-label",
3127
3175
  "label-title",
3128
3176
  "text"
3129
- ])]),
3177
+ ])])]),
3130
3178
  default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3131
3179
  key: 0,
3132
3180
  placement: tooltip.value.placement
@@ -3155,7 +3203,7 @@
3155
3203
  "last-values",
3156
3204
  "is-compare"
3157
3205
  ]))]),
3158
- _: 1
3206
+ _: 3
3159
3207
  }, 16, ["class"]), __props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3160
3208
  key: 0,
3161
3209
  placement: "top"
@@ -3171,12 +3219,18 @@
3171
3219
  _: 1
3172
3220
  })) : (0, vue.createCommentVNode)("v-if", true)], 64)) : type.value && display$3.value && showDiff.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 3 }, [
3173
3221
  (0, vue.createCommentVNode)(" 对比 "),
3174
- (0, vue.createCommentVNode)(" 上次内容 "),
3175
- (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicFormItem), (0, vue.mergeProps)(formItemProps.value, { class: {
3222
+ (0, vue.createCommentVNode)(" 自接管对比的字段类型(如 vs-code):只渲染一次组件,由字段内部用 diff 编辑器/视图自行展示前后差异 "),
3223
+ isSelfDiffField.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicFormItem), (0, vue.mergeProps)({ key: 0 }, formItemProps.value, { class: {
3176
3224
  "tmagic-form-hidden": `${itemLabelWidth.value}` === "0" || !text.value,
3177
- "show-diff": true
3225
+ "self-diff": true
3178
3226
  } }), {
3179
- label: (0, vue.withCtx)(() => [(0, vue.createVNode)(FormLabel_default, {
3227
+ label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
3228
+ config: __props.config,
3229
+ type: type.value,
3230
+ text: text.value,
3231
+ prop: itemProp.value,
3232
+ disabled: disabled.value
3233
+ }, () => [(0, vue.createVNode)(FormLabel_default, {
3180
3234
  tip: __props.config.tip,
3181
3235
  type: type.value,
3182
3236
  "use-label": __props.config.useLabel,
@@ -3188,88 +3242,148 @@
3188
3242
  "use-label",
3189
3243
  "label-title",
3190
3244
  "text"
3191
- ])]),
3245
+ ])])]),
3192
3246
  default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3193
3247
  key: 0,
3194
3248
  placement: tooltip.value.placement
3195
3249
  }, {
3196
3250
  content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: tooltip.value.text }, null, 8, _hoisted_4$4)]),
3197
3251
  default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)(fieldsProps.value, {
3198
- model: __props.lastValues,
3252
+ model: __props.model,
3253
+ "last-values": __props.lastValues,
3254
+ "is-compare": __props.isCompare,
3199
3255
  onChange: onChangeHandler
3200
- }), null, 16, ["model"]))]),
3256
+ }), null, 16, [
3257
+ "model",
3258
+ "last-values",
3259
+ "is-compare"
3260
+ ]))]),
3201
3261
  _: 1
3202
3262
  }, 8, ["placement"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)({ key: 1 }, fieldsProps.value, {
3203
- model: __props.lastValues,
3263
+ model: __props.model,
3264
+ "last-values": __props.lastValues,
3265
+ "is-compare": __props.isCompare,
3204
3266
  onChange: onChangeHandler
3205
- }), null, 16, ["model"]))]),
3206
- _: 1
3207
- }, 16, ["class"]),
3208
- __props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3209
- key: 0,
3210
- placement: "top"
3211
- }, {
3212
- content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: __props.config.tip }, null, 8, _hoisted_5$3)]),
3213
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicIcon), { style: {
3214
- "line-height": "40px",
3215
- "margin-left": "5px"
3216
- } }, {
3217
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.WarningFilled))]),
3218
- _: 1
3219
- })]),
3220
- _: 1
3221
- })) : (0, vue.createCommentVNode)("v-if", true),
3222
- (0, vue.createCommentVNode)(" 当前内容 "),
3223
- (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicFormItem), (0, vue.mergeProps)(formItemProps.value, {
3224
- style: __props.config.tip ? "flex: 1" : "",
3225
- class: {
3267
+ }), null, 16, [
3268
+ "model",
3269
+ "last-values",
3270
+ "is-compare"
3271
+ ]))]),
3272
+ _: 3
3273
+ }, 16, ["class"])) : ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [
3274
+ (0, vue.createCommentVNode)(" 普通字段:渲染前后两份独立的组件用于对比 "),
3275
+ (0, vue.createCommentVNode)(" 上次内容 "),
3276
+ (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicFormItem), (0, vue.mergeProps)(formItemProps.value, { class: {
3226
3277
  "tmagic-form-hidden": `${itemLabelWidth.value}` === "0" || !text.value,
3227
- "show-diff": true
3228
- }
3229
- }), {
3230
- label: (0, vue.withCtx)(() => [(0, vue.createVNode)(FormLabel_default, {
3231
- tip: __props.config.tip,
3232
- type: type.value,
3233
- "use-label": __props.config.useLabel,
3234
- "label-title": __props.config.labelTitle,
3235
- text: text.value
3236
- }, null, 8, [
3237
- "tip",
3238
- "type",
3239
- "use-label",
3240
- "label-title",
3241
- "text"
3242
- ])]),
3243
- default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3278
+ "show-before-diff": true
3279
+ } }), {
3280
+ label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
3281
+ config: __props.config,
3282
+ type: type.value,
3283
+ text: text.value,
3284
+ prop: itemProp.value,
3285
+ disabled: disabled.value
3286
+ }, () => [(0, vue.createVNode)(FormLabel_default, {
3287
+ tip: __props.config.tip,
3288
+ type: type.value,
3289
+ "use-label": __props.config.useLabel,
3290
+ "label-title": __props.config.labelTitle,
3291
+ text: text.value
3292
+ }, null, 8, [
3293
+ "tip",
3294
+ "type",
3295
+ "use-label",
3296
+ "label-title",
3297
+ "text"
3298
+ ])])]),
3299
+ default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3300
+ key: 0,
3301
+ placement: tooltip.value.placement
3302
+ }, {
3303
+ content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: tooltip.value.text }, null, 8, _hoisted_5$3)]),
3304
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)(fieldsProps.value, {
3305
+ model: __props.lastValues,
3306
+ onChange: onChangeHandler
3307
+ }), null, 16, ["model"]))]),
3308
+ _: 1
3309
+ }, 8, ["placement"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)({ key: 1 }, fieldsProps.value, {
3310
+ model: __props.lastValues,
3311
+ onChange: onChangeHandler
3312
+ }), null, 16, ["model"]))]),
3313
+ _: 3
3314
+ }, 16, ["class"]),
3315
+ __props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3244
3316
  key: 0,
3245
- placement: tooltip.value.placement
3317
+ placement: "top"
3246
3318
  }, {
3247
- content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: tooltip.value.text }, null, 8, _hoisted_6$1)]),
3248
- default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)(fieldsProps.value, {
3319
+ content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: __props.config.tip }, null, 8, _hoisted_6$1)]),
3320
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicIcon), { style: {
3321
+ "line-height": "40px",
3322
+ "margin-left": "5px"
3323
+ } }, {
3324
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.WarningFilled))]),
3325
+ _: 1
3326
+ })]),
3327
+ _: 1
3328
+ })) : (0, vue.createCommentVNode)("v-if", true),
3329
+ (0, vue.createCommentVNode)(" 当前内容 "),
3330
+ (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicFormItem), (0, vue.mergeProps)(formItemProps.value, {
3331
+ style: __props.config.tip ? "flex: 1" : "",
3332
+ class: {
3333
+ "tmagic-form-hidden": `${itemLabelWidth.value}` === "0" || !text.value,
3334
+ "show-after-diff": true
3335
+ }
3336
+ }), {
3337
+ label: (0, vue.withCtx)(() => [(0, vue.renderSlot)(_ctx.$slots, "label", {
3338
+ config: __props.config,
3339
+ type: type.value,
3340
+ text: text.value,
3341
+ prop: itemProp.value,
3342
+ disabled: disabled.value
3343
+ }, () => [(0, vue.createVNode)(FormLabel_default, {
3344
+ tip: __props.config.tip,
3345
+ type: type.value,
3346
+ "use-label": __props.config.useLabel,
3347
+ "label-title": __props.config.labelTitle,
3348
+ text: text.value
3349
+ }, null, 8, [
3350
+ "tip",
3351
+ "type",
3352
+ "use-label",
3353
+ "label-title",
3354
+ "text"
3355
+ ])])]),
3356
+ default: (0, vue.withCtx)(() => [tooltip.value.text ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3357
+ key: 0,
3358
+ placement: tooltip.value.placement
3359
+ }, {
3360
+ content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: tooltip.value.text }, null, 8, _hoisted_7$1)]),
3361
+ default: (0, vue.withCtx)(() => [((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)(fieldsProps.value, {
3362
+ model: __props.model,
3363
+ onChange: onChangeHandler
3364
+ }), null, 16, ["model"]))]),
3365
+ _: 1
3366
+ }, 8, ["placement"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)({ key: 1 }, fieldsProps.value, {
3249
3367
  model: __props.model,
3250
3368
  onChange: onChangeHandler
3251
3369
  }), null, 16, ["model"]))]),
3370
+ _: 3
3371
+ }, 16, ["style", "class"]),
3372
+ __props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3373
+ key: 1,
3374
+ placement: "top"
3375
+ }, {
3376
+ content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: __props.config.tip }, null, 8, _hoisted_8$1)]),
3377
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicIcon), { style: {
3378
+ "line-height": "40px",
3379
+ "margin-left": "5px"
3380
+ } }, {
3381
+ default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.WarningFilled))]),
3382
+ _: 1
3383
+ })]),
3252
3384
  _: 1
3253
- }, 8, ["placement"])) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)(tagName.value), (0, vue.mergeProps)({ key: 1 }, fieldsProps.value, {
3254
- model: __props.model,
3255
- onChange: onChangeHandler
3256
- }), null, 16, ["model"]))]),
3257
- _: 1
3258
- }, 16, ["style", "class"]),
3259
- __props.config.tip && type.value === "checkbox" && !__props.config.useLabel ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicTooltip), {
3260
- key: 1,
3261
- placement: "top"
3262
- }, {
3263
- content: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", { innerHTML: __props.config.tip }, null, 8, _hoisted_7$1)]),
3264
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicIcon), { style: {
3265
- "line-height": "40px",
3266
- "margin-left": "5px"
3267
- } }, {
3268
- default: (0, vue.withCtx)(() => [(0, vue.createVNode)((0, vue.unref)(_element_plus_icons_vue.WarningFilled))]),
3269
- _: 1
3270
- })]),
3271
- _: 1
3272
- })) : (0, vue.createCommentVNode)("v-if", true)
3385
+ })) : (0, vue.createCommentVNode)("v-if", true)
3386
+ ], 64))
3273
3387
  ], 64)) : items.value && display$3.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 4 }, [(isValidName() ? __props.model[name.value] : __props.model) ? ((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, { key: 0 }, (0, vue.renderList)(items.value, (item) => {
3274
3388
  return (0, vue.openBlock)(), (0, vue.createBlock)(_component_Container, {
3275
3389
  key: key(item),
@@ -3285,7 +3399,11 @@
3285
3399
  prop: itemProp.value,
3286
3400
  onChange: onChangeHandler,
3287
3401
  onAddDiffCount
3288
- }, null, 8, [
3402
+ }, (0, vue.createSlots)({ _: 2 }, [_ctx.$slots.label ? {
3403
+ name: "label",
3404
+ fn: (0, vue.withCtx)((labelProps) => [(0, vue.renderSlot)(_ctx.$slots, "label", (0, vue.mergeProps)({ ref_for: true }, labelProps))]),
3405
+ key: "0"
3406
+ } : void 0]), 1032, [
3289
3407
  "model",
3290
3408
  "last-values",
3291
3409
  "is-compare",
@@ -3297,7 +3415,7 @@
3297
3415
  "label-width",
3298
3416
  "prop"
3299
3417
  ]);
3300
- }), 128)) : (0, vue.createCommentVNode)("v-if", true)], 64)) : (0, vue.createCommentVNode)("v-if", true), __props.config.expand && type.value !== "fieldset" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_8$1, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
3418
+ }), 128)) : (0, vue.createCommentVNode)("v-if", true)], 64)) : (0, vue.createCommentVNode)("v-if", true), __props.config.expand && type.value !== "fieldset" ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_9, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
3301
3419
  type: "primary",
3302
3420
  size: "small",
3303
3421
  disabled: false,
@@ -3340,7 +3458,9 @@
3340
3458
  keyProp: { default: "__key" },
3341
3459
  popperClass: {},
3342
3460
  preventSubmitDefault: { type: Boolean },
3343
- extendState: {}
3461
+ extendState: {},
3462
+ showDiff: {},
3463
+ selfDiffFieldTypes: {}
3344
3464
  },
3345
3465
  emits: [
3346
3466
  "change",
@@ -3456,6 +3576,14 @@
3456
3576
  }
3457
3577
  });
3458
3578
  (0, vue.provide)("mForm", formState);
3579
+ (0, vue.provide)(FORM_DIFF_CONFIG_KEY, {
3580
+ get showDiff() {
3581
+ return props.showDiff;
3582
+ },
3583
+ get selfDiffFieldTypes() {
3584
+ return props.selfDiffFieldTypes;
3585
+ }
3586
+ });
3459
3587
  const changeRecords = (0, vue.shallowRef)([]);
3460
3588
  (0, vue.watch)([() => props.config, () => props.initValues], ([config], [preConfig]) => {
3461
3589
  changeRecords.value = [];
@@ -3572,7 +3700,11 @@
3572
3700
  "step-active": __props.stepActive,
3573
3701
  size: __props.size,
3574
3702
  onChange: changeHandler
3575
- }, null, 8, [
3703
+ }, (0, vue.createSlots)({ _: 2 }, [_ctx.$slots.label ? {
3704
+ name: "label",
3705
+ fn: (0, vue.withCtx)((labelProps) => [(0, vue.renderSlot)(_ctx.$slots, "label", (0, vue.mergeProps)({ ref_for: true }, labelProps))]),
3706
+ key: "0"
3707
+ } : void 0]), 1032, [
3576
3708
  "disabled",
3577
3709
  "config",
3578
3710
  "model",
@@ -3583,7 +3715,7 @@
3583
3715
  "size"
3584
3716
  ]);
3585
3717
  }), 128)) : (0, vue.createCommentVNode)("v-if", true)]),
3586
- _: 1
3718
+ _: 3
3587
3719
  }, 8, [
3588
3720
  "model",
3589
3721
  "label-width",
@@ -4723,6 +4855,9 @@
4723
4855
  prop: props.prop
4724
4856
  });
4725
4857
  });
4858
+ (0, vue.watch)([() => props.model, () => props.lastValues], () => {
4859
+ diffCount.value = {};
4860
+ });
4726
4861
  const tabItems = (tab) => props.config.dynamic ? props.config.items : tab.items;
4727
4862
  const tabClickHandler = (tab) => {
4728
4863
  if (typeof tab === "object") tabClick(mForm, tab, props);
@@ -4797,7 +4932,7 @@
4797
4932
  default: (0, vue.withCtx)(() => [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(tabs.value, (tab, tabIndex) => {
4798
4933
  return (0, vue.openBlock)(), (0, vue.createBlock)((0, vue.resolveDynamicComponent)((0, vue.unref)(tabPaneComponent)?.component || "el-tab-pane"), (0, vue.mergeProps)({ key: tab[(0, vue.unref)(mForm)?.keyProp || "__key"] ?? tabIndex }, { ref_for: true }, (0, vue.unref)(tabPaneComponent)?.props({
4799
4934
  name: filter(tab.status) || tabIndex.toString(),
4800
- lazy: tab.lazy || false
4935
+ lazy: __props.isCompare ? false : tab.lazy || false
4801
4936
  }) || {}), {
4802
4937
  label: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("span", null, [(0, vue.createTextVNode)((0, vue.toDisplayString)(filter(tab.title)), 1), (0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicBadge), {
4803
4938
  hidden: !diffCount.value[Number(tabIndex)],
@@ -4938,16 +5073,17 @@
4938
5073
  }), (0, vue.createElementVNode)("span", { innerHTML: title.value }, null, 8, _hoisted_1$9)]),
4939
5074
  _: 1
4940
5075
  }, 8, ["disabled"]),
4941
- (0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
5076
+ !__props.isCompare ? (0, vue.withDirectives)(((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
5077
+ key: 0,
4942
5078
  type: "danger",
4943
5079
  size: "small",
4944
5080
  link: "",
4945
5081
  icon: (0, vue.unref)(_element_plus_icons_vue.Delete),
4946
5082
  disabled: __props.disabled,
4947
5083
  onClick: removeHandler
4948
- }, null, 8, ["icon", "disabled"]), [[vue.vShow, showDelete.value]]),
4949
- copyable.value ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
4950
- key: 0,
5084
+ }, null, 8, ["icon", "disabled"])), [[vue.vShow, showDelete.value]]) : (0, vue.createCommentVNode)("v-if", true),
5085
+ copyable.value && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
5086
+ key: 1,
4951
5087
  link: "",
4952
5088
  size: "small",
4953
5089
  type: "primary",
@@ -4958,7 +5094,7 @@
4958
5094
  default: (0, vue.withCtx)(() => [..._cache[6] || (_cache[6] = [(0, vue.createTextVNode)("复制", -1)])]),
4959
5095
  _: 1
4960
5096
  }, 8, ["icon", "disabled"])) : (0, vue.createCommentVNode)("v-if", true),
4961
- movable.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 1 }, [(0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
5097
+ movable.value && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createElementBlock)(vue.Fragment, { key: 2 }, [(0, vue.withDirectives)((0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicButton), {
4962
5098
  link: "",
4963
5099
  size: "small",
4964
5100
  disabled: __props.disabled,
@@ -4977,8 +5113,8 @@
4977
5113
  default: (0, vue.withCtx)(() => [..._cache[8] || (_cache[8] = [(0, vue.createTextVNode)("下移", -1)])]),
4978
5114
  _: 1
4979
5115
  }, 8, ["disabled", "icon"]), [[vue.vShow, __props.index !== length.value - 1]])], 64)) : (0, vue.createCommentVNode)("v-if", true),
4980
- __props.config.moveSpecifyLocation ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicPopover), {
4981
- key: 2,
5116
+ __props.config.moveSpecifyLocation && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicPopover), {
5117
+ key: 3,
4982
5118
  trigger: "click",
4983
5119
  placement: "top",
4984
5120
  width: "200",
@@ -5024,7 +5160,7 @@
5024
5160
  _: 1
5025
5161
  }, 8, ["visible"])) : (0, vue.createCommentVNode)("v-if", true),
5026
5162
  itemExtra.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("span", {
5027
- key: 3,
5163
+ key: 4,
5028
5164
  innerHTML: itemExtra.value,
5029
5165
  class: "m-form-tip"
5030
5166
  }, null, 8, _hoisted_3$3)) : (0, vue.createCommentVNode)("v-if", true)
@@ -5064,7 +5200,10 @@
5064
5200
  key: 1,
5065
5201
  class: "el-table__empty-block"
5066
5202
  };
5067
- var _hoisted_4$1 = { class: "m-fields-group-list-footer" };
5203
+ var _hoisted_4$1 = {
5204
+ key: 3,
5205
+ class: "m-fields-group-list-footer"
5206
+ };
5068
5207
  var _hoisted_5 = { style: {
5069
5208
  "display": "flex",
5070
5209
  "justify-content": "flex-end",
@@ -5149,7 +5288,7 @@
5149
5288
  "group-model"
5150
5289
  ]);
5151
5290
  }), 128)),
5152
- (0, vue.createElementVNode)("div", _hoisted_4$1, [(0, vue.renderSlot)(_ctx.$slots, "toggle-button"), (0, vue.createElementVNode)("div", _hoisted_5, [(0, vue.renderSlot)(_ctx.$slots, "add-button")])])
5291
+ !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4$1, [(0, vue.renderSlot)(_ctx.$slots, "toggle-button"), (0, vue.createElementVNode)("div", _hoisted_5, [(0, vue.renderSlot)(_ctx.$slots, "add-button")])])) : (0, vue.createCommentVNode)("v-if", true)
5153
5292
  ]);
5154
5293
  };
5155
5294
  }
@@ -5751,7 +5890,7 @@
5751
5890
  default: (0, vue.withCtx)(() => [(0, vue.createTextVNode)((0, vue.toDisplayString)((0, vue.unref)(isFullscreen) ? "退出全屏" : "全屏编辑"), 1)]),
5752
5891
  _: 1
5753
5892
  }, 8, ["icon", "onClick"])) : (0, vue.createCommentVNode)("v-if", true),
5754
- (0, vue.unref)(importable) ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicUpload), {
5893
+ (0, vue.unref)(importable) && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicUpload), {
5755
5894
  key: 2,
5756
5895
  style: { "display": "inline-block" },
5757
5896
  ref: "excelBtn",
@@ -5771,7 +5910,7 @@
5771
5910
  }, 8, ["disabled"])]),
5772
5911
  _: 1
5773
5912
  }, 8, ["disabled", "on-change"])) : (0, vue.createCommentVNode)("v-if", true),
5774
- (0, vue.unref)(importable) ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
5913
+ (0, vue.unref)(importable) && !__props.isCompare ? ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_tmagic_design.TMagicButton), {
5775
5914
  key: 3,
5776
5915
  size: "small",
5777
5916
  type: "warning",
@@ -5782,7 +5921,7 @@
5782
5921
  default: (0, vue.withCtx)(() => [..._cache[1] || (_cache[1] = [(0, vue.createTextVNode)("清空", -1)])]),
5783
5922
  _: 1
5784
5923
  }, 8, ["disabled", "onClick"])) : (0, vue.createCommentVNode)("v-if", true)
5785
- ]), (0, vue.renderSlot)(_ctx.$slots, "add-button")]),
5924
+ ]), !__props.isCompare ? (0, vue.renderSlot)(_ctx.$slots, "add-button", { key: 0 }) : (0, vue.createCommentVNode)("v-if", true)]),
5786
5925
  __props.config.pagination ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_4, [(0, vue.createVNode)((0, vue.unref)(_tmagic_design.TMagicPagination), {
5787
5926
  layout: "total, sizes, prev, pager, next, jumper",
5788
5927
  "hide-on-single-page": __props.model[modelName.value].length < (0, vue.unref)(pageSize),
@@ -7861,6 +8000,7 @@
7861
8000
  } };
7862
8001
  var createForm = (config) => config;
7863
8002
  //#endregion
8003
+ exports.FORM_DIFF_CONFIG_KEY = FORM_DIFF_CONFIG_KEY;
7864
8004
  exports.MCascader = Cascader_default;
7865
8005
  exports.MCheckbox = Checkbox_default;
7866
8006
  exports.MCheckboxGroup = CheckboxGroup_default;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0-beta.1",
2
+ "version": "1.8.0-beta.2",
3
3
  "name": "@tmagic/form",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -52,9 +52,9 @@
52
52
  "peerDependencies": {
53
53
  "vue": "^3.5.34",
54
54
  "typescript": "^6.0.3",
55
- "@tmagic/design": "1.8.0-beta.1",
56
- "@tmagic/form-schema": "1.8.0-beta.1",
57
- "@tmagic/utils": "1.8.0-beta.1"
55
+ "@tmagic/design": "1.8.0-beta.2",
56
+ "@tmagic/form-schema": "1.8.0-beta.2",
57
+ "@tmagic/utils": "1.8.0-beta.2"
58
58
  },
59
59
  "peerDependenciesMeta": {
60
60
  "typescript": {
package/src/Form.vue CHANGED
@@ -22,7 +22,11 @@
22
22
  :step-active="stepActive"
23
23
  :size="size"
24
24
  @change="changeHandler"
25
- ></Container>
25
+ >
26
+ <template v-if="$slots.label" #label="labelProps">
27
+ <slot name="label" v-bind="labelProps"></slot>
28
+ </template>
29
+ </Container>
26
30
  </template>
27
31
  </TMagicForm>
28
32
  </template>
@@ -37,12 +41,23 @@ import { setValueByKeyPath } from '@tmagic/utils';
37
41
  import Container from './containers/Container.vue';
38
42
  import { getConfig } from './utils/config';
39
43
  import { initValue } from './utils/form';
40
- import type { ChangeRecord, ContainerChangeEventData, FormConfig, FormState, FormValue, ValidateError } from './schema';
44
+ import type {
45
+ ChangeRecord,
46
+ ContainerChangeEventData,
47
+ FormConfig,
48
+ FormSlots,
49
+ FormState,
50
+ FormValue,
51
+ ValidateError,
52
+ } from './schema';
53
+ import { FORM_DIFF_CONFIG_KEY } from './schema';
41
54
 
42
55
  defineOptions({
43
56
  name: 'MForm',
44
57
  });
45
58
 
59
+ defineSlots<FormSlots>();
60
+
46
61
  const props = withDefaults(
47
62
  defineProps<{
48
63
  /** 表单配置 */
@@ -65,6 +80,33 @@ const props = withDefaults(
65
80
  popperClass?: string;
66
81
  preventSubmitDefault?: boolean;
67
82
  extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
83
+ /**
84
+ * 自定义"是否展示对比内容"的判断函数(仅在 `isCompare === true` 时生效)。
85
+ *
86
+ * - 不传:使用默认逻辑 `!isEqual(curValue, lastValue)`;
87
+ * - 传函数:完全以函数返回值为准,返回 `true` 才展示前后两份对比内容。
88
+ *
89
+ * 通过 provide 下发给所有层级的 Container(含嵌套在容器组件内部的 Container),
90
+ * 调用方只需在 MForm 这一层传一次即可对整棵表单生效。
91
+ *
92
+ * 典型场景:某些字段语义上相等但结构不同(例如 `code-select` 字段中 `''` 与
93
+ * `{ hookType: 'code', hookData: [] }` 应视为相等),调用方在此处显式声明,
94
+ * 避免被 lodash `isEqual` 误判为差异。
95
+ */
96
+ showDiff?: (_data: { curValue: any; lastValue: any; config: any }) => boolean;
97
+ /**
98
+ * 自定义「自接管对比」的字段类型(仅在对比模式下生效)。
99
+ *
100
+ * 自接管对比的字段不会渲染前后两份独立组件,而是只渲染一次并由字段组件内部展示前后差异
101
+ * (如 vs-code 使用 monaco diff 编辑器;event-select / code-select-col 等复合字段逐项展示差异)。
102
+ *
103
+ * 支持两种形式:
104
+ * - 传数组:在内置类型基础上「追加」这些类型;
105
+ * - 传函数:入参为内置类型数组,返回值作为「最终」完整列表(可完全替换内置项)。
106
+ *
107
+ * 通过 provide 下发,对整棵表单的所有层级 Container 生效,只需在 MForm 这一层传一次。
108
+ */
109
+ selfDiffFieldTypes?: string[] | ((_defaultTypes: string[]) => string[]);
68
110
  }>(),
69
111
  {
70
112
  config: () => [],
@@ -201,6 +243,17 @@ watchEffect(async (onCleanup) => {
201
243
 
202
244
  provide('mForm', formState);
203
245
 
246
+ // 对比相关配置单独通过 provide 下发,所有层级的 Container 通过 inject 获取,无需逐层透传 prop。
247
+ // 用 getter 对象保证读取时回到最新的 props 值,维持响应式。
248
+ provide(FORM_DIFF_CONFIG_KEY, {
249
+ get showDiff() {
250
+ return props.showDiff;
251
+ },
252
+ get selfDiffFieldTypes() {
253
+ return props.selfDiffFieldTypes;
254
+ },
255
+ });
256
+
204
257
  const changeRecords = shallowRef<ChangeRecord[]>([]);
205
258
 
206
259
  watch(