@tmagic/editor 1.8.0-beta.10 → 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.
- package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +7 -4
- package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +4 -0
- package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +5 -2
- package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +60 -14
- package/dist/es/components/ScrollBar.js +0 -1
- package/dist/es/editorProps.js +6 -1
- package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +15 -3
- package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +2 -17
- package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +7 -0
- package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +5 -17
- package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +4 -0
- package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +29 -3
- package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +50 -88
- package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +2 -5
- package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +48 -23
- package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +6 -3
- package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +28 -22
- package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +28 -22
- package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +28 -22
- package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +6 -3
- package/dist/es/fields/UISelect.js +0 -1
- package/dist/es/index.js +9 -6
- package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +21 -6
- package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +9 -6
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +14 -4
- package/dist/es/layouts/history-list/composables.js +1 -1
- package/dist/es/layouts/history-list/useHistoryRevert.js +17 -6
- package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +44 -5
- package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +15 -8
- package/dist/es/layouts/props-panel/use-style-panel.js +3 -2
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +15 -31
- package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/layouts/sidebar/code-block/useContentMenu.js +18 -3
- package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +4 -0
- package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/layouts/sidebar/data-source/useContentMenu.js +18 -3
- package/dist/es/layouts/sidebar/layer/LayerNodeContent.js +5 -0
- package/dist/es/layouts/sidebar/layer/LayerNodeContent.vue_vue_type_script_setup_true_lang.js +46 -0
- package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +2 -1
- package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +8 -2
- package/dist/es/plugin.js +3 -1
- package/dist/es/services/editor.js +104 -5
- package/dist/es/services/props.js +2 -1
- package/dist/es/services/storage.js +1 -1
- package/dist/es/services/ui.js +3 -0
- package/dist/es/style.css +104 -55
- package/dist/es/utils/data-source/index.js +30 -11
- package/dist/es/utils/event.js +123 -0
- package/dist/es/utils/monaco-editor.js +22 -6
- package/dist/es/utils/props.js +94 -8
- package/dist/es/utils/type-match-rules.js +360 -0
- package/dist/style.css +104 -55
- package/dist/tmagic-editor.umd.cjs +16923 -15771
- package/package.json +8 -8
- package/src/Editor.vue +3 -1
- package/src/components/CodeBlockEditor.vue +3 -0
- package/src/components/CompareForm.vue +7 -0
- package/src/components/FloatingBox.vue +77 -8
- package/src/components/ScrollBar.vue +0 -67
- package/src/editorProps.ts +13 -0
- package/src/fields/CodeSelect.vue +4 -1
- package/src/fields/CondOpSelect.vue +2 -24
- package/src/fields/DataSourceFields.vue +5 -0
- package/src/fields/DataSourceInput.vue +7 -25
- package/src/fields/DataSourceMocks.vue +3 -0
- package/src/fields/DisplayConds.vue +14 -0
- package/src/fields/EventSelect.vue +84 -129
- package/src/fields/StyleSetter/Index.vue +2 -8
- package/src/fields/StyleSetter/pro/Background.vue +94 -55
- package/src/fields/StyleSetter/pro/Border.vue +9 -11
- package/src/fields/StyleSetter/pro/Font.vue +66 -64
- package/src/fields/StyleSetter/pro/Layout.vue +140 -138
- package/src/fields/StyleSetter/pro/Position.vue +69 -67
- package/src/fields/StyleSetter/pro/Transform.vue +23 -25
- package/src/fields/UISelect.vue +0 -12
- package/src/index.ts +1 -0
- package/src/layouts/CodeEditor.vue +32 -6
- package/src/layouts/Framework.vue +7 -6
- package/src/layouts/history-list/HistoryDiffDialog.vue +23 -5
- package/src/layouts/history-list/useHistoryRevert.ts +25 -5
- package/src/layouts/props-panel/FormPanel.vue +66 -5
- package/src/layouts/props-panel/PropsPanel.vue +20 -6
- package/src/layouts/props-panel/use-style-panel.ts +4 -3
- package/src/layouts/sidebar/Sidebar.vue +2 -0
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +4 -1
- package/src/layouts/sidebar/code-block/useContentMenu.ts +13 -1
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +3 -0
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +2 -2
- package/src/layouts/sidebar/data-source/useContentMenu.ts +13 -1
- package/src/layouts/sidebar/layer/LayerNodeContent.vue +57 -0
- package/src/layouts/sidebar/layer/LayerPanel.vue +4 -1
- package/src/layouts/workspace/viewer/NodeListMenu.vue +4 -1
- package/src/plugin.ts +3 -1
- package/src/services/editor.ts +168 -3
- package/src/services/ui.ts +6 -0
- package/src/theme/event.scss +8 -0
- package/src/theme/floating-box.scss +9 -1
- package/src/theme/layer-node-content.scss +14 -0
- package/src/theme/scroll-bar.scss +64 -0
- package/src/theme/theme.scss +3 -0
- package/src/theme/ui-select.scss +9 -0
- package/src/type.ts +72 -2
- package/src/utils/data-source/index.ts +30 -12
- package/src/utils/event.ts +224 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/monaco-editor.ts +27 -0
- package/src/utils/props.ts +160 -2
- package/src/utils/type-match-rules.ts +678 -0
- package/types/index.d.ts +391 -85
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.12",
|
|
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/stage": "1.8.0-beta.12",
|
|
62
|
+
"@tmagic/design": "1.8.0-beta.12",
|
|
63
|
+
"@tmagic/utils": "1.8.0-beta.12",
|
|
64
|
+
"@tmagic/form": "1.8.0-beta.12",
|
|
65
|
+
"@tmagic/table": "1.8.0-beta.12"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"@types/events": "^3.0.3",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"monaco-editor": "^0.55.1 ",
|
|
76
76
|
"type-fest": "^5.2.0",
|
|
77
77
|
"typescript": "^6.0.3",
|
|
78
|
-
"vue": "^3.5.
|
|
79
|
-
"@tmagic/core": "1.8.0-beta.
|
|
78
|
+
"vue": "^3.5.40",
|
|
79
|
+
"@tmagic/core": "1.8.0-beta.12"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
package/src/Editor.vue
CHANGED
|
@@ -158,7 +158,7 @@ import stageOverlayService from './services/stageOverlay';
|
|
|
158
158
|
import storageService from './services/storage';
|
|
159
159
|
import uiService from './services/ui';
|
|
160
160
|
import keybindingConfig from './utils/keybinding-config';
|
|
161
|
-
import { defaultEditorProps, EditorProps } from './editorProps';
|
|
161
|
+
import { defaultEditorProps, EditorProps, ENABLE_PROPS_FORM_VALIDATE } from './editorProps';
|
|
162
162
|
import { initServiceEvents, initServiceState } from './initService';
|
|
163
163
|
import type { TreeNodeData } from './type';
|
|
164
164
|
import type { EditorSlots, EventBus, Services, StageOptions } from './type';
|
|
@@ -233,6 +233,8 @@ provide('services', services);
|
|
|
233
233
|
|
|
234
234
|
provide('codeOptions', props.codeOptions);
|
|
235
235
|
provide('stageOptions', stageOptions);
|
|
236
|
+
/** 是否启用「属性配置表单校验」联动能力,供 PropsPanel / FormPanel 判断校验失败时是否仍更新节点并记录错误 */
|
|
237
|
+
provide(ENABLE_PROPS_FORM_VALIDATE, props.enablePropsFormValidate ?? false);
|
|
236
238
|
/**
|
|
237
239
|
* 把顶层 `extendFormState` 提供给非 PropsPanel 链路上的组件使用(例如历史差异对话框 HistoryDiffDialog
|
|
238
240
|
* 内部的 CompareForm)。这样所有依赖业务上下文的表单 filterFunction 都能拿到一致的扩展状态,
|
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
v-model:visible="boxVisible"
|
|
5
5
|
v-model:width="width"
|
|
6
6
|
v-model:height="codeBlockEditorHeight"
|
|
7
|
+
:body-style="{ padding: '0 16px' }"
|
|
7
8
|
:title="content.name ? `${disabled ? '查看' : '编辑'}${content.name}` : '新增代码'"
|
|
9
|
+
:framework-width="frameworkWidth"
|
|
8
10
|
:position="boxPosition"
|
|
9
11
|
:before-close="beforeClose"
|
|
10
12
|
>
|
|
@@ -206,6 +208,7 @@ const closedHandler = () => {
|
|
|
206
208
|
|
|
207
209
|
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
|
|
208
210
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
|
|
211
|
+
const frameworkWidth = computed(() => uiService.get('frameworkRect')?.width || 0);
|
|
209
212
|
|
|
210
213
|
watch(boxVisible, (visible) => {
|
|
211
214
|
nextTick(() => {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
:extend-state="mergedExtendState"
|
|
14
14
|
:show-diff="showDiff"
|
|
15
15
|
:self-diff-field-types="selfDiffFieldTypes"
|
|
16
|
+
:size="size"
|
|
16
17
|
></MForm>
|
|
17
18
|
</div>
|
|
18
19
|
</template>
|
|
@@ -22,6 +23,7 @@ import { computed, inject, provide, type Ref, ref, type ShallowRef, useTemplateR
|
|
|
22
23
|
import { isEqual } from 'lodash-es';
|
|
23
24
|
|
|
24
25
|
import { type CodeBlockContent, type DataSourceSchema, HookType, type MNode } from '@tmagic/core';
|
|
26
|
+
import { type FieldSize } from '@tmagic/design';
|
|
25
27
|
import { type FormConfig, type FormState, type FormValue, MForm } from '@tmagic/form';
|
|
26
28
|
|
|
27
29
|
import type { CompareCategory, CompareFormLoadConfig, Services } from '@editor/type';
|
|
@@ -68,6 +70,11 @@ const props = withDefaults(
|
|
|
68
70
|
baseFormState?: FormState;
|
|
69
71
|
/** 需要走 self diff 的字段类型(例如 mod-cond)。 */
|
|
70
72
|
selfDiffFieldTypes?: string[];
|
|
73
|
+
/**
|
|
74
|
+
* 表单内组件的尺寸(透传给 MForm 的 `size`),可选 'large' | 'default' | 'small'。
|
|
75
|
+
* 缺省时使用 MForm 内置默认尺寸。
|
|
76
|
+
*/
|
|
77
|
+
size?: FieldSize;
|
|
71
78
|
/**
|
|
72
79
|
* 自定义 FormConfig 加载逻辑。传入后将接管内置的按 `category`(node/data-source/code-block)
|
|
73
80
|
* 取配置逻辑,调用方可根据业务自行返回(或异步返回)表单配置。可通过
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<Teleport to="body" v-if="visible">
|
|
3
|
-
<div
|
|
3
|
+
<div
|
|
4
|
+
ref="target"
|
|
5
|
+
class="m-editor-float-box"
|
|
6
|
+
v-bind="$attrs"
|
|
7
|
+
:style="{ ...style, zIndex: curZIndex }"
|
|
8
|
+
@mousedown="nextZIndex"
|
|
9
|
+
>
|
|
4
10
|
<div ref="title" class="m-editor-float-box-title">
|
|
5
11
|
<slot name="title">
|
|
6
12
|
<span>{{ title }}</span>
|
|
@@ -9,7 +15,7 @@
|
|
|
9
15
|
<TMagicButton link size="small" @click="closeHandler"><MIcon :icon="Close"></MIcon></TMagicButton>
|
|
10
16
|
</div>
|
|
11
17
|
</div>
|
|
12
|
-
<div class="m-editor-float-box-body" :style="{ height: `${bodyHeight}px
|
|
18
|
+
<div class="m-editor-float-box-body" :style="{ height: `${bodyHeight}px`, ...bodyStyle }">
|
|
13
19
|
<slot name="body"></slot>
|
|
14
20
|
</div>
|
|
15
21
|
</div>
|
|
@@ -17,14 +23,13 @@
|
|
|
17
23
|
</template>
|
|
18
24
|
|
|
19
25
|
<script setup lang="ts">
|
|
20
|
-
import { computed, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
|
|
26
|
+
import { computed, type CSSProperties, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
|
|
21
27
|
import { Close } from '@element-plus/icons-vue';
|
|
22
28
|
import VanillaMoveable from 'moveable';
|
|
23
29
|
|
|
24
30
|
import { TMagicButton, useZIndex } from '@tmagic/design';
|
|
25
31
|
|
|
26
32
|
import MIcon from '@editor/components/Icon.vue';
|
|
27
|
-
import { useServices } from '@editor/hooks/use-services';
|
|
28
33
|
|
|
29
34
|
interface Position {
|
|
30
35
|
left: number;
|
|
@@ -39,11 +44,18 @@ const props = withDefaults(
|
|
|
39
44
|
defineProps<{
|
|
40
45
|
position?: Position;
|
|
41
46
|
title?: string;
|
|
47
|
+
bodyStyle?: CSSProperties;
|
|
48
|
+
/** 浮窗初始样式,会与内部计算样式合并,外部传入优先 */
|
|
49
|
+
initialStyle?: CSSProperties;
|
|
50
|
+
/** 用于约束浮窗 left 的容器宽度,传入时按宽度收敛 left,避免超出右边界;默认取视窗宽度 */
|
|
51
|
+
frameworkWidth?: number;
|
|
42
52
|
beforeClose?: (_done: (_cancel?: boolean) => void) => void;
|
|
43
53
|
}>(),
|
|
44
54
|
{
|
|
45
55
|
title: '',
|
|
46
56
|
position: () => ({ left: 0, top: 0 }),
|
|
57
|
+
initialStyle: () => ({}),
|
|
58
|
+
frameworkWidth: 0,
|
|
47
59
|
},
|
|
48
60
|
);
|
|
49
61
|
|
|
@@ -66,12 +78,11 @@ const bodyHeight = computed(() => {
|
|
|
66
78
|
return 'auto';
|
|
67
79
|
});
|
|
68
80
|
|
|
69
|
-
const { uiService } = useServices();
|
|
70
|
-
const frameworkWidth = computed(() => uiService.get('frameworkRect').width || 0);
|
|
71
81
|
const style = computed(() => {
|
|
72
82
|
let { left } = props.position;
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
const frameworkWidth = props.frameworkWidth || globalThis.window?.innerWidth || 0;
|
|
84
|
+
if (width.value && frameworkWidth) {
|
|
85
|
+
left = left + width.value > frameworkWidth ? frameworkWidth - width.value : left;
|
|
75
86
|
}
|
|
76
87
|
|
|
77
88
|
return {
|
|
@@ -79,11 +90,60 @@ const style = computed(() => {
|
|
|
79
90
|
top: `${props.position.top}px`,
|
|
80
91
|
width: width.value ? `${width.value}px` : 'auto',
|
|
81
92
|
height: height.value ? `${height.value}px` : 'auto',
|
|
93
|
+
...props.initialStyle,
|
|
82
94
|
};
|
|
83
95
|
});
|
|
84
96
|
|
|
85
97
|
let moveable: VanillaMoveable | null = null;
|
|
86
98
|
|
|
99
|
+
// 拖拽/缩放时用于覆盖 iframe 的遮罩,防止鼠标进入 iframe 区域后事件被 iframe 吞掉导致拖拽丢失
|
|
100
|
+
let dragMask: HTMLDivElement | null = null;
|
|
101
|
+
let dragMaskVisible = false;
|
|
102
|
+
|
|
103
|
+
// 兜底:正常情况下遮罩由 moveable 的 dragEnd/resizeEnd 移除;但某些极端场景
|
|
104
|
+
// (拖拽中鼠标移出窗口后松开、alt-tab 切走窗口失焦、标签页被隐藏等)结束事件不会触发,
|
|
105
|
+
// 会导致全屏透明遮罩残留、整个编辑器无法点击。这里挂全局 pointerup/blur/visibilitychange 强制收尾。
|
|
106
|
+
const bindDragMaskSafety = () => {
|
|
107
|
+
globalThis.window?.addEventListener('pointerup', hideDragMask, true);
|
|
108
|
+
globalThis.window?.addEventListener('blur', hideDragMask);
|
|
109
|
+
globalThis.document?.addEventListener('visibilitychange', hideDragMask);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const unbindDragMaskSafety = () => {
|
|
113
|
+
globalThis.window?.removeEventListener('pointerup', hideDragMask, true);
|
|
114
|
+
globalThis.window?.removeEventListener('blur', hideDragMask);
|
|
115
|
+
globalThis.document?.removeEventListener('visibilitychange', hideDragMask);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const showDragMask = () => {
|
|
119
|
+
if (dragMaskVisible) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!dragMask) {
|
|
123
|
+
dragMask = globalThis.document.createElement('div');
|
|
124
|
+
dragMask.className = 'm-editor-float-box-drag-mask';
|
|
125
|
+
}
|
|
126
|
+
globalThis.document.body.appendChild(dragMask);
|
|
127
|
+
dragMaskVisible = true;
|
|
128
|
+
bindDragMaskSafety();
|
|
129
|
+
|
|
130
|
+
// 拖拽标题时,root 上的 @mousedown="nextZIndex" 会把浮窗 z-index 抬高,
|
|
131
|
+
// 若此时才读取会拿到旧值导致遮罩被浮窗(及其内部 iframe)盖住,故用 nextTick 在 z-index 稳定后再设置到浮窗之上
|
|
132
|
+
const setMaskZIndex = () => {
|
|
133
|
+
if (dragMask) {
|
|
134
|
+
dragMask.style.zIndex = `${curZIndex.value + 1}`;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
setMaskZIndex();
|
|
138
|
+
nextTick(setMaskZIndex);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const hideDragMask = () => {
|
|
142
|
+
unbindDragMaskSafety();
|
|
143
|
+
dragMask?.parentNode?.removeChild(dragMask);
|
|
144
|
+
dragMaskVisible = false;
|
|
145
|
+
};
|
|
146
|
+
|
|
87
147
|
const initMoveable = () => {
|
|
88
148
|
moveable = new VanillaMoveable(globalThis.document.body, {
|
|
89
149
|
className: 'm-editor-floating-box-moveable',
|
|
@@ -101,6 +161,14 @@ const initMoveable = () => {
|
|
|
101
161
|
bounds: { left: 0, top: 0, right: 0, bottom: 0, position: 'css' },
|
|
102
162
|
});
|
|
103
163
|
|
|
164
|
+
// 仅在真正发生拖拽/缩放位移时插入遮罩:moveable 的 dragStart/resizeStart 在 mousedown 时即触发,
|
|
165
|
+
// 若此时就盖遮罩,会盖住浮窗本身导致 mouseup 落在遮罩上、关闭按钮的 click 无法触发(点击关闭不了)。
|
|
166
|
+
// 改为在 drag/resize(实际位移)时才显示,纯点击不再触发遮罩。
|
|
167
|
+
moveable.on('drag', showDragMask);
|
|
168
|
+
moveable.on('resize', showDragMask);
|
|
169
|
+
moveable.on('dragEnd', hideDragMask);
|
|
170
|
+
moveable.on('resizeEnd', hideDragMask);
|
|
171
|
+
|
|
104
172
|
moveable.on('drag', (e) => {
|
|
105
173
|
e.target.style.transform = e.transform;
|
|
106
174
|
});
|
|
@@ -115,6 +183,7 @@ const initMoveable = () => {
|
|
|
115
183
|
};
|
|
116
184
|
|
|
117
185
|
const destroyMoveable = () => {
|
|
186
|
+
hideDragMask();
|
|
118
187
|
moveable?.destroy();
|
|
119
188
|
moveable = null;
|
|
120
189
|
};
|
|
@@ -87,70 +87,3 @@ const scrollBy = (delta: number) => {
|
|
|
87
87
|
}
|
|
88
88
|
};
|
|
89
89
|
</script>
|
|
90
|
-
|
|
91
|
-
<style lang="scss">
|
|
92
|
-
.m-editor-scroll-bar {
|
|
93
|
-
position: absolute;
|
|
94
|
-
background-color: transparent;
|
|
95
|
-
opacity: 0.3;
|
|
96
|
-
transition:
|
|
97
|
-
background-color 0.2s linear,
|
|
98
|
-
opacity 0.2s linear;
|
|
99
|
-
|
|
100
|
-
.m-editor-scroll-bar-thumb {
|
|
101
|
-
background-color: #aaa;
|
|
102
|
-
border-radius: 6px;
|
|
103
|
-
position: absolute;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
&.horizontal {
|
|
107
|
-
width: 100%;
|
|
108
|
-
height: 15px;
|
|
109
|
-
bottom: 0;
|
|
110
|
-
|
|
111
|
-
.m-editor-scroll-bar-thumb {
|
|
112
|
-
height: 6px;
|
|
113
|
-
transition:
|
|
114
|
-
background-color 0.2s linear,
|
|
115
|
-
height 0.2s ease-in-out;
|
|
116
|
-
bottom: 2px;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
&.vertical {
|
|
121
|
-
height: 100%;
|
|
122
|
-
width: 15px;
|
|
123
|
-
right: 5px;
|
|
124
|
-
|
|
125
|
-
.m-editor-scroll-bar-thumb {
|
|
126
|
-
width: 6px;
|
|
127
|
-
transition:
|
|
128
|
-
background-color 0.2s linear,
|
|
129
|
-
width 0.2s ease-in-out;
|
|
130
|
-
right: 2px;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
&:hover,
|
|
135
|
-
&:focus {
|
|
136
|
-
background-color: #eee;
|
|
137
|
-
opacity: 0.9;
|
|
138
|
-
|
|
139
|
-
.m-editor-scroll-bar-thumb {
|
|
140
|
-
background-color: #999;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
&.horizontal {
|
|
144
|
-
.m-editor-scroll-bar-thumb {
|
|
145
|
-
height: 11px;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
&.vertical {
|
|
150
|
-
.m-editor-scroll-bar-thumb {
|
|
151
|
-
width: 11px;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
</style>
|
package/src/editorProps.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { InjectionKey } from 'vue';
|
|
2
|
+
|
|
1
3
|
import type { DataSourceSchema, EventOption, Id, MApp, MNode, MPage, MPageFragment } from '@tmagic/core';
|
|
2
4
|
import type { FormConfig, FormState } from '@tmagic/form';
|
|
3
5
|
import StageCore, {
|
|
@@ -26,6 +28,12 @@ import type {
|
|
|
26
28
|
TreeNodeData,
|
|
27
29
|
} from './type';
|
|
28
30
|
|
|
31
|
+
/**
|
|
32
|
+
* 「属性配置表单校验」联动能力的 provide/inject 注入键。
|
|
33
|
+
* 使用 Symbol 避免与其它字符串键冲突,供 PropsPanel / FormPanel 注入判断校验失败时是否仍更新节点并记录错误。
|
|
34
|
+
*/
|
|
35
|
+
export const ENABLE_PROPS_FORM_VALIDATE: InjectionKey<boolean> = Symbol('enablePropsFormValidate');
|
|
36
|
+
|
|
29
37
|
export interface EditorProps {
|
|
30
38
|
/** 页面初始值 */
|
|
31
39
|
modelValue?: MApp;
|
|
@@ -91,6 +99,11 @@ export interface EditorProps {
|
|
|
91
99
|
disabledFlashTip?: boolean;
|
|
92
100
|
/** 禁用双击在浮层中单独编辑选中组件 */
|
|
93
101
|
disabledStageOverlay?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* 是否启用「属性配置表单校验」联动能力:开启后属性/样式表单校验失败时仍更新节点,
|
|
104
|
+
* 并把错误信息集中记录到 editorService,用于组件树标红提示与保存拦截;默认 false(关闭)。
|
|
105
|
+
*/
|
|
106
|
+
enablePropsFormValidate?: boolean;
|
|
94
107
|
/** 禁用属性配置面板右下角显示源码的按钮 */
|
|
95
108
|
disabledShowSrc?: boolean;
|
|
96
109
|
/** 禁用数据源 */
|
|
@@ -93,7 +93,8 @@ const codeConfig = computed<GroupListConfig>(() => ({
|
|
|
93
93
|
{ value: HookCodeType.CODE, text: '代码块' },
|
|
94
94
|
{ value: HookCodeType.DATA_SOURCE_METHOD, text: '数据源方法' },
|
|
95
95
|
],
|
|
96
|
-
|
|
96
|
+
rules: [{ typeMatch: true, trigger: 'change' }],
|
|
97
|
+
defaultValue: HookCodeType.CODE,
|
|
97
98
|
onChange: (_mForm, v: HookCodeType, { setModel }) => {
|
|
98
99
|
if (v === HookCodeType.DATA_SOURCE_METHOD) {
|
|
99
100
|
setModel('codeId', []);
|
|
@@ -111,6 +112,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
|
|
|
111
112
|
labelWidth: 0,
|
|
112
113
|
display: (_mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
|
|
113
114
|
notEditable: () => !codeBlockService.getEditStatus(),
|
|
115
|
+
rules: [{ typeMatch: true, trigger: 'change' }],
|
|
114
116
|
},
|
|
115
117
|
{
|
|
116
118
|
type: 'data-source-method-select',
|
|
@@ -119,6 +121,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
|
|
|
119
121
|
labelWidth: 0,
|
|
120
122
|
display: (_mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
|
|
121
123
|
notEditable: () => !dataSourceService.get('editable'),
|
|
124
|
+
rules: [{ typeMatch: true, trigger: 'change' }],
|
|
122
125
|
},
|
|
123
126
|
],
|
|
124
127
|
},
|
|
@@ -33,7 +33,7 @@ import { getDesignConfig, TMagicSelect } from '@tmagic/design';
|
|
|
33
33
|
import type { CondOpSelectConfig, FieldProps } from '@tmagic/form';
|
|
34
34
|
|
|
35
35
|
import { useServices } from '@editor/hooks/use-services';
|
|
36
|
-
import {
|
|
36
|
+
import { getCondOpOptionsByFieldType, getFieldType } from '@editor/utils';
|
|
37
37
|
|
|
38
38
|
defineOptions({
|
|
39
39
|
name: 'MFieldsCondOpSelect',
|
|
@@ -51,31 +51,9 @@ const optionComponent = getDesignConfig('components')?.option;
|
|
|
51
51
|
|
|
52
52
|
const options = computed(() => {
|
|
53
53
|
const [id, ...fieldNames] = [...(props.config.parentFields || []), ...props.model.field];
|
|
54
|
-
|
|
55
54
|
const ds = dataSourceService.getDataSourceById(id);
|
|
56
|
-
|
|
57
55
|
const type = getFieldType(ds, fieldNames);
|
|
58
|
-
|
|
59
|
-
if (type === 'array') {
|
|
60
|
-
return arrayOptions;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (type === 'boolean' || type === 'null') {
|
|
64
|
-
return [
|
|
65
|
-
{ text: '是', value: 'is' },
|
|
66
|
-
{ text: '不是', value: 'not' },
|
|
67
|
-
];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (type === 'number') {
|
|
71
|
-
return [...eqOptions, ...numberOptions];
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (type === 'string') {
|
|
75
|
-
return [...arrayOptions, ...eqOptions];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return [...arrayOptions, ...eqOptions, ...numberOptions];
|
|
56
|
+
return getCondOpOptionsByFieldType(type);
|
|
79
57
|
});
|
|
80
58
|
|
|
81
59
|
const fieldChangeHandler = (v: string) => {
|
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
10
|
<FloatingBox
|
|
11
|
+
:body-style="{ padding: '0 16px' }"
|
|
11
12
|
v-model:visible="addDialogVisible"
|
|
12
13
|
v-model:width="width"
|
|
13
14
|
v-model:height="editorHeight"
|
|
14
15
|
:title="fieldTitle"
|
|
16
|
+
:framework-width="frameworkWidth"
|
|
15
17
|
:position="boxPosition"
|
|
16
18
|
>
|
|
17
19
|
<template #body>
|
|
@@ -28,10 +30,12 @@
|
|
|
28
30
|
</FloatingBox>
|
|
29
31
|
|
|
30
32
|
<FloatingBox
|
|
33
|
+
:body-style="{ padding: '0 16px' }"
|
|
31
34
|
v-model:visible="addFromJsonDialogVisible"
|
|
32
35
|
v-model:width="width"
|
|
33
36
|
v-model:height="editorHeight"
|
|
34
37
|
title="快速添加数据定义"
|
|
38
|
+
:framework-width="frameworkWidth"
|
|
35
39
|
:position="boxPosition"
|
|
36
40
|
>
|
|
37
41
|
<template #body>
|
|
@@ -358,6 +362,7 @@ const { height: editorHeight } = useEditorContentHeight();
|
|
|
358
362
|
|
|
359
363
|
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
|
|
360
364
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
|
|
365
|
+
const frameworkWidth = computed(() => uiService.get('frameworkRect')?.width || 0);
|
|
361
366
|
|
|
362
367
|
/**
|
|
363
368
|
* 由 DataSourceConfigPanel 注入:打开数据源详情后需要直接打开的字段路径(字段名数组)。
|
|
@@ -79,11 +79,11 @@ import { Coin } from '@element-plus/icons-vue';
|
|
|
79
79
|
import type { DataSchema, DataSourceSchema } from '@tmagic/core';
|
|
80
80
|
import { getDesignConfig, TMagicAutocomplete, TMagicInput, TMagicTag } from '@tmagic/design';
|
|
81
81
|
import type { DataSourceInputConfig, FieldProps } from '@tmagic/form';
|
|
82
|
-
import { getKeysArray
|
|
82
|
+
import { getKeysArray } from '@tmagic/utils';
|
|
83
83
|
|
|
84
84
|
import Icon from '@editor/components/Icon.vue';
|
|
85
85
|
import { useServices } from '@editor/hooks/use-services';
|
|
86
|
-
import { getDisplayField } from '@editor/utils/data-source';
|
|
86
|
+
import { getDisplayField, resolveFieldByPath } from '@editor/utils/data-source';
|
|
87
87
|
|
|
88
88
|
defineOptions({
|
|
89
89
|
name: 'MFieldsDataSourceInput',
|
|
@@ -251,34 +251,16 @@ const fieldQuerySearch = (
|
|
|
251
251
|
return;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
// 后面这些是字段
|
|
257
|
-
let key = keys.shift();
|
|
258
|
-
while (key) {
|
|
259
|
-
if (isNumber(key)) {
|
|
260
|
-
key = keys.shift();
|
|
261
|
-
continue;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
for (const field of fields) {
|
|
265
|
-
if (field.name === key) {
|
|
266
|
-
fields = field.fields || [];
|
|
267
|
-
key = keys.shift();
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
254
|
+
const { fields } = resolveFieldByPath(ds.fields, keys, { skipNumberIndices: true });
|
|
272
255
|
|
|
273
256
|
if (curCharIsDot(dotIndex)) {
|
|
274
257
|
// 当前输入的是.
|
|
275
|
-
result = fields
|
|
258
|
+
result = fields;
|
|
276
259
|
} else if (dotIndex > -1) {
|
|
277
260
|
const queryName = queryString.substring(dotIndex + 1).toLowerCase();
|
|
278
|
-
result =
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
) || [];
|
|
261
|
+
result = fields.filter(
|
|
262
|
+
(field) => field.name?.toLowerCase().includes(queryName) || field.title?.toLowerCase().includes(queryName),
|
|
263
|
+
);
|
|
282
264
|
}
|
|
283
265
|
|
|
284
266
|
cb(
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
9
|
<FloatingBox
|
|
10
|
+
:body-style="{ padding: '0 16px' }"
|
|
10
11
|
v-model:visible="addDialogVisible"
|
|
11
12
|
v-model:width="width"
|
|
12
13
|
v-model:height="editorHeight"
|
|
13
14
|
:title="drawerTitle"
|
|
15
|
+
:framework-width="frameworkWidth"
|
|
14
16
|
:position="boxPosition"
|
|
15
17
|
>
|
|
16
18
|
<template #body>
|
|
@@ -254,4 +256,5 @@ const addDialogVisible = defineModel<boolean>('visible', { default: false });
|
|
|
254
256
|
const { height: editorHeight } = useEditorContentHeight();
|
|
255
257
|
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
|
|
256
258
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
|
|
259
|
+
const frameworkWidth = computed(() => uiService.get('frameworkRect')?.width || 0);
|
|
257
260
|
</script>
|
|
@@ -98,6 +98,11 @@ const config = computed<GroupListConfig>(() => ({
|
|
|
98
98
|
label: '字段',
|
|
99
99
|
checkStrictly: false,
|
|
100
100
|
onChange: fieldOnChange,
|
|
101
|
+
defaultValue: () => [],
|
|
102
|
+
rules: [
|
|
103
|
+
{ required: true, trigger: 'blur', message: '请选择字段' },
|
|
104
|
+
{ typeMatch: true, trigger: 'change' },
|
|
105
|
+
],
|
|
101
106
|
}
|
|
102
107
|
: {
|
|
103
108
|
type: 'data-source-field-select',
|
|
@@ -107,6 +112,11 @@ const config = computed<GroupListConfig>(() => ({
|
|
|
107
112
|
checkStrictly: false,
|
|
108
113
|
dataSourceFieldType: ['string', 'number', 'boolean', 'any'],
|
|
109
114
|
onChange: fieldOnChange,
|
|
115
|
+
defaultValue: () => [],
|
|
116
|
+
rules: [
|
|
117
|
+
{ required: true, trigger: 'blur', message: '请选择字段' },
|
|
118
|
+
{ typeMatch: true, trigger: 'change' },
|
|
119
|
+
],
|
|
110
120
|
},
|
|
111
121
|
{
|
|
112
122
|
type: 'cond-op-select',
|
|
@@ -114,6 +124,10 @@ const config = computed<GroupListConfig>(() => ({
|
|
|
114
124
|
label: '条件',
|
|
115
125
|
width: 140,
|
|
116
126
|
name: 'op',
|
|
127
|
+
rules: [
|
|
128
|
+
{ required: true, trigger: 'blur', message: '请选择条件' },
|
|
129
|
+
{ typeMatch: true, trigger: 'change' },
|
|
130
|
+
],
|
|
117
131
|
},
|
|
118
132
|
{
|
|
119
133
|
label: '值',
|