@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
@@ -7,7 +7,6 @@
7
7
  </div>
8
8
 
9
9
  <FloatingBox
10
- v-if="!silentMode"
11
10
  :body-style="{ padding: '0 16px' }"
12
11
  v-model:visible="addDialogVisible"
13
12
  v-model:width="width"
@@ -35,14 +34,7 @@ import { computed, inject, Ref, ref } from 'vue';
35
34
 
36
35
  import type { MockSchema } from '@tmagic/core';
37
36
  import { TMagicButton, tMagicMessageBox, TMagicSwitch } from '@tmagic/design';
38
- import {
39
- type DataSourceMocksConfig,
40
- type FieldProps,
41
- FORM_SILENT_MODE_KEY,
42
- type FormConfig,
43
- type FormState,
44
- MFormBox,
45
- } from '@tmagic/form';
37
+ import { type DataSourceMocksConfig, type FieldProps, type FormConfig, type FormState, MFormBox } from '@tmagic/form';
46
38
  import { type ColumnConfig, MagicTable } from '@tmagic/table';
47
39
  import { getDefaultValueFromFields } from '@tmagic/utils';
48
40
 
@@ -65,7 +57,6 @@ const emit = defineEmits(['change']);
65
57
 
66
58
  const { uiService } = useServices();
67
59
  const mForm = inject<FormState | undefined>('mForm');
68
- const silentMode = inject(FORM_SILENT_MODE_KEY, false);
69
60
 
70
61
  /** 对比模式下隐藏新增/编辑/删除等操作按钮,仅保留只读展示。 */
71
62
  const isCompare = computed(() => Boolean(mForm?.isCompare));
