@tmagic/editor 1.8.0-beta.11 → 1.8.0-beta.12

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 (111) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +7 -4
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +4 -0
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +2 -1
  4. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +5 -2
  5. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +60 -14
  6. package/dist/es/components/ScrollBar.js +0 -1
  7. package/dist/es/editorProps.js +6 -1
  8. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +15 -3
  9. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +2 -17
  10. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +2 -1
  11. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +7 -0
  12. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +5 -17
  13. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +4 -0
  14. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +29 -3
  15. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +50 -88
  16. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +2 -5
  17. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +48 -23
  18. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +6 -3
  19. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +28 -22
  20. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +28 -22
  21. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +28 -22
  22. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +6 -3
  23. package/dist/es/fields/UISelect.js +0 -1
  24. package/dist/es/index.js +9 -6
  25. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +21 -6
  26. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +9 -6
  27. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +4 -1
  28. package/dist/es/layouts/history-list/composables.js +1 -1
  29. package/dist/es/layouts/history-list/useHistoryRevert.js +7 -3
  30. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +44 -5
  31. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +15 -8
  32. package/dist/es/layouts/props-panel/use-style-panel.js +3 -2
  33. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +15 -31
  34. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
  35. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +18 -3
  36. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +4 -0
  37. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
  38. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +18 -3
  39. package/dist/es/layouts/sidebar/layer/LayerNodeContent.js +5 -0
  40. package/dist/es/layouts/sidebar/layer/LayerNodeContent.vue_vue_type_script_setup_true_lang.js +46 -0
  41. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +2 -1
  42. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +8 -2
  43. package/dist/es/plugin.js +3 -1
  44. package/dist/es/services/editor.js +104 -5
  45. package/dist/es/services/props.js +2 -1
  46. package/dist/es/services/storage.js +1 -1
  47. package/dist/es/services/ui.js +3 -0
  48. package/dist/es/style.css +104 -55
  49. package/dist/es/utils/data-source/index.js +30 -11
  50. package/dist/es/utils/event.js +123 -0
  51. package/dist/es/utils/monaco-editor.js +22 -6
  52. package/dist/es/utils/props.js +94 -8
  53. package/dist/es/utils/type-match-rules.js +360 -0
  54. package/dist/style.css +104 -55
  55. package/dist/tmagic-editor.umd.cjs +16895 -15757
  56. package/package.json +8 -8
  57. package/src/Editor.vue +3 -1
  58. package/src/components/CodeBlockEditor.vue +3 -0
  59. package/src/components/CompareForm.vue +7 -0
  60. package/src/components/FloatingBox.vue +77 -8
  61. package/src/components/ScrollBar.vue +0 -67
  62. package/src/editorProps.ts +13 -0
  63. package/src/fields/CodeSelect.vue +4 -1
  64. package/src/fields/CondOpSelect.vue +2 -24
  65. package/src/fields/DataSourceFields.vue +5 -0
  66. package/src/fields/DataSourceInput.vue +7 -25
  67. package/src/fields/DataSourceMocks.vue +3 -0
  68. package/src/fields/DisplayConds.vue +14 -0
  69. package/src/fields/EventSelect.vue +84 -129
  70. package/src/fields/StyleSetter/Index.vue +2 -8
  71. package/src/fields/StyleSetter/pro/Background.vue +94 -55
  72. package/src/fields/StyleSetter/pro/Border.vue +9 -11
  73. package/src/fields/StyleSetter/pro/Font.vue +66 -64
  74. package/src/fields/StyleSetter/pro/Layout.vue +140 -138
  75. package/src/fields/StyleSetter/pro/Position.vue +69 -67
  76. package/src/fields/StyleSetter/pro/Transform.vue +23 -25
  77. package/src/fields/UISelect.vue +0 -12
  78. package/src/index.ts +1 -0
  79. package/src/layouts/CodeEditor.vue +32 -6
  80. package/src/layouts/Framework.vue +7 -6
  81. package/src/layouts/history-list/HistoryDiffDialog.vue +11 -1
  82. package/src/layouts/history-list/useHistoryRevert.ts +4 -0
  83. package/src/layouts/props-panel/FormPanel.vue +66 -5
  84. package/src/layouts/props-panel/PropsPanel.vue +20 -6
  85. package/src/layouts/props-panel/use-style-panel.ts +4 -3
  86. package/src/layouts/sidebar/Sidebar.vue +2 -0
  87. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +4 -1
  88. package/src/layouts/sidebar/code-block/useContentMenu.ts +13 -1
  89. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +3 -0
  90. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +2 -2
  91. package/src/layouts/sidebar/data-source/useContentMenu.ts +13 -1
  92. package/src/layouts/sidebar/layer/LayerNodeContent.vue +57 -0
  93. package/src/layouts/sidebar/layer/LayerPanel.vue +4 -1
  94. package/src/layouts/workspace/viewer/NodeListMenu.vue +4 -1
  95. package/src/plugin.ts +3 -1
  96. package/src/services/editor.ts +168 -3
  97. package/src/services/ui.ts +6 -0
  98. package/src/theme/event.scss +8 -0
  99. package/src/theme/floating-box.scss +9 -1
  100. package/src/theme/layer-node-content.scss +14 -0
  101. package/src/theme/scroll-bar.scss +64 -0
  102. package/src/theme/theme.scss +3 -0
  103. package/src/theme/ui-select.scss +9 -0
  104. package/src/type.ts +53 -2
  105. package/src/utils/data-source/index.ts +30 -12
  106. package/src/utils/event.ts +224 -0
  107. package/src/utils/index.ts +2 -0
  108. package/src/utils/monaco-editor.ts +27 -0
  109. package/src/utils/props.ts +160 -2
  110. package/src/utils/type-match-rules.ts +678 -0
  111. package/types/index.d.ts +372 -85
