@tmagic/editor 1.8.0-beta.2 → 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.
Files changed (45) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +9 -0
  2. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +32 -28
  3. package/dist/es/editorProps.js +2 -0
  4. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +2 -5
  5. package/dist/es/hooks/use-stage.js +2 -1
  6. package/dist/es/index.js +3 -1
  7. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +2 -5
  8. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +33 -14
  9. package/dist/es/layouts/history-list/BucketTab.js +5 -0
  10. package/dist/es/layouts/history-list/{CodeBlockTab.vue_vue_type_script_setup_true_lang.js → BucketTab.vue_vue_type_script_setup_true_lang.js} +27 -16
  11. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +71 -53
  12. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +78 -28
  13. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +136 -66
  14. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +15 -9
  15. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +12 -6
  16. package/dist/es/layouts/history-list/composables.js +20 -2
  17. package/dist/es/services/history.js +5 -2
  18. package/dist/es/style.css +44 -12
  19. package/dist/style.css +44 -12
  20. package/dist/tmagic-editor.umd.cjs +479 -326
  21. package/package.json +7 -7
  22. package/src/Editor.vue +8 -0
  23. package/src/components/CompareForm.vue +32 -19
  24. package/src/editorProps.ts +7 -0
  25. package/src/fields/CodeLink.vue +2 -5
  26. package/src/hooks/use-stage.ts +1 -0
  27. package/src/index.ts +2 -0
  28. package/src/layouts/CodeEditor.vue +2 -5
  29. package/src/layouts/history-list/Bucket.vue +53 -28
  30. package/src/layouts/history-list/BucketTab.vue +77 -0
  31. package/src/layouts/history-list/GroupRow.vue +95 -60
  32. package/src/layouts/history-list/HistoryDiffDialog.vue +51 -32
  33. package/src/layouts/history-list/HistoryListPanel.vue +161 -52
  34. package/src/layouts/history-list/InitialRow.vue +17 -6
  35. package/src/layouts/history-list/PageTab.vue +20 -6
  36. package/src/layouts/history-list/composables.ts +32 -1
  37. package/src/services/history.ts +3 -0
  38. package/src/theme/history-list-panel.scss +52 -13
  39. package/src/type.ts +90 -0
  40. package/types/index.d.ts +295 -135
  41. package/dist/es/layouts/history-list/CodeBlockTab.js +0 -5
  42. package/dist/es/layouts/history-list/DataSourceTab.js +0 -5
  43. package/dist/es/layouts/history-list/DataSourceTab.vue_vue_type_script_setup_true_lang.js +0 -62
  44. package/src/layouts/history-list/CodeBlockTab.vue +0 -61
  45. package/src/layouts/history-list/DataSourceTab.vue +0 -61
@@ -12,15 +12,11 @@
12
12
  <span class="m-editor-history-list-item-index" :title="headIndexTitle">{{ headIndexLabel }}</span>
13
13
  <span class="m-editor-history-list-item-op" :class="`op-${opType}`">{{ opLabel(opType) }}</span>
14
14
  <span class="m-editor-history-list-item-desc">{{ desc }}</span>
15
- <span v-if="isCurrent" class="m-editor-history-list-item-current">当前</span>
16
- <span
17
- v-if="!merged && headDiffable"
18
- class="m-editor-history-list-item-diff"
19
- title="查看修改差异"
20
- @click.stop="onDiffClick(subSteps[0].index)"
21
- >查看差异</span
22
- >
15
+
16
+ <span v-if="time" class="m-editor-history-list-item-time" :title="timeTitle || time">{{ time }}</span>
17
+
23
18
  <span v-if="merged" class="m-editor-history-list-item-merge">合并 {{ stepCount }} 步</span>
19
+
24
20
  <span
25
21
  v-if="!merged && headRevertable"
26
22
  class="m-editor-history-list-item-revert"
@@ -28,6 +24,20 @@
28
24
  @click.stop="onRevertClick(subSteps[0].index)"
29
25
  >回滚</span
30
26
  >