@@ -50,7 +50,7 @@ const props = withDefaults(defineProps<FieldProps<DataSourceSelect>>(), {
50
50
 
51
51
  const mForm = inject<FormState | undefined>('mForm');
52
52
  const { dataSourceService, uiService } = useServices();
53
- const eventBus = inject<EventBus>('eventBus');
53
+ const eventBus = inject<EventBus | null>('eventBus', null);
54
54
 
55
55
  const dataSources = computed(() => dataSourceService.get('dataSources'));
56
56
 
@@ -1,15 +1,17 @@
1
1
  <template>
2
- <MGroupList
3
- style="width: 100%"
4
- :config="config"
5
- :name="name"
6
- :disabled="disabled"
7
- :model="model"
8
- :last-values="lastValues"
9
- :prop="prop"
10
- :size="size"
11
- @change="changeHandler"
12
- ></MGroupList>
2
+ <div class="m-fields-display-conds">
3
+ <MGroupList
4
+ :config="config"
5
+ :name="name"
6
+ :disabled="disabled"
7
+ :model="model"
8
+ :last-values="lastValues"
9
+ :is-compare="isCompareMode"
10
+ :prop="prop"
11
+ :size="size"
12
+ @change="changeHandler"
13
+ ></MGroupList>
14
+ </div>
13
15
  </template>
14
16
 
15
17
  <script lang="ts" setup>
@@ -22,170 +24,32 @@ import {
22
24
  type FieldProps,
23
25
  filterFunction,
24
26
  type FormState,
25
- type GroupListConfig,
26
27
  MGroupList,
27
28
  } from '@tmagic/form';
28
29
 
29
- import { useServices } from '@editor/hooks/use-services';
30
- import { getCascaderOptionsFromFields, getFieldType } from '@editor/utils';
30
+ import { createDisplayCondsConfig } from '@editor/fields/configs/displayConds';
31
31
 
32
32
  defineOptions({
33
33
  name: 'm-fields-display-conds',
34
34
  });
35
35
 
36
36
  const emit = defineEmits<{
37
- change: [value: DisplayCond[], eventData?: ContainerChangeEventData];
37
+ change: [value: DisplayCond | DisplayCond[], eventData?: ContainerChangeEventData];
38
38
  }>();
39
39
 
40
40
  const props = withDefaults(defineProps<FieldProps<DisplayCondsConfig>>(), {
41
41
  disabled: false,
42
42
  });
43
43
 
44
- const { dataSourceService } = useServices();
45
44
  const mForm = inject<FormState | undefined>('mForm');
46
45
 
47
46
  const parentFields = computed(() => filterFunction<string[]>(mForm, props.config.parentFields, props) || []);
48
47
 
49
- const fieldOnChange = (_formState: FormState | undefined, v: string[], { model }: { model: Record<string, any> }) => {
50
- const [id, ...fieldNames] = [...parentFields.value, ...v];
51
- const ds = dataSourceService.getDataSourceById(id);
52
- const type = getFieldType(ds, fieldNames);
53
- if (type === 'number') {
54
- model.value = Number(model.value);
55
- } else if (type === 'boolean') {
56
- model.value = Boolean(model.value);
57
- } else if (type === 'null') {
58
- model.value = null;
59
- } else {
60
- model.value = `${model.value}`;
61
- }
62
- return v;
63
- };
64
-
65
- const config = computed<GroupListConfig>(() => ({
66
- type: 'groupList',
67
- name: props.name,
68
- titlePrefix: props.config.titlePrefix,
69
- expandAll: true,
70
- enableToggleMode: false,
71
- flat: props.config.flat,
72
- items: [
73
- {
74
- type: 'table',
75
- name: 'cond',
76
- operateColWidth: props.config.operateColWidth,
77
- enableToggleMode: false,
78
- fixed: props.config.fixed,
79
- flat: props.config.flat,
80
- items: [
81
- parentFields.value.length
82
- ? {
83
- type: 'cascader',
84
- options: () => {
85
- const [dsId, ...keys] = parentFields.value;
86
- const ds = dataSourceService.getDataSourceById(dsId);
87
- if (!ds) {
88
- return [];
89
- }
90
-
91
- let fields = ds.fields || [];
92
- keys.forEach((key) => {
93
- const field = fields.find((f) => f.name === key);
94
- fields = field?.fields || [];
95
- });
96
-
97
- return getCascaderOptionsFromFields(fields, ['string', 'number', 'boolean', 'any']);
98
- },
99
- name: 'field',
100
- value: 'key',
101
- label: '字段',
102
- checkStrictly: false,
103
- onChange: fieldOnChange,
104
- defaultValue: () => [],
105
- rules: [
106
- { required: true, trigger: 'blur', message: '请选择字段' },
107
- { typeMatch: true, trigger: 'change' },
108
- ],
109
- }
110
- : {
111
- type: 'data-source-field-select',
112
- name: 'field',
113
- value: 'key',
114
- label: '字段',
115
- checkStrictly: false,
116
- dataSourceFieldType: ['string', 'number', 'boolean', 'any'],
117
- onChange: fieldOnChange,
118
- defaultValue: () => [],
119
- rules: [
120
- { required: true, trigger: 'blur', message: '请选择字段' },
121
- { typeMatch: true, trigger: 'change' },
122
- ],
123
- },
124
- {
125
- type: 'cond-op-select',
126
- parentFields: parentFields.value,
127
- label: '条件',
128
- width: 140,
129
- name: 'op',
130
- rules: [
131
- { required: true, trigger: 'blur', message: '请选择条件' },
132
- { typeMatch: true, trigger: 'change' },
133
- ],
134
- },
135
- {
136
- label: '值',
137
- width: 160,
138
- items: [
139
- {
140
- name: 'value',
141
- type: (_mForm, { model }) => {
142
- const [id, ...fieldNames] = [...parentFields.value, ...model.field];
143
- const ds = dataSourceService.getDataSourceById(id);
144
- const type = getFieldType(ds, fieldNames);
48
+ const config = computed(() => createDisplayCondsConfig(props.config, props.name, parentFields.value));
145
49
 
146
- if (type === 'number') {
147
- return 'number';
148
- }
149
-
150
- if (type === 'boolean') {
151
- return 'select';
152
- }
153
-
154
- if (type === 'null') {
155
- return 'display';
156
- }
157
-
158
- return 'text';
159
- },
160
- options: [
161
- { text: 'true', value: true },
162
- { text: 'false', value: false },
163
- ],
164
- display: (_mForm, { model }) => !['between', 'not_between'].includes(model.op),
165
- displayText: (_mForm: FormState | undefined, { model }: any) => {
166
- if (model.value === null) {
167
- return 'null';
168
- }
169
- return model.value;
170
- },
171
- },
172
- {
173
- name: 'range',
174
- type: 'number-range',
175
- display: (vm, { model }) => ['between', 'not_between'].includes(model.op),
176
- },
177
- ],
178
- },
179
- ],
180
- },
181
- ],
182
- }));
50
+ const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues));
183
51
 
