@tmagic/editor 1.8.0-beta.1 → 1.8.0-beta.2
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 +11 -2
- package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +18 -110
- package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +6 -0
- package/dist/es/components/CompareForm.js +5 -0
- package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +167 -0
- package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +19 -3
- package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +18 -2
- package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +37 -6
- package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +3 -1
- package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +14 -6
- package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +3 -1
- package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +15 -7
- package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +14 -6
- package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +3 -1
- package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +58 -11
- package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +11 -6
- package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +8 -5
- package/dist/es/hooks/use-code-block-edit.js +2 -2
- package/dist/es/hooks/use-stage.js +15 -7
- package/dist/es/index.js +3 -1
- package/dist/es/initService.js +6 -6
- package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +4 -0
- package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +12 -7
- package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +8 -0
- package/dist/es/layouts/history-list/Bucket.js +5 -0
- package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +84 -0
- package/dist/es/layouts/history-list/CodeBlockTab.js +5 -0
- package/dist/es/layouts/history-list/CodeBlockTab.vue_vue_type_script_setup_true_lang.js +62 -0
- package/dist/es/layouts/history-list/DataSourceTab.js +5 -0
- package/dist/es/layouts/history-list/DataSourceTab.vue_vue_type_script_setup_true_lang.js +62 -0
- package/dist/es/layouts/history-list/GroupRow.js +5 -0
- package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +180 -0
- package/dist/es/layouts/history-list/HistoryDiffDialog.js +5 -0
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +195 -0
- package/dist/es/layouts/history-list/HistoryListPanel.js +5 -0
- package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +270 -0
- package/dist/es/layouts/history-list/InitialRow.js +5 -0
- package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +48 -0
- package/dist/es/layouts/history-list/PageTab.js +5 -0
- package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +99 -0
- package/dist/es/layouts/history-list/composables.js +154 -0
- package/dist/es/layouts/sidebar/layer/use-node-status.js +1 -1
- package/dist/es/services/BaseService.js +31 -68
- package/dist/es/services/codeBlock.js +205 -8
- package/dist/es/services/componentList.js +2 -2
- package/dist/es/services/dataSource.js +217 -8
- package/dist/es/services/dep.js +21 -9
- package/dist/es/services/editor.js +350 -160
- package/dist/es/services/events.js +2 -2
- package/dist/es/services/history.js +407 -12
- package/dist/es/services/keybinding.js +2 -2
- package/dist/es/services/props.js +8 -8
- package/dist/es/services/stageOverlay.js +2 -2
- package/dist/es/services/storage.js +2 -2
- package/dist/es/services/ui.js +2 -2
- package/dist/es/style.css +347 -1
- package/dist/es/utils/code-block.js +122 -0
- package/dist/es/utils/content-menu.js +4 -5
- package/dist/es/utils/dep/worker.js +1 -1
- package/dist/es/utils/editor.js +2 -2
- package/dist/es/utils/undo-redo.js +20 -0
- package/dist/style.css +347 -1
- package/dist/tmagic-editor.umd.cjs +3165 -774
- package/package.json +7 -7
- package/src/Editor.vue +7 -0
- package/src/components/CodeBlockEditor.vue +20 -109
- package/src/components/CodeParams.vue +6 -0
- package/src/components/CompareForm.vue +225 -0
- package/src/editorProps.ts +2 -0
- package/src/fields/Code.vue +19 -3
- package/src/fields/CodeSelect.vue +17 -1
- package/src/fields/CodeSelectCol.vue +30 -2
- package/src/fields/DataSourceFieldSelect/FieldSelect.vue +7 -1
- package/src/fields/DataSourceFieldSelect/Index.vue +2 -2
- package/src/fields/DataSourceFields.vue +12 -3
- package/src/fields/DataSourceMethodSelect.vue +4 -1
- package/src/fields/DataSourceMethods.vue +14 -4
- package/src/fields/DataSourceMocks.vue +13 -3
- package/src/fields/DataSourceSelect.vue +4 -1
- package/src/fields/EventSelect.vue +57 -8
- package/src/fields/KeyValue.vue +17 -4
- package/src/fields/UISelect.vue +5 -2
- package/src/hooks/use-code-block-edit.ts +5 -2
- package/src/hooks/use-stage.ts +23 -7
- package/src/index.ts +1 -0
- package/src/layouts/CodeEditor.vue +15 -0
- package/src/layouts/Framework.vue +10 -5
- package/src/layouts/NavMenu.vue +9 -0
- package/src/layouts/history-list/Bucket.vue +99 -0
- package/src/layouts/history-list/CodeBlockTab.vue +61 -0
- package/src/layouts/history-list/DataSourceTab.vue +61 -0
- package/src/layouts/history-list/GroupRow.vue +207 -0
- package/src/layouts/history-list/HistoryDiffDialog.vue +212 -0
- package/src/layouts/history-list/HistoryListPanel.vue +271 -0
- package/src/layouts/history-list/InitialRow.vue +41 -0
- package/src/layouts/history-list/PageTab.vue +94 -0
- package/src/layouts/history-list/composables.ts +195 -0
- package/src/layouts/sidebar/layer/use-node-status.ts +9 -6
- package/src/services/BaseService.ts +35 -86
- package/src/services/codeBlock.ts +256 -5
- package/src/services/dataSource.ts +257 -6
- package/src/services/dep.ts +32 -9
- package/src/services/editor.ts +454 -171
- package/src/services/history.ts +452 -8
- package/src/services/props.ts +2 -2
- package/src/theme/history-list-panel.scss +423 -0
- package/src/theme/theme.scss +1 -0
- package/src/type.ts +179 -6
- package/src/utils/code-block.ts +150 -0
- package/src/utils/content-menu.ts +8 -5
- package/src/utils/dep/worker.ts +5 -3
- package/src/utils/index.ts +1 -0
- package/src/utils/undo-redo.ts +23 -0
- package/types/index.d.ts +2105 -810
- package/dist/es/utils/compose.js +0 -44
- package/dist/es/utils/editor-history.js +0 -100
- package/src/utils/compose.ts +0 -52
- package/src/utils/editor-history.ts +0 -138
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.2",
|
|
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/design": "1.8.0-beta.
|
|
62
|
-
"@tmagic/
|
|
63
|
-
"@tmagic/
|
|
64
|
-
"@tmagic/
|
|
65
|
-
"@tmagic/
|
|
61
|
+
"@tmagic/design": "1.8.0-beta.2",
|
|
62
|
+
"@tmagic/table": "1.8.0-beta.2",
|
|
63
|
+
"@tmagic/utils": "1.8.0-beta.2",
|
|
64
|
+
"@tmagic/form": "1.8.0-beta.2",
|
|
65
|
+
"@tmagic/stage": "1.8.0-beta.2"
|
|
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.34",
|
|
79
|
-
"@tmagic/core": "1.8.0-beta.
|
|
79
|
+
"@tmagic/core": "1.8.0-beta.2"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
package/src/Editor.vue
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
:disabled-page-fragment="disabledPageFragment"
|
|
4
4
|
:page-bar-sort-options="pageBarSortOptions"
|
|
5
5
|
:page-filter-function="pageFilterFunction"
|
|
6
|
+
:hide-sidebar="hideSidebar"
|
|
6
7
|
>
|
|
7
8
|
<template #header>
|
|
8
9
|
<slot name="header"></slot>
|
|
@@ -229,6 +230,12 @@ provide('services', services);
|
|
|
229
230
|
|
|
230
231
|
provide('codeOptions', props.codeOptions);
|
|
231
232
|
provide('stageOptions', stageOptions);
|
|
233
|
+
/**
|
|
234
|
+
* 把顶层 `extendFormState` 提供给非 PropsPanel 链路上的组件使用(例如历史差异对话框 HistoryDiffDialog
|
|
235
|
+
* 内部的 CompareForm)。这样所有依赖业务上下文的表单 filterFunction 都能拿到一致的扩展状态,
|
|
236
|
+
* 与 PropsPanel 通过 `:extend-state` 显式传入的方式保持等价。
|
|
237
|
+
*/
|
|
238
|
+
provide('extendFormState', props.extendFormState);
|
|
232
239
|
|
|
233
240
|
provide<EventBus>('eventBus', new EventEmitter());
|
|
234
241
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
<Teleport to="body">
|
|
35
35
|
<TMagicDialog title="查看修改" v-model="difVisible" fullscreen destroy-on-close>
|
|
36
36
|
<div style="display: flex; margin-bottom: 10px">
|
|
37
|
-
<div style="flex: 1"><TMagicTag size="small" type="
|
|
37
|
+
<div style="flex: 1"><TMagicTag size="small" type="danger">修改前</TMagicTag></div>
|
|
38
38
|
<div style="flex: 1"><TMagicTag size="small" type="success">修改后</TMagicTag></div>
|
|
39
39
|
</div>
|
|
40
40
|
|
|
@@ -63,14 +63,7 @@ import { computed, inject, nextTick, Ref, ref, useTemplateRef, watch } from 'vue
|
|
|
63
63
|
|
|
64
64
|
import type { CodeBlockContent } from '@tmagic/core';
|
|
65
65
|
import { TMagicButton, TMagicDialog, tMagicMessage, tMagicMessageBox, TMagicTag } from '@tmagic/design';
|
|
66
|
-
import {
|
|
67
|
-
type ContainerChangeEventData,
|
|
68
|
-
defineFormConfig,
|
|
69
|
-
defineFormItem,
|
|
70
|
-
type FormConfig,
|
|
71
|
-
MFormBox,
|
|
72
|
-
type TableColumnConfig,
|
|
73
|
-
} from '@tmagic/form';
|
|
66
|
+
import { type ContainerChangeEventData, type FormConfig, MFormBox } from '@tmagic/form';
|
|
74
67
|
|
|
75
68
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
|
76
69
|
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
|
|
@@ -78,6 +71,7 @@ import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-positi
|
|
|
78
71
|
import { useServices } from '@editor/hooks/use-services';
|
|
79
72
|
import { useWindowRect } from '@editor/hooks/use-window-rect';
|
|
80
73
|
import CodeEditor from '@editor/layouts/CodeEditor.vue';
|
|
74
|
+
import { getCodeBlockFormConfig } from '@editor/utils/code-block';
|
|
81
75
|
import { getEditorConfig } from '@editor/utils/config';
|
|
82
76
|
|
|
83
77
|
defineOptions({
|
|
@@ -119,106 +113,23 @@ const diffChange = () => {
|
|
|
119
113
|
difVisible.value = false;
|
|
120
114
|
};
|
|
121
115
|
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
label: '字符串',
|
|
140
|
-
value: 'text',
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
text: '组件',
|
|
144
|
-
label: '组件',
|
|
145
|
-
value: 'ui-select',
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
const functionConfig = computed(
|
|
153
|
-
() =>
|
|
154
|
-
defineFormConfig([
|
|
155
|
-
{
|
|
156
|
-
text: '名称',
|
|
157
|
-
name: 'name',
|
|
158
|
-
rules: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
text: '描述',
|
|
162
|
-
name: 'desc',
|
|
163
|
-
},
|
|
164
|
-
{
|
|
165
|
-
text: '执行时机',
|
|
166
|
-
name: 'timing',
|
|
167
|
-
type: 'select',
|
|
168
|
-
options: () => {
|
|
169
|
-
const options = [
|
|
170
|
-
{ text: '初始化前', value: 'beforeInit' },
|
|
171
|
-
{ text: '初始化后', value: 'afterInit' },
|
|
172
|
-
];
|
|
173
|
-
if (props.dataSourceType !== 'base') {
|
|
174
|
-
options.push({ text: '请求前', value: 'beforeRequest' });
|
|
175
|
-
options.push({ text: '请求后', value: 'afterRequest' });
|
|
176
|
-
}
|
|
177
|
-
return options;
|
|
178
|
-
},
|
|
179
|
-
display: () => props.isDataSource,
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
type: 'table',
|
|
183
|
-
border: true,
|
|
184
|
-
text: '参数',
|
|
185
|
-
enableFullscreen: false,
|
|
186
|
-
enableToggleMode: false,
|
|
187
|
-
name: 'params',
|
|
188
|
-
dropSort: false,
|
|
189
|
-
items: [
|
|
190
|
-
{
|
|
191
|
-
type: 'text',
|
|
192
|
-
label: '参数名',
|
|
193
|
-
name: 'name',
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
type: 'text',
|
|
197
|
-
label: '描述',
|
|
198
|
-
name: 'extra',
|
|
199
|
-
},
|
|
200
|
-
codeBlockService.getParamsColConfig() || defaultParamColConfig,
|
|
201
|
-
],
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
name: 'content',
|
|
205
|
-
type: 'vs-code',
|
|
206
|
-
options: inject('codeOptions', {}),
|
|
207
|
-
autosize: { minRows: 10, maxRows: 30 },
|
|
208
|
-
onChange: (_formState, code: string) => {
|
|
209
|
-
try {
|
|
210
|
-
// 检测js代码是否存在语法错误
|
|
211
|
-
getEditorConfig('parseDSL')(code);
|
|
212
|
-
|
|
213
|
-
return code;
|
|
214
|
-
} catch (error: any) {
|
|
215
|
-
tMagicMessage.error(error.message);
|
|
216
|
-
|
|
217
|
-
throw error;
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
},
|
|
221
|
-
]) as FormConfig,
|
|
116
|
+
const codeOptions = inject<Record<string, any>>('codeOptions', {});
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* 代码块编辑表单配置。统一委托到 utils/code-block 的 `getCodeBlockFormConfig`,
|
|
120
|
+
* 与 CompareForm 等其它使用方共享同一份 schema,避免双份维护。
|
|
121
|
+
*
|
|
122
|
+
* 这里以 computed 包裹是为了让 `props.isDataSource` / `props.dataSourceType` 变化时
|
|
123
|
+
* "执行时机"字段的可见性与可选项实时刷新。
|
|
124
|
+
*/
|
|
125
|
+
const functionConfig = computed<FormConfig>(() =>
|
|
126
|
+
getCodeBlockFormConfig({
|
|
127
|
+
paramColConfig: codeBlockService.getParamsColConfig(),
|
|
128
|
+
isDataSource: () => Boolean(props.isDataSource),
|
|
129
|
+
dataSourceType: () => props.dataSourceType,
|
|
130
|
+
codeOptions,
|
|
131
|
+
editable: true,
|
|
132
|
+
}),
|
|
222
133
|
);
|
|
223
134
|
|
|
224
135
|
const parseContent = (content: any) => {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
ref="form"
|
|
4
4
|
:config="codeParamsConfig"
|
|
5
5
|
:init-values="model"
|
|
6
|
+
:last-values="lastValues"
|
|
7
|
+
:is-compare="isCompare"
|
|
6
8
|
:disabled="disabled"
|
|
7
9
|
:size="size"
|
|
8
10
|
:watch-props="false"
|
|
@@ -24,6 +26,10 @@ defineOptions({
|
|
|
24
26
|
|
|
25
27
|
const props = defineProps<{
|
|
26
28
|
model: any;
|
|
29
|
+
/** 对比模式下的历史值,透传给内部 MForm 用于逐项展示参数差异 */
|
|
30
|
+
lastValues?: any;
|
|
31
|
+
/** 是否开启对比模式 */
|
|
32
|
+
isCompare?: boolean;
|
|
27
33
|
size?: 'small' | 'default' | 'large';
|
|
28
34
|
disabled?: boolean;
|
|
29
35
|
name: string;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-compare-form-wrapper" :style="wrapperStyle">
|
|
3
|
+
<MForm
|
|
4
|
+
v-if="config.length"
|
|
5
|
+
ref="form"
|
|
6
|
+
class="m-editor-compare-form"
|
|
7
|
+
:config="config"
|
|
8
|
+
:init-values="currentValues"
|
|
9
|
+
:last-values="lastValuesProcessed"
|
|
10
|
+
:is-compare="true"
|
|
11
|
+
:disabled="true"
|
|
12
|
+
:label-width="labelWidth"
|
|
13
|
+
:extend-state="extendState"
|
|
14
|
+
:show-diff="showDiff"
|
|
15
|
+
></MForm>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script lang="ts" setup>
|
|
20
|
+
import { computed, inject, type Ref, ref, type ShallowRef, useTemplateRef, watch, watchEffect } from 'vue';
|
|
21
|
+
import { isEqual } from 'lodash-es';
|
|
22
|
+
|
|
23
|
+
import { type CodeBlockContent, type DataSourceSchema, HookType, type MNode } from '@tmagic/core';
|
|
24
|
+
import { type FormConfig, type FormState, type FormValue, MForm } from '@tmagic/form';
|
|
25
|
+
|
|
26
|
+
import { useServices } from '@editor/hooks/use-services';
|
|
27
|
+
import { getCodeBlockFormConfig } from '@editor/utils/code-block';
|
|
28
|
+
|
|
29
|
+
defineOptions({
|
|
30
|
+
name: 'MEditorCompareForm',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 对比类型:
|
|
35
|
+
* - node: 节点组件,按 `type` 从 propsService 获取属性表单配置
|
|
36
|
+
* - data-source: 数据源,按 `type`(base/http/...) 从 dataSourceService 获取数据源表单配置
|
|
37
|
+
* - code-block: 数据源代码块,使用内置的代码块表单配置
|
|
38
|
+
*/
|
|
39
|
+
export type CompareCategory = 'node' | 'data-source' | 'code-block';
|
|
40
|
+
|
|
41
|
+
const props = withDefaults(
|
|
42
|
+
defineProps<{
|
|
43
|
+
/** 当前值(修改后的值) */
|
|
44
|
+
value: Partial<MNode> | Partial<DataSourceSchema> | Partial<CodeBlockContent> | Record<string, any>;
|
|
45
|
+
/** 用于对比的旧值(修改前的值) */
|
|
46
|
+
lastValue?: Partial<MNode> | Partial<DataSourceSchema> | Partial<CodeBlockContent> | Record<string, any>;
|
|
47
|
+
/**
|
|
48
|
+
* 类型说明:
|
|
49
|
+
* - `category` 为 `node` 时,`type` 为节点组件的类型,例如 'text'、'button'、'page'、'container' 等
|
|
50
|
+
* - `category` 为 `data-source` 时,`type` 为数据源类型,例如 'base'、'http'
|
|
51
|
+
* - `category` 为 `code-block` 时,`type` 可不传
|
|
52
|
+
*/
|
|
53
|
+
type?: string;
|
|
54
|
+
/** 表单配置类别,决定从哪里取 FormConfig */
|
|
55
|
+
category?: CompareCategory;
|
|
56
|
+
/** 数据源代码块场景下的数据源类型(base/http),用于代码块表单中"执行时机"展示 */
|
|
57
|
+
dataSourceType?: string;
|
|
58
|
+
labelWidth?: string;
|
|
59
|
+
/**
|
|
60
|
+
* 外层容器高度。设置后表单内容超出时会在 CompareForm 内部出现滚动条,
|
|
61
|
+
* 避免 dialog / 面板使用方需要自行处理滚动。可传任意 CSS 长度,例如 `60vh` / `400px` / `100%`。
|
|
62
|
+
*/
|
|
63
|
+
height?: string;
|
|
64
|
+
/**
|
|
65
|
+
* 用户自定义注入到 MForm.formState 的扩展字段,与 Editor 顶层的 `extendFormState`、
|
|
66
|
+
* PropsPanel 的 `extend-state` 语义一致。表单 item 的 `display` / `disabled` 等
|
|
67
|
+
* filterFunction 经常依赖这里注入的字段(如 stage、自定义业务上下文等),
|
|
68
|
+
* 因此在差异对比场景下也需要透传,避免出现 `formState.xxx is undefined` 的运行时错误。
|
|
69
|
+
*/
|
|
70
|
+
extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
71
|
+
}>(),
|
|
72
|
+
{
|
|
73
|
+
category: 'node',
|
|
74
|
+
labelWidth: '120px',
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const { propsService, dataSourceService, codeBlockService, editorService } = useServices();
|
|
79
|
+
const services = useServices();
|
|
80
|
+
|
|
81
|
+
const config = ref<FormConfig>([]);
|
|
82
|
+
|
|
83
|
+
/** vs-code 编辑器的 monaco 配置项,沿用 Editor 顶层 provide('codeOptions', ...) 的注入。 */
|
|
84
|
+
const codeOptions = inject<Record<string, any>>('codeOptions', {});
|
|
85
|
+
|
|
86
|
+
/** 将代码块的 content 字段统一成字符串,便于在表单/对比中展示 */
|
|
87
|
+
const normalizeCodeBlockValue = (
|
|
88
|
+
v: Partial<CodeBlockContent> | Record<string, any> | undefined,
|
|
89
|
+
): Record<string, any> => {
|
|
90
|
+
if (!v) return {};
|
|
91
|
+
const next: Record<string, any> = { ...v };
|
|
92
|
+
if (next.content && typeof next.content !== 'string') {
|
|
93
|
+
try {
|
|
94
|
+
next.content = next.content.toString();
|
|
95
|
+
} catch {
|
|
96
|
+
next.content = '';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return next;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const currentValues = computed<FormValue>(() => {
|
|
103
|
+
if (props.category === 'code-block') {
|
|
104
|
+
return normalizeCodeBlockValue(props.value as Partial<CodeBlockContent>);
|
|
105
|
+
}
|
|
106
|
+
return (props.value || {}) as FormValue;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const lastValuesProcessed = computed<FormValue>(() => {
|
|
110
|
+
if (props.category === 'code-block') {
|
|
111
|
+
return normalizeCodeBlockValue(props.lastValue as Partial<CodeBlockContent>);
|
|
112
|
+
}
|
|
113
|
+
return (props.lastValue || {}) as FormValue;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* 外层包裹层的样式:当传入 `height` 时启用固定高度 + 内部滚动,
|
|
118
|
+
* 这样滚动条会出现在 CompareForm 内部,避免父容器(如 Dialog)自身也产生滚动。
|
|
119
|
+
*/
|
|
120
|
+
const wrapperStyle = computed(() => {
|
|
121
|
+
if (!props.height) return undefined;
|
|
122
|
+
return {
|
|
123
|
+
height: props.height,
|
|
124
|
+
overflow: 'auto',
|
|
125
|
+
} as Record<string, string>;
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* `code-select` 字段在历史数据中存在两种"语义为空"的形态:
|
|
130
|
+
* - 字符串 `''`(旧数据 / 用户从未配置过钩子);
|
|
131
|
+
* - `{ hookType: HookType.CODE, hookData: [] }`(CodeSelect.vue 在挂载时
|
|
132
|
+
* 写入的默认结构,参见 packages/editor/src/fields/CodeSelect.vue 中
|
|
133
|
+
* `props.model[props.name] = { hookType: HookType.CODE, hookData: [] }`)。
|
|
134
|
+
*
|
|
135
|
+
* 直接 `isEqual` 会把两者判为不等,从而在历史对比里对每个未配置过钩子的组件
|
|
136
|
+
* 都展示一份"差异",体验很糟糕。这里把它们视为相等,跳过对比。
|
|
137
|
+
*
|
|
138
|
+
* 其它类型字段沿用 MForm/Container 的默认 `!isEqual` 判断逻辑。
|
|
139
|
+
*/
|
|
140
|
+
const isEmptyCodeSelectValue = (v: any): boolean => {
|
|
141
|
+
if (v === '' || v === undefined || v === null) return true;
|
|
142
|
+
if (Array.isArray(v) && v.length === 0) return true;
|
|
143
|
+
return typeof v === 'object' && v.hookType === HookType.CODE && Array.isArray(v.hookData) && v.hookData.length === 0;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const showDiff = ({ curValue, lastValue, config }: { curValue: any; lastValue: any; config: any }) => {
|
|
147
|
+
if (config?.type === 'code-select') {
|
|
148
|
+
// 双方都是"空形态",视为相等,不展示对比
|
|
149
|
+
if (isEmptyCodeSelectValue(curValue) && isEmptyCodeSelectValue(lastValue)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return !isEqual(curValue, lastValue);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const loadConfig = async () => {
|
|
157
|
+
switch (props.category) {
|
|
158
|
+
case 'node': {
|
|
159
|
+
if (!props.type) {
|
|
160
|
+
config.value = [];
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
config.value = await propsService.getPropsConfig(props.type);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case 'data-source': {
|
|
167
|
+
config.value = dataSourceService.getFormConfig(props.type || 'base');
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'code-block': {
|
|
171
|
+
config.value = getCodeBlockFormConfig({
|
|
172
|
+
paramColConfig: codeBlockService.getParamsColConfig(),
|
|
173
|
+
// 通过传入 dataSourceType 间接表达"是数据源代码块"——在对比场景下 props.dataSourceType
|
|
174
|
+
// 由调用方按 step 上下文显式传入,未传则视为普通代码块,「执行时机」字段隐藏。
|
|
175
|
+
isDataSource: () => Boolean(props.dataSourceType),
|
|
176
|
+
dataSourceType: () => props.dataSourceType,
|
|
177
|
+
codeOptions,
|
|
178
|
+
// 对比模式只读,不需要校验/语法检查
|
|
179
|
+
editable: false,
|
|
180
|
+
});
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
default:
|
|
184
|
+
config.value = [];
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
watch(
|
|
189
|
+
[() => props.category, () => props.type, () => props.dataSourceType],
|
|
190
|
+
() => {
|
|
191
|
+
loadConfig();
|
|
192
|
+
},
|
|
193
|
+
{ immediate: true },
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const formRef = useTemplateRef<InstanceType<typeof MForm>>('form');
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 把 services / stage 注入 MForm 的 formState,避免 propsService 注入的表单配置中
|
|
200
|
+
* 形如 `display: ({ services }) => services.uiService.get(...)` 的 filterFunction
|
|
201
|
+
* 在执行时拿不到 `formState.services` 而报错。
|
|
202
|
+
*
|
|
203
|
+
* 与 props-panel/FormPanel.vue 中的注入方式保持一致:
|
|
204
|
+
* - services:整个 useServices() 返回的服务集合;
|
|
205
|
+
* - stage:当前 editorService.get('stage') 的最新值。
|
|
206
|
+
*/
|
|
207
|
+
const stage = computed(() => editorService.get('stage'));
|
|
208
|
+
|
|
209
|
+
watchEffect(() => {
|
|
210
|
+
if (formRef.value) {
|
|
211
|
+
formRef.value.formState.stage = stage.value;
|
|
212
|
+
formRef.value.formState.services = services;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
defineExpose<{
|
|
217
|
+
form: ShallowRef<InstanceType<typeof MForm> | null>;
|
|
218
|
+
config: Ref<FormConfig>;
|
|
219
|
+
reload: () => Promise<void>;
|
|
220
|
+
}>({
|
|
221
|
+
form: formRef,
|
|
222
|
+
config,
|
|
223
|
+
reload: loadConfig,
|
|
224
|
+
});
|
|
225
|
+
</script>
|
package/src/editorProps.ts
CHANGED
package/src/fields/Code.vue
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<MagicCodeEditor
|
|
3
3
|
:height="config.height"
|
|
4
|
-
:
|
|
4
|
+
:type="diffMode ? 'diff' : undefined"
|
|
5
|
+
:init-values="diffMode ? (lastValues || {})[name] : model[name]"
|
|
6
|
+
:modified-values="diffMode ? model[name] : undefined"
|
|
5
7
|
:language="config.language"
|
|
6
8
|
:options="{
|
|
7
9
|
...config.options,
|
|
8
|
-
readOnly: disabled,
|
|
10
|
+
readOnly: diffMode ? true : disabled,
|
|
9
11
|
}"
|
|
10
12
|
:autosize="config.autosize"
|
|
11
13
|
:parse="config.parse"
|
|
@@ -15,6 +17,8 @@
|
|
|
15
17
|
</template>
|
|
16
18
|
|
|
17
19
|
<script lang="ts" setup>
|
|
20
|
+
import { computed } from 'vue';
|
|
21
|
+
|
|
18
22
|
import type { CodeConfig, FieldProps } from '@tmagic/form';
|
|
19
23
|
|
|
20
24
|
import MagicCodeEditor from '@editor/layouts/CodeEditor.vue';
|
|
@@ -27,10 +31,22 @@ const emit = defineEmits<{
|
|
|
27
31
|
change: [value: string | any];
|
|
28
32
|
}>();
|
|
29
33
|
|
|
30
|
-
withDefaults(defineProps<FieldProps<CodeConfig>>(), {
|
|
34
|
+
const props = withDefaults(defineProps<FieldProps<CodeConfig>>(), {
|
|
31
35
|
disabled: false,
|
|
32
36
|
});
|
|
33
37
|
|
|
38
|
+
/**
|
|
39
|
+
* 对比模式判定:
|
|
40
|
+
*
|
|
41
|
+
* - 当 `isCompare === true` 时,由父级 `MFormContainer` 统一渲染一次本字段(不再渲染前后两份独立组件),
|
|
42
|
+
* 并把 `model`(当前值)与 `lastValues`(历史值)一并传入;
|
|
43
|
+
* - 此时本字段切换到 monaco 自带的 diff 编辑器(左侧旧、右侧新),相比"两个独立 monaco 实例"更直观,
|
|
44
|
+
* 也避免了同一表单内重复实例化重型编辑器带来的开销。
|
|
45
|
+
*
|
|
46
|
+
* 仅当存在历史值(lastValues)且开启了对比模式时启用 diff,避免在 lastValues 缺失时退化为空对比。
|
|
47
|
+
*/
|
|
48
|
+
const diffMode = computed(() => Boolean(props.isCompare && props.lastValues));
|
|
49
|
+
|
|
34
50
|
const save = (v: string | any) => {
|
|
35
51
|
emit('change', v);
|
|
36
52
|
};
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
:size="size"
|
|
7
7
|
:prop="prop"
|
|
8
8
|
:disabled="disabled"
|
|
9
|
-
:
|
|
9
|
+
:is-compare="isCompareMode"
|
|
10
|
+
:last-values="lastValues?.[name]"
|
|
10
11
|
:model="model[name]"
|
|
11
12
|
@change="changeHandler"
|
|
12
13
|
>
|
|
@@ -38,6 +39,21 @@ const { dataSourceService, codeBlockService } = useServices();
|
|
|
38
39
|
|
|
39
40
|
const props = withDefaults(defineProps<FieldProps<CodeSelectConfig>>(), {});
|
|
40
41
|
|
|
42
|
+
/**
|
|
43
|
+
* 对比模式判定:
|
|
44
|
+
*
|
|
45
|
+
* code-select 仅是对内部「钩子列表」group-list 的包裹,本身不渲染叶子字段。父级 `MFormContainer`
|
|
46
|
+
* 已将其归入「自接管对比字段」(见 Container.vue 的 `SELF_DIFF_FIELD_TYPES`),即对比时只渲染一次
|
|
47
|
+
* 本组件,并把当前值 `model` 与历史值 `lastValues` 一并传入,由本组件把 `is-compare`/`lastValues`
|
|
48
|
+
* 透传给内部 MContainer,再由 group-list / code-select-col 等子级逐项展示前后差异。
|
|
49
|
+
*
|
|
50
|
+
* 注意:`model` 传入的是 `model[name]`(钩子值本身),因此 `lastValues` 也必须同层取 `lastValues[name]`,
|
|
51
|
+
* 否则前后值的嵌套层级不一致会导致对比错位。
|
|
52
|
+
*
|
|
53
|
+
* 仅当存在历史值时才启用对比,避免 lastValues 缺失时退化为「全部新增」的空对比。
|
|
54
|
+
*/
|
|
55
|
+
const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues));
|
|
56
|
+
|
|
41
57
|
const codeConfig = computed<GroupListConfig>(() => ({
|
|
42
58
|
type: 'group-list',
|
|
43
59
|
name: 'hookData',
|
|
@@ -2,7 +2,20 @@
|
|
|
2
2
|
<div class="m-fields-code-select-col">
|
|
3
3
|
<div class="code-select-container">
|
|
4
4
|
<!-- 代码块下拉框 -->
|
|
5
|
+
<!-- 对比模式下交由 MFormContainer 展示下拉框的前后差异(codeId 变化时高亮新旧代码块名),
|
|
6
|
+
普通模式仍直接渲染 MSelect 以保留选择 / 写值逻辑 -->
|
|
7
|
+
<MFormContainer
|
|
8
|
+
v-if="isCompareMode"
|
|
9
|
+
class="select"
|
|
10
|
+
:config="selectConfig"
|
|
11
|
+
:model="model"
|
|
12
|
+
:last-values="lastValues"
|
|
13
|
+
:is-compare="true"
|
|
14
|
+
:size="size"
|
|
15
|
+
:prop="prop"
|
|
16
|
+
></MFormContainer>
|
|
5
17
|
<MSelect
|
|
18
|
+
v-else
|
|
6
19
|
class="select"
|
|
7
20
|
:config="selectConfig"
|
|
8
21
|
:name="name"
|
|
@@ -12,9 +25,9 @@
|
|
|
12
25
|
@change="onCodeIdChangeHandler"
|
|
13
26
|
></MSelect>
|
|
14
27
|
|
|
15
|
-
<!--
|
|
28
|
+
<!-- 查看/编辑按钮:对比模式为只读,不展示 -->
|
|
16
29
|
<TMagicButton
|
|
17
|
-
v-if="model[name] && hasCodeBlockSidePanel"
|
|
30
|
+
v-if="!isCompareMode && model[name] && hasCodeBlockSidePanel"
|
|
18
31
|
class="m-fields-select-action-button"
|
|
19
32
|
:size="size"
|
|
20
33
|
@click="editCode(model[name])"
|
|
@@ -29,6 +42,8 @@
|
|
|
29
42
|
name="params"
|
|
30
43
|
:key="model[name]"
|
|
31
44
|
:model="model"
|
|
45
|
+
:last-values="lastValues"
|
|
46
|
+
:is-compare="isCompareMode"
|
|
32
47
|
:size="size"
|
|
33
48
|
:disabled="disabled"
|
|
34
49
|
:params-config="paramsConfig"
|
|
@@ -52,6 +67,7 @@ import {
|
|
|
52
67
|
filterFunction,
|
|
53
68
|
type FormItemConfig,
|
|
54
69
|
type FormState,
|
|
70
|
+
MContainer as MFormContainer,
|
|
55
71
|
MSelect,
|
|
56
72
|
type SelectConfig,
|
|
57
73
|
} from '@tmagic/form';
|
|
@@ -77,6 +93,18 @@ const props = withDefaults(defineProps<FieldProps<CodeSelectColConfig>>(), {
|
|
|
77
93
|
disabled: false,
|
|
78
94
|
});
|
|
79
95
|
|
|
96
|
+
/**
|
|
97
|
+
* 对比模式判定:
|
|
98
|
+
*
|
|
99
|
+
* code-select-col 由「代码块下拉框 + 参数子表单」组成,属于复合字段。父级 `MFormContainer` 已将其
|
|
100
|
+
* 归入「自接管对比字段」(见 Container.vue 的 `SELF_DIFF_FIELD_TYPES`),即对比时只渲染一次本组件,
|
|
101
|
+
* 并把当前值 `model` 与历史值 `lastValues` 一并传入,由本组件把 `is-compare`/`lastValues` 透传给
|
|
102
|
+
* 内部的下拉框(MFormContainer)与参数表单(CodeParams),逐项展示前后差异。
|
|
103
|
+
*
|
|
104
|
+
* 仅当存在历史值时才启用对比,避免 lastValues 缺失时退化为「全部新增」的空对比。
|
|
105
|
+
*/
|
|
106
|
+
const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues));
|
|
107
|
+
|
|
80
108
|
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
|
|
81
109
|
|
|
82
110
|
const hasCodeBlockSidePanel = computed(() =>
|
|
@@ -72,7 +72,10 @@
|
|
|
72
72
|
@change="onChangeHandler"
|
|
73
73
|
></TMagicCascader>
|
|
74
74
|
|
|
75
|
-
<TMagicTooltip
|
|
75
|
+
<TMagicTooltip
|
|
76
|
+
v-if="selectDataSourceId && hasDataSourceSidePanel && !isCompare"
|
|
77
|
+
:content="notEditable ? '查看' : '编辑'"
|
|
78
|
+
>
|
|
76
79
|
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editHandler(selectDataSourceId)"
|
|
77
80
|
><MIcon :icon="!notEditable ? Edit : View"></MIcon
|
|
78
81
|
></TMagicButton>
|
|
@@ -133,6 +136,9 @@ const dataSources = computed(() => {
|
|
|
133
136
|
const valueIsKey = computed(() => props.value === 'key');
|
|
134
137
|
const notEditable = computed(() => filterFunction(mForm, props.notEditable, props));
|
|
135
138
|
|
|
139
|
+
/** 对比模式下隐藏查看/编辑操作按钮,仅保留只读展示。 */
|
|
140
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
141
|
+
|
|
136
142
|
const dataSourcesOptions = computed<SelectOption[]>(() =>
|
|
137
143
|
dataSources.value.map((ds) => ({
|
|
138
144
|
text: ds.title || ds.id,
|
|
@@ -28,14 +28,14 @@
|
|
|
28
28
|
></component>
|
|
29
29
|
|
|
30
30
|
<TMagicTooltip
|
|
31
|
-
v-if="config.fieldConfig && !disabledDataSource"
|
|
31
|
+
v-if="config.fieldConfig && !disabledDataSource && !mForm?.isCompare"
|
|
32
32
|
:disabled="showDataSourceFieldSelect"
|
|
33
33
|
content="选择数据源"
|
|
34
34
|
>
|
|
35
35
|
<TMagicButton
|
|
36
36
|
:type="showDataSourceFieldSelect ? 'primary' : 'default'"
|
|
37
37
|
:size="size"
|
|
38
|
-
:disabled="disabled
|
|
38
|
+
:disabled="disabled"
|
|
39
39
|
@click="onToggleDataSourceFieldSelectHandler"
|
|
40
40
|
><MIcon :icon="Coin"></MIcon
|
|
41
41
|
></TMagicButton>
|