@tmagic/editor 1.8.0-beta.4 → 1.8.0-beta.6
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/components/CompareForm.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +6 -6
- package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +4 -1
- package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +8 -2
- package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +25 -8
- package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +6 -4
- package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +25 -12
- package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +27 -5
- package/dist/es/index.js +6 -3
- package/dist/es/initService.js +1 -1
- package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +19 -55
- package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +22 -39
- package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +149 -103
- package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +32 -8
- package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +335 -213
- package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +36 -7
- package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +47 -60
- package/dist/es/layouts/history-list/composables.js +73 -32
- package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +4 -2
- package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +5 -1
- package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +16 -3
- package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +23 -1
- package/dist/es/services/codeBlock.js +96 -37
- package/dist/es/services/dataSource.js +70 -26
- package/dist/es/services/editor.js +266 -104
- package/dist/es/services/history.js +271 -212
- package/dist/es/services/ui.js +3 -0
- package/dist/es/style.css +53 -7
- package/dist/es/utils/data-source/index.js +2 -0
- package/dist/es/utils/editor.js +103 -49
- package/dist/es/utils/history.js +232 -0
- package/dist/es/utils/indexed-db.js +86 -0
- package/dist/es/utils/undo-redo.js +60 -1
- package/dist/style.css +53 -7
- package/dist/tmagic-editor.umd.cjs +2064 -1000
- package/package.json +7 -7
- package/src/components/CompareForm.vue +3 -1
- package/src/components/ToolButton.vue +2 -2
- package/src/fields/CodeSelectCol.vue +7 -1
- package/src/fields/DataSourceFieldSelect/FieldSelect.vue +16 -2
- package/src/fields/DataSourceFields.vue +37 -8
- package/src/fields/DataSourceMethodSelect.vue +9 -4
- package/src/fields/DataSourceMethods.vue +42 -21
- package/src/fields/StyleSetter/components/Border.vue +15 -6
- package/src/index.ts +1 -0
- package/src/initService.ts +1 -1
- package/src/layouts/Framework.vue +1 -1
- package/src/layouts/history-list/Bucket.vue +34 -71
- package/src/layouts/history-list/BucketTab.vue +46 -54
- package/src/layouts/history-list/GroupRow.vue +146 -111
- package/src/layouts/history-list/HistoryDiffDialog.vue +94 -68
- package/src/layouts/history-list/HistoryListPanel.vue +298 -115
- package/src/layouts/history-list/InitialRow.vue +28 -9
- package/src/layouts/history-list/PageTab.vue +57 -51
- package/src/layouts/history-list/composables.ts +157 -36
- package/src/layouts/page-bar/PageBar.vue +4 -2
- package/src/layouts/sidebar/Sidebar.vue +6 -1
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +30 -2
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +26 -1
- package/src/services/codeBlock.ts +113 -37
- package/src/services/dataSource.ts +94 -40
- package/src/services/editor.ts +376 -136
- package/src/services/history.ts +306 -209
- package/src/services/ui.ts +7 -0
- package/src/theme/history-list-panel.scss +72 -5
- package/src/theme/page-bar.scss +0 -4
- package/src/theme/style-setter/border.scss +4 -1
- package/src/type.ts +183 -64
- package/src/utils/data-source/index.ts +2 -0
- package/src/utils/editor.ts +166 -60
- package/src/utils/history.ts +308 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/indexed-db.ts +122 -0
- package/src/utils/undo-redo.ts +88 -0
- package/types/index.d.ts +813 -302
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.8.0-beta.
|
|
2
|
+
"version": "1.8.0-beta.6",
|
|
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/table": "1.8.0-beta.
|
|
65
|
-
"@tmagic/utils": "1.8.0-beta.
|
|
61
|
+
"@tmagic/design": "1.8.0-beta.6",
|
|
62
|
+
"@tmagic/stage": "1.8.0-beta.6",
|
|
63
|
+
"@tmagic/form": "1.8.0-beta.6",
|
|
64
|
+
"@tmagic/table": "1.8.0-beta.6",
|
|
65
|
+
"@tmagic/utils": "1.8.0-beta.6"
|
|
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.6"
|
|
80
80
|
},
|
|
81
81
|
"peerDependenciesMeta": {
|
|
82
82
|
"typescript": {
|
|
@@ -180,7 +180,9 @@ const defaultLoadConfig = async (): Promise<FormConfig> => {
|
|
|
180
180
|
if (!props.type) {
|
|
181
181
|
return [];
|
|
182
182
|
}
|
|
183
|
-
return removeStyleDisplayConfig(
|
|
183
|
+
return removeStyleDisplayConfig(
|
|
184
|
+
await propsService.getPropsConfig(props.type, { node: props.value as unknown as MNode }),
|
|
185
|
+
);
|
|
184
186
|
}
|
|
185
187
|
case 'data-source': {
|
|
186
188
|
return dataSourceService.getFormConfig(props.type || 'base');
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
<template v-else-if="data.type === 'button'">
|
|
15
15
|
<TMagicTooltip v-if="data.tooltip" effect="dark" placement="bottom-start" :content="data.tooltip">
|
|
16
|
-
<TMagicButton size="small" link :disabled="disabled">
|
|
16
|
+
<TMagicButton size="small" link :disabled="disabled" v-bind="data.buttonProps || {}">
|
|
17
17
|
<template #icon v-if="data.icon">
|
|
18
18
|
<MIcon :icon="data.icon"></MIcon>
|
|
19
19
|
</template>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</TMagicButton>
|
|
22
22
|
</TMagicTooltip>
|
|
23
23
|
|
|
24
|
-
<TMagicButton v-else size="small" link :disabled="disabled" :title="data.text">
|
|
24
|
+
<TMagicButton v-else size="small" link :disabled="disabled" :title="data.text" v-bind="data.buttonProps || {}">
|
|
25
25
|
<template #icon v-if="data.icon">
|
|
26
26
|
<MIcon :icon="data.icon"></MIcon>
|
|
27
27
|
</template>
|
|
@@ -107,10 +107,12 @@ const isCompareMode = computed(() => Boolean(props.isCompare && props.lastValues
|
|
|
107
107
|
|
|
108
108
|
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
|
|
109
109
|
|
|
110
|
-
const
|
|
110
|
+
const codeBlockSidePanel = computed(() =>
|
|
111
111
|
(uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.CODE_BLOCK),
|
|
112
112
|
);
|
|
113
113
|
|
|
114
|
+
const hasCodeBlockSidePanel = computed(() => codeBlockSidePanel.value);
|
|
115
|
+
|
|
114
116
|
/**
|
|
115
117
|
* 根据代码块id获取代码块参数配置
|
|
116
118
|
* @param codeId 代码块ID
|
|
@@ -191,6 +193,10 @@ const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData)
|
|
|
191
193
|
};
|
|
192
194
|
|
|
193
195
|
const editCode = (id: string) => {
|
|
196
|
+
const sideBarItem = codeBlockSidePanel.value;
|
|
197
|
+
if (sideBarItem) {
|
|
198
|
+
uiService.set('sideBarActiveTabName', sideBarItem.text || sideBarItem.$key || SideItemKey.CODE_BLOCK);
|
|
199
|
+
}
|
|
194
200
|
eventBus?.emit('edit-code', id);
|
|
195
201
|
};
|
|
196
202
|
</script>
|
|
@@ -211,11 +211,25 @@ const onChangeHandler = (v: string[] = []) => {
|
|
|
211
211
|
emit('change', v);
|
|
212
212
|
};
|
|
213
213
|
|
|
214
|
-
const
|
|
214
|
+
const dataSourceSidePanel = computed(() =>
|
|
215
215
|
uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
|
|
216
216
|
);
|
|
217
217
|
|
|
218
|
+
const hasDataSourceSidePanel = computed(() => dataSourceSidePanel.value);
|
|
219
|
+
|
|
218
220
|
const editHandler = (id: string) => {
|
|
219
|
-
|
|
221
|
+
const sideBarItem = dataSourceSidePanel.value;
|
|
222
|
+
if (sideBarItem) {
|
|
223
|
+
uiService.set('sideBarActiveTabName', sideBarItem.text || sideBarItem.$key || SideItemKey.DATA_SOURCE);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const dataSourceId = removeDataSourceFieldPrefix(id);
|
|
227
|
+
const fieldPath = selectFieldsId.value;
|
|
228
|
+
|
|
229
|
+
if (fieldPath.length) {
|
|
230
|
+
eventBus?.emit('edit-data-source-field', dataSourceId, [...fieldPath]);
|
|
231
|
+
} else {
|
|
232
|
+
eventBus?.emit('edit-data-source', dataSourceId);
|
|
233
|
+
}
|
|
220
234
|
};
|
|
221
235
|
</script>
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
</template>
|
|
48
48
|
|
|
49
49
|
<script setup lang="ts">
|
|
50
|
-
import { computed, inject, Ref, ref } from 'vue';
|
|
50
|
+
import { computed, type ComputedRef, inject, onMounted, provide, Ref, ref } from 'vue';
|
|
51
51
|
|
|
52
52
|
import type { DataSchema } from '@tmagic/core';
|
|
53
53
|
import { TMagicButton, tMagicMessage, tMagicMessageBox } from '@tmagic/design';
|
|
@@ -101,6 +101,16 @@ const newHandler = () => {
|
|
|
101
101
|
addDialogVisible.value = true;
|
|
102
102
|
};
|
|
103
103
|
|
|
104
|
+
const editField = (row: Record<string, any>, index: number) => {
|
|
105
|
+
fieldValues.value = {
|
|
106
|
+
...row,
|
|
107
|
+
index,
|
|
108
|
+
};
|
|
109
|
+
fieldTitle.value = `编辑${row.title}`;
|
|
110
|
+
calcBoxPosition();
|
|
111
|
+
addDialogVisible.value = true;
|
|
112
|
+
};
|
|
113
|
+
|
|
104
114
|
const fieldChange = ({ index, ...value }: Record<string, any>, data: ContainerChangeEventData) => {
|
|
105
115
|
addDialogVisible.value = false;
|
|
106
116
|
|
|
@@ -158,13 +168,7 @@ const fieldColumns: ColumnConfig[] = [
|
|
|
158
168
|
{
|
|
159
169
|
text: '编辑',
|
|
160
170
|
handler: (row: Record<string, any>, index: number) => {
|
|
161
|
-
|
|
162
|
-
...row,
|
|
163
|
-
index,
|
|
164
|
-
};
|
|
165
|
-
fieldTitle.value = `编辑${row.title}`;
|
|
166
|
-
calcBoxPosition();
|
|
167
|
-
addDialogVisible.value = true;
|
|
171
|
+
editField(row, index);
|
|
168
172
|
},
|
|
169
173
|
},
|
|
170
174
|
{
|
|
@@ -354,4 +358,29 @@ const { height: editorHeight } = useEditorContentHeight();
|
|
|
354
358
|
|
|
355
359
|
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
|
|
356
360
|
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* 由 DataSourceConfigPanel 注入:打开数据源详情后需要直接打开的字段路径(字段名数组)。
|
|
364
|
+
* 当前层消费 path[0],并把剩余路径下发给嵌套字段,实现逐层打开。
|
|
365
|
+
*/
|
|
366
|
+
const editingFieldPath = inject<ComputedRef<string[]>>(
|
|
367
|
+
'editingDataSourceFieldPath',
|
|
368
|
+
computed(() => []),
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
provide(
|
|
372
|
+
'editingDataSourceFieldPath',
|
|
373
|
+
computed(() => editingFieldPath.value.slice(1)),
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
onMounted(() => {
|
|
377
|
+
const path = editingFieldPath.value;
|
|
378
|
+
if (!path.length) return;
|
|
379
|
+
|
|
380
|
+
const fields: Record<string, any>[] = props.model[props.name] || [];
|
|
381
|
+
const index = fields.findIndex((field) => field.name === path[0]);
|
|
382
|
+
if (index === -1) return;
|
|
383
|
+
|
|
384
|
+
editField(fields[index], index);
|
|
385
|
+
});
|
|
357
386
|
</script>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
></MCascader>
|
|
14
14
|
|
|
15
15
|
<TMagicTooltip
|
|
16
|
-
v-if="model[name] && isCustomMethod &&
|
|
16
|
+
v-if="model[name] && isCustomMethod && dataSourceSidePanel && !isCompare"
|
|
17
17
|
:content="notEditable ? '查看' : '编辑'"
|
|
18
18
|
>
|
|
19
19
|
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editCodeHandler">
|
|
@@ -75,7 +75,7 @@ const props = withDefaults(defineProps<FieldProps<DataSourceMethodSelectConfig>>
|
|
|
75
75
|
disabled: false,
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
const
|
|
78
|
+
const dataSourceSidePanel = computed(() =>
|
|
79
79
|
(uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
|
|
80
80
|
);
|
|
81
81
|
|
|
@@ -208,12 +208,17 @@ const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData)
|
|
|
208
208
|
};
|
|
209
209
|
|
|
210
210
|
const editCodeHandler = () => {
|
|
211
|
-
const [id] = props.model[props.name];
|
|
211
|
+
const [id, methodName] = props.model[props.name];
|
|
212
212
|
|
|
213
213
|
const dataSource = dataSourceService.getDataSourceById(id);
|
|
214
214
|
|
|
215
215
|
if (!dataSource) return;
|
|
216
216
|
|
|
217
|
-
|
|
217
|
+
const sideBarItem = dataSourceSidePanel.value;
|
|
218
|
+
if (sideBarItem) {
|
|
219
|
+
uiService.set('sideBarActiveTabName', sideBarItem.text || sideBarItem.$key || SideItemKey.DATA_SOURCE);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
eventBus?.emit('edit-data-source-method', id, methodName);
|
|
218
223
|
};
|
|
219
224
|
</script>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
</template>
|
|
22
22
|
|
|
23
23
|
<script setup lang="ts">
|
|
24
|
-
import { computed, inject, nextTick, ref, useTemplateRef } from 'vue';
|
|
24
|
+
import { computed, type ComputedRef, inject, nextTick, onMounted, ref, useTemplateRef } from 'vue';
|
|
25
25
|
import { cloneDeep } from 'lodash-es';
|
|
26
26
|
|
|
27
27
|
import type { CodeBlockContent } from '@tmagic/core';
|
|
@@ -52,6 +52,29 @@ const codeBlockEditorRef = useTemplateRef<InstanceType<typeof CodeBlockEditor>>(
|
|
|
52
52
|
|
|
53
53
|
let editIndex = -1;
|
|
54
54
|
|
|
55
|
+
const editMethod = (method: CodeBlockContent, index: number) => {
|
|
56
|
+
let codeContent: string = '({ params, dataSource, app }) => {\n // place your code here\n}';
|
|
57
|
+
|
|
58
|
+
if (method.content) {
|
|
59
|
+
if (typeof method.content !== 'string') {
|
|
60
|
+
codeContent = method.content.toString();
|
|
61
|
+
} else {
|
|
62
|
+
codeContent = method.content;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
codeConfig.value = {
|
|
67
|
+
...cloneDeep(method),
|
|
68
|
+
content: codeContent,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
editIndex = index;
|
|
72
|
+
|
|
73
|
+
nextTick(() => {
|
|
74
|
+
codeBlockEditorRef.value?.show();
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
55
78
|
const methodColumns: ColumnConfig[] = [
|
|
56
79
|
{
|
|
57
80
|
label: '名称',
|
|
@@ -77,26 +100,7 @@ const methodColumns: ColumnConfig[] = [
|
|
|
77
100
|
{
|
|
78
101
|
text: '编辑',
|
|
79
102
|
handler: (method: CodeBlockContent, index: number) => {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (method.content) {
|
|
83
|
-
if (typeof method.content !== 'string') {
|
|
84
|
-
codeContent = method.content.toString();
|
|
85
|
-
} else {
|
|
86
|
-
codeContent = method.content;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
codeConfig.value = {
|
|
91
|
-
...cloneDeep(method),
|
|
92
|
-
content: codeContent,
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
editIndex = index;
|
|
96
|
-
|
|
97
|
-
nextTick(() => {
|
|
98
|
-
codeBlockEditorRef.value?.show();
|
|
99
|
-
});
|
|
103
|
+
editMethod(method, index);
|
|
100
104
|
},
|
|
101
105
|
},
|
|
102
106
|
{
|
|
@@ -158,4 +162,21 @@ const submitCodeHandler = (value: CodeBlockContent, data: ContainerChangeEventDa
|
|
|
158
162
|
|
|
159
163
|
codeBlockEditorRef.value?.hide();
|
|
160
164
|
};
|
|
165
|
+
|
|
166
|
+
/** 由 DataSourceConfigPanel 注入:打开数据源详情后需要直接打开的方法名 */
|
|
167
|
+
const editingMethodName = inject<ComputedRef<string | undefined>>(
|
|
168
|
+
'editingDataSourceMethodName',
|
|
169
|
+
computed(() => ''),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
onMounted(() => {
|
|
173
|
+
const methodName = editingMethodName.value;
|
|
174
|
+
if (!methodName) return;
|
|
175
|
+
|
|
176
|
+
const methods: CodeBlockContent[] = props.model[props.name] || [];
|
|
177
|
+
const index = methods.findIndex((method) => method.name === methodName);
|
|
178
|
+
if (index === -1) return;
|
|
179
|
+
|
|
180
|
+
editMethod(methods[index], index);
|
|
181
|
+
});
|
|
161
182
|
</script>
|
|
@@ -4,27 +4,31 @@
|
|
|
4
4
|
<div class="border-icon-container-row">
|
|
5
5
|
<div
|
|
6
6
|
class="border-icon border-icon-top"
|
|
7
|
-
:class="{ active: direction === 'Top' }"
|
|
7
|
+
:class="{ active: direction === 'Top', configured: isConfigured('Top') }"
|
|
8
8
|
@click="selectDirection('Top')"
|
|
9
9
|
></div>
|
|
10
10
|
</div>
|
|
11
11
|
<div class="border-icon-container-row">
|
|
12
12
|
<div
|
|
13
13
|
class="border-icon border-icon-left"
|
|
14
|
-
:class="{ active: direction === 'Left' }"
|
|
14
|
+
:class="{ active: direction === 'Left', configured: isConfigured('Left') }"
|
|
15
15
|
@click="selectDirection('Left')"
|
|
16
16
|
></div>
|
|
17
|
-
<div
|
|
17
|
+
<div
|
|
18
|
+
class="border-icon"
|
|
19
|
+
:class="{ active: direction === '', configured: isConfigured('') }"
|
|
20
|
+
@click="selectDirection()"
|
|
21
|
+
></div>
|
|
18
22
|
<div
|
|
19
23
|
class="border-icon border-icon-right"
|
|
20
|
-
:class="{ active: direction === 'Right' }"
|
|
24
|
+
:class="{ active: direction === 'Right', configured: isConfigured('Right') }"
|
|
21
25
|
@click="selectDirection('Right')"
|
|
22
26
|
></div>
|
|
23
27
|
</div>
|
|
24
28
|
<div class="border-icon-container-row">
|
|
25
29
|
<div
|
|
26
30
|
class="border-icon border-icon-bottom"
|
|
27
|
-
:class="{ active: direction === 'Bottom' }"
|
|
31
|
+
:class="{ active: direction === 'Bottom', configured: isConfigured('Bottom') }"
|
|
28
32
|
@click="selectDirection('Bottom')"
|
|
29
33
|
></div>
|
|
30
34
|
</div>
|
|
@@ -98,7 +102,7 @@ const emit = defineEmits<{
|
|
|
98
102
|
addDiffCount: [];
|
|
99
103
|
}>();
|
|
100
104
|
|
|
101
|
-
withDefaults(
|
|
105
|
+
const props = withDefaults(
|
|
102
106
|
defineProps<{
|
|
103
107
|
model: FormValue;
|
|
104
108
|
lastValues?: FormValue;
|
|
@@ -109,6 +113,11 @@ withDefaults(
|
|
|
109
113
|
{},
|
|
110
114
|
);
|
|
111
115
|
|
|
116
|
+
const hasValue = (value: unknown) => value !== undefined && value !== null && value !== '';
|
|
117
|
+
|
|
118
|
+
const isConfigured = (d: string) =>
|
|
119
|
+
['Width', 'Color', 'Style'].some((key) => hasValue(props.model?.[`border${d}${key}`]));
|
|
120
|
+
|
|
112
121
|
const change = (value: StyleSchema, eventData: ContainerChangeEventData) => {
|
|
113
122
|
eventData.changeRecords?.forEach((record) => {
|
|
114
123
|
emit('change', record.value, {
|
package/src/index.ts
CHANGED
|
@@ -72,6 +72,7 @@ export { default as Resizer } from './components/Resizer.vue';
|
|
|
72
72
|
export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
|
|
73
73
|
export { default as CompareForm } from './components/CompareForm.vue';
|
|
74
74
|
export { default as HistoryListBucket } from './layouts/history-list/Bucket.vue';
|
|
75
|
+
export { default as HistoryListBucketTab } from './layouts/history-list/BucketTab.vue';
|
|
75
76
|
export { default as HistoryDiffDialog } from './layouts/history-list/HistoryDiffDialog.vue';
|
|
76
77
|
export { default as FloatingBox } from './components/FloatingBox.vue';
|
|
77
78
|
export { default as Tree } from './components/Tree.vue';
|
package/src/initService.ts
CHANGED
|
@@ -168,7 +168,7 @@ onBeforeUnmount(() => {
|
|
|
168
168
|
const saveCode = (value: string) => {
|
|
169
169
|
try {
|
|
170
170
|
const parseDSL = getEditorConfig('parseDSL');
|
|
171
|
-
editorService.set('root', parseDSL(value));
|
|
171
|
+
editorService.set('root', parseDSL(value), { historySource: 'root-code' });
|
|
172
172
|
} catch (e: any) {
|
|
173
173
|
console.error(e);
|
|
174
174
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-history-list-bucket">
|
|
3
3
|
<div class="m-editor-history-list-bucket-title">
|
|
4
|
-
<span>{{ title }}</span>
|
|
4
|
+
<span>{{ config.title }}</span>
|
|
5
5
|
<code>{{ String(bucketId) }}</code>
|
|
6
6
|
<span class="m-editor-history-list-bucket-count">{{ groups.length }} 组</span>
|
|
7
7
|
</div>
|
|
@@ -9,32 +9,10 @@
|
|
|
9
9
|
<ul class="m-editor-history-list-ul">
|
|
10
10
|
<GroupRow
|
|
11
11
|
v-for="group in groups"
|
|
12
|
-
:key="
|
|
13
|
-
:group
|
|
14
|
-
:
|
|
15
|
-
:
|
|
16
|
-
:op-type="group.opType"
|
|
17
|
-
:desc="describeGroup(group)"
|
|
18
|
-
:source="groupSource(group)"
|
|
19
|
-
:time="formatHistoryTime(groupTimestamp(group))"
|
|
20
|
-
:time-title="formatHistoryFullTime(groupTimestamp(group))"
|
|
21
|
-
:step-count="group.steps.length"
|
|
22
|
-
:sub-steps="
|
|
23
|
-
group.steps.map((s: any) => ({
|
|
24
|
-
index: s.index,
|
|
25
|
-
applied: s.applied,
|
|
26
|
-
isCurrent: s.isCurrent,
|
|
27
|
-
desc: describeStep(s.step),
|
|
28
|
-
diffable: isStepDiffable ? isStepDiffable(s.step) : false,
|
|
29
|
-
revertable: s.applied && (isStepRevertable ? isStepRevertable(s.step) : true),
|
|
30
|
-
source: s.step.source,
|
|
31
|
-
time: formatHistoryTime(s.step.timestamp),
|
|
32
|
-
timeTitle: formatHistoryFullTime(s.step.timestamp),
|
|
33
|
-
}))
|
|
34
|
-
"
|
|
35
|
-
:is-current="group.isCurrent"
|
|
36
|
-
:expanded="!!expanded[`${prefix}-${bucketId}-${group.steps[0]?.index}`]"
|
|
37
|
-
:goto-enabled="gotoEnabled"
|
|
12
|
+
:key="rowKey(group)"
|
|
13
|
+
:group="toRow(group)"
|
|
14
|
+
:expanded="isHistoryGroupExpanded(expanded, rowKey(group))"
|
|
15
|
+
:goto-enabled="config.gotoEnabled"
|
|
38
16
|
@toggle="(key: string) => $emit('toggle', key)"
|
|
39
17
|
@goto="(index: number) => $emit('goto', bucketId, index)"
|
|
40
18
|
@diff-step="(index: number) => $emit('diff-step', bucketId, index)"
|
|
@@ -43,24 +21,25 @@
|
|
|
43
21
|
<!--
|
|
44
22
|
初始状态项:永远位于该 bucket 列表底部(同样按倒序展示,最底部 = 最早状态)。
|
|
45
23
|
当 bucket 内所有 group 都未 applied 时即为当前位置。
|
|
46
|
-
showInitial=false 时不展示(用于没有"撤销到初始状态"语义的自定义历史,如业务模块历史)。
|
|
24
|
+
config.showInitial=false 时不展示(用于没有"撤销到初始状态"语义的自定义历史,如业务模块历史)。
|
|
47
25
|
-->
|
|
48
26
|
<InitialRow
|
|
49
|
-
v-if="showInitial !== false"
|
|
27
|
+
v-if="config.showInitial !== false"
|
|
50
28
|
:is-current="isInitial"
|
|
51
|
-
:goto-enabled="gotoEnabled"
|
|
29
|
+
:goto-enabled="config.gotoEnabled"
|
|
52
30
|
@goto-initial="$emit('goto-initial', bucketId)"
|
|
53
31
|
/>
|
|
54
32
|
</ul>
|
|
55
33
|
</div>
|
|
56
34
|
</template>
|
|
57
35
|
|
|
58
|
-
<script lang="ts" setup>
|
|
36
|
+
<script lang="ts" setup generic="T extends BaseStepValue = BaseStepValue">
|
|
59
37
|
import { computed } from 'vue';
|
|
60
38
|
|
|
61
|
-
import type {
|
|
39
|
+
import type { BaseStepValue, HistoryBucketConfig } from '@editor/type';
|
|
62
40
|
|
|
63
|
-
import {
|
|
41
|
+
import type { HistoryBucketGroup, HistoryRowGroup } from './composables';
|
|
42
|
+
import { isHistoryGroupExpanded, toRowGroup } from './composables';
|
|
64
43
|
import GroupRow from './GroupRow.vue';
|
|
65
44
|
import InitialRow from './InitialRow.vue';
|
|
66
45
|
|
|
@@ -68,44 +47,19 @@ defineOptions({
|
|
|
68
47
|
name: 'MEditorHistoryListBucket',
|
|
69
48
|
});
|
|
70
49
|
|
|
71
|
-
const props =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/** 当前 bucket 下的所有历史分组,按时间倒序展示(最近的操作在前)。 */
|
|
85
|
-
groups: {
|
|
86
|
-
applied: boolean;
|
|
87
|
-
isCurrent?: boolean;
|
|
88
|
-
opType: HistoryOpType;
|
|
89
|
-
steps: { index: number; applied: boolean; isCurrent?: boolean; step: any }[];
|
|
90
|
-
}[];
|
|
91
|
-
/** 组级描述文案生成器,接收一个 group,返回展示文本。由父组件按业务类型注入。 */
|
|
92
|
-
describeGroup: (_group: any) => string;
|
|
93
|
-
/** 单步描述文案生成器,接收一个 step,返回展示文本。用于合并组展开后的子步列表。 */
|
|
94
|
-
describeStep: (_step: any) => string;
|
|
95
|
-
/** 判断某个 step 是否可查看差异(前后值都存在)。由父组件按业务类型注入;不传则一律不展示差异入口。 */
|
|
96
|
-
isStepDiffable?: (_step: any) => boolean;
|
|
97
|
-
/** 判断某个 step 是否支持回滚(如更新需带 changeRecords)。由父组件按业务类型注入;不传则已应用即可回滚。 */
|
|
98
|
-
isStepRevertable?: (_step: any) => boolean;
|
|
99
|
-
/** 共享的折叠状态表(key -> 是否展开),由顶层 panel 统一维护以便跨 tab 复用。 */
|
|
100
|
-
expanded: Record<string, boolean>;
|
|
101
|
-
/** 是否支持「跳转到该记录」(goto)。默认 true。 */
|
|
102
|
-
gotoEnabled?: boolean;
|
|
103
|
-
}>(),
|
|
104
|
-
{
|
|
105
|
-
showInitial: true,
|
|
106
|
-
gotoEnabled: true,
|
|
107
|
-
},
|
|
108
|
-
);
|
|
50
|
+
const props = defineProps<{
|
|
51
|
+
/**
|
|
52
|
+
* 该类历史的整体渲染配置(title / prefix / describe* / isStep* / showInitial / gotoEnabled)。
|
|
53
|
+
* 由父组件按业务类型注入,组件内部按需读取,避免逐项透传多个 props。
|
|
54
|
+
*/
|
|
55
|
+
config: HistoryBucketConfig<T>;
|
|
56
|
+
/** 当前 bucket 对应的目标 id(dataSource.id 或 codeBlock.id),同时用于组装子项的 key。 */
|
|
57
|
+
bucketId: string | number;
|
|
58
|
+
/** 当前 bucket 下的所有历史分组,按时间倒序展示(最近的操作在前)。 */
|
|
59
|
+
groups: HistoryBucketGroup<T>[];
|
|
60
|
+
/** 共享的折叠状态表(key -> 是否展开,缺省或 true 为展开、false 为收起),由顶层 panel 统一维护以便跨 tab 复用。 */
|
|
61
|
+
expanded: Record<string, boolean>;
|
|
62
|
+
}>();
|
|
109
63
|
|
|
110
64
|
defineEmits<{
|
|
111
65
|
/** 透传子组件 GroupRow 的 toggle,由上层 panel 更新 expanded。 */
|
|
@@ -123,6 +77,15 @@ defineEmits<{
|
|
|
123
77
|
(_e: 'revert-step', _bucketId: string | number, _index: number): void;
|
|
124
78
|
}>();
|
|
125
79
|
|
|
80
|
+
/**
|
|
81
|
+
* 子项 / 折叠状态 key:`${prefix}-${bucketId}-${组内首步 index}`。
|
|
82
|
+
* 以稳定的 step 索引(而非展示位置)标识分组,历史数据更新后已展开的分组状态仍能正确保持。
|
|
83
|
+
*/
|
|
84
|
+
const rowKey = (group: HistoryBucketGroup<T>) => `${props.config.prefix}-${props.bucketId}-${group.steps[0]?.index}`;
|
|
85
|
+
|
|
86
|
+
/** 把原始分组派生为 GroupRow 直接消费的视图模型。 */
|
|
87
|
+
const toRow = (group: HistoryBucketGroup<T>): HistoryRowGroup => toRowGroup(group, rowKey(group), props.config);
|
|
88
|
+
|
|
126
89
|
/** 该 bucket 是否处于初始状态(栈 cursor=0),等价于全部 group 都未 applied。 */
|
|
127
90
|
const isInitial = computed(() => props.groups.length > 0 && props.groups.every((g) => !g.applied));
|
|
128
91
|
</script>
|