@tmagic/editor 1.8.0-beta.24 → 1.8.0-beta.26

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.
Files changed (113) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +10 -16
  2. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +4 -4
  3. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +5 -8
  4. package/dist/es/components/ViewForm.vue_vue_type_script_setup_true_lang.js +3 -5
  5. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +3 -11
  6. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +29 -124
  7. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +5 -6
  8. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +3 -2
  9. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  10. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +7 -6
  11. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +6 -10
  12. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +5 -7
  13. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +1 -4
  14. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +4 -6
  15. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +9 -137
  17. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +51 -326
  18. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +2 -4
  19. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +3 -34
  20. package/dist/es/fields/StyleSetter/configs.js +663 -0
  21. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +5 -108
  22. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +3 -9
  23. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +6 -103
  24. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +5 -265
  25. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +5 -85
  26. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +3 -22
  27. package/dist/es/fields/configs/codeSelect.js +98 -0
  28. package/dist/es/fields/configs/displayConds.js +158 -0
  29. package/dist/es/fields/configs/eventSelect.js +225 -0
  30. package/dist/es/fields/configs/stickyAddButton.js +20 -0
  31. package/dist/es/fields/headless-validation.js +139 -0
  32. package/dist/es/headless.js +2 -0
  33. package/dist/es/hooks/use-compare-form.js +6 -23
  34. package/dist/es/hooks/use-form-context.js +19 -0
  35. package/dist/es/index.js +3 -2
  36. package/dist/es/initService.js +17 -1
  37. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +1 -7
  38. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +1 -11
  39. package/dist/es/layouts/history-list/useHistoryRevert.js +3 -11
  40. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +10 -23
  41. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +3 -13
  42. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  43. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
  44. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  45. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +1 -1
  46. package/dist/es/plugin.js +33 -32
  47. package/dist/es/style.css +120 -60
  48. package/dist/es/utils/data-source/index.js +2 -1
  49. package/dist/es/utils/props.js +1 -4
  50. package/dist/es/utils/type-match-rules.js +23 -22
  51. package/dist/style.css +120 -60
  52. package/dist/themes/magic-admin.css +132 -67
  53. package/dist/tmagic-editor-headless.umd.cjs +10867 -0
  54. package/dist/tmagic-editor.umd.cjs +2151 -2034
  55. package/package.json +12 -7
  56. package/src/Editor.vue +12 -16
  57. package/src/components/CodeParams.vue +10 -4
  58. package/src/components/CompareForm.vue +4 -5
  59. package/src/components/ViewForm.vue +2 -3
  60. package/src/editorProps.ts +1 -2
  61. package/src/fields/Code.vue +2 -10
  62. package/src/fields/CodeSelect.vue +20 -116
  63. package/src/fields/CodeSelectCol.vue +3 -5
  64. package/src/fields/CondOpSelect.vue +3 -2
  65. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +1 -1
  66. package/src/fields/DataSourceFieldSelect/Index.vue +7 -6
  67. package/src/fields/DataSourceFields.vue +0 -7
  68. package/src/fields/DataSourceMethodSelect.vue +2 -5
  69. package/src/fields/DataSourceMethods.vue +1 -6
  70. package/src/fields/DataSourceMocks.vue +1 -10
  71. package/src/fields/DataSourceSelect.vue +1 -1
  72. package/src/fields/DisplayConds.vue +17 -153
  73. package/src/fields/EventSelect.vue +28 -375
  74. package/src/fields/KeyValue.vue +3 -4
  75. package/src/fields/StyleSetter/components/Border.vue +4 -38
  76. package/src/fields/StyleSetter/configs.ts +690 -0
  77. package/src/fields/StyleSetter/pro/Background.vue +4 -104
  78. package/src/fields/StyleSetter/pro/Border.vue +3 -12
  79. package/src/fields/StyleSetter/pro/Font.vue +5 -124
  80. package/src/fields/StyleSetter/pro/Layout.vue +4 -219
  81. package/src/fields/StyleSetter/pro/Position.vue +6 -110
  82. package/src/fields/StyleSetter/pro/Transform.vue +4 -30
  83. package/src/fields/configs/codeSelect.ts +127 -0
  84. package/src/fields/configs/displayConds.ts +184 -0
  85. package/src/fields/configs/eventSelect.ts +309 -0
  86. package/src/fields/configs/stickyAddButton.ts +31 -0
  87. package/src/fields/headless-validation.ts +177 -0
  88. package/src/headless.ts +33 -0
  89. package/src/hooks/use-compare-form.ts +7 -25
  90. package/src/hooks/use-form-context.ts +40 -0
  91. package/src/index.ts +2 -0
  92. package/src/initService.ts +23 -1
  93. package/src/layouts/history-list/HistoryDiffDialog.vue +0 -10
  94. package/src/layouts/history-list/HistoryListPanel.vue +1 -13
  95. package/src/layouts/history-list/useHistoryRevert.ts +7 -13
  96. package/src/layouts/props-panel/FormPanel.vue +17 -28
  97. package/src/layouts/props-panel/PropsPanel.vue +1 -4
  98. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +1 -1
  99. package/src/layouts/sidebar/code-block/useContentMenu.ts +1 -1
  100. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
  101. package/src/layouts/sidebar/data-source/useContentMenu.ts +1 -1
  102. package/src/plugin.ts +32 -33
  103. package/src/theme/code-block.scss +0 -37
  104. package/src/theme/display-conds.scss +4 -0
  105. package/src/theme/event.scss +22 -45
  106. package/src/theme/props-panel.scss +17 -3
  107. package/src/theme/themes/magic-admin/index.scss +12 -1
  108. package/src/type.ts +10 -29
  109. package/src/utils/data-source/index.ts +1 -0
  110. package/src/utils/props.ts +1 -4
  111. package/src/utils/type-match-rules.ts +37 -31
  112. package/types/headless.d.ts +18 -0
  113. package/types/index.d.ts +155 -180
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.8.0-beta.24",
2
+ "version": "1.8.0-beta.26",
3
3
  "name": "@tmagic/editor",