27
+ <span
28
+ v-if="!merged && gotoEnabled && !isCurrent && subSteps.length"
29
+ class="m-editor-history-list-item-goto"
30
+ title="回到该记录"
31
+ @click.stop="onGotoClick(subSteps[0].index)"
32
+ >回到</span
33
+ >
34
+ <span
35
+ v-if="!merged && headDiffable"
36
+ class="m-editor-history-list-item-diff"
37
+ title="查看修改差异"
38
+ @click.stop="onDiffClick(subSteps[0].index)"
39
+ >查看差异</span
40
+ >
31
41
  <span v-if="merged" class="m-editor-history-list-group-toggle" :class="{ 'is-expanded': expanded }">▾</span>
32
42
  </div>
33
43
 
@@ -35,20 +45,12 @@
35
45
  <li
36
46
  v-for="s in subStepsDisplay"
37
47
  :key="s.index"
38
- :class="{ 'is-undone': !s.applied, 'is-current': s.isCurrent, 'is-clickable': !s.isCurrent }"
39
- :title="s.isCurrent ? '当前所在记录' : '点击跳转到该记录'"
40
- @click="onSubStepClick(s)"
48
+ :class="{ 'is-undone': !s.applied, 'is-current': s.isCurrent }"
49
+ :title="subStepTitle(s)"
41
50
  >
42
51
  <span class="m-editor-history-list-item-index">#{{ s.index + 1 }}</span>
43
52
  <span class="m-editor-history-list-substep-desc">{{ s.desc }}</span>
44
- <span v-if="s.isCurrent" class="m-editor-history-list-item-current">当前</span>
45
- <span
46
- v-if="s.diffable"
47
- class="m-editor-history-list-item-diff"
48
- title="查看修改差异"
49
- @click.stop="onDiffClick(s.index)"
50
- >查看差异</span
51
- >
53
+ <span v-if="s.time" class="m-editor-history-list-item-time" :title="s.timeTitle || s.time">{{ s.time }}</span>
52
54
  <span
53
55
  v-if="s.revertable"
54
56
  class="m-editor-history-list-item-revert"
@@ -56,6 +58,20 @@
56
58
  @click.stop="onRevertClick(s.index)"
57
59
  >回滚</span
58
60
  >
61
+ <span
62
+ v-if="gotoEnabled && !s.isCurrent"
63
+ class="m-editor-history-list-item-goto"
64
+ title="回到该记录"
65
+ @click.stop="onGotoClick(s.index)"
66
+ >回到</span
67
+ >
68
+ <span
69
+ v-if="s.diffable"
70
+ class="m-editor-history-list-item-diff"
71
+ title="查看修改差异"
72
+ @click.stop="onDiffClick(s.index)"
73
+ >查看差异</span
74
+ >
59
75
  </li>
60
76
  </ul>
61
77
  </li>
@@ -72,34 +88,54 @@ defineOptions({
72
88
  name: 'MEditorHistoryListGroupRow',
73
89
  });
74
90
 
