@tmagic/editor 1.8.0-beta.1 → 1.8.0-beta.11

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 (232) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +31 -5
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +21 -113
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +7 -1
  4. package/dist/es/components/CompareForm.js +5 -0
  5. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +196 -0
  6. package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  7. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +2 -2
  8. package/dist/es/components/Icon.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/es/components/Resizer.vue_vue_type_script_setup_true_lang.js +1 -1
  10. package/dist/es/components/ScrollBar.vue_vue_type_script_setup_true_lang.js +1 -1
  11. package/dist/es/components/ScrollViewer.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/es/components/SearchInput.vue_vue_type_script_setup_true_lang.js +1 -1
  13. package/dist/es/components/SplitView.vue_vue_type_script_setup_true_lang.js +1 -1
  14. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +7 -7
  15. package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +1 -1
  17. package/dist/es/editorProps.js +2 -0
  18. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +20 -4
  19. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +3 -6
  20. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +19 -3
  21. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +42 -8
  22. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  23. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +14 -6
  24. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +3 -3
  25. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +41 -16
  26. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +2 -2
  27. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +9 -5
  28. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +40 -19
  29. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +17 -9
  30. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +4 -2
  31. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +1 -1
  32. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +59 -12
  33. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +12 -7
  34. package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  35. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +1 -1
  36. package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +1 -1
  37. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +28 -6
  38. package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +1 -1
  39. package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +1 -1
  40. package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +1 -1
  41. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +1 -1
  42. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +1 -1
  43. package/dist/es/fields/StyleSetter/icons/display/Block.js +1 -1
  44. package/dist/es/fields/StyleSetter/icons/display/Flex.js +1 -1
  45. package/dist/es/fields/StyleSetter/icons/display/Inline.js +1 -1
  46. package/dist/es/fields/StyleSetter/icons/display/InlineBlock.js +1 -1
  47. package/dist/es/fields/StyleSetter/icons/display/None.js +1 -1
  48. package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +1 -1
  49. package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +1 -1
  50. package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +1 -1
  51. package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +1 -1
  52. package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +1 -1
  53. package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +1 -1
  54. package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +1 -1
  55. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +1 -1
  56. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +1 -1
  57. package/dist/es/fields/StyleSetter/icons/text-align/Center.js +1 -1
  58. package/dist/es/fields/StyleSetter/icons/text-align/Left.js +1 -1
  59. package/dist/es/fields/StyleSetter/icons/text-align/Right.js +1 -1
  60. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +1 -1
  61. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +1 -1
  62. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +1 -1
  63. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +1 -1
  64. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +1 -1
  65. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +1 -1
  66. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +9 -6
  67. package/dist/es/hooks/use-code-block-edit.js +7 -4
  68. package/dist/es/hooks/use-data-source-edit.js +5 -2
  69. package/dist/es/hooks/use-stage.js +22 -10
  70. package/dist/es/icons/CenterIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  71. package/dist/es/icons/CodeIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  72. package/dist/es/icons/FolderMinusIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  73. package/dist/es/index.js +14 -3
  74. package/dist/es/initService.js +7 -7
  75. package/dist/es/layouts/AddPageBox.vue_vue_type_script_setup_true_lang.js +1 -1
  76. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +7 -6
  77. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +14 -9
  78. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +12 -4
  79. package/dist/es/layouts/NavMenuColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  80. package/dist/es/layouts/history-list/Bucket.js +5 -0
  81. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +71 -0
  82. package/dist/es/layouts/history-list/BucketTab.js +5 -0
  83. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +62 -0
  84. package/dist/es/layouts/history-list/GroupRow.js +5 -0
  85. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +269 -0
  86. package/dist/es/layouts/history-list/HistoryDiffDialog.js +5 -0
  87. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +294 -0
  88. package/dist/es/layouts/history-list/HistoryListPanel.js +5 -0
  89. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +327 -0
  90. package/dist/es/layouts/history-list/InitialRow.js +5 -0
  91. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +83 -0
  92. package/dist/es/layouts/history-list/PageTab.js +5 -0
  93. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +95 -0
  94. package/dist/es/layouts/history-list/composables.js +184 -0
  95. package/dist/es/layouts/history-list/useHistoryList.js +56 -0
  96. package/dist/es/layouts/history-list/useHistoryRevert.js +313 -0
  97. package/dist/es/layouts/page-bar/AddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  98. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +5 -3
  99. package/dist/es/layouts/page-bar/PageBarScrollContainer.vue_vue_type_script_setup_true_lang.js +1 -1
  100. package/dist/es/layouts/page-bar/PageList.vue_vue_type_script_setup_true_lang.js +1 -1
  101. package/dist/es/layouts/page-bar/Search.vue_vue_type_script_setup_true_lang.js +2 -2
  102. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  103. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +6 -2
  104. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
  105. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +8 -4
  106. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +4 -4
  107. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +2 -2
  108. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +1 -1
  109. package/dist/es/layouts/sidebar/data-source/DataSourceAddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  110. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +19 -6
  111. package/dist/es/layouts/sidebar/data-source/DataSourceList.vue_vue_type_script_setup_true_lang.js +1 -1
  112. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +25 -3
  113. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +1 -1
  114. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +6 -6
  115. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +2 -2
  116. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  117. package/dist/es/layouts/sidebar/layer/use-node-status.js +1 -1
  118. package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +1 -1
  119. package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +1 -1
  120. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  121. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +2 -2
  122. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -1
  123. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +9 -9
  124. package/dist/es/services/BaseService.js +31 -68
  125. package/dist/es/services/codeBlock.js +286 -8
  126. package/dist/es/services/componentList.js +2 -2
  127. package/dist/es/services/dataSource.js +287 -8
  128. package/dist/es/services/dep.js +21 -9
  129. package/dist/es/services/editor.js +655 -194
  130. package/dist/es/services/events.js +2 -2
  131. package/dist/es/services/history.js +390 -47
  132. package/dist/es/services/keybinding.js +5 -5
  133. package/dist/es/services/props.js +8 -8
  134. package/dist/es/services/stageOverlay.js +2 -2
  135. package/dist/es/services/storage.js +2 -2
  136. package/dist/es/services/ui.js +5 -2
  137. package/dist/es/style.css +456 -8
  138. package/dist/es/utils/code-block.js +122 -0
  139. package/dist/es/utils/content-menu.js +19 -12
  140. package/dist/es/utils/data-source/index.js +2 -0
  141. package/dist/es/utils/dep/worker.js +1 -1
  142. package/dist/es/utils/editor.js +105 -51
  143. package/dist/es/utils/history.js +220 -0
  144. package/dist/es/utils/indexed-db.js +86 -0
  145. package/dist/es/utils/undo-redo.js +80 -1
  146. package/dist/style.css +456 -8
  147. package/dist/tmagic-editor.umd.cjs +11149 -6646
  148. package/package.json +8 -8
  149. package/src/Editor.vue +25 -1
  150. package/src/components/CodeBlockEditor.vue +20 -109
  151. package/src/components/CodeParams.vue +6 -0
  152. package/src/components/CompareForm.vue +277 -0
  153. package/src/components/ToolButton.vue +2 -2
  154. package/src/editorProps.ts +9 -0
  155. package/src/fields/Code.vue +19 -3
  156. package/src/fields/CodeLink.vue +2 -5
  157. package/src/fields/CodeSelect.vue +17 -1
  158. package/src/fields/CodeSelectCol.vue +37 -3
  159. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +23 -3
  160. package/src/fields/DataSourceFieldSelect/Index.vue +2 -2
  161. package/src/fields/DataSourceFields.vue +48 -10
  162. package/src/fields/DataSourceInput.vue +3 -3
  163. package/src/fields/DataSourceMethodSelect.vue +12 -4
  164. package/src/fields/DataSourceMethods.vue +55 -24
  165. package/src/fields/DataSourceMocks.vue +13 -3
  166. package/src/fields/DataSourceSelect.vue +4 -1
  167. package/src/fields/EventSelect.vue +57 -8
  168. package/src/fields/KeyValue.vue +17 -4
  169. package/src/fields/StyleSetter/components/Border.vue +15 -6
  170. package/src/fields/UISelect.vue +5 -2
  171. package/src/hooks/use-code-block-edit.ts +9 -5
  172. package/src/hooks/use-data-source-edit.ts +2 -2
  173. package/src/hooks/use-stage.ts +26 -9
  174. package/src/index.ts +8 -0
  175. package/src/initService.ts +1 -1
  176. package/src/layouts/CodeEditor.vue +17 -5
  177. package/src/layouts/Framework.vue +11 -6
  178. package/src/layouts/NavMenu.vue +12 -3
  179. package/src/layouts/history-list/Bucket.vue +91 -0
  180. package/src/layouts/history-list/BucketTab.vue +72 -0
  181. package/src/layouts/history-list/GroupRow.vue +304 -0
  182. package/src/layouts/history-list/HistoryDiffDialog.vue +281 -0
  183. package/src/layouts/history-list/HistoryListPanel.vue +367 -0
  184. package/src/layouts/history-list/InitialRow.vue +71 -0
  185. package/src/layouts/history-list/PageTab.vue +118 -0
  186. package/src/layouts/history-list/composables.ts +309 -0
  187. package/src/layouts/history-list/useHistoryList.ts +60 -0
  188. package/src/layouts/history-list/useHistoryRevert.ts +421 -0
  189. package/src/layouts/page-bar/PageBar.vue +4 -2
  190. package/src/layouts/props-panel/PropsPanel.vue +5 -1
  191. package/src/layouts/sidebar/ComponentListPanel.vue +9 -5
  192. package/src/layouts/sidebar/Sidebar.vue +8 -3
  193. package/src/layouts/sidebar/code-block/CodeBlockList.vue +5 -5
  194. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +1 -1
  195. package/src/layouts/sidebar/code-block/useContentMenu.ts +1 -1
  196. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +31 -2
  197. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +27 -2
  198. package/src/layouts/sidebar/data-source/useContentMenu.ts +1 -1
  199. package/src/layouts/sidebar/layer/LayerMenu.vue +19 -11
  200. package/src/layouts/sidebar/layer/LayerNodeTool.vue +7 -4
  201. package/src/layouts/sidebar/layer/use-node-status.ts +9 -6
  202. package/src/layouts/workspace/viewer/Stage.vue +1 -1
  203. package/src/layouts/workspace/viewer/ViewerMenu.vue +8 -8
  204. package/src/services/BaseService.ts +35 -86
  205. package/src/services/codeBlock.ts +361 -5
  206. package/src/services/dataSource.ts +336 -6
  207. package/src/services/dep.ts +32 -9
  208. package/src/services/editor.ts +834 -194
  209. package/src/services/history.ts +509 -49
  210. package/src/services/keybinding.ts +3 -3
  211. package/src/services/props.ts +2 -2
  212. package/src/services/ui.ts +7 -0
  213. package/src/theme/history-list-panel.scss +557 -0
  214. package/src/theme/page-bar.scss +0 -4
  215. package/src/theme/props-panel.scss +3 -3
  216. package/src/theme/style-setter/border.scss +4 -1
  217. package/src/theme/theme.scss +1 -0
  218. package/src/type.ts +613 -31
  219. package/src/utils/code-block.ts +150 -0
  220. package/src/utils/content-menu.ts +25 -13
  221. package/src/utils/data-source/index.ts +2 -0
  222. package/src/utils/dep/worker.ts +5 -3
  223. package/src/utils/editor.ts +166 -60
  224. package/src/utils/history.ts +286 -0
  225. package/src/utils/index.ts +3 -0
  226. package/src/utils/indexed-db.ts +122 -0
  227. package/src/utils/undo-redo.ts +111 -0
  228. package/types/index.d.ts +3197 -896
  229. package/dist/es/utils/compose.js +0 -44
  230. package/dist/es/utils/editor-history.js +0 -100
  231. package/src/utils/compose.ts +0 -52
  232. package/src/utils/editor-history.ts +0 -138