184
52
  const changeHandler = (v: DisplayCond[], eventData?: ContainerChangeEventData) => {
185
- if (!Array.isArray(props.model[props.name])) {
186
- props.model[props.name] = [];
187
- }
188
-
189
53
  emit('change', v, eventData);
190
54
  };
191
55
  </script>
@@ -15,100 +15,50 @@
15
15
  <div v-else class="fullWidth event-select-container">
16
16
  <div class="event-select-header">
17
17
  <div class="event-select-title">事件配置</div>
18
- <TMagicButton
19
- v-if="!isCompareMode && displayList.length > 0"
20
- class="create-button"
21
- text
22
- type="primary"
23
- :icon="Plus"
24
- :size="size"
25
- :disabled="disabled"
26
- @click="addEvent()"
27
- >添加事件</TMagicButton
28
- >
29
18
  </div>
30
- <MPanel
31
- v-for="entry in displayList"
32
- :key="entry.index"
19
+ <MGroupList
20
+ :config="eventConfig"
21
+ :name="name"
33
22
  :disabled="disabled"
34
- :size="size"
35
- :prop="`${prop}.${entry.index}`"
36
- :config="actionsConfig"
37
- :model="entry.cardItem"
38
- :last-values="entry.lastCardItem"
23
+ :model="model"
24
+ :last-values="lastValues"
39
25
  :is-compare="isCompareMode"
40
- :hide-expand="false"
41
- :label-width="config.labelWidth || '100px'"
26
+ :prop="prop"
27
+ :size="size"
42
28
  @change="onChangeHandler"
43
29
  >
44
- <template #header>
30
+ <template #title="{ model: itemModel, lastValues: itemLastValues, prop: itemProp }">
45
31
  <div class="event-item-header">
46
32
  <MFormContainer
47
33
  class="fullWidth"
48
34
  :config="eventNameConfig"
49
- :model="entry.cardItem"
50
- :last-values="entry.lastCardItem"
35
+ :model="itemModel"
36
+ :last-values="itemLastValues"
51
37
  :is-compare="isCompareMode"
52
38
  :disabled="disabled"
53
39
  :size="size"
54
- :prop="`${prop}.${entry.index}`"
55
- @change="eventNameChangeHandler"
40
+ :prop="itemProp"
41
+ @change="onChangeHandler"
56
42
  ></MFormContainer>
57
- <TMagicButton
58
- class="event-item-delete-button"
59
- v-if="!isCompareMode"
60
- link
61
- :icon="Delete"
62
- :disabled="disabled"
63
- :size="size"
64
- @click="removeEvent(Number(entry.index))"
65
- ></TMagicButton>
66
43
  </div>
67
44
  </template>
68
- </MPanel>
69
-
70
- <TMagicButton
71
- v-if="!isCompareMode"
72
- class="create-button fullWidth"
73
- :icon="Plus"
74
- :disabled="disabled"
75
- @click="addEvent()"
76
- >添加事件</TMagicButton
77
- >
45
+ </MGroupList>
78
46
  </div>
79
47
  </div>
80
48
  </template>
81
49
 
82
50
  <script lang="ts" setup>
83
51
  import { computed } from 'vue';
84
- import { Delete } from '@element-plus/icons-vue';
85
- import { Plus } from '@element-plus/icons-vue';
86
- import { has } from 'lodash-es';
87
52
 
88
- import { ActionType, MNode } from '@tmagic/core';
89
- import { TMagicButton } from '@tmagic/design';
90
- import type {
91
- CodeSelectColConfig,
92
- ContainerChangeEventData,
93
- DataSourceMethodSelectConfig,
94
- DynamicTypeConfig,
95
- EventSelectConfig,
96
- FieldProps,
97
- FormState,
98
- PanelConfig,
99
- TableConfig,
100
- UISelectConfig,
101
- } from '@tmagic/form';
102
- import { defineFormItem, MContainer as MFormContainer, MPanel, MTable } from '@tmagic/form';
53
+ import type { ContainerChangeEventData, EventSelectConfig, FieldProps } from '@tmagic/form';
54
+ import { MContainer as MFormContainer, MGroupList, MTable } from '@tmagic/form';
103
55
 
