@tmagic/editor 1.8.0-beta.12 → 1.8.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +15 -3
- package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +13 -121
- package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +11 -4
- package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +7 -2
- package/dist/es/components/ViewForm.js +5 -0
- package/dist/es/components/ViewForm.vue_vue_type_script_setup_true_lang.js +58 -0
- package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +80 -55
- package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +10 -7
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +4 -1
- package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +8 -2
- package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +99 -64
- package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +5 -4
- package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/fields/StyleSetter/icons/align-items/Center.js +31 -0
- package/dist/es/fields/StyleSetter/icons/align-items/FlexEnd.js +31 -0
- package/dist/es/fields/StyleSetter/icons/align-items/FlexStart.js +31 -0
- package/dist/es/fields/StyleSetter/icons/align-items/SpaceAround.js +50 -0
- package/dist/es/fields/StyleSetter/icons/align-items/SpaceBetween.js +50 -0
- package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +6 -6
- package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +5 -2
- package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +28 -5
- package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +31 -5
- package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +10 -2
- package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +10 -2
- package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +10 -2
- package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +10 -2
- package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +16 -2
- package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +18 -2
- package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +18 -2
- package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +36 -2
- package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +32 -2
- package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +7 -6
- package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +6 -3
- package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +47 -25
- package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/hooks/use-compare-form.js +119 -0
- package/dist/es/icons/DatasourceIcon.js +17 -0
- package/dist/es/index.js +6 -4
- package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +14 -10
- package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +10 -1
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/es/plugin.js +2 -1
- package/dist/es/services/codeBlock.js +1 -1
- package/dist/es/services/editor.js +54 -13
- package/dist/es/services/history.js +1 -1
- package/dist/es/services/props.js +1 -1
- package/dist/es/services/storage.js +1 -1
- package/dist/es/services/ui.js +5 -3
- package/dist/es/style.css +303 -40
- package/dist/es/utils/config.js +4 -1
- package/dist/es/utils/const.js +2 -2
- package/dist/es/utils/props.js +69 -30
- package/dist/style.css +303 -40
- package/dist/themes/magic-admin.css +3000 -0
- package/dist/tmagic-editor.umd.cjs +1748 -1079
- package/package.json +7 -7
- package/src/Editor.vue +22 -1
- package/src/components/CompareForm.vue +15 -202
- package/src/components/ContentMenu.vue +10 -2
- package/src/components/FloatingBox.vue +8 -2
- package/src/components/ViewForm.vue +55 -0
- package/src/editorProps.ts +4 -0
- package/src/fields/CodeSelect.vue +54 -43
- package/src/fields/CodeSelectCol.vue +1 -0
- package/src/fields/DataSourceFieldSelect/Index.vue +14 -5
- package/src/fields/DisplayConds.vue +4 -1
- package/src/fields/EventSelect.vue +56 -32
- package/src/fields/StyleSetter/Index.vue +1 -4
- package/src/fields/StyleSetter/components/Border.vue +1 -1
- package/src/fields/StyleSetter/icons/align-items/Center.vue +19 -0
- package/src/fields/StyleSetter/icons/align-items/FlexEnd.vue +19 -0
- package/src/fields/StyleSetter/icons/align-items/FlexStart.vue +19 -0
- package/src/fields/StyleSetter/icons/align-items/SpaceAround.vue +34 -0
- package/src/fields/StyleSetter/icons/align-items/SpaceBetween.vue +34 -0
- package/src/fields/StyleSetter/icons/align-items/index.ts +5 -0
- package/src/fields/StyleSetter/icons/background-repeat/NoRepeat.vue +20 -4
- package/src/fields/StyleSetter/icons/background-repeat/Repeat.vue +10 -28
- package/src/fields/StyleSetter/icons/background-repeat/RepeatX.vue +4 -5
- package/src/fields/StyleSetter/icons/background-repeat/RepeatY.vue +4 -5
- package/src/fields/StyleSetter/icons/flex-direction/Column.vue +4 -3
- package/src/fields/StyleSetter/icons/flex-direction/ColumnReverse.vue +4 -3
- package/src/fields/StyleSetter/icons/flex-direction/Row.vue +4 -3
- package/src/fields/StyleSetter/icons/flex-direction/RowReverse.vue +4 -3
- package/src/fields/StyleSetter/icons/justify-content/Center.vue +5 -2
- package/src/fields/StyleSetter/icons/justify-content/FlexEnd.vue +15 -2
- package/src/fields/StyleSetter/icons/justify-content/FlexStart.vue +15 -2
- package/src/fields/StyleSetter/icons/justify-content/SpaceAround.vue +28 -3
- package/src/fields/StyleSetter/icons/justify-content/SpaceBetween.vue +28 -2
- package/src/fields/StyleSetter/pro/Background.vue +7 -2
- package/src/fields/StyleSetter/pro/Font.vue +3 -3
- package/src/fields/StyleSetter/pro/Layout.vue +98 -28
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-compare-form.ts +187 -0
- package/src/icons/DatasourceIcon.vue +7 -0
- package/src/index.ts +4 -0
- package/src/layouts/Framework.vue +7 -1
- package/src/layouts/props-panel/PropsPanel.vue +19 -1
- package/src/layouts/sidebar/Sidebar.vue +3 -3
- package/src/plugin.ts +3 -2
- package/src/services/editor.ts +54 -11
- package/src/services/ui.ts +20 -5
- package/src/theme/code-block.scss +37 -0
- package/src/theme/common/var.scss +1 -1
- package/src/theme/component-list-panel.scss +2 -2
- package/src/theme/data-source-field.scss +4 -0
- package/src/theme/display-conds.scss +11 -0
- package/src/theme/event.scss +58 -4
- package/src/theme/framework.scss +4 -1
- package/src/theme/history-list-panel.scss +9 -9
- package/src/theme/nav-menu.scss +5 -1
- package/src/theme/props-panel.scss +2 -1
- package/src/theme/style-setter/border.scss +14 -5
- package/src/theme/theme.scss +1 -0
- package/src/theme/themes/magic-admin/index.scss +214 -0
- package/src/theme/ui-select.scss +1 -1
- package/src/type.ts +79 -6
- package/src/utils/config.ts +9 -1
- package/src/utils/const.ts +3 -2
- package/src/utils/props.ts +51 -9
- package/types/index.d.ts +314 -203
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.14",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"moveable": "^0.53.0",
|
|
59
59
|
"serialize-javascript": "^7.0.0",
|
|
60
60
|
"sortablejs": "^1.15.6",
|
|
61
|
-
"@tmagic/
|
|
62
|
-
"@tmagic/
|
|
63
|
-
"@tmagic/
|
|
64
|
-
"@tmagic/
|
|
65
|
-
"@tmagic/
|
|
61
|
+
"@tmagic/design": "1.8.0-beta.14",
|
|
62
|
+
"@tmagic/form": "1.8.0-beta.14",
|
|
63
|
+
"@tmagic/stage": "1.8.0-beta.14",
|
|
64
|
+
"@tmagic/table": "1.8.0-beta.14",
|
|
65
|
+
"@tmagic/utils": "1.8.0-beta.14"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/events": "^3.0.3",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"type-fest": "^5.2.0",
|
|
77
77
|
"typescript": "^6.0.3",
|
|
78
78
|
"vue": "^3.5.40",
|
|
79
|
-
"@tmagic/core": "1.8.0-beta.
|
|
79
|
+
"@tmagic/core": "1.8.0-beta.14"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
package/src/Editor.vue
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
:page-bar-sort-options="pageBarSortOptions"
|
|
5
5
|
:page-filter-function="pageFilterFunction"
|
|
6
6
|
:hide-sidebar="hideSidebar"
|
|
7
|
+
:theme="theme"
|
|
7
8
|
>
|
|
8
9
|
<template #header>
|
|
9
10
|
<slot name="header"></slot>
|
|
@@ -135,9 +136,10 @@
|
|
|
135
136
|
<script lang="ts" setup>
|
|
136
137
|
import { EventEmitter } from 'events';
|
|
137
138
|
|
|
138
|
-
import { provide, ref } from 'vue';
|
|
139
|
+
import { computed, provide, ref } from 'vue';
|
|
139
140
|
|
|
140
141
|
import type { MApp } from '@tmagic/core';
|
|
142
|
+
import { M_THEME_KEY } from '@tmagic/design';
|
|
141
143
|
|
|
142
144
|
import Framework from './layouts/Framework.vue';
|
|
143
145
|
import TMagicNavMenu from './layouts/NavMenu.vue';
|
|
@@ -241,6 +243,15 @@ provide(ENABLE_PROPS_FORM_VALIDATE, props.enablePropsFormValidate ?? false);
|
|
|
241
243
|
* 与 PropsPanel 通过 `:extend-state` 显式传入的方式保持等价。
|
|
242
244
|
*/
|
|
243
245
|
provide('extendFormState', props.extendFormState);
|
|
246
|
+
|
|
247
|
+
// 用 computed 包一层再 provide,否则传下去的是 provide 那一刻的值快照,
|
|
248
|
+
// props.isLargeStageContainer 后续变化不会同步到子孙(如 @tmagic/design/ColorPicker)。
|
|
249
|
+
// 子孙侧 `inject<ComputedRef<boolean>>('isLargeStageContainer', computed(() => false))`
|
|
250
|
+
// 用 `.value` 或模板自动解包读取即可拿到最新值。
|
|
251
|
+
provide(
|
|
252
|
+
'isLargeStageContainer',
|
|
253
|
+
computed(() => props.isLargeStageContainer),
|
|
254
|
+
);
|
|
244
255
|
/**
|
|
245
256
|
* 提供 PropsPanel 主属性表单的 formState getter,供历史差异弹窗复用,
|
|
246
257
|
* 让 CompareForm 与 PropsPanel 的 filterFunction 上下文保持一致。
|
|
@@ -256,6 +267,16 @@ provide('historyListExtraTabs', props.historyListExtraTabs);
|
|
|
256
267
|
|
|
257
268
|
provide<EventBus>('eventBus', new EventEmitter());
|
|
258
269
|
|
|
270
|
+
/**
|
|
271
|
+
* 把当前主题以响应式 ref 形式 provide 给后代,供包含 `Teleport` 的组件
|
|
272
|
+
* (如 `TMagicPopover` / `FloatingBox` / `ContentMenu`)在传送目标上挂 `m-theme--<theme>`
|
|
273
|
+
* 类,让主题级 CSS 变量在 portal 节点上也能命中。详见 `@tmagic/design/theme.ts`。
|
|
274
|
+
*/
|
|
275
|
+
provide(
|
|
276
|
+
M_THEME_KEY,
|
|
277
|
+
computed(() => props.theme ?? ''),
|
|
278
|
+
);
|
|
279
|
+
|
|
259
280
|
const propsPanelMountedHandler = (e: InstanceType<typeof FormPanel>) => {
|
|
260
281
|
propsPanelRef.value = e;
|
|
261
282
|
emit('props-panel-mounted', e);
|
|
@@ -19,107 +19,36 @@
|
|
|
19
19
|
</template>
|
|
20
20
|
|
|
21
21
|
<script lang="ts" setup>
|
|
22
|
-
import { computed,
|
|
22
|
+
import { computed, type Ref, type ShallowRef } from 'vue';
|
|
23
23
|
import { isEqual } from 'lodash-es';
|
|
24
24
|
|
|
25
|
-
import { type CodeBlockContent,
|
|
26
|
-
import { type
|
|
27
|
-
import { type FormConfig, type FormState, type FormValue, MForm } from '@tmagic/form';
|
|
25
|
+
import { type CodeBlockContent, HookType } from '@tmagic/core';
|
|
26
|
+
import { type FormConfig, type FormValue, MForm } from '@tmagic/form';
|
|
28
27
|
|
|
29
|
-
import
|
|
30
|
-
import {
|
|
28
|
+
import { useCompareForm } from '@editor/hooks/use-compare-form';
|
|
29
|
+
import type { CompareFormBaseProps } from '@editor/type';
|
|
31
30
|
|
|
32
31
|
defineOptions({
|
|
33
32
|
name: 'MEditorCompareForm',
|
|
34
33
|
});
|
|
35
34
|
|
|
36
35
|
const props = withDefaults(
|
|
37
|
-
defineProps<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* - `category` 为 `data-source` 时,`type` 为数据源类型,例如 'base'、'http'
|
|
46
|
-
* - `category` 为 `code-block` 时,`type` 可不传
|
|
47
|
-
*/
|
|
48
|
-
type?: string;
|
|
49
|
-
/** 表单配置类别,决定从哪里取 FormConfig */
|
|
50
|
-
category?: CompareCategory;
|
|
51
|
-
/** 数据源代码块场景下的数据源类型(base/http),用于代码块表单中"执行时机"展示 */
|
|
52
|
-
dataSourceType?: string;
|
|
53
|
-
labelWidth?: string;
|
|
54
|
-
/**
|
|
55
|
-
* 外层容器高度。设置后表单内容超出时会在 CompareForm 内部出现滚动条,
|
|
56
|
-
* 避免 dialog / 面板使用方需要自行处理滚动。可传任意 CSS 长度,例如 `60vh` / `400px` / `100%`。
|
|
57
|
-
*/
|
|
58
|
-
height?: string;
|
|
59
|
-
/**
|
|
60
|
-
* 用户自定义注入到 MForm.formState 的扩展字段,与 Editor 顶层的 `extendFormState`、
|
|
61
|
-
* PropsPanel 的 `extend-state` 语义一致。表单 item 的 `display` / `disabled` 等
|
|
62
|
-
* filterFunction 经常依赖这里注入的字段(如 stage、自定义业务上下文等),
|
|
63
|
-
* 因此在差异对比场景下也需要透传,避免出现 `formState.xxx is undefined` 的运行时错误。
|
|
64
|
-
*/
|
|
65
|
-
extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
66
|
-
/**
|
|
67
|
-
* 外部透传的基础 formState(通常来自 PropsPanel 主属性表单)。
|
|
68
|
-
* CompareForm 会提取其中的扩展字段覆盖到自己的 formState,保证 filterFunction 上下文一致。
|
|
69
|
-
*/
|
|
70
|
-
baseFormState?: FormState;
|
|
71
|
-
/** 需要走 self diff 的字段类型(例如 mod-cond)。 */
|
|
72
|
-
selfDiffFieldTypes?: string[];
|
|
73
|
-
/**
|
|
74
|
-
* 表单内组件的尺寸(透传给 MForm 的 `size`),可选 'large' | 'default' | 'small'。
|
|
75
|
-
* 缺省时使用 MForm 内置默认尺寸。
|
|
76
|
-
*/
|
|
77
|
-
size?: FieldSize;
|
|
78
|
-
/**
|
|
79
|
-
* 自定义 FormConfig 加载逻辑。传入后将接管内置的按 `category`(node/data-source/code-block)
|
|
80
|
-
* 取配置逻辑,调用方可根据业务自行返回(或异步返回)表单配置。可通过
|
|
81
|
-
* `ctx.defaultLoadConfig()` 复用默认结果再做二次加工。返回的 config 直接用于对比展示。
|
|
82
|
-
*/
|
|
83
|
-
loadConfig?: CompareFormLoadConfig;
|
|
84
|
-
/** 编辑器服务集合,由调用方传入(不再通过 inject('services') 获取)。 */
|
|
85
|
-
services?: Services;
|
|
86
|
-
}>(),
|
|
36
|
+
defineProps<
|
|
37
|
+
CompareFormBaseProps & {
|
|
38
|
+
/** 用于对比的旧值(修改前的值) */
|
|
39
|
+
lastValue?: CompareFormBaseProps['value'];
|
|
40
|
+
/** 需要走 self diff 的字段类型(例如 mod-cond)。 */
|
|
41
|
+
selfDiffFieldTypes?: string[];
|
|
42
|
+
}
|
|
43
|
+
>(),
|
|
87
44
|
{
|
|
88
45
|
category: 'node',
|
|
89
46
|
labelWidth: '120px',
|
|
90
|
-
extendState: (state: FormState) => state,
|
|
91
47
|
},
|
|
92
48
|
);
|
|
93
49
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const config = ref<FormConfig>([]);
|
|
97
|
-
|
|
98
|
-
/** vs-code 编辑器的 monaco 配置项,沿用 Editor 顶层 provide('codeOptions', ...) 的注入。 */
|
|
99
|
-
const codeOptions = inject<Record<string, any>>('codeOptions', {});
|
|
100
|
-
|
|
101
|
-
/** 将代码块的 content 字段统一成字符串,便于在表单/对比中展示 */
|
|
102
|
-
const normalizeCodeBlockValue = (
|
|
103
|
-
v: Partial<CodeBlockContent> | Record<string, any> | undefined,
|
|
104
|
-
): Record<string, any> => {
|
|
105
|
-
if (!v) return {};
|
|
106
|
-
const next: Record<string, any> = { ...v };
|
|
107
|
-
if (next.content && typeof next.content !== 'string') {
|
|
108
|
-
try {
|
|
109
|
-
next.content = next.content.toString();
|
|
110
|
-
} catch {
|
|
111
|
-
next.content = '';
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return next;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const currentValues = computed<FormValue>(() => {
|
|
118
|
-
if (props.category === 'code-block') {
|
|
119
|
-
return normalizeCodeBlockValue(props.value as Partial<CodeBlockContent>);
|
|
120
|
-
}
|
|
121
|
-
return (props.value || {}) as FormValue;
|
|
122
|
-
});
|
|
50
|
+
const { config, currentValues, wrapperStyle, mergedExtendState, loadConfig, formRef, normalizeCodeBlockValue } =
|
|
51
|
+
useCompareForm(props);
|
|
123
52
|
|
|
124
53
|
const lastValuesProcessed = computed<FormValue>(() => {
|
|
125
54
|
if (props.category === 'code-block') {
|
|
@@ -128,18 +57,6 @@ const lastValuesProcessed = computed<FormValue>(() => {
|
|
|
128
57
|
return (props.lastValue || {}) as FormValue;
|
|
129
58
|
});
|
|
130
59
|
|
|
131
|
-
/**
|
|
132
|
-
* 外层包裹层的样式:当传入 `height` 时启用固定高度 + 内部滚动,
|
|
133
|
-
* 这样滚动条会出现在 CompareForm 内部,避免父容器(如 Dialog)自身也产生滚动。
|
|
134
|
-
*/
|
|
135
|
-
const wrapperStyle = computed(() => {
|
|
136
|
-
if (!props.height) return undefined;
|
|
137
|
-
return {
|
|
138
|
-
height: props.height,
|
|
139
|
-
overflow: 'auto',
|
|
140
|
-
} as Record<string, string>;
|
|
141
|
-
});
|
|
142
|
-
|
|
143
60
|
/**
|
|
144
61
|
* `code-select` 字段在历史数据中存在两种"语义为空"的形态:
|
|
145
62
|
* - 字符串 `''`(旧数据 / 用户从未配置过钩子);
|
|
@@ -168,110 +85,6 @@ const showDiff = ({ curValue, lastValue, config }: { curValue: any; lastValue: a
|
|
|
168
85
|
return !isEqual(curValue, lastValue);
|
|
169
86
|
};
|
|
170
87
|
|
|
171
|
-
const removeStyleDisplayConfig = (formConfig: FormConfig): FormConfig =>
|
|
172
|
-
formConfig.map((item) => {
|
|
173
|
-
if (!('type' in item)) return item;
|
|
174
|
-
if (item?.type !== 'tab' || !Array.isArray(item.items)) return item;
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
...item,
|
|
178
|
-
items: item.items.map((tabPane) => {
|
|
179
|
-
if (tabPane?.title !== '样式' || !Array.isArray(tabPane.items)) return tabPane;
|
|
180
|
-
|
|
181
|
-
return {
|
|
182
|
-
...tabPane,
|
|
183
|
-
display: true,
|
|
184
|
-
};
|
|
185
|
-
}),
|
|
186
|
-
};
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const mergedExtendState = (state: FormState) => {
|
|
190
|
-
return props.extendState(props.baseFormState || state);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* 内置的默认 FormConfig 加载逻辑:按 `category` 从对应 service / 工具取配置。
|
|
195
|
-
* 作为 ctx.defaultLoadConfig 透传给自定义 `loadConfig`,方便复用与二次加工。
|
|
196
|
-
*/
|
|
197
|
-
const defaultLoadConfig = async (): Promise<FormConfig> => {
|
|
198
|
-
if (!props.services) {
|
|
199
|
-
return [];
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
switch (props.category) {
|
|
203
|
-
case 'node': {
|
|
204
|
-
if (!props.type) {
|
|
205
|
-
return [];
|
|
206
|
-
}
|
|
207
|
-
return removeStyleDisplayConfig(
|
|
208
|
-
await props.services.propsService.getPropsConfig(props.type, { node: props.value as unknown as MNode }),
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
case 'data-source': {
|
|
212
|
-
const config = props.services.dataSourceService.getFormConfig(props.type || 'base');
|
|
213
|
-
// 数据源表单外层 tab 的「数据定义」项 status 为 'fields',tab-pane name 随之为 'fields'。
|
|
214
|
-
// 未显式设置 active 时,Tabs 默认取 '0',与 'fields' 不匹配会导致打开弹窗时无默认激活项,
|
|
215
|
-
// 这里与 DataSourceConfigPanel 保持一致,默认激活「数据定义」tab。
|
|
216
|
-
return config.map((item) => ('type' in item && item.type === 'tab' ? { ...item, active: 'fields' } : item));
|
|
217
|
-
}
|
|
218
|
-
case 'code-block': {
|
|
219
|
-
return getCodeBlockFormConfig({
|
|
220
|
-
paramColConfig: props.services.codeBlockService.getParamsColConfig(),
|
|
221
|
-
// 通过传入 dataSourceType 间接表达"是数据源代码块"——在对比场景下 props.dataSourceType
|
|
222
|
-
// 由调用方按 step 上下文显式传入,未传则视为普通代码块,「执行时机」字段隐藏。
|
|
223
|
-
isDataSource: () => Boolean(props.dataSourceType),
|
|
224
|
-
dataSourceType: () => props.dataSourceType,
|
|
225
|
-
codeOptions,
|
|
226
|
-
// 对比模式只读,不需要校验/语法检查
|
|
227
|
-
editable: false,
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
default:
|
|
231
|
-
return [];
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const loadConfig = async () => {
|
|
236
|
-
if (props.loadConfig) {
|
|
237
|
-
config.value = await props.loadConfig({
|
|
238
|
-
category: props.category,
|
|
239
|
-
type: props.type,
|
|
240
|
-
dataSourceType: props.dataSourceType,
|
|
241
|
-
defaultLoadConfig,
|
|
242
|
-
});
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
config.value = await defaultLoadConfig();
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
watch(
|
|
250
|
-
[() => props.category, () => props.type, () => props.dataSourceType, () => props.loadConfig],
|
|
251
|
-
() => {
|
|
252
|
-
loadConfig();
|
|
253
|
-
},
|
|
254
|
-
{ immediate: true },
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
const formRef = useTemplateRef<InstanceType<typeof MForm>>('form');
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* 把 services / stage 注入 MForm 的 formState,避免 propsService 注入的表单配置中
|
|
261
|
-
* 形如 `display: ({ services }) => services.uiService.get(...)` 的 filterFunction
|
|
262
|
-
* 在执行时拿不到 `formState.services` 而报错。
|
|
263
|
-
*
|
|
264
|
-
* 与 props-panel/FormPanel.vue 中的注入方式保持一致:
|
|
265
|
-
* - services:整个 useServices() 返回的服务集合;
|
|
266
|
-
* - stage:当前 editorService.get('stage') 的最新值。
|
|
267
|
-
*/
|
|
268
|
-
watchEffect(() => {
|
|
269
|
-
if (formRef.value && props.services) {
|
|
270
|
-
formRef.value.formState.stage = props.services.editorService.get('stage');
|
|
271
|
-
formRef.value.formState.services = props.services;
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
|
|
275
88
|
defineExpose<{
|
|
276
89
|
form: ShallowRef<InstanceType<typeof MForm> | null>;
|
|
277
90
|
config: Ref<FormConfig>;
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<transition name="fade">
|
|
3
3
|
<div
|
|
4
4
|
v-show="visible"
|
|
5
|
-
class="magic-editor-content-menu"
|
|
6
5
|
ref="menu"
|
|
6
|
+
:class="['magic-editor-content-menu', themeClass]"
|
|
7
7
|
:style="menuStyle"
|
|
8
8
|
@mouseenter="mouseenterHandler()"
|
|
9
9
|
@contextmenu.prevent
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
<script lang="ts" setup>
|
|
40
40
|
import { computed, nextTick, onBeforeUnmount, onMounted, type Ref, ref, useTemplateRef } from 'vue';
|
|
41
41
|
|
|
42
|
-
import { useZIndex } from '@tmagic/design';
|
|
42
|
+
import { useThemeClass, useZIndex } from '@tmagic/design';
|
|
43
43
|
|
|
44
44
|
import { MenuButton, MenuComponent } from '@editor/type';
|
|
45
45
|
|
|
@@ -73,6 +73,14 @@ const menuEl = useTemplateRef<HTMLDivElement>('menu');
|
|
|
73
73
|
const buttonRefs = useTemplateRef<InstanceType<typeof ToolButton>[]>('buttons');
|
|
74
74
|
const subMenuRef = useTemplateRef<any>('subMenu');
|
|
75
75
|
const visible = ref(false);
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 主题修饰类(来自最近的 `<MEditor>` / `<MForm>` 祖先 provide)。
|
|
79
|
+
* - 顶层 `ContentMenu` 渲染在编辑器子树中,加上去是冗余但无害;
|
|
80
|
+
* - 子菜单(递归 `<content-menu :is-sub-menu="true">` 被 `<teleport to="body">` 送到 body)
|
|
81
|
+
* 不在编辑器子树中,必须显式挂上主题类,主题级 CSS 变量才能命中。
|
|
82
|
+
*/
|
|
83
|
+
const themeClass = useThemeClass();
|
|
76
84
|
const subMenuData: Ref<(MenuButton | MenuComponent)[]> = ref<(MenuButton | MenuComponent)[]>([]);
|
|
77
85
|
const zIndex = useZIndex();
|
|
78
86
|
const curZIndex = ref<number>(0);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<Teleport to="body" v-if="visible">
|
|
3
3
|
<div
|
|
4
4
|
ref="target"
|
|
5
|
-
class="m-editor-float-box"
|
|
5
|
+
:class="['m-editor-float-box', themeClass]"
|
|
6
6
|
v-bind="$attrs"
|
|
7
7
|
:style="{ ...style, zIndex: curZIndex }"
|
|
8
8
|
@mousedown="nextZIndex"
|
|
@@ -27,7 +27,7 @@ import { computed, type CSSProperties, nextTick, onBeforeUnmount, provide, ref,
|
|
|
27
27
|
import { Close } from '@element-plus/icons-vue';
|
|
28
28
|
import VanillaMoveable from 'moveable';
|
|
29
29
|
|
|
30
|
-
import { TMagicButton, useZIndex } from '@tmagic/design';
|
|
30
|
+
import { TMagicButton, useThemeClass, useZIndex } from '@tmagic/design';
|
|
31
31
|
|
|
32
32
|
import MIcon from '@editor/components/Icon.vue';
|
|
33
33
|
|
|
@@ -62,6 +62,12 @@ const props = withDefaults(
|
|
|
62
62
|
const targetEl = useTemplateRef<HTMLDivElement>('target');
|
|
63
63
|
const titleEl = useTemplateRef<HTMLDivElement>('title');
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* 主题修饰类(来自最近的 `<MEditor>` / `<MForm>` 祖先 provide)。
|
|
67
|
+
* 挂在 `Teleport` 出去的浮动面板根节点上,让主题级 CSS 变量在 portal 节点上也能命中。
|
|
68
|
+
*/
|
|
69
|
+
const themeClass = useThemeClass();
|
|
70
|
+
|
|
65
71
|
const zIndex = useZIndex();
|
|
66
72
|
const curZIndex = ref<number>(0);
|
|
67
73
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-view-form-wrapper" :style="wrapperStyle">
|
|
3
|
+
<MForm
|
|
4
|
+
v-if="config.length"
|
|
5
|
+
ref="form"
|
|
6
|
+
class="m-editor-view-form"
|
|
7
|
+
:config="config"
|
|
8
|
+
:init-values="currentValues"
|
|
9
|
+
:disabled="disabled"
|
|
10
|
+
:label-width="labelWidth"
|
|
11
|
+
:extend-state="mergedExtendState"
|
|
12
|
+
:size="size"
|
|
13
|
+
></MForm>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script lang="ts" setup>
|
|
18
|
+
import { type Ref, type ShallowRef } from 'vue';
|
|
19
|
+
|
|
20
|
+
import { type FormConfig, MForm } from '@tmagic/form';
|
|
21
|
+
|
|
22
|
+
import { useCompareForm } from '@editor/hooks/use-compare-form';
|
|
23
|
+
import type { CompareFormBaseProps } from '@editor/type';
|
|
24
|
+
|
|
25
|
+
defineOptions({
|
|
26
|
+
name: 'MEditorViewForm',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const props = withDefaults(
|
|
30
|
+
defineProps<
|
|
31
|
+
CompareFormBaseProps & {
|
|
32
|
+
/** 是否禁用表单(默认只读展示)。 */
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
}
|
|
35
|
+
>(),
|
|
36
|
+
{
|
|
37
|
+
category: 'node',
|
|
38
|
+
labelWidth: '120px',
|
|
39
|
+
disabled: true,
|
|
40
|
+
// extendState 的默认值由 useCompareForm 内部兜底(props.extendState ?? ...),此处无需重复提供
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const { config, currentValues, wrapperStyle, mergedExtendState, loadConfig, formRef } = useCompareForm(props);
|
|
45
|
+
|
|
46
|
+
defineExpose<{
|
|
47
|
+
form: ShallowRef<InstanceType<typeof MForm> | null>;
|
|
48
|
+
config: Ref<FormConfig>;
|
|
49
|
+
reload: () => Promise<void>;
|
|
50
|
+
}>({
|
|
51
|
+
form: formRef,
|
|
52
|
+
config,
|
|
53
|
+
reload: loadConfig,
|
|
54
|
+
});
|
|
55
|
+
</script>
|
package/src/editorProps.ts
CHANGED
|
@@ -35,6 +35,10 @@ import type {
|
|
|
35
35
|
export const ENABLE_PROPS_FORM_VALIDATE: InjectionKey<boolean> = Symbol('enablePropsFormValidate');
|
|
36
36
|
|
|
37
37
|
export interface EditorProps {
|
|
38
|
+
/** 是否是大屏模拟器容器,大屏容器时,左侧属性面板会扩展为两列(正常3列),颜色表单不扩展两列等 */
|
|
39
|
+
isLargeStageContainer?: boolean;
|
|
40
|
+
|
|
41
|
+
theme?: string;
|
|
38
42
|
/** 页面初始值 */
|
|
39
43
|
modelValue?: MApp;
|
|
40
44
|
/** 左侧面板中的组件类型列表 */
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-fields-code-select" :class="config.className">
|
|
3
|
-
<TMagicCard>
|
|
3
|
+
<TMagicCard :flat="config.flat">
|
|
4
4
|
<MContainer
|
|
5
5
|
:config="codeConfig"
|
|
6
6
|
:size="size"
|
|
7
|
+
class="code-select-content"
|
|
7
8
|
:prop="prop"
|
|
8
9
|
:disabled="disabled"
|
|
9
10
|
:is-compare="isCompareMode"
|
|
@@ -12,16 +13,20 @@
|
|
|
12
13
|
@change="changeHandler"
|
|
13
14
|
>
|
|
14
15
|
</MContainer>
|
|
16
|
+
<TMagicButton class="create-button fullWidth" :icon="Plus" :size="size" :disabled="disabled" @click="newHandler()"
|
|
17
|
+
>添加{{ config.text }}</TMagicButton
|
|
18
|
+
>
|
|
15
19
|
</TMagicCard>
|
|
16
20
|
</div>
|
|
17
21
|
</template>
|
|
18
22
|
|
|
19
23
|
<script lang="ts" setup>
|
|
20
24
|
import { computed, watch } from 'vue';
|
|
25
|
+
import { Plus } from '@element-plus/icons-vue';
|
|
21
26
|
import { isEmpty } from 'lodash-es';
|
|
22
27
|
|
|
23
28
|
import { HookCodeType, HookType } from '@tmagic/core';
|
|
24
|
-
import { TMagicCard } from '@tmagic/design';
|
|
29
|
+
import { TMagicButton, TMagicCard } from '@tmagic/design';
|
|
25
30
|
import type { CodeSelectConfig, ContainerChangeEventData, FieldProps, GroupListConfig } from '@tmagic/form';
|
|
26
31
|
import { MContainer } from '@tmagic/form';
|
|
27
32
|
|
|
@@ -53,12 +58,23 @@ const props = withDefaults(defineProps<FieldProps<CodeSelectConfig>>(), {});
|
|
|
53
58
|
* 仅当存在历史值时才启用对比,避免 lastValues 缺失时退化为「全部新增」的空对比。
|
|
54
59
|
*/
|
|
55
60
|
const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues));
|
|
56
|
-
|
|
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
|
+
};
|
|
57
72
|
const codeConfig = computed<GroupListConfig>(() => ({
|
|
58
73
|
type: 'group-list',
|
|
59
74
|
name: 'hookData',
|
|
60
75
|
enableToggleMode: false,
|
|
61
76
|
expandAll: true,
|
|
77
|
+
addable: () => false,
|
|
62
78
|
title: (mForm, { model, index }: any) => {
|
|
63
79
|
if (model.codeType === HookCodeType.DATA_SOURCE_METHOD) {
|
|
64
80
|
if (Array.isArray(model.codeId)) {
|
|
@@ -81,49 +97,44 @@ const codeConfig = computed<GroupListConfig>(() => ({
|
|
|
81
97
|
|
|
82
98
|
return model.codeId || index;
|
|
83
99
|
},
|
|
100
|
+
titlePrefix: props.config.name === undefined ? undefined : String(props.config.name),
|
|
84
101
|
items: [
|
|
85
102
|
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
{ value: HookCodeType.DATA_SOURCE_METHOD, text: '数据源方法' },
|
|
95
|
-
],
|
|
96
|
-
rules: [{ typeMatch: true, trigger: 'change' }],
|
|
97
|
-
defaultValue: HookCodeType.CODE,
|
|
98
|
-
onChange: (_mForm, v: HookCodeType, { setModel }) => {
|
|
99
|
-
if (v === HookCodeType.DATA_SOURCE_METHOD) {
|
|
100
|
-
setModel('codeId', []);
|
|
101
|
-
} else {
|
|
102
|
-
setModel('codeId', '');
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return v;
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
type: 'code-select-col',
|
|
110
|
-
name: 'codeId',
|
|
111
|
-
span: 18,
|
|
112
|
-
labelWidth: 0,
|
|
113
|
-
display: (_mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
|
|
114
|
-
notEditable: () => !codeBlockService.getEditStatus(),
|
|
115
|
-
rules: [{ typeMatch: true, trigger: 'change' }],
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
type: 'data-source-method-select',
|
|
119
|
-
name: 'codeId',
|
|
120
|
-
span: 18,
|
|
121
|
-
labelWidth: 0,
|
|
122
|
-
display: (_mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
|
|
123
|
-
notEditable: () => !dataSourceService.get('editable'),
|
|
124
|
-
rules: [{ typeMatch: true, trigger: 'change' }],
|
|
125
|
-
},
|
|
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: '数据源方法' },
|
|
126
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'),
|
|
127
138
|
},
|
|
128
139
|
],
|
|
129
140
|
}));
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="m-fields-data-source-field-select"
|
|
4
|
+
:class="{
|
|
5
|
+
[`data-source-field-${type}`]: !isSelectValid,
|
|
6
|
+
}"
|
|
7
|
+
>
|
|
3
8
|
<FieldSelect
|
|
4
|
-
v-if="
|
|
9
|
+
v-if="isSelectValid"
|
|
5
10
|
:model-value="model[name]"
|
|
6
11
|
:disabled="disabled"
|
|
7
12
|
:size="size"
|
|
@@ -37,15 +42,14 @@
|
|
|
37
42
|
:size="size"
|
|
38
43
|
:disabled="disabled"
|
|
39
44
|
@click="onToggleDataSourceFieldSelectHandler"
|
|
40
|
-
><MIcon :icon="
|
|
41
|
-
|
|
45
|
+
><MIcon :icon="dataSourceIcon"></MIcon>
|
|
46
|
+
</TMagicButton>
|
|
42
47
|
</TMagicTooltip>
|
|
43
48
|
</div>
|
|
44
49
|
</template>
|
|
45
50
|
|
|
46
51
|
<script setup lang="ts">
|
|
47
52
|
import { computed, inject, ref, resolveComponent, watch } from 'vue';
|
|
48
|
-
import { Coin } from '@element-plus/icons-vue';
|
|
49
53
|
|
|
50
54
|
import { DataSchema } from '@tmagic/core';
|
|
51
55
|
import { TMagicButton, tMagicMessage, TMagicTooltip } from '@tmagic/design';
|
|
@@ -61,6 +65,8 @@ import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX, removeDataSourceFieldPrefix } f
|
|
|
61
65
|
import MIcon from '@editor/components/Icon.vue';
|
|
62
66
|
import { useServices } from '@editor/hooks/use-services';
|
|
63
67
|
|
|
68
|
+
import dataSourceIcon from '../../icons/DatasourceIcon.vue';
|
|
69
|
+
|
|
64
70
|
import FieldSelect from './FieldSelect.vue';
|
|
65
71
|
|
|
66
72
|
defineOptions({
|
|
@@ -75,6 +81,9 @@ const props = withDefaults(defineProps<FieldProps<DataSourceFieldSelectConfig>>(
|
|
|
75
81
|
|
|
76
82
|
const showDataSourceFieldSelect = ref(false);
|
|
77
83
|
|
|
84
|
+
const isSelectValid = computed(
|
|
85
|
+
() => !disabledDataSource.value && (showDataSourceFieldSelect.value || !props.config.fieldConfig),
|
|
86
|
+
);
|
|
78
87
|
watch(
|
|
79
88
|
() => props.model[props.name],
|
|
80
89
|
(value) => {
|