4
4
  "type": "module",
5
5
  "sideEffects": [
@@ -17,6 +17,11 @@
17
17
  "import": "./dist/es/index.js",
18
18
  "require": "./dist/tmagic-editor.umd.cjs"
19
19
  },
20
+ "./headless": {
21
+ "types": "./types/headless.d.ts",
22
+ "import": "./dist/es/headless.js",
23
+ "require": "./dist/tmagic-editor-headless.umd.cjs"
24
+ },
20
25
  "./dist/style.css": {
21
26
  "import": "./dist/style.css",
22
27
  "require": "./dist/style.css"
@@ -58,11 +63,11 @@
58
63
  "moveable": "^0.53.0",
59
64
  "serialize-javascript": "^7.0.7",
60
65
  "sortablejs": "^1.15.7",
61
- "@tmagic/design": "1.8.0-beta.24",
62
- "@tmagic/form": "1.8.0-beta.24",
63
- "@tmagic/utils": "1.8.0-beta.24",
64
- "@tmagic/stage": "1.8.0-beta.24",
65
- "@tmagic/table": "1.8.0-beta.24"
66
+ "@tmagic/design": "1.8.0-beta.26",
67
+ "@tmagic/form": "1.8.0-beta.26",
68
+ "@tmagic/stage": "1.8.0-beta.26",
69
+ "@tmagic/table": "1.8.0-beta.26",
70
+ "@tmagic/utils": "1.8.0-beta.26"
66
71
  },
67
72
  "devDependencies": {
68
73
  "@types/events": "^3.0.3",
@@ -76,7 +81,7 @@
76
81
  "type-fest": "^5.2.0",
77
82
  "typescript": "^6.0.3",
78
83
  "vue": "^3.5.41",
79
- "@tmagic/core": "1.8.0-beta.24"
84
+ "@tmagic/core": "1.8.0-beta.26"
80
85
  },