@@ -0,0 +1,367 @@
1
+ <template>
2
+ <TMagicPopover
3
+ popper-class="m-editor-history-list-popover"
4
+ placement="bottom"
5
+ trigger="click"
6
+ v-model:visible="visible"
7
+ :width="660"
8
+ >
9
+ <div class="m-editor-history-list">
10
+ <TMagicTooltip effect="dark" placement="top" content="关闭">
11
+ <TMagicButton class="m-editor-history-list-close" size="small" link @click="visible = false">
12
+ <template #icon>
13
+ <MIcon :icon="CloseIcon"></MIcon>
14
+ </template>
15
+ </TMagicButton>
16
+ </TMagicTooltip>
17
+
18
+ <TMagicTabs v-model="activeTab" class="m-editor-history-list-tabs">
19
+ <component
20
+ :is="tabPaneComponent?.component || 'el-tab-pane'"
21
+ v-bind="
22
+ tabPaneComponent?.props({ name: 'page', label: `${pageName} (${pageGroups.length})`, lazy: true }) || {}
23
+ "
24
+ >
25
+ <PageTab
26
+ :list="pageGroupsDisplay"
27
+ :expanded="expanded"
28
+ :marker="pageMarker"
29
+ @toggle="toggleGroup"
30
+ @goto="onPageGoto"
31
+ @goto-initial="onPageGotoInitial"
32
+ @diff-step="onPageDiff"
33
+ @revert-step="onPageRevert"
34
+ @select="onPageSelect"
35
+ @clear="onPageClear"
36
+ />
37
+ </component>
38
+
39
+ <component
40
+ v-if="!disabledDataSource"
41
+ :is="tabPaneComponent?.component || 'el-tab-pane'"
42
+ v-bind="
43
+ tabPaneComponent?.props({
44
+ name: 'data-source',
45
+ label: `${dataSourceName} (${dataSourceGroups.length})`,
46
+ lazy: true,
47
+ }) || {}
48
+ "
49
+ >
50
+ <BucketTab
51
+ :config="dataSourceConfig"
52
+ :buckets="dataSourceGroupsByTarget"
53
+ :expanded="expanded"
54
+ @toggle="toggleGroup"
55
+ @goto="onDataSourceGoto"
56
+ @goto-initial="onDataSourceGotoInitial"
57
+ @diff-step="onDataSourceDiff"
58
+ @revert-step="onDataSourceRevert"
59
+ @clear="onDataSourceClear"
60
+ />
61
+ </component>
62
+
63
+ <component
64
+ v-if="!disabledCodeBlock"
65
+ :is="tabPaneComponent?.component || 'el-tab-pane'"
66
+ v-bind="
67
+ tabPaneComponent?.props({
68
+ name: 'code-block',
69
+ label: `${codeBlockName} (${codeBlockGroups.length})`,
70
+ lazy: true,
71
+ }) || {}
72
+ "
73
+ >
74
+ <BucketTab
75
+ :config="codeBlockConfig"
76
+ :buckets="codeBlockGroupsByTarget"
77
+ :expanded="expanded"
78
+ @toggle="toggleGroup"
79
+ @goto="onCodeBlockGoto"
80
+ @goto-initial="onCodeBlockGotoInitial"
81
+ @diff-step="onCodeBlockDiff"
82
+ @revert-step="onCodeBlockRevert"
83
+ @clear="onCodeBlockClear"
84
+ />
85
+ </component>
86
+
87
+ <component
88
+ v-for="tab in extraTabs"
89
+ :key="tab.name"
90
+ :is="tabPaneComponent?.component || 'el-tab-pane'"
91
+ v-bind="tabPaneComponent?.props({ name: tab.name, label: resolveTabLabel(tab), lazy: true }) || {}"
92
+ >
93
+ <component :is="tab.component" v-bind="tab.props || {}" v-on="tab.listeners || {}" />
94
+ </component>
95
+ </TMagicTabs>
96
+ </div>
97
+
98
+ <template #reference>
99
+ <TMagicTooltip effect="dark" placement="bottom" content="历史记录">
100
+ <TMagicButton size="small" link @click="visible = !visible">
101
+ <template #icon>
102
+ <MIcon :icon="ClockIcon"></MIcon>
103
+ </template>
104
+ </TMagicButton>
105
+ </TMagicTooltip>
106
+ </template>
107
+ </TMagicPopover>
108
+ </template>
109
+
110
+ <script lang="ts" setup>
111
+ /**
112
+ * 历史记录面板:在顶部 NavMenu 上点击图标打开 popover,分三个 tab:
113
+ * - 页面:当前活动页面的历史栈,连续修改同一节点的多步会被合并成一组
114
+ * - 数据源:以 dataSource.id 分桶,每条操作记录独立展示
115
+ * - 代码块:同上,按 codeBlock.id 分桶,每条操作记录独立展示
116
+ *
117
+ * 数据通过 historyService 暴露的聚合 API 读取,UI 仅用于只读展示,
118
+ * 同时支持点击任意一条记录跳转至该状态:
119
+ * - 页面 tab:调用 editorService.gotoPageStep(targetCursor)
120
+ * - 数据源 tab:调用 dataSourceService.goto(id, targetCursor)
121
+ * - 代码块 tab:调用 codeBlockService.goto(id, targetCursor)
122
+ *
123
+ * 这里的 targetCursor = 用户点击的 step.index + 1,即"应用至此步完成的状态"。
124
+ *
125
+ * 此外每条 step 上提供"查看差异"入口(仅在前后值都存在的 update 步骤显示),
126
+ * 点击后弹出 HistoryDiffDialog,使用 CompareForm 组件以表单形式展示新旧值差异。
127
+ *
128
+ * 各 tab 的内容拆分为独立的 SFC:页面用 PageTab,数据源 / 代码块复用通用的 BucketTab
129
+ * (通过 title / prefix / describe* / isStepDiffable 注入差异)。
130
+ * 共享的描述生成与折叠状态在 composables.ts 中维护。
131
+ */
132
+ import { computed, inject, markRaw, ref, watch } from 'vue';
133
+ import { Clock, Close } from '@element-plus/icons-vue';
134
+
135
+ import { getDesignConfig, TMagicButton, tMagicMessage, TMagicPopover, TMagicTabs, TMagicTooltip } from '@tmagic/design';
136
+ import type { FormState } from '@tmagic/form';
137
+
138
+ import MIcon from '@editor/components/Icon.vue';
139
+ import { useServices } from '@editor/hooks/use-services';
140
+ import type {
141
+ BaseStepValue,
142
+ CodeBlockStepValue,
143
+ DataSourceStepValue,
144
+ HistoryBucketConfig,
145
+ HistoryListExtraTab,
146
+ } from '@editor/type';
147
+
148
+ import BucketTab from './BucketTab.vue';
149
+ import { confirmHistoryAction, describeStep, isSingleDiffStepRevertable } from './composables';
150
+ import PageTab from './PageTab.vue';
151
+ import { useHistoryList } from './useHistoryList';
152
+ import { useHistoryRevert } from './useHistoryRevert';
153
+
154
+ defineOptions({
155
+ name: 'MEditorHistoryListPanel',
156
+ });
157
+
158
+ const ClockIcon = markRaw(Clock);
159
+ const CloseIcon = markRaw(Close);
160
+ const activeTab = ref<string>('page');
161
+
162
+ /**
163
+ * 面板显隐受控:reference 图标点击切换,右上角关闭按钮置为 false。
164
+ * 点击面板以外区域的自动收起由 TMagicPopover 通过 v-model:visible 回写完成。
165
+ */
166
+ const visible = ref(false);
167
+
168
+ const tabPaneComponent = getDesignConfig('components')?.tabPane;
169
+
170
+ /**
171
+ * 业务方自定义的扩展 tab,由 Editor 顶层通过 `historyListExtraTabs` 注入。
172
+ * 追加在内置「页面 / 数据源 / 代码块」三个 tab 之后,未提供时为空数组。
173
+ */
174
+ const extraTabs = inject<HistoryListExtraTab[]>('historyListExtraTabs', []);
175
+
176
+ /** label 支持字符串或函数,函数形式便于展示动态数量等内容。 */
177
+ const resolveTabLabel = (tab: HistoryListExtraTab) => (typeof tab.label === 'function' ? tab.label() : tab.label);
178
+
179
+ const services = useServices();
180
+ const { editorService, dataSourceService, codeBlockService, historyService, propsService, stageOverlayService } =
181
+ services;
182
+
183
+ /**
184
+ * 数据源 / 代码块功能可被业务方通过 `disabledDataSource` / `disabledCodeBlock` 禁用,
185
+ * 禁用后对应的历史记录 tab 不再展示。若当前激活的 tab 恰好被禁用,则回退到「页面」tab。
186
+ */
187
+ const disabledDataSource = computed(() => propsService.getDisabledDataSource());
188
+ const disabledCodeBlock = computed(() => propsService.getDisabledCodeBlock());
189
+
190
+ watch([disabledDataSource, disabledCodeBlock], ([dsDisabled, cbDisabled]) => {
191
+ if ((activeTab.value === 'data-source' && dsDisabled) || (activeTab.value === 'code-block' && cbDisabled)) {
192
+ activeTab.value = 'page';
193
+ }
194
+ });
195
+
196
+ /**
197
+ * 通过 inject 拿到 Editor 顶层注入的 `extendFormState`,转交给 HistoryDiffDialog
198
+ * 内部的 CompareForm,使差异对比表单的 filterFunction 能拿到完整的业务上下文。
199
+ * 未提供时为 undefined,CompareForm/MForm 会跳过 extendState 处理。
200
+ */
201
+ const extendFormState = inject<((_state: FormState) => Record<string, any> | Promise<Record<string, any>>) | undefined>(
202
+ 'extendFormState',
203
+ undefined,
204
+ );
205
+ const getPropsPanelFormState = inject<(() => FormState | undefined) | undefined>('getPropsPanelFormState', undefined);
206
+
207
+ const {
208
+ expanded,
209
+ toggleGroup,
210
+ pageGroups,
211
+ dataSourceGroups,
212
+ codeBlockGroups,
213
+ pageGroupsDisplay,
214
+ dataSourceGroupsByTarget,
215
+ codeBlockGroupsByTarget,
216
+ } = useHistoryList();
217
+
218
+ /**
219
+ * 当前活动页的「加载/初始」标记记录(设置 root 时生成),透传给 PageTab 的底部初始行展示。
220
+ * 基于 historyService 的 reactive state 派生,活动页切换或标记写入后自动刷新。
221
+ */
222
+ const pageMarker = computed(() => historyService.getMarker('page', editorService.get('page')?.id));
223
+
224
+ /**
225
+ * 各历史类型的展示名称(页面 / 数据源 / 代码块),由 historyService.state.stepNames 配置,
226
+ * 业务方可通过 historyService.setStepName / registerStepType 覆盖。基于 reactive state 派生,配置变更后自动刷新。
227
+ */
228
+ const pageName = computed(() => historyService.getStepName('page'));
229
+ const dataSourceName = computed(() => historyService.getStepName('dataSource'));
230
+ const codeBlockName = computed(() => historyService.getStepName('codeBlock'));
231
+
232
+ /** 代码块 step 仅 update(前后 content 都存在)时可查看差异。 */
233
+ const isStepDiffable = (step: BaseStepValue) => Boolean(step.diff?.[0]?.oldSchema && step.diff?.[0]?.newSchema);
234
+
235
+ /**
236
+ * 数据源 / 代码块两类 bucket 历史的整体渲染配置:把 title / prefix 与各自的描述、
237
+ * 可差异、可回滚判定收敛为单一对象整体注入 BucketTab,组件内部按需读取。
238
+ * title / 描述回退名取自可配置的展示名称,故用 computed 使其随 stepNames 变更刷新。
239
+ */
240
+ // 数据源/代码块不做相邻合并,每组恒为单步,省略 describeGroup,由 toRowGroup 回退到 describeStep。
241
+ const dataSourceConfig = computed<HistoryBucketConfig<DataSourceStepValue>>(() => ({
242
+ title: dataSourceName.value,
243
+ prefix: 'ds',
244
+ describeStep: (step: DataSourceStepValue): string =>
245
+ describeStep(step, (schema) => schema?.title, dataSourceName.value),
246
+ isStepDiffable,
247
+ isStepRevertable: isSingleDiffStepRevertable,
248
+ }));
249
+
250
+ const codeBlockConfig = computed<HistoryBucketConfig<CodeBlockStepValue>>(() => ({
251
+ title: codeBlockName.value,
252
+ prefix: 'cb',
253
+ describeStep: (step: CodeBlockStepValue): string =>
254
+ describeStep(step, (content) => content?.name, codeBlockName.value),
255
+ isStepDiffable,
256
+ isStepRevertable: isSingleDiffStepRevertable,
257
+ }));
258
+
259
+ /** 把"目标 step 索引"翻译成"目标 cursor"(已应用步骤数量)。 */
260
+ const indexToCursor = (index: number) => index + 1;
261
+
262
+ const onPageGoto = (index: number) => {
263
+ editorService.gotoPageStep(indexToCursor(index));
264
+ };
265
+
266
+ /**
267
+ * 点击页面历史记录行:选中该记录对应的画布节点。
268
+ * - 从目标 step 的 diff 中取节点 id(优先 newSchema,回退 oldSchema),按出现顺序找到第一个当前仍存在的节点;
269
+ * - 与图层树点击选中一致:editorService.select + 画布 / overlay 画布 select 三者联动;
270
+ * - 该 step 涉及的节点都已不存在(如删除记录、被撤销的新增)时给出提示,不做选中。
271
+ */
272
+ const onPageSelect = async (index: number) => {
273
+ const step = historyService.getStepList('page', editorService.get('page')?.id)[index]?.step;
274
+ if (!step) return;
275
+ const targetId = (step.diff ?? [])
276
+ .map((item) => item.newSchema?.id ?? item.oldSchema?.id)
277
+ .find((id) => id !== undefined && id !== null && editorService.getNodeById(id, false));
278
+ if (targetId === undefined || targetId === null) {
279
+ tMagicMessage.warning('该记录对应的节点已不存在,无法选中');
280
+ return;
281
+ }
282
+ const node = editorService.getNodeById(targetId, false);
283
+ if (!node) return;
284
+ await editorService.select(node);
285
+ editorService.get('stage')?.select(targetId);
286
+ stageOverlayService.get('stage')?.select(targetId);
287
+ };
288
+
289
+ const onDataSourceGoto = (id: string | number, index: number) => {
290
+ dataSourceService.goto(id, indexToCursor(index));
291
+ };
292
+
293
+ const onCodeBlockGoto = (id: string | number, index: number) => {
294
+ codeBlockService.goto(id, indexToCursor(index));
295
+ };
296
+
297
+ /**
298
+ * "回到初始状态" = 把对应栈 cursor 移到 0(全部已撤销)。
299
+ * 复用 service.goto*(0) 即可,所有真实 step 的反向应用由 service 层的 undo 链路完成。
300
+ */
301
+ const onPageGotoInitial = () => {
302
+ editorService.gotoPageStep(0);
303
+ };
304
+
305
+ const onDataSourceGotoInitial = (id: string | number) => {
306
+ dataSourceService.goto(id, 0);
307
+ };
308
+
309
+ const onCodeBlockGotoInitial = (id: string | number) => {
310
+ codeBlockService.goto(id, 0);
311
+ };
312
+
313
+ /**
314
+ * 「单步回滚」与「查看差异」的完整逻辑收敛到 useHistoryRevert,面板与业务方共用:
315
+ * 二者均由 useHistoryRevert 内部按需动态挂载 HistoryDiffDialog,
316
+ * 业务方亦可直接 import useHistoryRevert(options, services) 调用,无需自行挂载任何弹窗。
317
+ */
318
+ const { onPageRevert, onDataSourceRevert, onCodeBlockRevert, onPageDiff, onDataSourceDiff, onCodeBlockDiff } =
319
+ useHistoryRevert({ extendState: extendFormState, getPropsPanelFormState }, services);
320
+
321
+ /**
322
+ * 把内存中(已清空对应类别后的)历史状态重新写回 IndexedDB,
323
+ * 使本地持久化的那份与内存保持一致——即「连同本地保存的一并删除」。
324
+ * 不支持 IndexedDB 或写入失败时静默忽略(内存清空已生效)。
325
+ */
326
+ const syncIndexedDB = async () => {
327
+ try {
328
+ await historyService.saveToIndexedDB();
329
+ // eslint-disable-next-line no-unused-vars
330
+ } catch (e) {
331
+ // ignore: 内存清空已生效,本地同步失败不阻塞交互
332
+ }
333
+ };
334
+
335
+ const onPageClear = async () => {
336
+ if (
337
+ await confirmHistoryAction(
338
+ '确定清空当前页面的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。',
339
+ )
340
+ ) {
341
+ historyService.clear('page', editorService.get('page')?.id);
342
+ await syncIndexedDB();
343
+ }
344
+ };
345
+
346
+ const onDataSourceClear = async () => {
347
+ if (
348
+ await confirmHistoryAction(
349
+ '确定清空数据源的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。',
350
+ )
351
+ ) {
352
+ historyService.clear('dataSource');
353
+ await syncIndexedDB();
354
+ }
355
+ };
356
+
357
+ const onCodeBlockClear = async () => {
358
+ if (
359
+ await confirmHistoryAction(
360
+ '确定清空代码块的历史记录吗?清空后将无法撤销/重做之前的操作,本地保存的记录也会一并删除。',
361
+ )
362
+ ) {
363
+ historyService.clear('codeBlock');
364
+ await syncIndexedDB();
365
+ }
366
+ };
367
+ </script>
@@ -0,0 +1,71 @@
1
+ <template>
2
+ <li
3
+ class="m-editor-history-list-item m-editor-history-list-initial"
4
+ :class="{ 'is-current': isCurrent, 'is-clickable': !isCurrent }"
5
+ :title="rowTitle"
6
+ >
7
+ <span class="m-editor-history-list-item-index" title="历史步骤编号 #0(未修改的初始状态)">#0</span>
8
+ <span class="m-editor-history-list-item-op op-initial">初始</span>
9
+ <span class="m-editor-history-list-item-desc">{{ desc }}</span>
10
+ <span v-if="saved" class="m-editor-history-list-item-saved" title="该记录为最近一次保存的状态">已保存</span>
11
+ <span v-if="gotoEnabled && !isCurrent" class="m-editor-history-list-item-actions">
12
+ <span class="m-editor-history-list-item-goto" title="回到该记录" @click.stop="onClick">回到</span>
13
+ </span>
14
+ <span v-if="time" class="m-editor-history-list-item-time" :title="timeTitle">{{ time }}</span>
15
+ </li>
16
+ </template>
17
+
18
+ <script lang="ts" setup>
19
+ /**
20
+ * 「初始状态」记录行:渲染于历史列表底部,作为整个栈的"零点"。
21
+ * - 点击该行会把对应栈撤销到 cursor === 0(即没有任何已应用步骤),等同于回到所有修改之前。
22
+ * - 当对应栈本身已处于 cursor === 0 时(isCurrent=true),用户已在初始状态,点击不再触发动作。
23
+ * - 当上层传入 `marker`(设置 root 时为该页生成的「未修改的初始状态」标记)时,
24
+ * 用标记的文案与时间渲染本行;标记不进入撤销/重做栈,仅作为该页基线展示。
25
+ *
26
+ * 该行不是真实 step,仅作为 UI 入口;上层负责把"点击"翻译为 `service.goto*(0)`。
27
+ */
28
+ import { computed } from 'vue';
29
+
30
+ import type { StepValue } from '@editor/type';
31
+
32
+ import { formatHistoryFullTime, formatHistoryTime } from './composables';
33
+
34
+ defineOptions({
35
+ name: 'MEditorHistoryListInitialRow',
36
+ });
37
+
38
+ const props = withDefaults(
39
+ defineProps<{
40
+ /** 当前对应栈是否已经处于初始状态 (cursor === 0)。true 时用蓝条高亮并禁用点击。 */
41
+ isCurrent: boolean;
42
+ gotoEnabled?: boolean;
43
+ /** 该页面的「加载/初始」基线记录(设置 root 时生成的 `opType: 'initial'` StepValue);提供时用其文案与时间展示。 */
44
+ marker?: StepValue;
45
+ }>(),
46
+ {
47
+ gotoEnabled: true,
48
+ marker: undefined,
49
+ },
50
+ );
51
+
52
+ const desc = computed(() => props.marker?.historyDescription || '未修改的初始状态');
53
+ /** 基线(初始状态)是否为最近一次保存点:仅页面栈的 `initial` 基线 step 会被 markSaved 标记。 */
54
+ const saved = computed(() => Boolean(props.marker?.saved));
55
+ const time = computed(() => formatHistoryTime(props.marker?.timestamp));
56
+ const timeTitle = computed(() => formatHistoryFullTime(props.marker?.timestamp));
57
+ const rowTitle = computed(() => {
58
+ const base = props.marker?.historyDescription || '未修改的初始状态';
59
+ return props.isCurrent ? `当前已回到${base}` : `点击回到${base}`;
60
+ });
61
+
62
+ const emit = defineEmits<{
63
+ /** 点击非当前的初始项时触发,由上层调用对应 service 的 goto 把 cursor 移到 0。 */
64
+ (_e: 'goto-initial'): void;
65
+ }>();
66
+
67
+ const onClick = () => {
68
+ if (props.isCurrent) return;
69
+ emit('goto-initial');
70
+ };
71
+ </script>
@@ -0,0 +1,118 @@
1
+ <template>
2
+ <div v-if="!list.length && !marker" class="m-editor-history-list-empty">暂无操作记录</div>
3
+ <template v-else>
4
+ <div v-if="list.length" class="m-editor-history-list-toolbar">
5
+ <span class="m-editor-history-list-clear" title="清空当前页面的历史记录" @click="$emit('clear')">清空</span>
6
+ </div>
7
+ <TMagicScrollbar max-height="360px">
8
+ <ul class="m-editor-history-list-ul">
9
+ <GroupRow
10
+ v-for="group in list"
11
+ :key="rowKey(group)"
12
+ :group="toRow(group)"
13
+ :expanded="isHistoryGroupExpanded(expanded, rowKey(group))"
14
+ :select-enabled="true"
15
+ @toggle="(key: string) => $emit('toggle', key)"
16
+ @goto="(index: number) => $emit('goto', index)"
17
+ @diff-step="(index: number) => $emit('diff-step', index)"
18
+ @revert-step="(index: number) => $emit('revert-step', index)"
19
+ @select="(index: number) => $emit('select', index)"
20
+ />
21
+ <!--
22
+ 初始状态项:永远位于列表底部(页面 tab 倒序展示,最底部=最早),
23
+ 作为"未修改"零点。当所有 group 都未 applied 时它即为当前位置。
24
+ 设置 root 时生成的「未修改的初始状态」标记(marker)会作为该行的文案与时间来源。
25
+ -->
26
+ <InitialRow :is-current="isInitial" :marker="marker" @goto-initial="$emit('goto-initial')" />
27
+ </ul>
28
+ </TMagicScrollbar>
29
+ </template>
30
+ </template>
31
+
32
+ <script lang="ts" setup>
33
+ import { computed } from 'vue';
34
+
35
+ import { TMagicScrollbar } from '@tmagic/design';
36
+
37
+ import type { HistoryGroup, HistoryRowDescriptor, StepValue } from '@editor/type';
38
+
39
+ import type { HistoryRowGroup } from './composables';
40
+ import {
41
+ describePageGroup,
42
+ describePageStep,
43
+ isHistoryGroupExpanded,
44
+ isPageStepRevertable,
45
+ toRowGroup,
46
+ } from './composables';
47
+ import GroupRow from './GroupRow.vue';
48
+ import InitialRow from './InitialRow.vue';
49
+
50
+ defineOptions({
51
+ name: 'MEditorHistoryListPageTab',
52
+ });
53
+
54
+ const props = defineProps<{
55
+ /** 当前活动页面的历史分组列表,已按时间倒序排好(最新一组在最前)。空数组时显示空态。 */
56
+ list: HistoryGroup<StepValue>[];
57
+ /**
58
+ * 共享的折叠状态表(key -> 是否展开,缺省或 true 为展开、false 为收起),由顶层 panel 统一维护。
59
+ * 本 tab 使用 `pg-${组内首步 index}` 作为 key——以稳定的 step 索引而非展示位置标识分组,
60
+ * 这样历史数据更新(新增 / 撤销重做导致列表顺序变化)后,已展开的分组状态仍能正确保持。
61
+ */
62
+ expanded: Record<string, boolean>;
63
+ /**
64
+ * 当前活动页的「加载/初始」基线记录(设置 root 时生成的 `opType: 'initial'` StepValue)。
65
+ * 提供时即使没有任何操作记录也会展示底部初始行,并用其文案 / 时间渲染。
66
+ */
67
+ marker?: StepValue;
68
+ }>();
69
+
70
+ defineEmits<{
71
+ /** 透传 GroupRow 的 toggle 事件给上层 panel,由其更新 expanded。 */
72
+ (_e: 'toggle', _key: string): void;
73
+ /** 透传 GroupRow 的 goto 事件,携带目标 step 在栈中的索引。 */
74
+ (_e: 'goto', _index: number): void;
75
+ /** 用户点击初始项希望回到未修改的状态(cursor=0)。 */
76
+ (_e: 'goto-initial'): void;
77
+ /** 用户点击"查看差异"按钮,携带目标 step 在栈中的索引。 */
78
+ (_e: 'diff-step', _index: number): void;
79
+ /** 用户点击"回滚"按钮,携带目标 step 在栈中的索引,类 git revert。 */
80
+ (_e: 'revert-step', _index: number): void;
81
+ /** 用户点击记录行希望选中对应节点,携带目标 step 在栈中的索引。 */
82
+ (_e: 'select', _index: number): void;
83
+ /** 用户点击"清空"按钮,请求清空当前页面的历史记录(由上层弹窗二次确认后执行)。 */
84
+ (_e: 'clear'): void;
85
+ }>();
86
+
87
+ /**
88
+ * 当前 step 是否可查看差异:
89
+ * - 仅 update 操作;
90
+ * - 单节点更新(diff.length === 1),且 oldSchema / newSchema 都存在。
91
+ * 多节点更新难以选定单一对比目标,统一不展示差异入口。
92
+ */
93
+ const isPageStepDiffable = (step: StepValue): boolean => {
94
+ if (step.opType !== 'update') return false;
95
+ const items = step.diff ?? [];
96
+ if (items.length !== 1) return false;
97
+ return Boolean(items[0]?.oldSchema && items[0]?.newSchema);
98
+ };
99
+
100
+ /** 页面历史的描述 / 可操作性判定集合,注入给统一的 `toRowGroup`。 */
101
+ const descriptor: HistoryRowDescriptor<StepValue> = {
102
+ describeGroup: describePageGroup,
103
+ describeStep: describePageStep,
104
+ isStepDiffable: isPageStepDiffable,
105
+ isStepRevertable: isPageStepRevertable,
106
+ };
107
+
108
+ const rowKey = (group: HistoryGroup<StepValue>) => `pg-${group.steps[0]?.index}`;
109
+
110
+ const toRow = (group: HistoryGroup<StepValue>): HistoryRowGroup => toRowGroup(group, rowKey(group), descriptor);
111
+
112
+ /**
113
+ * 是否处于"初始状态"——即对应页面历史栈 cursor===0:
114
+ * 当 list 中所有 group 的 applied 都为 false 时即为该状态(空列表 `every` 返回 true,
115
+ * 即仅有 marker、无任何操作记录时也视为处于初始状态)。
116
+ */
117
+ const isInitial = computed(() => props.list.every((g) => !g.applied));
118
+ </script>