104
- import { useServices } from '@editor/hooks/use-services';
105
56
  import {
106
- getCompActionAllowedValues,
107
- getCompActionOptions,
108
- getEventNameAllowedValues,
109
- getEventNameOptions,
110
- normalizeCompActionValue,
111
- } from '@editor/utils';
57
+ createEventNameConfig,
58
+ createEventSelectConfig,
59
+ createLegacyTableConfig,
60
+ isLegacyEventValue,
61
+ } from '@editor/fields/configs/eventSelect';
112
62
 
113
63
  defineOptions({
114
64
  name: 'MFieldsEventSelect',
@@ -120,322 +70,25 @@ const emit = defineEmits<{
120
70
  change: [v: any, eventData?: ContainerChangeEventData];
121
71
  }>();
122
72
 
123
- const { editorService, dataSourceService, eventsService, codeBlockService, propsService } = useServices();
124
-
125
- // 事件名称下拉框表单配置
126
- const eventNameConfig = computed(() => {
127
- const defaultEventNameConfig = {
128
- name: 'name',
129
- text: '事件类型',
130
- type: (mForm: FormState | undefined, { formValue }: any) => {
131
- if (
132
- props.config.src !== 'component' ||
133
- (formValue.type === 'page-fragment-container' && formValue.pageFragmentId)
134
- ) {
135
- return 'cascader';
136
- }
137
- return 'select';
138
- },
139
- labelWidth: '70px',
140
- checkStrictly: () => props.config.src !== 'component',
141
- valueSeparator: '.',
142
- options: (_mForm: FormState, { formValue }: any) => getEventNameOptions(props.config.src, formValue),
143
- rules: [
144
- {
145
- validator: ({ value, callback }: any, { formValue }: any) => {
146
- const allowedNames = getEventNameAllowedValues(props.config as any, formValue);
147
- if (allowedNames && allowedNames.size > 0 && value && !allowedNames.has(value)) {
148
- return callback(`事件名(${value})不存在`);
149
- }
150
- callback();
151
- },
152
- trigger: 'blur',
153
- },
154
- ],
155
- };
156
- return { ...defaultEventNameConfig, ...props.config.eventNameConfig };
157
- });
158
-
159
- const actionTypeOptions = computed(() => {
160
- const o: {
161
- text: string;
162
- label: string;
163
- value: string;
164
- disabled?: boolean;
165
- }[] = [
166
- {
167
- text: '组件',
168
- label: '组件',
169
- value: ActionType.COMP,
170
- },
171
- ];
172
-
173
- if (!propsService.getDisabledCodeBlock()) {
174
- o.push({
175
- text: '代码',
176
- label: '代码',
177
- disabled: !Object.keys(codeBlockService.getCodeDsl() || {}).length,
178
- value: ActionType.CODE,
179
- });
180
- }
181
-
182
- if (!propsService.getDisabledDataSource()) {
183
- o.push({
184
- text: '数据源',
185
- label: '数据源',
186
- value: ActionType.DATA_SOURCE,
187
- });
188
- }
189
-
190
- return o;
191
- });
192
-
193
- // 联动类型
194
- const actionTypeConfig = computed(() => {
195
- const defaultActionTypeConfig = {
196
- name: 'actionType',
197
- text: '联动类型',
198
- type: 'select',
199
- labelPosition: 'left',
200
- defaultValue: ActionType.COMP,
201
- options: actionTypeOptions.value,
202
- rules: [
203
- {
204
- required: true,
205
- message: '联动类型不能为空',
206
- },
207
- {
208
- typeMatch: true,
209
- trigger: 'blur',
210
- },
211
- ],
212
- onChange: (_mForm: FormState, _v: string, { setModel }: any) => {
213
- setModel('to', '');
214
- setModel('method', '');
215
- setModel('codeId', '');
216
- setModel('dataSourceMethod', []);
217
- },
218
- };
219
- return { ...defaultActionTypeConfig, ...props.config.actionTypeConfig };
220
- });
221
-
222
- // 联动组件配置
223
- const targetCompConfig = computed(() => {
224
- const defaultTargetCompConfig: UISelectConfig = {
225
- name: 'to',
226
- text: '联动组件',
227
- type: 'ui-select',
228
- labelPosition: 'left',
229
- display: (_mForm, { model }) => model.actionType === ActionType.COMP,
230
- onChange: (_mForm, _v, { setModel }) => {
231
- setModel('method', '');
232
- },
233
- rules: [
234
- {
235
- typeMatch: true,
236
- trigger: 'blur',
237
- },
238
- ],
239
- };
240
- return { ...defaultTargetCompConfig, ...props.config.targetCompConfig };
241
- });
242
-
243
- // 联动组件动作配置
244
- const compActionConfig = computed(() => {
245
- const defaultCompActionConfig: DynamicTypeConfig = {
246
- name: 'method',
247
- text: '动作',
248
- labelPosition: 'left',
249
- type: (mForm: FormState | undefined, { model }: any) => {
250
- const to = editorService.getNodeById(model.to);
251
-
252
- if (to && to.type === 'page-fragment-container' && to.pageFragmentId) {
253
- return 'cascader';
254
- }
255
-
256
- return 'select';
257
- },
258
- checkStrictly: () => props.config.src !== 'component',
259
- display: (mForm: FormState | undefined, { model }: any) => model.actionType === ActionType.COMP,
260
- options: (_mForm: FormState, { model }: any) => getCompActionOptions(model.to),
261
- rules: [
262
- {
263
- trigger: 'blur',
264
- validator: ({ value, callback }: any, { model }: any) => {
265
- const allowedMethods = getCompActionAllowedValues(props.config as any, model);
266
- const normalized = normalizeCompActionValue(value);
267
- if (allowedMethods && allowedMethods.size > 0 && normalized && !allowedMethods.has(normalized)) {
268
- return callback(`动作名(${normalized})不存在`);
269
- }
270
- callback();
271
- },
272
- },
273
- ],
274
- };
275
- return { ...defaultCompActionConfig, ...props.config.compActionConfig };
276
- });
277
-
278
- // 代码联动配置
279
- const codeActionConfig = computed(() => {
280
- const defaultCodeActionConfig: CodeSelectColConfig = {
281
- type: 'code-select-col',
282
- text: '代码块',
283
- name: 'codeId',
284
- notEditable: () => !codeBlockService.getEditStatus(),
285
- display: (mForm, { model }) => model.actionType === ActionType.CODE,
286
- };
287
- return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
288
- });
73
+ const tableConfig = computed(() => createLegacyTableConfig(props.config));
289
74
 
290
- // 数据源联动配置
291
- const dataSourceActionConfig = computed(() => {
292
- const defaultDataSourceActionConfig: DataSourceMethodSelectConfig = {
293
- type: 'data-source-method-select',
294
- text: '数据源方法',
295
- name: 'dataSourceMethod',
296
- notEditable: () => !dataSourceService.get('editable'),
297
- display: (mForm, { model }) => model.actionType === ActionType.DATA_SOURCE,
298
- };
299
- return { ...defaultDataSourceActionConfig, ...props.config.dataSourceActionConfig };
300
- });
301
-
302
- // 兼容旧的数据格式
303
- const tableConfig = computed(
304
- () =>
305
- defineFormItem({
306
- type: 'table',
307
- name: 'events',
308
- items: [
309
- {
310
- name: 'name',
311
- label: '事件名',
312
- type: eventNameConfig.value.type,
313
- options: (mForm: FormState, { formValue }: any) =>
314
- eventsService
315
- .getEvent(formValue.type, { node: editorService.getNodeById(formValue.id) })
316
- .map((option: any) => ({
317
- text: option.label,
318
- value: option.value,
319
- })),
320
- },
321
- {
322
- name: 'to',
323
- label: '联动组件',
324
- type: 'ui-select',
325
- },
326
- {
327
- name: 'method',
328
- label: '动作',
329
- type: compActionConfig.value.type,
330
- options: (mForm: FormState, { model, formValue }: any) => {
331
- const node = editorService.getNodeById(model.to) || (formValue as MNode);
332
- if (!node?.type) return [];
75
+ const eventNameConfig = computed(() => createEventNameConfig(props.config));
333
76
 
334
- return eventsService.getMethod(node.type, { targetId: model.to, node }).map((option: any) => ({
335
- text: option.label,
336
- value: option.value,
337
- }));
338
- },
339
- },
340
- ],
341
- }) as TableConfig,
342
- );
77
+ const eventConfig = computed(() => createEventSelectConfig(props.config, props.name));
343
78
 
344
- // 组件动作组表单配置
345
- const actionsConfig = computed(
346
- () =>
347
- defineFormItem({
348
- type: 'panel',
349
- labelPosition: 'left',
350
- items: [
351
- {
352
- type: 'group-list',
353
- name: 'actions',
354
- expandAll: true,
355
- enableToggleMode: false,
356
- titlePrefix: '动作',
357
- labelPosition: 'left',
358
- items: [
359
- actionTypeConfig.value,
360
- targetCompConfig.value,
361
- compActionConfig.value,
362
- codeActionConfig.value,
363
- dataSourceActionConfig.value,
364
- ],
365
- },
366
- ],
367
- }) as PanelConfig,
368
- );
369
-
370
- // 是否为旧的数据格式
371
- const isOldVersion = computed(() => {
372
- if (props.model[props.name].length === 0) return false;
373
- return !has(props.model[props.name][0], 'actions');
374
- });
79
+ const isOldVersion = computed(() => isLegacyEventValue(props.model[props.name]));
375
80
 
376
81
  /**
377
82
  * 对比模式判定:
378
83
  *
379
- * event-select 内部由「事件列表 + 嵌套子表单」组成,属于复合字段。父级 `MFormContainer` 已将其
380
- * 归入「自接管对比字段」(见 Container.vue 的 `SELF_DIFF_FIELD_TYPES`),即对比时只渲染一次本组件,
381
- * 并把当前值 `model` 与历史值 `lastValues` 一并传入,由本组件把 `is-compare`/`lastValues` 透传给
382
- * 内部的 MPanel / MFormContainer,逐项(事件名、动作)展示前后差异。
84
+ * event-select 内部是事件列表 group-list。父级 `MFormContainer` 已将其归入「自接管对比字段」
85
+ * (见 Container.vue 的 `SELF_DIFF_FIELD_TYPES`),对比时只渲染一次本组件,并把 `is-compare` /
86
+ * `lastValues` 透传给内部 MGroupList title slot 里的事件名表单。
383
87
  *
384
88
  * 仅当存在历史值时才启用对比,避免 lastValues 缺失时退化为「全部新增」的空对比。
385
89
  */
386
90
  const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues));