81
86
  "peerDependenciesMeta": {
82
87
  "typescript": {
package/src/Editor.vue CHANGED
@@ -101,7 +101,6 @@
101
101
  <template #props-panel>
102
102
  <slot name="props-panel">
103
103
  <PropsPanel
104
- :extend-state="extendFormState"
105
104
  :disabled-show-src="disabledShowSrc"
106
105
  @mounted="propsPanelMountedHandler"
107
106
  @unmounted="propsPanelUnmountedHandler"
@@ -136,11 +135,13 @@
136
135
  <script lang="ts" setup>
137
136
  import { EventEmitter } from 'events';
138
137
 
139
- import { computed, provide, ref } from 'vue';
138
+ import { computed, provide } from 'vue';
140
139
 
141
140
  import type { MApp } from '@tmagic/core';
142
141
  import { M_THEME_KEY } from '@tmagic/design';
142
+ import { FORM_CONTEXT_KEY } from '@tmagic/form';
143
143
 
144
+ import { useEditorFormContext } from './hooks/use-form-context';
144
145
  import Framework from './layouts/Framework.vue';
145
146
  import TMagicNavMenu from './layouts/NavMenu.vue';
146
147
  import FormPanel from './layouts/props-panel/FormPanel.vue';
@@ -230,8 +231,6 @@ const stageOptions: StageOptions = {
230
231
 
231
232
  stageOverlayService.set('stageOptions', stageOptions);
232
233
 
233
- const propsPanelRef = ref<InstanceType<typeof FormPanel> | null>(null);
234
-
235
234
  provide('services', services);
236
235
 
237
236
  provide('codeOptions', props.codeOptions);
@@ -239,11 +238,16 @@ provide('stageOptions', stageOptions);
239
238
  /** 是否启用「属性配置表单校验」联动能力,供 PropsPanel / FormPanel 判断校验失败时是否仍更新节点并记录错误 */
240
239
  provide(ENABLE_PROPS_FORM_VALIDATE, props.enablePropsFormValidate ?? false);
241
240
  /**
242
- * 把顶层 `extendFormState` 提供给非 PropsPanel 链路上的组件使用(例如历史差异对话框 HistoryDiffDialog
243
- * 内部的 CompareForm)。这样所有依赖业务上下文的表单 filterFunction 都能拿到一致的扩展状态,
244
- * 与 PropsPanel 通过 `:extend-state` 显式传入的方式保持等价。
241
+ * 编辑器注入给整棵表单树的业务上下文(`services` / `stage`),属性面板、对比表单、
242
+ * 侧边栏里的嵌套 MForm 都通过 inject 自动继承。
243
+ *
244
+ * 业务方要追加自己的字段,在 `<MEditor>` 外层再 `provide(FORM_CONTEXT_KEY, computed(...))`
245
+ * 即可,配置回调统一通过 `mForm.xxx` 读取。
245
246
  */
246
- provide('extendFormState', props.extendFormState);
247
+ provide(
248
+ FORM_CONTEXT_KEY,
249
+ useEditorFormContext(() => services),
250
+ );
247
251
 
248
252
  // 用 computed 包一层再 provide,否则传下去的是 provide 那一刻的值快照,
249
253
  // props.isLargeStageContainer 后续变化不会同步到子孙(如 @tmagic/design/ColorPicker)。
@@ -253,12 +257,6 @@ provide(
253
257
  'isLargeStageContainer',
254
258
  computed(() => props.isLargeStageContainer),
255
259
  );
256
- /**
257
- * 提供 PropsPanel 主属性表单的 formState getter,供历史差异弹窗复用,
258
- * 让 CompareForm 与 PropsPanel 的 filterFunction 上下文保持一致。
259
- */
260
- provide('getPropsPanelFormState', () => propsPanelRef.value?.configForm?.formState);
261
-
262
260
  /**
263
261
  * 把历史记录面板的自定义扩展 tab 提供给深层的 HistoryListPanel(它挂在 NavMenu 中,
264
262
  * 以 markRaw component 形式渲染,无法直接通过 props 透传)。业务方可借此在历史记录
@@ -279,11 +277,9 @@ provide(
279
277
  );
280
278
 
281
279
  const propsPanelMountedHandler = (e: InstanceType<typeof FormPanel>) => {
282
- propsPanelRef.value = e;
283
280
  emit('props-panel-mounted', e);
284
281
  };
285
282
  const propsPanelUnmountedHandler = () => {
286
- propsPanelRef.value = null;
287
283
  emit('props-panel-unmounted');
288
284
  };
289
285
 
@@ -15,7 +15,13 @@
15
15
  <script lang="ts" setup>
16
16
  import { computed, useTemplateRef } from 'vue';
17
17
 
18
- import { type ContainerChangeEventData, type FormItemConfig, type FormValue, MForm } from '@tmagic/form';
18
+ import {
19
+ type ContainerChangeEventData,
20
+ defineFormItem,
21
+ type FormItemConfig,
22
+ type FormValue,
23
+ MForm,
24
+ } from '@tmagic/form';
19
25
 
20
26
  import type { CodeParamStatement } from '@editor/type';
21
27
  import { error } from '@editor/utils';
@@ -41,13 +47,13 @@ const emit = defineEmits(['change']);
41
47
  const formRef = useTemplateRef<InstanceType<typeof MForm>>('form');
42
48
 
43
49
  const getFormConfig = (items: FormItemConfig[] = []) => [
44
- {
50
+ defineFormItem({
45
51
  type: 'fieldset',
46
52
  items,
47
53
  legend: '参数',
48
- labelWidth: '120px',
54
+ labelPosition: 'top',
49
55
  name: props.name,
50
- },
56
+ }),
51
57
  ];
52
58
 
53
59
  const codeParamsConfig = computed(() =>
@@ -10,7 +10,7 @@
10
10
  :is-compare="true"
11
11
  :disabled="true"
12
12
  :label-width="labelWidth"
13
- :extend-state="mergedExtendState"
13
+ :context="formContext"
14
14
  :show-diff="showDiff"
15
15
  :self-diff-field-types="selfDiffFieldTypes"
16
16
  :size="size"
@@ -47,7 +47,7 @@ const props = withDefaults(
47
47
  },
48
48
  );
49
49
 
50
- const { config, currentValues, wrapperStyle, mergedExtendState, loadConfig, formRef, normalizeCodeBlockValue } =
50
+ const { config, currentValues, wrapperStyle, formContext, loadConfig, formRef, normalizeCodeBlockValue } =
51
51
  useCompareForm(props);
52
52
 
53
53
  const lastValuesProcessed = computed<FormValue>(() => {
@@ -60,9 +60,8 @@ const lastValuesProcessed = computed<FormValue>(() => {
60
60
  /**
61
61
  * `code-select` 字段在历史数据中存在两种"语义为空"的形态:
62
62
  * - 字符串 `''`(旧数据 / 用户从未配置过钩子);
63
- * - `{ hookType: HookType.CODE, hookData: [] }`(CodeSelect.vue 在挂载时
64
- * 写入的默认结构,参见 packages/editor/src/fields/CodeSelect.vue 中
65
- * `props.model[props.name] = { hookType: HookType.CODE, hookData: [] }`)。
63
+ * - `{ hookType: HookType.CODE, hookData: [] }`(`normalizeCodeSelectValue`
64
+ * 写入的默认结构)。
66
65
  *
67
66
  * 直接 `isEqual` 会把两者判为不等,从而在历史对比里对每个未配置过钩子的组件
68
67
  * 都展示一份"差异",体验很糟糕。这里把它们视为相等,跳过对比。
@@ -8,7 +8,7 @@
8
8
  :init-values="currentValues"
9
9
  :disabled="disabled"
10
10
  :label-width="labelWidth"
11
- :extend-state="mergedExtendState"
11
+ :context="formContext"
12
12
  :size="size"
13
13
  ></MForm>
14
14
  </div>
@@ -37,11 +37,10 @@ const props = withDefaults(
37
37
  category: 'node',
38
38
  labelWidth: '120px',
39
39
  disabled: true,
40
- // extendState 的默认值由 useCompareForm 内部兜底(props.extendState ?? ...),此处无需重复提供
41
40
  },
42
41
  );
43
42
 
44
- const { config, currentValues, wrapperStyle, mergedExtendState, loadConfig, formRef } = useCompareForm(props);
43
+ const { config, currentValues, wrapperStyle, formContext, loadConfig, formRef } = useCompareForm(props);
45
44
 
46
45
  defineExpose<{
47
46
  form: ShallowRef<InstanceType<typeof MForm> | null>;
@@ -1,7 +1,7 @@
1
1
  import type { InjectionKey } from 'vue';
2
2
 
3
3
  import type { DataSourceSchema, EventOption, Id, MApp, MNode, MPage, MPageFragment } from '@tmagic/core';
4
- import type { FormConfig, FormState } from '@tmagic/form';
4
+ import type { FormConfig } from '@tmagic/form';
5
5
  import StageCore, {
6
6
  CONTAINER_HIGHLIGHT_CLASS_NAME,
7
7
  ContainerHighlightType,
@@ -149,7 +149,6 @@ export interface EditorProps {
149
149
  beforeDblclick?: (event: MouseEvent) => Promise<boolean | void> | boolean | void;
150
150
  /** 组件树节点双击前的钩子函数,返回 false 则阻止默认的双击行为 */
151
151
  beforeLayerNodeDblclick?: (event: MouseEvent, data: TreeNodeData) => Promise<boolean | void> | boolean | void;
152
- extendFormState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
153
152
  /** 历史记录面板的自定义扩展 tab,追加在内置的页面/数据源/代码块 tab 之后 */
154
153
  historyListExtraTabs?: HistoryListExtraTab[];
155
154
  /** 页面顺序拖拽配置参数 */
@@ -1,6 +1,5 @@
1
1
  <template>
2
2
  <MagicCodeEditor
3
- v-if="!silentMode"
4
3
  :height="config.height"
5
4
  :type="diffMode ? 'diff' : undefined"
6
5
  :init-values="diffMode ? (lastValues || {})[name] : model[name]"
@@ -18,9 +17,9 @@
18
17
  </template>
19
18
 
20
19
  <script lang="ts" setup>
21
- import { computed, inject } from 'vue';
20
+ import { computed } from 'vue';
22
21
 
23
- import { type CodeConfig, type FieldProps, FORM_SILENT_MODE_KEY } from '@tmagic/form';
22
+ import { type CodeConfig, type FieldProps } from '@tmagic/form';
24
23
 
25
24
  import MagicCodeEditor from '@editor/layouts/CodeEditor.vue';
26
25
 
@@ -28,13 +27,6 @@ defineOptions({
28
27
  name: 'MFieldsVsCode',
29
28
  });
30
29
 
31
- /**
32
- * 静默模式(submitForm/validateForm 隐藏挂载)下跳过 monaco 渲染:
33
- * 校验由 FormItem 针对 model 中的值完成,与编辑器实例无关;本组件挂载无值副作用
34
- * (save 仅由用户操作/编辑器内容变更触发),跳过可省去 monaco worker/model 的无谓实例化。
35
- */
36
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
37
-
38
30
  const emit = defineEmits<{
39
31
  change: [value: string | any];
40
32
  }>();
@@ -1,36 +1,29 @@
1
1
  <template>
2
2
  <div class="m-fields-code-select" :class="config.className">
3
- <TMagicCard :flat="config.flat">
4
- <MContainer
5
- :config="codeConfig"
6
- :size="size"
7
- class="code-select-content"
8
- :prop="prop"
9
- :disabled="disabled"
10
- :is-compare="isCompareMode"
11
- :last-values="lastValues?.[name]"
12
- :model="model[name]"
13
- @change="changeHandler"
14
- >
15
- </MContainer>
16
- <TMagicButton class="create-button fullWidth" :icon="Plus" :size="size" :disabled="disabled" @click="newHandler()"
17
- >添加{{ config.text }}</TMagicButton
18
- >
19
- </TMagicCard>
3
+ <MContainer
4
+ :config="codeConfig"
5
+ :size="size"
6
+ class="code-select-content"
7
+ :prop="prop"
8
+ :disabled="disabled"
9
+ :is-compare="isCompareMode"
10
+ :last-values="lastValues?.[name]"
11
+ :model="model[name]"
12
+ :label-position="config.labelPosition"
13
+ :label-width="config.labelWidth"
14
+ @change="changeHandler"
15
+ >
16
+ </MContainer>
20
17
  </div>
21
18
  </template>
22
19
 
23
20
  <script lang="ts" setup>
24
21
  import { computed, watch } from 'vue';
25
- import { Plus } from '@element-plus/icons-vue';
26
- import { isEmpty } from 'lodash-es';
27
22
 
28
- import { HookCodeType, HookType } from '@tmagic/core';
29
- import { TMagicButton, TMagicCard } from '@tmagic/design';
30
- import type { CodeSelectConfig, ContainerChangeEventData, FieldProps, GroupListConfig } from '@tmagic/form';
23
+ import type { CodeSelectConfig, ContainerChangeEventData, FieldProps } from '@tmagic/form';
31
24
  import { MContainer } from '@tmagic/form';
32
25
 
33
- import { useServices } from '@editor/hooks/use-services';
26
+ import { createCodeSelectConfig, normalizeCodeSelectValue } from '@editor/fields/configs/codeSelect';
34
27
 
35
28
  defineOptions({
36
29
  name: 'MFieldsCodeSelect',
@@ -40,8 +33,6 @@ const emit = defineEmits<{
40
33
  change: [v: any, eventData: ContainerChangeEventData];
41
34
  }>();
42
35
 
43
- const { dataSourceService, codeBlockService } = useServices();
44
-
45
36
  const props = withDefaults(defineProps<FieldProps<CodeSelectConfig>>(), {});
46
37
 
47
38
  /**
@@ -58,101 +49,14 @@ const props = withDefaults(defineProps<FieldProps<CodeSelectConfig>>(), {});
58
49
  * 仅当存在历史值时才启用对比,避免 lastValues 缺失时退化为「全部新增」的空对比。
59
50
  */
60
51
  const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues));
61
- const newHandler = () => {
62
- const defaultCode = {
63
- codeType: HookCodeType.CODE,
64
- codeId: '',
65
- };
66
- const name = props.config.name || '';
67
- const hookData = props.model[name]?.hookData || [];
68
- emit('change', defaultCode, {
69
- modifyKey: `hookData.${hookData.length}`,
70
- });
71
- };
72
- const codeConfig = computed<GroupListConfig>(() => ({
73
- type: 'group-list',
74
- name: 'hookData',
75
- enableToggleMode: false,
76
- expandAll: true,
77
- addable: () => false,
78
- title: (mForm, { model, index }: any) => {
79
- if (model.codeType === HookCodeType.DATA_SOURCE_METHOD) {
80
- if (Array.isArray(model.codeId)) {
81
- if (model.codeId.length < 2) {
82
- return index;
83
- }
84
-
85
- const ds = dataSourceService.getDataSourceById(model.codeId[0]);
86
- return `${ds?.title} / ${model.codeId[1]}`;
87
- }
88
-
89
- return Array.isArray(model.codeId) ? model.codeId.join('/') : index;
90
- }
91
-
92
- const codeContent = codeBlockService.getCodeContentById(model.codeId);
93
52
 
94
- if (codeContent) {
95
- return codeContent.name;
96
- }
97
-
98
- return model.codeId || index;
99
- },
100
- titlePrefix: props.config.name === undefined ? undefined : String(props.config.name),
101
- items: [
102
- {
103
- text: '代码类型',
104
- type: 'select',
105
- name: 'codeType',
106
- labelPosition: 'right',
107
- rules: [{ typeMatch: true, trigger: 'change' }],
108
- options: [
109
- { value: HookCodeType.CODE, text: '代码块' },
110
- { value: HookCodeType.DATA_SOURCE_METHOD, text: '数据源方法' },
111
- ],
112
- defaultValue: HookCodeType.CODE,
113
- onChange: (_mForm, v: HookCodeType, { setModel }) => {
114
- if (v === HookCodeType.DATA_SOURCE_METHOD) {
115
- setModel('codeId', []);
116
- } else {
117
- setModel('codeId', '');
118
- }
119
- return v;
120
- },
121
- },
122
- {
123
- type: 'code-select-col',
124
- name: 'codeId',
125
- text: '代码块',
126
- rules: [{ typeMatch: true, trigger: 'change' }],
127
-
128
- display: (_mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
129
- notEditable: () => !codeBlockService.getEditStatus(),
130
- },
131
- {
132
- type: 'data-source-method-select',
133
- name: 'codeId',
134
- text: '数据源字段',
135
- rules: [{ typeMatch: true, trigger: 'change' }],
136
- display: (_mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
137
- notEditable: () => !dataSourceService.get('editable'),
138
- },
139
- ],
140
- }));
53
+ const codeConfig = computed(() => createCodeSelectConfig(props.config));
141
54
 
55
+ // 挂载时的归一化由 applyMountValueEffects → code-select 的 effect 完成,这里只兜运行期被置空
142
56
  watch(
143
57
  () => props.model[props.name],
144
- (value) => {
145
- // 兼容旧的数据结构
146
- if (isEmpty(value)) {
147
- // 空值或者空数组
148
- props.model[props.name] = {
149
- hookType: HookType.CODE,
150
- hookData: [],
151
- };
152
- }
153
- },
154
- {
155
- immediate: true,
58
+ () => {
59
+ normalizeCodeSelectValue(props.model, props.name);
156
60
  },
157
61
  );
158
62
 
@@ -15,7 +15,7 @@
15
15
  :prop="prop"
16
16
  ></MFormContainer>
17
17
  <MSelect
18
- v-else-if="!silentMode"
18
+ v-else
19
19
  class="select"
20
20
  :config="selectConfig"
21
21
  :name="name"
@@ -27,7 +27,7 @@
27
27
 
28
28
  <!-- 查看/编辑按钮:对比模式为只读,不展示 -->
29
29
  <TMagicButton
30
- v-if="!isCompareMode && !silentMode && model[name] && hasCodeBlockSidePanel"
30
+ v-if="!isCompareMode && model[name] && hasCodeBlockSidePanel"
31
31
  class="m-fields-select-action-button"
32
32
  :size="size"
33
33
  @click="editCode(model[name])"
@@ -65,7 +65,6 @@ import {
65
65
  createValues,
66
66
  type FieldProps,
67
67
  filterFunction,
68
- FORM_SILENT_MODE_KEY,
69
68
  type FormItemConfig,
70
69
  type FormState,
71
70
  MContainer as MFormContainer,
@@ -84,9 +83,8 @@ defineOptions({
84
83
  });
85
84
 
86
85
  const mForm = inject<FormState | undefined>('mForm');
87
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
88
86
  const { codeBlockService, uiService } = useServices();
89
- const eventBus = inject<EventBus>('eventBus');
87
+ const eventBus = inject<EventBus | null>('eventBus', null);
90
88
  const emit = defineEmits<{
91
89
  change: [v: any, eventData: ContainerChangeEventData];
92
90
  }>();
@@ -31,6 +31,7 @@ import { computed } from 'vue';
31
31
 
32
32
  import { getDesignConfig, TMagicSelect } from '@tmagic/design';
33
33
  import type { CondOpSelectConfig, FieldProps } from '@tmagic/form';
34
+ import { removeDataSourceFieldPrefix } from '@tmagic/utils';
34
35
 
35
36
  import { useServices } from '@editor/hooks/use-services';
36
37
  import { getCondOpOptionsByFieldType, getFieldType } from '@editor/utils';
@@ -50,8 +51,8 @@ const props = defineProps<FieldProps<CondOpSelectConfig>>();
50
51
  const optionComponent = getDesignConfig('components')?.option;
51
52
 
52
53
  const options = computed(() => {
53
- const [id, ...fieldNames] = [...(props.config.parentFields || []), ...props.model.field];
54
- const ds = dataSourceService.getDataSourceById(id);
54
+ const [id, ...fieldNames] = [...(props.config.parentFields || []), ...(props.model.field || [])];
55
+ const ds = id ? dataSourceService.getDataSourceById(removeDataSourceFieldPrefix(id)) : undefined;
55
56
  const type = getFieldType(ds, fieldNames);
56
57
  return getCondOpOptionsByFieldType(type);
57
58
  });
@@ -124,7 +124,7 @@ const optionComponent = getDesignConfig('components')?.option;
124
124
 
125
125
  const { dataSourceService, uiService } = useServices();
126
126
  const mForm = inject<FormState | undefined>('mForm');
127
- const eventBus = inject<EventBus>('eventBus');
127
+ const eventBus = inject<EventBus | null>('eventBus', null);
128
128
 
129
129
  const allDataSources = computed(() => dataSourceService.get('dataSources') || []);
130
130
 
@@ -6,7 +6,7 @@
6
6
  }"
7
7
  >
8
8
  <FieldSelect
9
- v-if="isSelectValid && !silentMode"
9
+ v-if="isSelectValid"
10
10
  :model-value="model[name]"
11
11
  :disabled="disabled"
12
12
  :size="size"
@@ -18,7 +18,7 @@
18
18
  ></FieldSelect>
19
19
 
20
20
  <component
21
- v-else-if="!silentMode"
21
+ v-else
22
22
  :is="tagName"
23
23
  :config="config.fieldConfig"
24
24
  :model="model"
@@ -33,7 +33,7 @@
33
33
  ></component>
34
34
 
35
35
  <TMagicTooltip
36
- v-if="!silentMode && config.fieldConfig && !disabledDataSource && !mForm?.isCompare"
36
+ v-if="config.fieldConfig && !disabledDataSource && !mForm?.isCompare"
37
37
  :disabled="showDataSourceFieldSelect"
38
38
  content="选择数据源"
39
39
  >
@@ -57,7 +57,6 @@ import {
57
57
  type ContainerChangeEventData,
58
58
  type DataSourceFieldSelectConfig,
59
59
  type FieldProps,
60
- FORM_SILENT_MODE_KEY,
61
60
  type FormState,
62
61
  getFormField,
63
62
  } from '@tmagic/form';
@@ -105,7 +104,6 @@ watch(
105
104
 
106
105
  const { dataSourceService, propsService } = useServices();
107
106
  const mForm = inject<FormState | undefined>('mForm');
108
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
109
107
 
110
108
  const dataSources = computed(() => dataSourceService.get('dataSources') || []);
111
109
  const disabledDataSource = computed(() => propsService.getDisabledDataSource());
@@ -143,7 +141,10 @@ const checkStrictly = computed(() => {
143
141
  if (typeof props.config.checkStrictly !== 'function') {
144
142
  value = props.config.checkStrictly;
145
143
  } else {
146
- const dsId = removeDataSourceFieldPrefix(props.model[0]);
144
+ const fieldValue = props.model[props.name];
145
+ // 指定了 dataSourceId 时取值里只有字段名,数据源 id 只能取自 config
146
+ const rawDsId = props.config.dataSourceId ?? (Array.isArray(fieldValue) ? fieldValue[0] : fieldValue);
147
+ const dsId = removeDataSourceFieldPrefix(`${rawDsId}`);
147
148
  const dataSource = dataSources.value.find((ds) => ds.id === dsId);
148
149
 
149
150
  value = props.config.checkStrictly(mForm, {
@@ -8,7 +8,6 @@
8
8
  </div>
9
9
 
10
10
  <FloatingBox
11
- v-if="!silentMode"
12
11
  :body-style="{ padding: '0 16px' }"
13
12
  v-model:visible="addDialogVisible"
14
13
  v-model:width="width"
@@ -31,7 +30,6 @@
31
30
  </FloatingBox>
32
31
 
33
32
  <FloatingBox
34
- v-if="!silentMode"
35
33
  :body-style="{ padding: '0 16px' }"
36
34
  v-model:visible="addFromJsonDialogVisible"
37
35
  v-model:width="width"
@@ -62,7 +60,6 @@ import {
62
60
  type ContainerChangeEventData,
63
61
  type DataSourceFieldsConfig,
64
62
  type FieldProps,
65
- FORM_SILENT_MODE_KEY,
66
63
  type FormConfig,
67
64
  type FormState,
68
65
  MFormBox,
@@ -92,8 +89,6 @@ const emit = defineEmits<{
92
89
 
93
90
  const { uiService } = useServices();
94
91
  const mForm = inject<FormState | undefined>('mForm');
95
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
96
-
97
92
  /** 对比模式下隐藏新增/编辑/删除等操作按钮,仅保留只读展示。 */
98
93
  const isCompare = computed(() => Boolean(mForm?.isCompare));
99
94
 
@@ -383,8 +378,6 @@ provide(
383
378
  );
384
379
 
385
380
  onMounted(() => {
386
- if (silentMode) return;
387
-
388
381
  const path = editingFieldPath.value;
389
382
  if (!path.length) return;
390
383
 
@@ -2,7 +2,6 @@
2
2
  <div class="m-fields-data-source-method-select">
3
3
  <div class="data-source-method-select-container">
4
4
  <MCascader
5
- v-if="!silentMode"
6
5
  class="select"
7
6
  :config="cascaderConfig"
8
7
  :model="model"
@@ -14,7 +13,7 @@
14
13
  ></MCascader>
15
14
 
16
15
  <TMagicTooltip
17
- v-if="!silentMode && model[name] && isCustomMethod && dataSourceSidePanel && !isCompare"
16
+ v-if="model[name] && isCustomMethod && dataSourceSidePanel && !isCompare"
18
17
  :content="notEditable ? '查看' : '编辑'"
19
18
  >
20
19
  <TMagicButton class="m-fields-select-action-button" :size="size" @click="editCodeHandler">
@@ -49,7 +48,6 @@ import {
49
48
  type DataSourceMethodSelectConfig,
50
49
  type FieldProps,
51
50
  filterFunction,
52
- FORM_SILENT_MODE_KEY,
53
51
  type FormItemConfig,
54
52
  type FormState,
55
53
  MCascader,
@@ -69,8 +67,7 @@ defineOptions({
69
67
 
70
68
  const { dataSourceService, uiService } = useServices();
71
69
  const mForm = inject<FormState | undefined>('mForm');
72
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
73
- const eventBus = inject<EventBus>('eventBus');
70
+ const eventBus = inject<EventBus | null>('eventBus', null);
74
71
 
75
72
  const emit = defineEmits(['change']);
76
73
 
@@ -9,7 +9,7 @@
9
9
  </div>
10
10
 
11
11
  <CodeBlockEditor
12
- v-if="codeConfig && !silentMode"
12
+ v-if="codeConfig"
13
13
  ref="codeBlockEditor"
14
14
  :disabled="disabled"
15
15
  :content="codeConfig"
@@ -30,7 +30,6 @@ import {
30
30
  type ContainerChangeEventData,
31
31
  type DataSourceMethodsConfig,
32
32
  type FieldProps,
33
- FORM_SILENT_MODE_KEY,
34
33
  type FormState,
35
34
  } from '@tmagic/form';
36
35
  import { type ColumnConfig, MagicTable } from '@tmagic/table';
@@ -49,8 +48,6 @@ const props = withDefaults(defineProps<FieldProps<DataSourceMethodsConfig>>(), {
49
48
  const emit = defineEmits(['change']);
50
49
 
51
50
  const mForm = inject<FormState | undefined>('mForm');
52
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
53
-
54
51
  /** 对比模式下隐藏新增/编辑/删除等操作按钮,仅保留只读展示。 */
55
52
  const isCompare = computed(() => Boolean(mForm?.isCompare));
56
53
 
@@ -177,8 +174,6 @@ const editingMethodName = inject<ComputedRef<string | undefined>>(
177
174
  );
178
175
 
179
176
  onMounted(() => {
180
- if (silentMode) return;
181
-
182
177
  const methodName = editingMethodName.value;
183
178
  if (!methodName) return;
184
179