@@ -67,11 +67,9 @@ import { computed } from 'vue';
67
67
  import { Delete } from '@element-plus/icons-vue';
68
68
  import { has } from 'lodash-es';
69
69
 
70
- import type { EventOption, MComponent, MContainer } from '@tmagic/core';
71
70
  import { ActionType } from '@tmagic/core';
72
71
  import { TMagicButton } from '@tmagic/design';
73
72
  import type {
74
- CascaderOption,
75
73
  CodeSelectColConfig,
76
74
  ContainerChangeEventData,
77
75
  DataSourceMethodSelectConfig,
@@ -84,10 +82,15 @@ import type {
84
82
  UISelectConfig,
85
83
  } from '@tmagic/form';
86
84
  import { defineFormItem, MContainer as MFormContainer, MPanel, MTable } from '@tmagic/form';
87
- import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils';
88
85
 
89
86
  import { useServices } from '@editor/hooks/use-services';
90
- import { getCascaderOptionsFromFields } from '@editor/utils';
87
+ import {
88
+ getCompActionAllowedValues,
89
+ getCompActionOptions,
90
+ getEventNameAllowedValues,
91
+ getEventNameOptions,
92
+ normalizeCompActionValue,
93
+ } from '@editor/utils';
91
94
 
92
95
  defineOptions({
93
96
  name: 'MFieldsEventSelect',
@@ -118,68 +121,57 @@ const eventNameConfig = computed(() => {
118
121
  labelWidth: '40px',
119
122
  checkStrictly: () => props.config.src !== 'component',
120
123
  valueSeparator: '.',
121
- options: (mForm: FormState, { formValue }: any) => {
122
- let events: EventOption[] | CascaderOption[] = [];
123
-
124
- if (props.config.src === 'component') {
125
- events = eventsService.getEvent(formValue.type);
126
-
127
- if (formValue.type === 'page-fragment-container' && formValue.pageFragmentId) {
128
- const pageFragment = editorService.get('root')?.items?.find((page) => page.id === formValue.pageFragmentId);
129
- if (pageFragment) {
130
- events = [
131
- {
132
- label: pageFragment.name || '页面片容器',
133
- value: pageFragment.id,
134
- children: events,
135
- },
136
- ];
137
- pageFragment.items.forEach((node) => {
138
- traverseNode<MComponent | MContainer>(node, (node) => {
139
- const nodeEvents = (node.type && eventsService.getEvent(node.type)) || [];
140
-
141
- events.push({
142
- label: `${node.name}_${node.id}`,
143
- value: `${node.id}`,
144
- children: nodeEvents,
145
- });
146
- });
147
- });
148
-
149
- return events;
124
+ options: (_mForm: FormState, { formValue }: any) => getEventNameOptions(props.config.src, formValue),
125
+ rules: [
126
+ {
127
+ validator: ({ value, callback }: any, { formValue }: any) => {
128
+ const allowedNames = getEventNameAllowedValues(props.config as any, formValue);
129
+ if (allowedNames && allowedNames.size > 0 && value && !allowedNames.has(value)) {
130
+ return callback(`事件名(${value})不存在`);
150
131
  }
151
- }
152
-
153
- return events.map((option) => ({
154
- text: option.label,
155
- value: option.value,
156
- }));
157
- }
158
-
159
- if (props.config.src === 'datasource') {
160
- // 从数据源类型中获取到相关事件
161
- events = dataSourceService.getFormEvent(formValue.type);
162
- // 从数据源类型和实例中分别获取数据以追加数据变化的事件
163
- const dataSource = dataSourceService.getDataSourceById(formValue.id);
164
- const fields = dataSource?.fields || [];
165
- if (fields.length > 0) {
166
- return [
167
- ...events,
168
- {
169
- label: '数据变化',
170
- value: DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX,
171
- children: getCascaderOptionsFromFields(fields),
172
- },
173
- ];
174
- }
175
-
176
- return events;
177
- }
178
- },
132
+ callback();
133
+ },
134
+ trigger: 'blur',
135
+ },
136
+ ],
179
137
  };
180
138
  return { ...defaultEventNameConfig, ...props.config.eventNameConfig };
181
139
  });
182
140
 
141
+ const actionTypeOptions = computed(() => {
142
+ const o: {
143
+ text: string;
144
+ label: string;
145
+ value: string;
146
+ disabled?: boolean;
147
+ }[] = [
148
+ {
149
+ text: '组件',
150
+ label: '组件',
151
+ value: ActionType.COMP,
152
+ },
153
+ ];
154
+
155
+ if (!propsService.getDisabledCodeBlock()) {
156
+ o.push({
157
+ text: '代码',
158
+ label: '代码',
159
+ disabled: !Object.keys(codeBlockService.getCodeDsl() || {}).length,
160
+ value: ActionType.CODE,
161
+ });
162
+ }
163
+
164
+ if (!propsService.getDisabledDataSource()) {
165
+ o.push({
166
+ text: '数据源',
167
+ label: '数据源',
168
+ value: ActionType.DATA_SOURCE,
169
+ });
170
+ }
171
+
172
+ return o;
173
+ });
174
+
183
175
  // 联动类型
184
176
  const actionTypeConfig = computed(() => {
185
177
  const defaultActionTypeConfig = {
@@ -187,39 +179,17 @@ const actionTypeConfig = computed(() => {
187
179
  text: '联动类型',
188
180
  type: 'select',
189
181
  defaultValue: ActionType.COMP,
190
- options: () => {
191
- const o: {
192
- text: string;
193
- label: string;
194
- value: string;
195
- disabled?: boolean;
196
- }[] = [
197
- {
198
- text: '组件',
199
- label: '组件',
200
- value: ActionType.COMP,
201
- },
202
- ];
203
-
204
- if (!propsService.getDisabledCodeBlock()) {
205
- o.push({
206
- text: '代码',
207
- label: '代码',
208
- disabled: !Object.keys(codeBlockService.getCodeDsl() || {}).length,
209
- value: ActionType.CODE,
210
- });
211
- }
212
-
213
- if (!propsService.getDisabledDataSource()) {
214
- o.push({
215
- text: '数据源',
216
- label: '数据源',
217
- value: ActionType.DATA_SOURCE,
218
- });
219
- }
220
-
221
- return o;
222
- },
182
+ options: actionTypeOptions.value,
183
+ rules: [
184
+ {
185
+ required: true,
186
+ message: '联动类型不能为空',
187
+ },
188
+ {
189
+ typeMatch: true,
190
+ trigger: 'blur',
191
+ },
192
+ ],
223
193
  };
224
194
  return { ...defaultActionTypeConfig, ...props.config.actionTypeConfig };
225
195
  });
@@ -234,6 +204,12 @@ const targetCompConfig = computed(() => {
234
204
  onChange: (_MForm, _v, { setModel }) => {
235
205
  setModel('method', '');
236
206
  },
207
+ rules: [
208
+ {
209
+ typeMatch: true,
210
+ trigger: 'blur',
211
+ },
212
+ ],
237
213
  };
238
214
  return { ...defaultTargetCompConfig, ...props.config.targetCompConfig };
239
215
  });
@@ -254,41 +230,20 @@ const compActionConfig = computed(() => {
254
230
  },
255
231
  checkStrictly: () => props.config.src !== 'component',
256
232
  display: (mForm: FormState | undefined, { model }: any) => model.actionType === ActionType.COMP,
257
- options: (mForm: FormState, { model }: any) => {
258
- const node = editorService.getNodeById(model.to);
259
- if (!node?.type) return [];
260
-
261
- let methods: EventOption[] | CascaderOption[] = [];
262
-
263
- methods = eventsService.getMethod(node.type, model.to);
264
-
265
- if (node.type === 'page-fragment-container' && node.pageFragmentId) {
266
- const pageFragment = editorService.get('root')?.items?.find((page) => page.id === node.pageFragmentId);
267
- if (pageFragment) {
268
- methods = [];
269
- pageFragment.items.forEach((node: MComponent | MContainer) => {
270
- traverseNode<MComponent | MContainer>(node, (node) => {
271
- const nodeMethods = (node.type && eventsService.getMethod(node.type, node.id)) || [];
272
-
273
- if (nodeMethods.length) {
274
- methods.push({
275
- label: `${node.name}_${node.id}`,
276
- value: `${node.id}`,
277
- children: nodeMethods,
278
- });
279
- }
280
- });
281
- });
282
-
283
- return methods;
284
- }
285
- }
286
-
287
- return methods.map((method) => ({
288
- text: method.label,
289
- value: method.value,
290
- }));
291
- },
233
+ options: (_mForm: FormState, { model }: any) => getCompActionOptions(model.to),
234
+ rules: [
235
+ {
236
+ trigger: 'blur',
237
+ validator: ({ value, callback }: any, { model }: any) => {
238
+ const allowedMethods = getCompActionAllowedValues(props.config as any, model);
239
+ const normalized = normalizeCompActionValue(value);
240
+ if (allowedMethods && allowedMethods.size > 0 && normalized && !allowedMethods.has(normalized)) {
241
+ return callback(`动作名(${normalized})不存在`);
242
+ }
243
+ callback();
244
+ },
245
+ },
246
+ ],
292
247
  };
293
248
  return { ...defaultCompActionConfig, ...props.config.compActionConfig };
294
249
  });
@@ -7,6 +7,7 @@
7
7
  v-if="item.component"
8
8
  :is="item.component"
9
9
  :values="model[name]"
10
+ :prop="prop || name"
10
11
  :last-values="lastValues?.[name]"
11
12
  :is-compare="isCompare"
12
13
  :size="size"
@@ -35,7 +36,7 @@ defineOptions({
35
36
  name: 'MFieldsStyleSetter',
36
37
  });
37
38
 
38
- const props = defineProps<FieldProps<StyleSchema>>();
39
+ defineProps<FieldProps<StyleSchema>>();
39
40
 
40
41
  const emit = defineEmits<{
41
42
  change: [v: any, eventData: ContainerChangeEventData];
@@ -77,13 +78,6 @@ const collapseValue = shallowRef(
77
78
  );
78
79
 
79
80
  const change = (v: any, eventData: ContainerChangeEventData) => {
80
- eventData.changeRecords?.forEach((record) => {
81
- if (props.prop) {
82
- record.propPath = `${props.prop}.${record.propPath}`;
83
- } else if (props.name) {
84
- record.propPath = `${props.name}.${record.propPath}`;
85
- }
86
- });
87
81
  emit('change', v, eventData);
88
82
  };
89
83
 
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <MContainer
3
- :config="config"
3
+ v-for="item in formConfig"
4
+ :prop="prop"
5
+ :key="item.name"
6
+ :config="item"
4
7
  :model="values"
5
8
  :last-values="lastValues"
6
9
  :is-compare="isCompare"
@@ -14,7 +17,7 @@
14
17
  <script lang="ts" setup>
15
18
  import { markRaw } from 'vue';
16
19
 
17
- import { type ContainerChangeEventData, defineFormItem, MContainer } from '@tmagic/form';
20
+ import { type ContainerChangeEventData, defineFormConfig, MContainer } from '@tmagic/form';
18
21
  import type { StyleSchema } from '@tmagic/schema';
19
22
 
20
23
  import BackgroundPosition from '../components/BackgroundPosition.vue';
@@ -26,6 +29,7 @@ defineProps<{
26
29
  isCompare?: boolean;
27
30
  disabled?: boolean;
28
31
  size?: 'large' | 'default' | 'small';
32
+ prop?: string;
29
33
  }>();
30
34
 
31
35
  const emit = defineEmits<{
@@ -33,60 +37,95 @@ const emit = defineEmits<{
33
37
  addDiffCount: [];
34
38
  }>();
35
39
 
36
- const config = defineFormItem({
37
- items: [
38
- {
39
- name: 'backgroundColor',
40
- text: '背景色',
41
- labelWidth: '68px',
42
- type: 'data-source-field-select',
43
- fieldConfig: {
44
- type: 'colorPicker',
45
- },
46
- },
47
- {
48
- name: 'backgroundImage',
49
- text: '背景图',
50
- labelWidth: '68px',
51
- type: 'data-source-field-select',
52
- fieldConfig: {
53
- type: 'img-upload',
54
- } as any,
55
- },
56
- {
57
- name: 'backgroundSize',
58
- text: '背景尺寸',
59
- type: 'radioGroup',
60
- childType: 'button',
61
- labelWidth: '68px',
62
- options: [
63
- { value: 'auto', text: '默认', tooltip: '默认 auto' },
64
- { value: 'contain', text: '等比填充', tooltip: '等比填充 contain' },
65
- { value: 'cover', text: '等比覆盖', tooltip: '等比覆盖 cover' },
66
- ],
67
- },
68
- {
69
- name: 'backgroundRepeat',
70
- text: '重复显示',
71
- type: 'radioGroup',
72
- childType: 'button',
73
- labelWidth: '68px',
74
- options: [
75
- { value: 'repeat', icon: markRaw(BackgroundRepeat), tooltip: '垂直和水平方向重复 repeat' },
76
- { value: 'repeat-x', icon: markRaw(BackgroundRepeatX), tooltip: '水平方向重复 repeat-x' },
77
- { value: 'repeat-y', icon: markRaw(BackgroundRepeatY), tooltip: '垂直方向重复 repeat-y' },
78
- { value: 'no-repeat', icon: markRaw(BackgroundNoRepeat), tooltip: '不重复 no-repeat' },
79
- ],
40
+ const formConfig = defineFormConfig([
41
+ {
42
+ name: 'backgroundColor',
43
+ text: '背景色',
44
+ labelWidth: '68px',
45
+ type: 'data-source-field-select',
46
+ fieldConfig: {
47
+ type: 'colorPicker',
80
48
  },
81
- {
82
- name: 'backgroundPosition',
83
- text: '背景定位',
84
- type: 'component',
85
- component: BackgroundPosition,
86
- labelWidth: '68px',
87
- },
88
- ],
89
- });
49
+ },
50
+ {
51
+ name: 'backgroundImage',
52
+ text: '背景图',
53
+ labelWidth: '68px',
54
+ type: 'data-source-field-select',
55
+ fieldConfig: {
56
+ type: 'img-upload',
57
+ } as any,
58
+ },
59
+ {
60
+ name: 'backgroundSize',
61
+ text: '背景尺寸',
62
+ type: 'radioGroup',
63
+ childType: 'button',
64
+ labelWidth: '68px',
65
+ options: [
66
+ { value: 'auto', text: '默认', tooltip: '默认 auto' },
67
+ { value: 'contain', text: '等比填充', tooltip: '等比填充 contain' },
68
+ { value: 'cover', text: '等比覆盖', tooltip: '等比覆盖 cover' },
69
+ ],
70
+ rules: [
71
+ {
72
+ typeMatch: false,
73
+ },
74
+ {
75
+ validator: ({ value, callback }) => {
76
+ if (value === '' || value === null || value === undefined) {
77
+ return callback();
78
+ }
79
+
80
+ const keywords = ['auto', 'cover', 'contain', 'inherit', 'initial', 'revert', 'unset'];
81
+ // 单值:关键字 或 长度/百分比
82
+ const lengthPercent = /^-?\d+(\.\d+)?(px|em|rem|ex|ch|vw|vh|vmin|vmax|cm|mm|in|pt|pc|%)$/;
83
+ const singleValue = (v: string) => keywords.includes(v) || lengthPercent.test(v);
84
+
85
+ const str = String(value).trim();
86
+ const parts = str.split(/\s+/);
87
+
88
+ // cover / contain 不能与其他值组合
89
+ if (parts.length > 1 && (parts.includes('cover') || parts.includes('contain'))) {
90
+ return callback('cover/contain 不能与其他值组合');
91
+ }
92
+
93
+ // 多值最多两个
94
+ if (parts.length > 2) {
95
+ return callback('backgroundSize 最多支持两个值');
96
+ }
97
+
98
+ // 关键字 auto 在多值场景中允许与其他长度/百分比组合
99
+ if (parts.every((part) => singleValue(part))) {
100
+ return callback();
101
+ }
102
+
103
+ return callback('backgroundSize 值不合法');
104
+ },
105
+ },
106
+ ],
107
+ },
108
+ {
109
+ name: 'backgroundRepeat',
110
+ text: '重复显示',
111
+ type: 'radioGroup',
112
+ childType: 'button',
113
+ labelWidth: '68px',
114
+ options: [
115
+ { value: 'repeat', icon: markRaw(BackgroundRepeat), tooltip: '垂直和水平方向重复 repeat' },
116
+ { value: 'repeat-x', icon: markRaw(BackgroundRepeatX), tooltip: '水平方向重复 repeat-x' },
117
+ { value: 'repeat-y', icon: markRaw(BackgroundRepeatY), tooltip: '垂直方向重复 repeat-y' },
118
+ { value: 'no-repeat', icon: markRaw(BackgroundNoRepeat), tooltip: '不重复 no-repeat' },
119
+ ],
120
+ },
121
+ {
122
+ name: 'backgroundPosition',
123
+ text: '背景定位',
124
+ type: 'component',
125
+ component: BackgroundPosition,
126
+ labelWidth: '68px',
127
+ },
128
+ ]);
90
129
 
91
130
  const change = (value: StyleSchema, eventData: ContainerChangeEventData) => {
92
131
  emit('change', value, eventData);
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <MContainer
3
+ :prop="prop"
3
4
  :config="config"
4
5
  :model="values"
5
6
  :last-values="lastValues"
@@ -32,6 +33,7 @@ defineProps<{
32
33
  isCompare?: boolean;
33
34
  disabled?: boolean;
34
35
  size?: 'large' | 'default' | 'small';
36
+ prop?: string;
35
37
  }>();
36
38
 
37
39
  const emit = defineEmits<{
@@ -40,17 +42,13 @@ const emit = defineEmits<{
40
42
  }>();
41
43
 
42
44
  const config = defineFormItem({
43
- items: [
44
- {
45
- labelWidth: '68px',
46
- name: 'borderRadius',
47
- text: '圆角',
48
- type: 'data-source-field-select',
49
- fieldConfig: {
50
- type: 'text',
51
- },
52
- },
53
- ],
45
+ labelWidth: '68px',
46
+ name: 'borderRadius',
47
+ text: '圆角',
48
+ type: 'data-source-field-select',
49
+ fieldConfig: {
50
+ type: 'text',
51
+ },
54
52
  });
55
53
 
56
54
  const change = (value: StyleSchema, eventData: ContainerChangeEventData) => {
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <MContainer
3
- :config="config"
3
+ v-for="(item, index) in formConfig"
4
+ :prop="prop"
5
+ :key="index"
6
+ :config="item"
4
7
  :model="values"
5
8
  :last-values="lastValues"
6
9
  :is-compare="isCompare"
@@ -14,7 +17,7 @@
14
17
  <script lang="ts" setup>
15
18
  import { markRaw } from 'vue';
16
19
 
17
- import { type ContainerChangeEventData, defineFormItem, MContainer } from '@tmagic/form';
20
+ import { type ContainerChangeEventData, defineFormConfig, MContainer } from '@tmagic/form';
18
21
  import type { StyleSchema } from '@tmagic/schema';
19
22
 
20
23
  import { AlignCenter, AlignLeft, AlignRight } from '../icons/text-align';
@@ -25,6 +28,7 @@ defineProps<{
25
28
  isCompare?: boolean;
26
29
  disabled?: boolean;
27
30
  size?: 'large' | 'default' | 'small';
31
+ prop?: string;
28
32
  }>();
29
33
 
30
34
  const emit = defineEmits<{
@@ -32,73 +36,71 @@ const emit = defineEmits<{
32
36
  addDiffCount: [];
33
37
  }>();
34
38
 
35
- const config = defineFormItem({
36
- items: [
37
- {
38
- type: 'row',
39
- items: [
40
- {
41
- labelWidth: '68px',
42
- name: 'fontSize',
43
- text: '字号',
44
- type: 'data-source-field-select',
45
- fieldConfig: {
46
- type: 'text',
47
- },
39
+ const formConfig = defineFormConfig([
40
+ {
41
+ type: 'row',
42
+ items: [
43
+ {
44
+ labelWidth: '68px',
45
+ name: 'fontSize',
46
+ text: '字号',
47
+ type: 'data-source-field-select',
48
+ fieldConfig: {
49
+ type: 'text',
48
50
  },
49
- {
50
- labelWidth: '68px',
51
- name: 'lineHeight',
52
- text: '行高',
53
- type: 'data-source-field-select',
54
- fieldConfig: {
55
- type: 'text',
56
- },
57
- },
58
- ],
59
- },
60
- {
61
- name: 'fontWeight',
62
- text: '字重',
63
- labelWidth: '68px',
64
- type: 'data-source-field-select',
65
- fieldConfig: {
66
- type: 'select',
67
- options: ['normal', 'bold']
68
- .concat(
69
- Array(7)
70
- .fill(1)
71
- .map((x, i) => `${i + 1}00`),
72
- )
73
- .map((item) => ({
74
- value: item,
75
- text: item,
76
- })),
77
51
  },
78
- },
79
- {
80
- labelWidth: '68px',
81
- name: 'color',
82
- text: '颜色',
83
- type: 'data-source-field-select',
84
- fieldConfig: {
85
- type: 'colorPicker',
52
+ {
53
+ labelWidth: '68px',
54
+ name: 'lineHeight',
55
+ text: '行高',
56
+ type: 'data-source-field-select',
57
+ fieldConfig: {
58
+ type: 'text',
59
+ },
86
60
  },
61
+ ],
62
+ },
63
+ {
64
+ name: 'fontWeight',
65
+ text: '字重',
66
+ labelWidth: '68px',
67
+ type: 'data-source-field-select',
68
+ fieldConfig: {
69
+ type: 'select',
70
+ options: ['normal', 'bold']
71
+ .concat(
72
+ Array(7)
73
+ .fill(1)
74
+ .map((x, i) => `${i + 1}00`),
75
+ )
76
+ .map((item) => ({
77
+ value: item,
78
+ text: item,
79
+ })),
87
80
  },
88
- {
89
- name: 'textAlign',
90
- text: '对齐',
91
- type: 'radioGroup',
92
- childType: 'button',
93
- labelWidth: '68px',
94
- options: [
95
- { value: 'left', icon: markRaw(AlignLeft), tooltip: '左对齐 row' },
96
- { value: 'center', icon: markRaw(AlignCenter), tooltip: '居中对齐 center' },
97
- { value: 'right', icon: markRaw(AlignRight), tooltip: '右对齐 right' },
98
- ],
81
+ },
82
+ {
83
+ labelWidth: '68px',
84
+ name: 'color',
85
+ text: '颜色',
86
+ type: 'data-source-field-select',
87
+ fieldConfig: {
88
+ type: 'colorPicker',
99
89
  },
100
- ],
101
- });
90
+ },
91
+ {
92
+ name: 'textAlign',
93
+ text: '对齐',
94
+ type: 'radioGroup',
95
+ childType: 'button',
96
+ labelWidth: '68px',
97
+ options: [
98
+ { value: 'left', icon: markRaw(AlignLeft), tooltip: '左对齐 row' },
99
+ { value: 'center', icon: markRaw(AlignCenter), tooltip: '居中对齐 center' },
100
+ { value: 'right', icon: markRaw(AlignRight), tooltip: '右对齐 right' },
101
+ ],
102
+ },
103
+ ]);
102
104
 
103
105
  const change = (value: StyleSchema, eventData: ContainerChangeEventData) => {
104
106
  emit('change', value, eventData);