@tmagic/editor 1.8.0-beta.1 → 1.8.0-beta.3
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 +20 -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 +171 -0
- package/dist/es/editorProps.js +2 -0
- package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +19 -3
- package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +2 -5
- 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 +17 -8
- package/dist/es/index.js +5 -1
- package/dist/es/initService.js +6 -6
- package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +6 -5
- 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 +103 -0
- package/dist/es/layouts/history-list/BucketTab.js +5 -0
- package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +73 -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 +198 -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 +245 -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 +340 -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 +54 -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 +105 -0
- package/dist/es/layouts/history-list/composables.js +172 -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 +410 -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 +379 -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 +379 -1
- package/dist/tmagic-editor.umd.cjs +3331 -787
- package/package.json +7 -7
- package/src/Editor.vue +15 -0
- package/src/components/CodeBlockEditor.vue +20 -109
- package/src/components/CodeParams.vue +6 -0
- package/src/components/CompareForm.vue +238 -0
- package/src/editorProps.ts +9 -0
- package/src/fields/Code.vue +19 -3
- package/src/fields/CodeLink.vue +2 -5
- 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 +24 -7
- package/src/index.ts +3 -0
- package/src/layouts/CodeEditor.vue +17 -5
- package/src/layouts/Framework.vue +10 -5
- package/src/layouts/NavMenu.vue +9 -0
- package/src/layouts/history-list/Bucket.vue +124 -0
- package/src/layouts/history-list/BucketTab.vue +77 -0
- package/src/layouts/history-list/GroupRow.vue +242 -0
- package/src/layouts/history-list/HistoryDiffDialog.vue +231 -0
- package/src/layouts/history-list/HistoryListPanel.vue +380 -0
- package/src/layouts/history-list/InitialRow.vue +52 -0
- package/src/layouts/history-list/PageTab.vue +108 -0
- package/src/layouts/history-list/composables.ts +226 -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 +455 -8
- package/src/services/props.ts +2 -2
- package/src/theme/history-list-panel.scss +462 -0
- package/src/theme/theme.scss +1 -0
- package/src/type.ts +269 -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 +2274 -819
- 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
|
@@ -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>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-data-source-fields">
|
|
3
|
-
<MagicTable :data="model[name]" :columns="
|
|
3
|
+
<MagicTable :data="model[name]" :columns="displayColumns" :border="true"></MagicTable>
|
|
4
4
|
|
|
5
|
-
<div class="m-editor-data-source-fields-footer">
|
|
5
|
+
<div v-if="!isCompare" class="m-editor-data-source-fields-footer">
|
|
6
6
|
<TMagicButton size="small" :disabled="disabled" plain @click="newFromJsonHandler()">快速添加</TMagicButton>
|
|
7
7
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
|
8
8
|
</div>
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
</template>
|
|
48
48
|
|
|
49
49
|
<script setup lang="ts">
|
|
50
|
-
import { inject, Ref, ref } from 'vue';
|
|
50
|
+
import { computed, inject, Ref, ref } from 'vue';
|
|
51
51
|
|
|
52
52
|
import type { DataSchema } from '@tmagic/core';
|
|
53
53
|
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
|
@@ -84,6 +84,10 @@ const emit = defineEmits<{
|
|
|
84
84
|
}>();
|
|
85
85
|
|
|
86
86
|
const { uiService } = useServices();
|
|
87
|
+
const mForm = inject<FormState | undefined>('mForm');
|
|
88
|
+
|
|
89
|
+
/** 对比模式下隐藏新增/编辑/删除等操作按钮,仅保留只读展示。 */
|
|
90
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
87
91
|
|
|
88
92
|
const fieldValues = ref<Record<string, any>>({});
|
|
89
93
|
const fieldTitle = ref('');
|
|
@@ -176,6 +180,11 @@ const fieldColumns: ColumnConfig[] = [
|
|
|
176
180
|
},
|
|
177
181
|
];
|
|
178
182
|
|
|
183
|
+
/** 对比模式下移除「操作」列(编辑/删除按钮),仅保留只读列。 */
|
|
184
|
+
const displayColumns = computed<ColumnConfig[]>(() =>
|
|
185
|
+
isCompare.value ? fieldColumns.filter((col) => !col.actions) : fieldColumns,
|
|
186
|
+
);
|
|
187
|
+
|
|
179
188
|
const dataSourceFieldsConfig: FormConfig = [
|
|
180
189
|
{ name: 'index', type: 'hidden', filter: 'number', defaultValue: -1 },
|
|
181
190
|
{
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
></MCascader>
|
|
14
14
|
|
|
15
15
|
<TMagicTooltip
|
|
16
|
-
v-if="model[name] && isCustomMethod && hasDataSourceSidePanel"
|
|
16
|
+
v-if="model[name] && isCustomMethod && hasDataSourceSidePanel && !isCompare"
|
|
17
17
|
:content="notEditable ? '查看' : '编辑'"
|
|
18
18
|
>
|
|
19
19
|
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editCodeHandler">
|
|
@@ -81,6 +81,9 @@ const hasDataSourceSidePanel = computed(() =>
|
|
|
81
81
|
|
|
82
82
|
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
|
|
83
83
|
|
|
84
|
+
/** 对比模式下隐藏查看/编辑操作按钮,仅保留只读展示。 */
|
|
85
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
86
|
+
|
|
84
87
|
const dataSources = computed(() => dataSourceService.get('dataSources'));
|
|
85
88
|
|
|
86
89
|
const isCustomMethod = computed(() => {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-data-source-methods">
|
|
3
|
-
<MagicTable :data="model[name]" :columns="
|
|
3
|
+
<MagicTable :data="model[name]" :columns="displayColumns" :border="true"></MagicTable>
|
|
4
4
|
|
|
5
|
-
<div class="m-editor-data-source-methods-footer">
|
|
5
|
+
<div v-if="!isCompare" class="m-editor-data-source-methods-footer">
|
|
6
6
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="createCodeHandler"
|
|
7
7
|
>添加</TMagicButton
|
|
8
8
|
>
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
</template>
|
|
22
22
|
|
|
23
23
|
<script setup lang="ts">
|
|
24
|
-
import { nextTick, ref, useTemplateRef } from 'vue';
|
|
24
|
+
import { computed, inject, nextTick, ref, useTemplateRef } from 'vue';
|
|
25
25
|
import { cloneDeep } from 'lodash-es';
|
|
26
26
|
|
|
27
27
|
import type { CodeBlockContent } from '@tmagic/core';
|
|
28
28
|
import { TMagicButton, tMagicMessageBox } from '@tmagic/design';
|
|
29
|
-
import type { ContainerChangeEventData, DataSourceMethodsConfig, FieldProps } from '@tmagic/form';
|
|
29
|
+
import type { ContainerChangeEventData, DataSourceMethodsConfig, FieldProps, FormState } from '@tmagic/form';
|
|
30
30
|
import { type ColumnConfig, MagicTable } from '@tmagic/table';
|
|
31
31
|
|
|
32
32
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
|
@@ -42,6 +42,11 @@ const props = withDefaults(defineProps<FieldProps<DataSourceMethodsConfig>>(), {
|
|
|
42
42
|
|
|
43
43
|
const emit = defineEmits(['change']);
|
|
44
44
|
|
|
45
|
+
const mForm = inject<FormState | undefined>('mForm');
|
|
46
|
+
|
|
47
|
+
/** 对比模式下隐藏新增/编辑/删除等操作按钮,仅保留只读展示。 */
|
|
48
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
49
|
+
|
|
45
50
|
const codeConfig = ref<Omit<CodeBlockContent, 'content'> & { content: string }>();
|
|
46
51
|
const codeBlockEditorRef = useTemplateRef<InstanceType<typeof CodeBlockEditor>>('codeBlockEditor');
|
|
47
52
|
|
|
@@ -107,6 +112,11 @@ const methodColumns: ColumnConfig[] = [
|
|
|
107
112
|
},
|
|
108
113
|
];
|
|
109
114
|
|
|
115
|
+
/** 对比模式下移除「操作」列(编辑/删除按钮),仅保留只读列。 */
|
|
116
|
+
const displayColumns = computed<ColumnConfig[]>(() =>
|
|
117
|
+
isCompare.value ? methodColumns.filter((col) => !col.actions) : methodColumns,
|
|
118
|
+
);
|
|
119
|
+
|
|
110
120
|
const createCodeHandler = () => {
|
|
111
121
|
codeConfig.value = {
|
|
112
122
|
name: '',
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-data-source-fields">
|
|
3
|
-
<MagicTable :data="model[name]" :columns="
|
|
3
|
+
<MagicTable :data="model[name]" :columns="displayColumns"></MagicTable>
|
|
4
4
|
|
|
5
|
-
<div class="m-editor-data-source-fields-footer">
|
|
5
|
+
<div v-if="!isCompare" class="m-editor-data-source-fields-footer">
|
|
6
6
|
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="newHandler()">添加</TMagicButton>
|
|
7
7
|
</div>
|
|
8
8
|
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
</template>
|
|
29
29
|
|
|
30
30
|
<script setup lang="ts">
|
|
31
|
-
import { inject, Ref, ref } from 'vue';
|
|
31
|
+
import { computed, inject, Ref, ref } from 'vue';
|
|
32
32
|
|
|
33
33
|
import type { MockSchema } from '@tmagic/core';
|
|
34
34
|
import { TMagicButton, tMagicMessageBox, TMagicSwitch } from '@tmagic/design';
|
|
@@ -54,6 +54,11 @@ const props = withDefaults(defineProps<FieldProps<DataSourceMocksConfig>>(), {
|
|
|
54
54
|
const emit = defineEmits(['change']);
|
|
55
55
|
|
|
56
56
|
const { uiService } = useServices();
|
|
57
|
+
const mForm = inject<FormState | undefined>('mForm');
|
|
58
|
+
|
|
59
|
+
/** 对比模式下隐藏新增/编辑/删除等操作按钮,仅保留只读展示。 */
|
|
60
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
61
|
+
|
|
57
62
|
const width = defineModel<number>('width', { default: 670 });
|
|
58
63
|
|
|
59
64
|
const drawerTitle = ref('');
|
|
@@ -202,6 +207,11 @@ const columns: ColumnConfig[] = [
|
|
|
202
207
|
},
|
|
203
208
|
];
|
|
204
209
|
|
|
210
|
+
/** 对比模式下移除「操作」列(编辑/删除按钮),仅保留只读列。 */
|
|
211
|
+
const displayColumns = computed<ColumnConfig[]>(() =>
|
|
212
|
+
isCompare.value ? columns.filter((col) => !col.actions) : columns,
|
|
213
|
+
);
|
|
214
|
+
|
|
205
215
|
const newHandler = () => {
|
|
206
216
|
const isFirstRow = props.model[props.name].length === 0;
|
|
207
217
|
formValues.value = {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
@change="changeHandler"
|
|
12
12
|
></MSelect>
|
|
13
13
|
|
|
14
|
-
<TMagicTooltip v-if="model[name] && hasDataSourceSidePanel" :content="notEditable ? '查看' : '编辑'">
|
|
14
|
+
<TMagicTooltip v-if="model[name] && hasDataSourceSidePanel && !isCompare" :content="notEditable ? '查看' : '编辑'">
|
|
15
15
|
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editHandler"
|
|
16
16
|
><MIcon :icon="!notEditable ? Edit : View"></MIcon
|
|
17
17
|
></TMagicButton>
|
|
@@ -56,6 +56,9 @@ const dataSources = computed(() => dataSourceService.get('dataSources'));
|
|
|
56
56
|
|
|
57
57
|
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
|
|
58
58
|
|
|
59
|
+
/** 对比模式下隐藏查看/编辑操作按钮,仅保留只读展示。 */
|
|
60
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
61
|
+
|
|
59
62
|
const hasDataSourceSidePanel = computed(() =>
|
|
60
63
|
uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
|
|
61
64
|
);
|
|
@@ -6,22 +6,32 @@
|
|
|
6
6
|
:size="size"
|
|
7
7
|
:disabled="disabled"
|
|
8
8
|
:model="model"
|
|
9
|
+
:last-values="lastValues"
|
|
10
|
+
:is-compare="isCompareMode"
|
|
9
11
|
:config="tableConfig"
|
|
10
12
|
@change="onChangeHandler"
|
|
11
13
|
></MTable>
|
|
12
14
|
|
|
13
15
|
<div v-else class="fullWidth">
|
|
14
|
-
<TMagicButton
|
|
16
|
+
<TMagicButton
|
|
17
|
+
v-if="!isCompareMode"
|
|
18
|
+
class="create-button"
|
|
19
|
+
type="primary"
|
|
20
|
+
:size="size"
|
|
21
|
+
:disabled="disabled"
|
|
22
|
+
@click="addEvent()"
|
|
15
23
|
>添加事件</TMagicButton
|
|
16
24
|
>
|
|
17
25
|
<MPanel
|
|
18
|
-
v-for="
|
|
19
|
-
:key="index"
|
|
26
|
+
v-for="entry in displayList"
|
|
27
|
+
:key="entry.index"
|
|
20
28
|
:disabled="disabled"
|
|
21
29
|
:size="size"
|
|
22
|
-
:prop="`${prop}.${index}`"
|
|
30
|
+
:prop="`${prop}.${entry.index}`"
|
|
23
31
|
:config="actionsConfig"
|
|
24
|
-
:model="cardItem"
|
|
32
|
+
:model="entry.cardItem"
|
|
33
|
+
:last-values="entry.lastCardItem"
|
|
34
|
+
:is-compare="isCompareMode"
|
|
25
35
|
:label-width="config.labelWidth || '100px'"
|
|
26
36
|
@change="onChangeHandler"
|
|
27
37
|
>
|
|
@@ -29,19 +39,22 @@
|
|
|
29
39
|
<MFormContainer
|
|
30
40
|
class="fullWidth"
|
|
31
41
|
:config="eventNameConfig"
|
|
32
|
-
:model="cardItem"
|
|
42
|
+
:model="entry.cardItem"
|
|
43
|
+
:last-values="entry.lastCardItem"
|
|
44
|
+
:is-compare="isCompareMode"
|
|
33
45
|
:disabled="disabled"
|
|
34
46
|
:size="size"
|
|
35
|
-
:prop="`${prop}.${index}`"
|
|
47
|
+
:prop="`${prop}.${entry.index}`"
|
|
36
48
|
@change="eventNameChangeHandler"
|
|
37
49
|
></MFormContainer>
|
|
38
50
|
<TMagicButton
|
|
51
|
+
v-if="!isCompareMode"
|
|
39
52
|
style="color: #f56c6c"
|
|
40
53
|
link
|
|
41
54
|
:icon="Delete"
|
|
42
55
|
:disabled="disabled"
|
|
43
56
|
:size="size"
|
|
44
|
-
@click="removeEvent(Number(index))"
|
|
57
|
+
@click="removeEvent(Number(entry.index))"
|
|
45
58
|
></TMagicButton>
|
|
46
59
|
</template>
|
|
47
60
|
</MPanel>
|
|
@@ -374,6 +387,42 @@ const isOldVersion = computed(() => {
|
|
|
374
387
|
return !has(props.model[props.name][0], 'actions');
|
|
375
388
|
});
|
|
376
389
|
|
|
390
|
+
/**
|
|
391
|
+
* 对比模式判定:
|
|
392
|
+
*
|
|
393
|
+
* event-select 内部由「事件列表 + 嵌套子表单」组成,属于复合字段。父级 `MFormContainer` 已将其
|
|
394
|
+
* 归入「自接管对比字段」(见 Container.vue 的 `SELF_DIFF_FIELD_TYPES`),即对比时只渲染一次本组件,
|
|
395
|
+
* 并把当前值 `model` 与历史值 `lastValues` 一并传入,由本组件把 `is-compare`/`lastValues` 透传给
|
|
396
|
+
* 内部的 MPanel / MFormContainer,逐项(事件名、动作)展示前后差异。
|
|
397
|
+
*
|
|
398
|
+
* 仅当存在历史值时才启用对比,避免 lastValues 缺失时退化为「全部新增」的空对比。
|
|
399
|
+
*/
|
|
400
|
+
const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues));
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* 待渲染的事件卡片列表。
|
|
404
|
+
*
|
|
405
|
+
* - 非对比模式:直接映射当前事件列表,`lastCardItem` 为空;
|
|
406
|
+
* - 对比模式:按索引对齐当前值与历史值,取两者长度的最大值,使得「新增」(仅当前有)与
|
|
407
|
+
* 「删除」(仅历史有)的事件都能被渲染出来;缺失的一侧用空对象兜底,从而让子级正确高亮差异。
|
|
408
|
+
*/
|
|
409
|
+
const displayList = computed<{ cardItem: any; lastCardItem: any; index: number }[]>(() => {
|
|
410
|
+
const current = props.model[props.name] || [];
|
|
411
|
+
|
|
412
|
+
if (!isCompareMode.value) {
|
|
413
|
+
return current.map((cardItem: any, index: number) => ({ cardItem, lastCardItem: undefined, index }));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const last = props.lastValues?.[props.name] || [];
|
|
417
|
+
const length = Math.max(current.length, last.length);
|
|
418
|
+
|
|
419
|
+
return Array.from({ length }, (_, index) => ({
|
|
420
|
+
cardItem: current[index] ?? {},
|
|
421
|
+
lastCardItem: last[index] ?? {},
|
|
422
|
+
index,
|
|
423
|
+
}));
|
|
424
|
+
});
|
|
425
|
+
|
|
377
426
|
// 添加事件
|
|
378
427
|
const addEvent = () => {
|
|
379
428
|
const defaultEvent = {
|
package/src/fields/KeyValue.vue
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
></TMagicInput>
|
|
20
20
|
|
|
21
21
|
<TMagicButton
|
|
22
|
+
v-if="!isCompare"
|
|
22
23
|
class="m-fields-key-value-delete"
|
|
23
24
|
type="danger"
|
|
24
25
|
:size="size"
|
|
@@ -30,7 +31,14 @@
|
|
|
30
31
|
></TMagicButton>
|
|
31
32
|
</div>
|
|
32
33
|
|
|
33
|
-
<TMagicButton
|
|
34
|
+
<TMagicButton
|
|
35
|
+
v-if="!isCompare"
|
|
36
|
+
type="primary"
|
|
37
|
+
:size="size"
|
|
38
|
+
:disabled="disabled"
|
|
39
|
+
plain
|
|
40
|
+
:icon="Plus"
|
|
41
|
+
@click="addHandler"
|
|
34
42
|
>添加</TMagicButton
|
|
35
43
|
>
|
|
36
44
|
</div>
|
|
@@ -52,7 +60,7 @@
|
|
|
52
60
|
></MagicCodeEditor>
|
|
53
61
|
|
|
54
62
|
<TMagicButton
|
|
55
|
-
v-if="config.advanced"
|
|
63
|
+
v-if="config.advanced && !isCompare"
|
|
56
64
|
size="default"
|
|
57
65
|
:disabled="disabled"
|
|
58
66
|
link
|
|
@@ -63,11 +71,11 @@
|
|
|
63
71
|
</template>
|
|
64
72
|
|
|
65
73
|
<script setup lang="ts">
|
|
66
|
-
import { ref, watchEffect } from 'vue';
|
|
74
|
+
import { computed, inject, ref, watchEffect } from 'vue';
|
|
67
75
|
import { Delete, Plus } from '@element-plus/icons-vue';
|
|
68
76
|
|
|
69
77
|
import { TMagicButton, TMagicInput } from '@tmagic/design';
|
|
70
|
-
import type { FieldProps, KeyValueConfig } from '@tmagic/form';
|
|
78
|
+
import type { FieldProps, FormState, KeyValueConfig } from '@tmagic/form';
|
|
71
79
|
|
|
72
80
|
import CodeIcon from '@editor/icons/CodeIcon.vue';
|
|
73
81
|
import MagicCodeEditor from '@editor/layouts/CodeEditor.vue';
|
|
@@ -84,6 +92,11 @@ const emit = defineEmits<{
|
|
|
84
92
|
change: [value: Record<string, any>];
|
|
85
93
|
}>();
|
|
86
94
|
|
|
95
|
+
const mForm = inject<FormState | undefined>('mForm');
|
|
96
|
+
|
|
97
|
+
/** 对比模式下隐藏增删/代码切换等操作按钮,仅保留只读展示。 */
|
|
98
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
99
|
+
|
|
87
100
|
const records = ref<[string, string][]>([]);
|
|
88
101
|
const showCode = ref(false);
|
|
89
102
|
|
package/src/fields/UISelect.vue
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
</div>
|
|
7
7
|
<div class="m-fields-ui-select" v-else style="display: flex">
|
|
8
8
|
<template v-if="val">
|
|
9
|
-
<TMagicTooltip content="清除" placement="top">
|
|
9
|
+
<TMagicTooltip v-if="!isCompare" content="清除" placement="top">
|
|
10
10
|
<TMagicButton
|
|
11
11
|
style="padding: 0"
|
|
12
12
|
type="danger"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
</TMagicTooltip>
|
|
33
33
|
</template>
|
|
34
34
|
|
|
35
|
-
<TMagicTooltip v-else content="点击此处选择" placement="top">
|
|
35
|
+
<TMagicTooltip v-else-if="!isCompare" content="点击此处选择" placement="top">
|
|
36
36
|
<TMagicButton link style="padding: 0; margin: 0" :disabled="disabled" :size="size" @click="startSelect"
|
|
37
37
|
>点击此处选择</TMagicButton
|
|
38
38
|
>
|
|
@@ -67,6 +67,9 @@ const mForm = inject<FormState>('mForm');
|
|
|
67
67
|
const val = computed(() => props.model[props.name]);
|
|
68
68
|
const uiSelectMode = ref(false);
|
|
69
69
|
|
|
70
|
+
/** 对比模式下隐藏清除/选择等操作按钮,仅保留只读展示。 */
|
|
71
|
+
const isCompare = computed(() => Boolean(mForm?.isCompare));
|
|
72
|
+
|
|
70
73
|
const cancelHandler = () => {
|
|
71
74
|
uiService.set('uiSelectMode', false);
|
|
72
75
|
uiSelectMode.value = false;
|
|
@@ -3,6 +3,7 @@ import { cloneDeep } from 'lodash-es';
|
|
|
3
3
|
|
|
4
4
|
import type { CodeBlockContent } from '@tmagic/core';
|
|
5
5
|
import { tMagicMessage } from '@tmagic/design';
|
|
6
|
+
import type { ContainerChangeEventData } from '@tmagic/form';
|
|
6
7
|
|
|
7
8
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
|
8
9
|
import type { Services } from '@editor/type';
|
|
@@ -61,10 +62,12 @@ export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService'])
|
|
|
61
62
|
codeBlockService.deleteCodeDslByIds([key]);
|
|
62
63
|
};
|
|
63
64
|
|
|
64
|
-
const submitCodeBlockHandler = async (values: CodeBlockContent) => {
|
|
65
|
+
const submitCodeBlockHandler = async (values: CodeBlockContent, eventData?: ContainerChangeEventData) => {
|
|
65
66
|
if (!codeId.value) return;
|
|
66
67
|
|
|
67
|
-
await codeBlockService.setCodeDslById(codeId.value, values
|
|
68
|
+
await codeBlockService.setCodeDslById(codeId.value, values, {
|
|
69
|
+
changeRecords: eventData?.changeRecords,
|
|
70
|
+
});
|
|
68
71
|
|
|
69
72
|
codeBlockEditorRef.value?.hide();
|
|
70
73
|
};
|
package/src/hooks/use-stage.ts
CHANGED
|
@@ -48,6 +48,7 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
48
48
|
disabledMultiSelect: stageOptions.disabledMultiSelect,
|
|
49
49
|
alwaysMultiSelect: stageOptions.alwaysMultiSelect,
|
|
50
50
|
disabledRule: stageOptions.disabledRule,
|
|
51
|
+
disabledFlashTip: stageOptions.disabledFlashTip,
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
watch(
|
|
@@ -97,23 +98,39 @@ export const useStage = (stageOptions: StageOptions) => {
|
|
|
97
98
|
|
|
98
99
|
stage.on('update', (ev: UpdateEventData) => {
|
|
99
100
|
if (ev.parentEl) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
// 拖动多选元素到一个新容器:整批合成一次 moveToContainer,只产生一条历史记录
|
|
102
|
+
const pId = getIdFromEl()(ev.parentEl);
|
|
103
|
+
if (!pId) return;
|
|
104
|
+
const configs = ev.data
|
|
105
|
+
.map((data) => {
|
|
106
|
+
const id = getIdFromEl()(data.el);
|
|
107
|
+
if (!id) return null;
|
|
108
|
+
const cfg: MNode = { id, style: data.style };
|
|
109
|
+
return cfg;
|
|
110
|
+
})
|
|
111
|
+
.filter((cfg): cfg is MNode => Boolean(cfg));
|
|
112
|
+
if (configs.length > 0) {
|
|
113
|
+
editorService.moveToContainer(configs, pId);
|
|
104
114
|
}
|
|
105
115
|
return;
|
|
106
116
|
}
|
|
107
117
|
|
|
108
|
-
//
|
|
118
|
+
// 多选拖动 / 多选缩放:所有元素整批走一次 update,避免历史栈被切成 N 条
|
|
119
|
+
// changeRecordList 与 configs 同序,每个节点保留自己的 records;
|
|
120
|
+
// 不能把多个节点的 records 合并到同一个数组里,否则 doUpdate / nodeUpdateHandler 会把别的节点的 propPath 当成自己的。
|
|
121
|
+
const configs: MNode[] = [];
|
|
122
|
+
const changeRecordList: ReturnType<typeof buildChangeRecords>[] = [];
|
|
109
123
|
ev.data.forEach((data) => {
|
|
110
124
|
const id = getIdFromEl()(data.el);
|
|
111
125
|
if (!id) return;
|
|
112
126
|
|
|
113
127
|
const { style = {} } = data;
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
configs.push({ id, style });
|
|
129
|
+
changeRecordList.push(buildChangeRecords(style, 'style'));
|
|
116
130
|
});
|
|
131
|
+
if (configs.length === 0) return;
|
|
132
|
+
|
|
133
|
+
editorService.update(configs, { changeRecordList });
|
|
117
134
|
});
|
|
118
135
|
|
|
119
136
|
stage.on('sort', (ev: SortEventData) => {
|
package/src/index.ts
CHANGED
|
@@ -70,6 +70,9 @@ export { default as LayoutContainer } from './components/SplitView.vue';
|
|
|
70
70
|
export { default as SplitView } from './components/SplitView.vue';
|
|
71
71
|
export { default as Resizer } from './components/Resizer.vue';
|
|
72
72
|
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
|
73
|
+
export { default as CompareForm } from './components/CompareForm.vue';
|
|
74
|
+
export { default as HistoryListBucket } from './layouts/history-list/Bucket.vue';
|
|
75
|
+
export { default as HistoryDiffDialog } from './layouts/history-list/HistoryDiffDialog.vue';
|
|
73
76
|
export { default as FloatingBox } from './components/FloatingBox.vue';
|
|
74
77
|
export { default as Tree } from './components/Tree.vue';
|
|
75
78
|
export { default as TreeNode } from './components/TreeNode.vue';
|
|
@@ -21,12 +21,12 @@ import { computed, nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, useTe
|
|
|
21
21
|
import { FullScreen } from '@element-plus/icons-vue';
|
|
22
22
|
import { throttle } from 'lodash-es';
|
|
23
23
|
import type * as Monaco from 'monaco-editor';
|
|
24
|
-
import serialize from 'serialize-javascript';
|
|
25
24
|
|
|
26
25
|
import { TMagicButton } from '@tmagic/design';
|
|
27
26
|
|
|
28
27
|
import MIcon from '@editor/components/Icon.vue';
|
|
29
28
|
import { getEditorConfig } from '@editor/utils/config';
|
|
29
|
+
import { serializeConfig } from '@editor/utils/editor';
|
|
30
30
|
import loadMonaco from '@editor/utils/monaco-editor';
|
|
31
31
|
|
|
32
32
|
defineOptions({
|
|
@@ -163,10 +163,7 @@ const toString = (v: string | any, language: string): string => {
|
|
|
163
163
|
if (language === 'json') {
|
|
164
164
|
value = JSON.stringify(v, null, 2);
|
|
165
165
|
} else {
|
|
166
|
-
value =
|
|
167
|
-
space: 2,
|
|
168
|
-
unsafe: true,
|
|
169
|
-
}).replace(/"(\w+)":\s/g, '$1: ');
|
|
166
|
+
value = serializeConfig(v);
|
|
170
167
|
}
|
|
171
168
|
} else {
|
|
172
169
|
value = v;
|
|
@@ -330,6 +327,21 @@ watch(
|
|
|
330
327
|
},
|
|
331
328
|
);
|
|
332
329
|
|
|
330
|
+
// diff 模式下,对比的"当前值"(modifiedValues)也可能在外部变化(例如 lastValues 不变、当前 model 变了),
|
|
331
|
+
// 此时同样需要重新设置编辑器值,否则右侧编辑器内容会停留在初始化时的快照。
|
|
332
|
+
watch(
|
|
333
|
+
() => props.modifiedValues,
|
|
334
|
+
(v, preV) => {
|
|
335
|
+
if (props.type !== 'diff') return;
|
|
336
|
+
if (v !== preV) {
|
|
337
|
+
setEditorValue(props.initValues, props.modifiedValues);
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
deep: true,
|
|
342
|
+
},
|
|
343
|
+
);
|
|
344
|
+
|
|
333
345
|
watch(
|
|
334
346
|
() => props.options,
|
|
335
347
|
(v) => {
|
|
@@ -23,15 +23,15 @@
|
|
|
23
23
|
left-class="m-editor-framework-left"
|
|
24
24
|
center-class="m-editor-framework-center"
|
|
25
25
|
right-class="m-editor-framework-right"
|
|
26
|
-
:left="columnWidth.left"
|
|
26
|
+
:left="hideSidebar ? undefined : columnWidth.left"
|
|
27
27
|
:right="columnWidth.right"
|
|
28
|
-
:min-left="MIN_LEFT_COLUMN_WIDTH"
|
|
28
|
+
:min-left="hideSidebar ? 0 : MIN_LEFT_COLUMN_WIDTH"
|
|
29
29
|
:min-right="MIN_RIGHT_COLUMN_WIDTH"
|
|
30
30
|
:min-center="MIN_CENTER_COLUMN_WIDTH"
|
|
31
31
|
:width="frameworkRect.width"
|
|
32
32
|
@change="columnWidthChange"
|
|
33
33
|
>
|
|
34
|
-
<template #left>
|
|
34
|
+
<template v-if="!hideSidebar" #left>
|
|
35
35
|
<slot name="sidebar"></slot>
|
|
36
36
|
</template>
|
|
37
37
|
|
|
@@ -94,10 +94,12 @@ defineOptions({
|
|
|
94
94
|
name: 'MEditorFramework',
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
defineProps<{
|
|
97
|
+
const props = defineProps<{
|
|
98
98
|
disabledPageFragment: boolean;
|
|
99
99
|
pageBarSortOptions?: PageBarSortOptions;
|
|
100
100
|
pageFilterFunction?: (_page: MPage | MPageFragment, _keyword: string) => boolean;
|
|
101
|
+
/** 是否隐藏左侧面板 */
|
|
102
|
+
hideSidebar?: boolean;
|
|
101
103
|
}>();
|
|
102
104
|
|
|
103
105
|
const codeOptions = inject('codeOptions', {});
|
|
@@ -132,7 +134,10 @@ watch(
|
|
|
132
134
|
);
|
|
133
135
|
|
|
134
136
|
const columnWidthChange = (columnW: GetColumnWidth) => {
|
|
135
|
-
|
|
137
|
+
// 隐藏左侧面板时 left 恒为 0,不覆盖已持久化的左栏宽度,避免重新展示时丢失宽度
|
|
138
|
+
if (!props.hideSidebar) {
|
|
139
|
+
storageService.setItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, columnW.left, { protocol: Protocol.NUMBER });
|
|
140
|
+
}
|
|
136
141
|
storageService.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, columnW.right, { protocol: Protocol.NUMBER });
|
|
137
142
|
|
|
138
143
|
uiService.set('columnWidth', columnW);
|