387
91
 
388
- /**
389
- * 待渲染的事件卡片列表。
390
- *
391
- * - 非对比模式:直接映射当前事件列表,`lastCardItem` 为空;
392
- * - 对比模式:按索引对齐当前值与历史值,取两者长度的最大值,使得「新增」(仅当前有)与
393
- * 「删除」(仅历史有)的事件都能被渲染出来;缺失的一侧用空对象兜底,从而让子级正确高亮差异。
394
- */
395
- const displayList = computed<{ cardItem: any; lastCardItem: any; index: number }[]>(() => {
396
- const current = props.model[props.name] || [];
397
-
398
- if (!isCompareMode.value) {
399
- return current.map((cardItem: any, index: number) => ({ cardItem, lastCardItem: undefined, index }));
400
- }
401
-
402
- const last = props.lastValues?.[props.name] || [];
403
- const length = Math.max(current.length, last.length);
404
-
405
- return Array.from({ length }, (_, index) => ({
406
- cardItem: current[index] ?? {},
407
- lastCardItem: last[index] ?? {},
408
- index,
409
- }));
410
- });
411
-
412
- // 添加事件
413
- const addEvent = () => {
414
- const defaultEvent = {
415
- name: '',
416
- actions: [],
417
- };
418
-
419
- if (!props.model[props.name]) {
420
- props.model[props.name] = [];
421
- }
422
-
423
- emit('change', defaultEvent, {
424
- modifyKey: props.model[props.name].length,
425
- });
426
- };
427
-
428
- // 删除事件
429
- const removeEvent = (index: number) => {
430
- if (!props.name) return;
431
- props.model[props.name].splice(index, 1);
432
- emit('change', props.model[props.name]);
433
- };
434
-
435
- const eventNameChangeHandler = (v: any, eventData: ContainerChangeEventData) => {
436
- emit('change', props.model[props.name], eventData);
437
- };
438
-
439
- const onChangeHandler = (v: any, eventData: ContainerChangeEventData) =>
92
+ const onChangeHandler = (_v: any, eventData?: ContainerChangeEventData) =>
440
93
  emit('change', props.model[props.name], eventData);
441
94
  </script>