75
- const props = defineProps<{
76
- /** 唯一标识当前组的 key,作为 toggle 事件的 payload 回传给上层。形如 `pg-${idx}` / `ds-${id}-${idx}` / `cb-${id}-${idx}`。 */
77
- groupKey: string;
78
- /** 该组当前是否处于已应用状态(false 表示已被 undo 撤销,UI 会显示为灰态)。 */
79
- applied: boolean;
80
- /** 是否为合并组(即组内 step 数大于 1,由多次连续操作合并而来)。决定是否展示合并标记与可展开的子步列表。 */
81
- merged: boolean;
82
- /** 操作类型:`add` / `remove` / `update`,用于决定操作徽标的颜色和文案。 */
83
- opType: HistoryOpType;
84
- /** 组的整体描述文案,由上层根据 step / group 计算后传入,例如 "修改 button · style.color"。 */
85
- desc: string;
86
- /** 组内的 step 总数,仅在 merged 为 true 时显示为 "合并 N 步"。 */
87
- stepCount: number;
88
- /** 子步列表,用于在展开状态下逐条展示每个 step 的索引、应用状态与描述文案。 */
89
- subSteps: {
90
- index: number;
91
+ const props = withDefaults(
92
+ defineProps<{
93
+ /** 唯一标识当前组的 key,作为 toggle 事件的 payload 回传给上层。形如 `pg-${首步 index}` / `ds-${id}-${首步 index}` / `cb-${id}-${首步 index}`,以稳定的 step 索引标识分组。 */
94
+ groupKey: string;
95
+ /** 该组当前是否处于已应用状态(false 表示已被 undo 撤销,UI 会显示为灰态)。 */
91
96
  applied: boolean;
97
+ /** 是否为合并组(即组内 step 数大于 1,由多次连续操作合并而来)。决定是否展示合并标记与可展开的子步列表。 */
98
+ merged: boolean;
99
+ /** 操作类型:`add` / `remove` / `update`,用于决定操作徽标的颜色和文案。 */
100
+ opType: HistoryOpType;
101
+ /** 组的整体描述文案,由上层根据 step / group 计算后传入,例如 "修改 button · style.color"。 */
92
102
  desc: string;
103
+ /** 组头部展示的时间文案(一般为组内最近一步的时间),为空时不渲染。 */
104
+ time?: string;
105
+ /** 组头部时间的 title 悬浮提示(完整时间),缺省时回退为 time。 */
106
+ timeTitle?: string;
107
+ /** 组内的 step 总数,仅在 merged 为 true 时显示为 "合并 N 步"。 */
108
+ stepCount: number;
109
+ /** 子步列表,用于在展开状态下逐条展示每个 step 的索引、应用状态与描述文案。 */
110
+ subSteps: {
111
+ index: number;
112
+ applied: boolean;
113
+ desc: string;
114
+ isCurrent?: boolean;
115
+ diffable?: boolean;
116
+ /** 是否可对该子步执行「回滚」(已应用 + 业务侧确认支持反向)。父级根据 step 与 applied 决定。 */
117
+ revertable?: boolean;
118
+ /** 该子步的时间文案,为空时不渲染。 */
119
+ time?: string;
120
+ /** 该子步时间的 title 悬浮提示(完整时间),缺省时回退为 time。 */
121
+ timeTitle?: string;
122
+ }[];
123
+ /** 当前组是否处于展开状态。仅在 merged 为 true 时生效,控制子步列表是否渲染。 */
124
+ expanded: boolean;
125
+ /** 是否为当前所在的分组(包含栈中最近一次已应用步骤的那一组),UI 高亮展示。 */
93
126
  isCurrent?: boolean;
94
- diffable?: boolean;
95
- /** 是否可对该子步执行「回滚」(已应用 + 业务侧确认支持反向)。父级根据 step 与 applied 决定。 */
96
- revertable?: boolean;
97
- }[];
98
- /** 当前组是否处于展开状态。仅在 merged 为 true 时生效,控制子步列表是否渲染。 */
99
- expanded: boolean;
100
- /** 是否为当前所在的分组(包含栈中最近一次已应用步骤的那一组),UI 高亮展示。 */
101
- isCurrent?: boolean;
102
- }>();
127
+ /**
128
+ * 是否支持「跳转到该记录」(goto)。默认 true。
129
+ * 为 false 时:单步组头部与子步条目都不再可点击跳转、也不会触发 goto 事件,
130
+ * 仅保留合并组头部的展开 / 收起能力,以及查看差异、回滚等其它入口。
131
+ */
132
+ gotoEnabled?: boolean;
133
+ }>(),
134
+ {
135
+ isCurrent: false,
136
+ gotoEnabled: true,
137
+ },
138
+ );
103
139
 
104
140
  const emit = defineEmits<{
105
141
  /**
@@ -129,36 +165,35 @@ const emit = defineEmits<{
129
165
  (_e: 'revert-step', _index: number): void;
130
166
  }>();
131
167
 
132
- /** 单步组:头部可点击 goto;合并组:头部可点击切换展开。当前组(isCurrent)的单步组头部不可点击。 */
133
- const isHeadClickable = computed(() => {
134
- if (props.merged) return true;
135
- return !props.isCurrent;
136
- });
168
+ /**
169
+ * 仅合并组头部可点击(切换展开 / 收起);
170
+ * 单步组的跳转改由头部的「回退」按钮触发,整行不再可点击。
171
+ */
172
+ const isHeadClickable = computed(() => props.merged);
137
173
 
138
174
  const headTitle = computed(() => {
139
175
  if (props.merged) return props.expanded ? '点击收起子步' : '点击展开子步';
140
176
  if (props.isCurrent) return '当前所在记录';
141
- return '点击跳转到该记录';
177
+ return '';
142
178
  });
143
179
 
144
180
  /**
145
- * 头部点击行为分流:
146
- * - 合并组:仅切换展开 / 收起,不触发 goto;
147
- * - 单步组:跳转到该唯一步骤;当前组忽略点击。
181
+ * 头部点击行为:仅合并组切换展开 / 收起;单步组不再响应整行点击。
148
182
  */
149
183
  const onHeadClick = () => {
150
184
  if (props.merged) {
151
185
  emit('toggle', props.groupKey);
152
- return;
153
186
  }
154
- if (props.isCurrent) return;
155
- if (!props.subSteps.length) return;
156
- emit('goto', props.subSteps[0].index);
157
187
  };
158
188
 
159
- const onSubStepClick = (s: { index: number; isCurrent?: boolean }) => {
160
- if (s.isCurrent) return;
161
- emit('goto', s.index);
189
+ const onGotoClick = (index: number) => {
190
+ if (!props.gotoEnabled) return;
191
+ emit('goto', index);
192
+ };
193
+
194
+ const subStepTitle = (s: { isCurrent?: boolean }) => {
195
+ if (s.isCurrent) return '当前所在记录';
196
+ return '';
162
197
  };
163
198
 
164
199
  /** 单步组头部是否展示"查看差异"入口:要求该唯一子步本身可对比。 */
@@ -3,13 +3,16 @@
3
3
  <TMagicDialog
4
4
  v-model="visible"
5
5
  class="m-editor-history-diff-dialog"
6
- title="查看修改差异"
7
- width="900px"
6
+ :title="dialogTitle"
8
7
  top="5vh"
9
8
  destroy-on-close
10
9
  append-to-body
10
+ :width="width"
11
+ @close="onClose"
11
12
  >
12
13
  <div v-if="payload" class="m-editor-history-diff-dialog-body">
14
+ <div v-if="onConfirm" class="m-editor-history-diff-dialog-notice">仅回滚有差异的字段</div>
15
+
13
16
  <div class="m-editor-history-diff-dialog-header">
14
17
  <span class="m-editor-history-diff-dialog-target">{{ targetText }}</span>
15
18
  <div class="m-editor-history-diff-dialog-controls">
@@ -42,6 +45,8 @@
42
45
  :value="rightValue"
43
46
  :last-value="leftValue"
44
47
  :extend-state="extendState"
48
+ :load-config="loadConfig"
49
+ :self-diff-field-types="selfDiffFieldTypes"
45
50
  height="70vh"
46
51
  />
47
52
 
@@ -58,7 +63,11 @@
58
63
  </div>
59
64
 
60
65
  <template #footer>
61
- <TMagicButton size="small" @click="visible = false">关闭</TMagicButton>
66
+ <template v-if="onConfirm">
67
+ <TMagicButton size="small" @click="visible = false">取消</TMagicButton>
68
+ <TMagicButton size="small" type="primary" @click="onConfirmClick">确定回滚</TMagicButton>
69
+ </template>
70
+ <TMagicButton v-else size="small" @click="visible = false">关闭</TMagicButton>
62
71
  </template>
63
72
  </TMagicDialog>
64
73
  </Teleport>
@@ -71,41 +80,37 @@ import { isEqual } from 'lodash-es';
71
80
  import { TMagicButton, TMagicDialog, TMagicRadioButton, TMagicRadioGroup, TMagicTag } from '@tmagic/design';
72
81
  import type { FormState } from '@tmagic/form';
73
82
 
74
- import CompareForm, { type CompareCategory } from '@editor/components/CompareForm.vue';
83
+ import CompareForm from '@editor/components/CompareForm.vue';
75
84
  import CodeEditor from '@editor/layouts/CodeEditor.vue';
85
+ import type { CompareCategory, CompareFormLoadConfig, DiffDialogPayload } from '@editor/type';
76
86
 
77
87
  defineOptions({
78
88
  name: 'MEditorHistoryDiffDialog',
79
89
  });
80
90
 
81
- defineProps<{
82
- /**
83
- * 来自 Editor 顶层的 `extendFormState`,用于扩展 MForm.formState。
84
- * 透传给 CompareForm,从而让差异对比时表单 item 中依赖业务上下文的
85
- * `display` / `disabled` 等 filterFunction 正常工作。
86
- */
87
- extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
88
- }>();
89
-
90
- /** 差异对话框的入参 */
91
- export interface DiffDialogPayload {
92
- /** 表单类别 */
93
- category: CompareCategory;
94
- /** 节点类型 / 数据源类型 */
95
- type?: string;
96
- /** 代码块场景下的数据源类型 */
97
- dataSourceType?: string;
98
- /** 该 step 修改前的值(oldNode / oldSchema / oldContent) */
99
- lastValue: Record<string, any>;
100
- /** 该 step 修改后的值(newNode / newSchema / newContent) */
101
- value: Record<string, any>;
102
- /** 当前编辑器中实际的最新值;不传或为 null 时禁用「与当前对比」 */
103
- currentValue?: Record<string, any> | null;
104
- /** 用于标题展示的目标名称 */
105
- targetLabel?: string;
106
- /** 用于标题展示的目标 id */
107
- id?: string | number;
108
- }
91
+ const props = withDefaults(
92
+ defineProps<{
93
+ /**
94
+ * 来自 Editor 顶层的 `extendFormState`,用于扩展 MForm.formState。
95
+ * 透传给 CompareForm,从而让差异对比时表单 item 中依赖业务上下文的
96
+ * `display` / `disabled` 等 filterFunction 正常工作。
97
+ */
98
+ extendState?: (_state: FormState) => Record<string, any> | Promise<Record<string, any>>;
99
+ /**
100
+ * 自定义 FormConfig 加载逻辑,透传给 CompareForm。传入后将接管内置的按 `category`
101
+ * 取配置逻辑,可通过 `ctx.defaultLoadConfig()` 复用默认结果再做二次加工。
102
+ */
103
+ loadConfig?: CompareFormLoadConfig;
104
+ width?: string;
105
+ onConfirm?: () => void;
106
+ selfDiffFieldTypes?: string[];
107
+ }>(),
108
+ {
109
+ width: '900px',
110
+ },
111
+ );
112
+
113
+ const emit = defineEmits(['close']);
109
114
 
110
115
  /**
111
116
  * 差异对比模式:
@@ -146,6 +151,8 @@ const codeDiffOptions = {
146
151
  },
147
152
  };
148
153
 
154
+ const dialogTitle = computed(() => (props.onConfirm ? '确认回滚' : '查看修改差异'));
155
+
149
156
  const hasCurrent = computed(() => payload.value?.currentValue !== undefined && payload.value?.currentValue !== null);
150
157
 
151
158
  /** 左侧(旧/参照)值 */
@@ -171,6 +178,14 @@ const isSameAsCurrent = computed(() => {
171
178
  return isEqual(payload.value.value, payload.value.currentValue);
172
179
  });
173
180
 
181
+ const onConfirmClick = () => {
182
+ const cb = props.onConfirm;
183
+
184
+ cb?.();
185
+
186
+ visible.value = false;
187
+ };
188
+
174
189
  const targetText = computed(() => {
175
190
  if (!payload.value) return '';
176
191
  const categoryText: Record<CompareCategory, string> = {
@@ -205,6 +220,10 @@ watch(visible, (v) => {
205
220
  }
206
221
  });
207
222
 
223
+ const onClose = () => {
224
+ emit('close');
225
+ };
226
+
208
227
  defineExpose({
209
228
  open,
210
229
  close,