@tmagic/editor 1.8.0-beta.2 → 1.8.0-beta.20

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 (306) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +43 -9
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +8 -4
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +3 -2
  4. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +22 -98
  5. package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +32 -9
  6. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +68 -17
  7. package/dist/es/components/Icon.vue_vue_type_script_setup_true_lang.js +1 -1
  8. package/dist/es/components/Resizer.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/es/components/ScrollBar.js +0 -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/components/ViewForm.js +5 -0
  18. package/dist/es/components/ViewForm.vue_vue_type_script_setup_true_lang.js +58 -0
  19. package/dist/es/editorProps.js +9 -1
  20. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +16 -5
  21. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +7 -7
  22. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +81 -44
  23. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +10 -3
  24. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +7 -19
  25. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +13 -6
  26. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +11 -8
  27. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +39 -12
  28. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +11 -20
  29. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +11 -6
  30. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +30 -14
  31. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +11 -4
  32. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +5 -2
  33. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +38 -6
  34. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +151 -153
  35. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +5 -2
  36. package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +5 -2
  37. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +8 -10
  38. package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +5 -2
  39. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +28 -6
  40. package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +1 -1
  41. package/dist/es/fields/StyleSetter/icons/align-items/Center.js +31 -0
  42. package/dist/es/fields/StyleSetter/icons/align-items/FlexEnd.js +31 -0
  43. package/dist/es/fields/StyleSetter/icons/align-items/FlexStart.js +31 -0
  44. package/dist/es/fields/StyleSetter/icons/align-items/SpaceAround.js +50 -0
  45. package/dist/es/fields/StyleSetter/icons/align-items/SpaceBetween.js +50 -0
  46. package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +7 -7
  47. package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +6 -3
  48. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +29 -6
  49. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +32 -6
  50. package/dist/es/fields/StyleSetter/icons/display/Block.js +1 -1
  51. package/dist/es/fields/StyleSetter/icons/display/Flex.js +1 -1
  52. package/dist/es/fields/StyleSetter/icons/display/Inline.js +1 -1
  53. package/dist/es/fields/StyleSetter/icons/display/InlineBlock.js +1 -1
  54. package/dist/es/fields/StyleSetter/icons/display/None.js +1 -1
  55. package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +11 -3
  56. package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +11 -3
  57. package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +11 -3
  58. package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +11 -3
  59. package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +17 -3
  60. package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +19 -3
  61. package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +19 -3
  62. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +37 -3
  63. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +33 -3
  64. package/dist/es/fields/StyleSetter/icons/text-align/Center.js +1 -1
  65. package/dist/es/fields/StyleSetter/icons/text-align/Left.js +1 -1
  66. package/dist/es/fields/StyleSetter/icons/text-align/Right.js +1 -1
  67. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +56 -30
  68. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +7 -4
  69. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +35 -26
  70. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +76 -48
  71. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +29 -23
  72. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +7 -4
  73. package/dist/es/fields/UISelect.js +0 -1
  74. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +5 -2
  75. package/dist/es/hooks/use-code-block-edit.js +6 -3
  76. package/dist/es/hooks/use-compare-form.js +119 -0
  77. package/dist/es/hooks/use-data-source-edit.js +5 -2
  78. package/dist/es/hooks/use-stage.js +9 -4
  79. package/dist/es/icons/CenterIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  80. package/dist/es/icons/CodeIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  81. package/dist/es/icons/DatasourceIcon.js +17 -0
  82. package/dist/es/icons/FolderMinusIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  83. package/dist/es/index.js +24 -10
  84. package/dist/es/initService.js +19 -13
  85. package/dist/es/layouts/AddPageBox.vue_vue_type_script_setup_true_lang.js +1 -1
  86. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +24 -12
  87. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +25 -18
  88. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +4 -4
  89. package/dist/es/layouts/NavMenuColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  90. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +25 -38
  91. package/dist/es/layouts/history-list/BucketTab.js +5 -0
  92. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +62 -0
  93. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +177 -88
  94. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +139 -37
  95. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +164 -107
  96. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +44 -9
  97. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +49 -53
  98. package/dist/es/layouts/history-list/composables.js +138 -108
  99. package/dist/es/layouts/history-list/useHistoryList.js +56 -0
  100. package/dist/es/layouts/history-list/useHistoryRevert.js +317 -0
  101. package/dist/es/layouts/page-bar/AddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  102. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +5 -3
  103. package/dist/es/layouts/page-bar/PageBarScrollContainer.vue_vue_type_script_setup_true_lang.js +1 -1
  104. package/dist/es/layouts/page-bar/PageList.vue_vue_type_script_setup_true_lang.js +1 -1
  105. package/dist/es/layouts/page-bar/Search.vue_vue_type_script_setup_true_lang.js +2 -2
  106. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +50 -7
  107. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +33 -13
  108. package/dist/es/layouts/props-panel/use-style-panel.js +3 -2
  109. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +3 -3
  110. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +26 -38
  111. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +4 -4
  112. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +4 -4
  113. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +19 -4
  114. package/dist/es/layouts/sidebar/data-source/DataSourceAddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  115. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +23 -6
  116. package/dist/es/layouts/sidebar/data-source/DataSourceList.vue_vue_type_script_setup_true_lang.js +1 -1
  117. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +27 -5
  118. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +19 -4
  119. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +8 -8
  120. package/dist/es/layouts/sidebar/layer/LayerNodeContent.js +5 -0
  121. package/dist/es/layouts/sidebar/layer/LayerNodeContent.vue_vue_type_script_setup_true_lang.js +46 -0
  122. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +2 -2
  123. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +3 -2
  124. package/dist/es/layouts/sidebar/layer/use-click.js +3 -3
  125. package/dist/es/layouts/sidebar/layer/use-node-status.js +2 -2
  126. package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +1 -1
  127. package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +1 -1
  128. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +9 -3
  129. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +2 -2
  130. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -1
  131. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +16 -16
  132. package/dist/es/plugin.js +5 -2
  133. package/dist/es/services/codeBlock.js +137 -56
  134. package/dist/es/services/dataSource.js +122 -52
  135. package/dist/es/services/dep.js +162 -41
  136. package/dist/es/services/editor.js +613 -179
  137. package/dist/es/services/history.js +350 -386
  138. package/dist/es/services/keybinding.js +6 -6
  139. package/dist/es/services/props.js +3 -2
  140. package/dist/es/services/storage.js +2 -2
  141. package/dist/es/services/ui.js +10 -2
  142. package/dist/es/style.css +549 -113
  143. package/dist/es/utils/config.js +4 -1
  144. package/dist/es/utils/const.js +2 -2
  145. package/dist/es/utils/content-menu.js +19 -11
  146. package/dist/es/utils/data-source/index.js +34 -11
  147. package/dist/es/utils/dep/collect-worker-client.js +114 -0
  148. package/dist/es/utils/dep/idle-task.js +47 -22
  149. package/dist/es/utils/dep/worker.js +1 -1
  150. package/dist/es/utils/editor.js +103 -49
  151. package/dist/es/utils/event.js +123 -0
  152. package/dist/es/utils/history.js +220 -0
  153. package/dist/es/utils/indexed-db.js +86 -0
  154. package/dist/es/utils/monaco-editor.js +22 -6
  155. package/dist/es/utils/operator.js +2 -2
  156. package/dist/es/utils/props.js +114 -33
  157. package/dist/es/utils/type-match-rules.js +367 -0
  158. package/dist/es/utils/undo-redo.js +60 -1
  159. package/dist/style.css +549 -113
  160. package/dist/themes/magic-admin.css +3028 -0
  161. package/dist/tmagic-editor.umd.cjs +14273 -10029
  162. package/package.json +8 -8
  163. package/src/Editor.vue +43 -2
  164. package/src/components/CodeBlockEditor.vue +3 -0
  165. package/src/components/CompareForm.vue +18 -146
  166. package/src/components/ContentMenu.vue +44 -8
  167. package/src/components/FloatingBox.vue +84 -9
  168. package/src/components/ScrollBar.vue +0 -67
  169. package/src/components/ToolButton.vue +2 -2
  170. package/src/components/ViewForm.vue +55 -0
  171. package/src/editorProps.ts +30 -0
  172. package/src/fields/Code.vue +10 -2
  173. package/src/fields/CodeLink.vue +2 -5
  174. package/src/fields/CodeSelect.vue +54 -40
  175. package/src/fields/CodeSelectCol.vue +8 -1
  176. package/src/fields/CondOpSelect.vue +2 -24
  177. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +16 -2
  178. package/src/fields/DataSourceFieldSelect/Index.vue +14 -5
  179. package/src/fields/DataSourceFields.vue +42 -8
  180. package/src/fields/DataSourceInput.vue +10 -28
  181. package/src/fields/DataSourceMethodSelect.vue +9 -4
  182. package/src/fields/DataSourceMethods.vue +42 -21
  183. package/src/fields/DataSourceMocks.vue +3 -0
  184. package/src/fields/DisplayConds.vue +18 -1
  185. package/src/fields/EventSelect.vue +145 -161
  186. package/src/fields/StyleSetter/Index.vue +3 -12
  187. package/src/fields/StyleSetter/components/Border.vue +15 -6
  188. package/src/fields/StyleSetter/icons/align-items/Center.vue +19 -0
  189. package/src/fields/StyleSetter/icons/align-items/FlexEnd.vue +19 -0
  190. package/src/fields/StyleSetter/icons/align-items/FlexStart.vue +19 -0
  191. package/src/fields/StyleSetter/icons/align-items/SpaceAround.vue +34 -0
  192. package/src/fields/StyleSetter/icons/align-items/SpaceBetween.vue +34 -0
  193. package/src/fields/StyleSetter/icons/align-items/index.ts +5 -0
  194. package/src/fields/StyleSetter/icons/background-repeat/NoRepeat.vue +20 -4
  195. package/src/fields/StyleSetter/icons/background-repeat/Repeat.vue +10 -28
  196. package/src/fields/StyleSetter/icons/background-repeat/RepeatX.vue +4 -5
  197. package/src/fields/StyleSetter/icons/background-repeat/RepeatY.vue +4 -5
  198. package/src/fields/StyleSetter/icons/flex-direction/Column.vue +4 -3
  199. package/src/fields/StyleSetter/icons/flex-direction/ColumnReverse.vue +4 -3
  200. package/src/fields/StyleSetter/icons/flex-direction/Row.vue +4 -3
  201. package/src/fields/StyleSetter/icons/flex-direction/RowReverse.vue +4 -3
  202. package/src/fields/StyleSetter/icons/justify-content/Center.vue +5 -2
  203. package/src/fields/StyleSetter/icons/justify-content/FlexEnd.vue +15 -2
  204. package/src/fields/StyleSetter/icons/justify-content/FlexStart.vue +15 -2
  205. package/src/fields/StyleSetter/icons/justify-content/SpaceAround.vue +28 -3
  206. package/src/fields/StyleSetter/icons/justify-content/SpaceBetween.vue +28 -2
  207. package/src/fields/StyleSetter/pro/Background.vue +99 -55
  208. package/src/fields/StyleSetter/pro/Border.vue +9 -11
  209. package/src/fields/StyleSetter/pro/Font.vue +66 -64
  210. package/src/fields/StyleSetter/pro/Layout.vue +211 -139
  211. package/src/fields/StyleSetter/pro/Position.vue +69 -67
  212. package/src/fields/StyleSetter/pro/Transform.vue +23 -25
  213. package/src/fields/UISelect.vue +0 -12
  214. package/src/hooks/index.ts +1 -0
  215. package/src/hooks/use-code-block-edit.ts +4 -3
  216. package/src/hooks/use-compare-form.ts +187 -0
  217. package/src/hooks/use-data-source-edit.ts +2 -2
  218. package/src/hooks/use-stage.ts +5 -3
  219. package/src/icons/DatasourceIcon.vue +7 -0
  220. package/src/index.ts +12 -0
  221. package/src/initService.ts +26 -16
  222. package/src/layouts/CodeEditor.vue +34 -11
  223. package/src/layouts/Framework.vue +15 -8
  224. package/src/layouts/NavMenu.vue +3 -3
  225. package/src/layouts/history-list/Bucket.vue +33 -41
  226. package/src/layouts/history-list/BucketTab.vue +72 -0
  227. package/src/layouts/history-list/GroupRow.vue +190 -93
  228. package/src/layouts/history-list/HistoryDiffDialog.vue +169 -90
  229. package/src/layouts/history-list/HistoryListPanel.vue +191 -95
  230. package/src/layouts/history-list/InitialRow.vue +38 -8
  231. package/src/layouts/history-list/PageTab.vue +67 -43
  232. package/src/layouts/history-list/composables.ts +239 -125
  233. package/src/layouts/history-list/useHistoryList.ts +60 -0
  234. package/src/layouts/history-list/useHistoryRevert.ts +425 -0
  235. package/src/layouts/page-bar/PageBar.vue +4 -2
  236. package/src/layouts/props-panel/FormPanel.vue +72 -6
  237. package/src/layouts/props-panel/PropsPanel.vue +62 -18
  238. package/src/layouts/props-panel/use-style-panel.ts +4 -3
  239. package/src/layouts/sidebar/ComponentListPanel.vue +10 -6
  240. package/src/layouts/sidebar/Sidebar.vue +13 -6
  241. package/src/layouts/sidebar/code-block/CodeBlockList.vue +5 -5
  242. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +5 -2
  243. package/src/layouts/sidebar/code-block/useContentMenu.ts +14 -2
  244. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +34 -2
  245. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +29 -4
  246. package/src/layouts/sidebar/data-source/useContentMenu.ts +14 -2
  247. package/src/layouts/sidebar/layer/LayerMenu.vue +21 -13
  248. package/src/layouts/sidebar/layer/LayerNodeContent.vue +57 -0
  249. package/src/layouts/sidebar/layer/LayerNodeTool.vue +7 -4
  250. package/src/layouts/sidebar/layer/LayerPanel.vue +4 -1
  251. package/src/layouts/sidebar/layer/use-click.ts +3 -3
  252. package/src/layouts/sidebar/layer/use-node-status.ts +2 -2
  253. package/src/layouts/workspace/viewer/NodeListMenu.vue +4 -1
  254. package/src/layouts/workspace/viewer/Stage.vue +1 -1
  255. package/src/layouts/workspace/viewer/ViewerMenu.vue +15 -15
  256. package/src/plugin.ts +6 -3
  257. package/src/services/codeBlock.ts +152 -47
  258. package/src/services/dataSource.ts +120 -41
  259. package/src/services/dep.ts +292 -47
  260. package/src/services/editor.ts +820 -224
  261. package/src/services/history.ts +449 -412
  262. package/src/services/keybinding.ts +6 -6
  263. package/src/services/ui.ts +33 -5
  264. package/src/theme/code-block.scss +37 -0
  265. package/src/theme/common/var.scss +1 -1
  266. package/src/theme/component-list-panel.scss +2 -2
  267. package/src/theme/data-source-field.scss +4 -0
  268. package/src/theme/display-conds.scss +11 -0
  269. package/src/theme/event.scss +70 -1
  270. package/src/theme/floating-box.scss +9 -1
  271. package/src/theme/framework.scss +4 -1
  272. package/src/theme/history-list-panel.scss +157 -23
  273. package/src/theme/layer-node-content.scss +14 -0
  274. package/src/theme/nav-menu.scss +5 -1
  275. package/src/theme/page-bar.scss +0 -4
  276. package/src/theme/props-panel.scss +5 -4
  277. package/src/theme/scroll-bar.scss +64 -0
  278. package/src/theme/style-setter/border.scss +17 -5
  279. package/src/theme/theme.scss +4 -0
  280. package/src/theme/themes/magic-admin/index.scss +214 -0
  281. package/src/theme/ui-select.scss +9 -0
  282. package/src/type.ts +668 -124
  283. package/src/utils/config.ts +9 -1
  284. package/src/utils/const.ts +3 -2
  285. package/src/utils/content-menu.ts +20 -11
  286. package/src/utils/data-source/index.ts +32 -12
  287. package/src/utils/dep/collect-worker-client.ts +149 -0
  288. package/src/utils/dep/idle-task.ts +72 -35
  289. package/src/utils/dep/worker.ts +113 -9
  290. package/src/utils/editor.ts +166 -60
  291. package/src/utils/event.ts +224 -0
  292. package/src/utils/history.ts +286 -0
  293. package/src/utils/index.ts +4 -0
  294. package/src/utils/indexed-db.ts +122 -0
  295. package/src/utils/monaco-editor.ts +27 -0
  296. package/src/utils/operator.ts +2 -2
  297. package/src/utils/props.ts +114 -11
  298. package/src/utils/type-match-rules.ts +684 -0
  299. package/src/utils/undo-redo.ts +88 -0
  300. package/types/index.d.ts +1979 -515
  301. package/dist/es/layouts/history-list/CodeBlockTab.js +0 -5
  302. package/dist/es/layouts/history-list/CodeBlockTab.vue_vue_type_script_setup_true_lang.js +0 -62
  303. package/dist/es/layouts/history-list/DataSourceTab.js +0 -5
  304. package/dist/es/layouts/history-list/DataSourceTab.vue_vue_type_script_setup_true_lang.js +0 -62
  305. package/src/layouts/history-list/CodeBlockTab.vue +0 -61
  306. package/src/layouts/history-list/DataSourceTab.vue +0 -61
@@ -0,0 +1,425 @@
1
+ import { createApp, getCurrentInstance } from 'vue';
2
+
3
+ import { tMagicMessage } from '@tmagic/design';
4
+
5
+ import type {
6
+ ConfirmAndRevertOptions,
7
+ CustomDiffFormOptions,
8
+ DiffDialogPayload,
9
+ Services,
10
+ UseHistoryRevertOptions,
11
+ } from '@editor/type';
12
+
13
+ import { confirmHistoryAction } from './composables';
14
+
15
+ /**
16
+ * 三类历史(页面 / 数据源 / 代码块)差异弹窗入参的构造差异,收敛为一份配置:
17
+ * 仅「分组来源、当前值读取、类型 / 展示名提取」不同,定位 step、校验前后值、组装 payload 的流程共用。
18
+ */
19
+ interface DiffPayloadSource {
20
+ /** 表单类别:节点 / 数据源 / 代码块。 */
21
+ category: DiffDialogPayload['category'];
22
+ /** 该类别按时间正序的历史分组列表(含已撤销)。 */
23
+ groups: () => { id?: string | number; steps: { index: number; step: { diff?: any[] } }[] }[];
24
+ /** 读取目标当前实际值,用于「与当前对比」;不存在时返回空即禁用对比。 */
25
+ getCurrent: (_id: string | number) => Record<string, any> | null | undefined;
26
+ /** 由新/旧快照提取展示名(含各自的兜底,如节点回退 type、数据源 / 代码块回退 id)。 */
27
+ resolveLabel: (_newSchema: Record<string, any>, _oldSchema: Record<string, any>, _id: string | number) => string;
28
+ /** 由新/旧快照提取类型;代码块无 type 字段则不传。 */
29
+ resolveType?: (_newSchema: Record<string, any>, _oldSchema: Record<string, any>) => string;
30
+ }
31
+
32
+ /**
33
+ * 构造差异弹窗入参:仅 update(前后值都存在)可对比。
34
+ * - 页面(无 id):在全部分组中按 index 定位 step,目标 id 取自快照;
35
+ * - 数据源 / 代码块(带 id):先匹配分组 id 再按 index 定位。
36
+ * 无可对比内容(多节点 / add / remove)或定位不到时返回 null。
37
+ */
38
+ const buildDiffPayload = (source: DiffPayloadSource, index: number, id?: string | number): DiffDialogPayload | null => {
39
+ for (const group of source.groups()) {
40
+ if (id !== undefined && group.id !== id) continue;
41
+ const step = group.steps.find((s) => s.index === index)?.step;
42
+ if (!step) continue;
43
+ const oldSchema = step.diff?.[0]?.oldSchema as Record<string, any> | undefined;
44
+ const newSchema = step.diff?.[0]?.newSchema as Record<string, any> | undefined;
45
+ if (!oldSchema || !newSchema) return null;
46
+ const targetId = id ?? newSchema.id ?? oldSchema.id;
47
+ const type = source.resolveType?.(newSchema, oldSchema);
48
+ return {
49
+ category: source.category,
50
+ ...(type !== undefined ? { type } : {}),
51
+ lastValue: oldSchema,
52
+ value: newSchema,
53
+ currentValue: (targetId !== undefined ? source.getCurrent(targetId) : null) || null,
54
+ targetLabel: source.resolveLabel(newSchema, oldSchema, targetId),
55
+ id: targetId,
56
+ };
57
+ }
58
+ return null;
59
+ };
60
+
61
+ interface MountedDiffDialog {
62
+ app: ReturnType<typeof createApp>;
63
+ instance: {
64
+ open: (_payload: DiffDialogPayload) => void;
65
+ confirm: (_payload: DiffDialogPayload) => Promise<boolean>;
66
+ };
67
+ /** 卸载弹窗并清理容器(延迟以等待关闭过渡播放完成,避免动画被强行打断)。 */
68
+ destroy: () => void;
69
+ }
70
+
71
+ /**
72
+ * 按需动态 import 并挂载一个游离的 HistoryDiffDialog,返回其实例与清理函数。
73
+ * 通过继承 appContext 复用全局组件 / 指令 / provide / 插件(Element Plus、@tmagic/form 字段组件等),
74
+ * 弹窗组件动态 import,避免拖累其它消费者。供「确认回滚」与「查看差异」两种交互共用。
75
+ */
76
+ const mountHistoryDiffDialog = async (
77
+ options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
78
+ CustomDiffFormOptions & {
79
+ services?: Services;
80
+ isConfirm?: boolean;
81
+ onClose?: () => void;
82
+ },
83
+ ): Promise<MountedDiffDialog> => {
84
+ const { default: historyDiffDialog } = await import('./HistoryDiffDialog.vue');
85
+
86
+ const container = document.createElement('div');
87
+ container.style.display = 'none';
88
+ document.body.appendChild(container);
89
+
90
+ const app = createApp(historyDiffDialog, {
91
+ services: options.services,
92
+ isConfirm: options.isConfirm,
93
+ extendState: options.extendState,
94
+ loadConfig: options.loadConfig,
95
+ selfDiffFieldTypes: options.selfDiffFieldTypes,
96
+ compareFormState: options.compareFormState,
97
+ width: options.width,
98
+ size: options.size ?? options.services?.uiService?.get('propsPanelSize'),
99
+ onClose: options.onClose,
100
+ });
101
+ if (options.appContext) {
102
+ Object.assign(app._context, options.appContext);
103
+ }
104
+
105
+ const instance = app.mount(container) as unknown as MountedDiffDialog['instance'];
106
+
107
+ const destroy = () => {
108
+ setTimeout(() => {
109
+ try {
110
+ app.unmount();
111
+ } catch {
112
+ // ignore
113
+ }
114
+ container.parentNode?.removeChild(container);
115
+ }, 300);
116
+ };
117
+
118
+ return { app, instance, destroy };
119
+ };
120
+
121
+ /**
122
+ * 动态挂载一个「确认回滚」模式的 HistoryDiffDialog,等待用户确认:
123
+ * - 用户点「确定回滚」resolve(true),取消 / 关闭 resolve(false);
124
+ * - 确认流程结束后自动卸载。
125
+ */
126
+ const confirmRevertWithDiffDialog = async (
127
+ payload: DiffDialogPayload,
128
+ options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
129
+ CustomDiffFormOptions & {
130
+ services?: Services;
131
+ },
132
+ ): Promise<boolean> => {
133
+ const { instance, destroy } = await mountHistoryDiffDialog({
134
+ ...options,
135
+ isConfirm: true,
136
+ });
137
+ try {
138
+ return await instance.confirm(payload);
139
+ } finally {
140
+ destroy();
141
+ }
142
+ };
143
+
144
+ /**
145
+ * 动态挂载一个「查看差异」模式(只读)的 HistoryDiffDialog 并打开:
146
+ * 弹窗保持打开直到用户关闭,关闭(close 事件)后自动卸载并清理容器。
147
+ */
148
+ const viewHistoryDiffDialog = async (
149
+ payload: DiffDialogPayload,
150
+ options: Pick<UseHistoryRevertOptions, 'appContext' | 'extendState'> &
151
+ CustomDiffFormOptions & {
152
+ services?: Services;
153
+ },
154
+ ): Promise<void> => {
155
+ // onClose 在用户关闭弹窗时才触发,此时 handle.destroy 早已赋值。
156
+ const handle: { destroy?: () => void } = {};
157
+ const { instance, destroy } = await mountHistoryDiffDialog({
158
+ ...options,
159
+ isConfirm: false,
160
+ onClose: () => handle.destroy?.(),
161
+ });
162
+ handle.destroy = destroy;
163
+ instance.open(payload);
164
+ };
165
+
166
+ /**
167
+ * 历史记录「单步回滚」与「查看差异」的完整交互流程,供历史面板与业务方共用。
168
+ *
169
+ * 单步回滚(类 git revert):
170
+ * 1. 前置校验:目标数据已被删除(update 写回目标 / 页面 remove 的原父容器缺失)时给出「回滚失败」提示并中止;
171
+ * 2. 二次确认:可差异对比的步骤(单实体 update)弹出差异确认弹窗,其余步骤弹普通二次确认框;
172
+ * 3. 用户确认后执行对应 service 的 revert(页面 / 数据源 / 代码块)。
173
+ *
174
+ * 查看差异:可差异对比的步骤动态挂载只读 HistoryDiffDialog 展示前后值差异。
175
+ *
176
+ * 上述弹窗均按需动态挂载 HistoryDiffDialog 实现,业务方无需自行挂载任何弹窗。
177
+ *
178
+ * 返回的能力分三组:
179
+ * - 内置三类(页面 / 数据源 / 代码块):`onPageRevert` / `onDataSourceRevert` / `onCodeBlockRevert` 单步回滚,
180
+ * `onPageDiff` / `onDataSourceDiff` / `onCodeBlockDiff` 查看差异,及配套的 `buildXxxDiffPayload` / `isXxxRevertTargetMissing`;
181
+ * - 业务自有历史(如管理台「模块」):`confirmAndRevert` / `viewDiff` 复用同一套交互流程,由业务方自行构造差异入参;
182
+ * 即上述内置三类本质上是它们各自预置好 payload 构造与 service.revert 的特例。
183
+ *
184
+ * 业务方可在拿到 Editor 实例暴露的 `services` 后直接 import 调用:
185
+ *
186
+ * ```ts
187
+ * import { useHistoryRevert } from '@tmagic/editor';
188
+ *
189
+ * const { onPageRevert, onPageDiff } = useHistoryRevert({}, editorRef.value); // editorRef.value 即 Editor 暴露的 services
190
+ * await onPageRevert(index); // 弹出差异 / 二次确认弹窗后回滚
191
+ * onPageDiff(index); // 弹出只读差异弹窗查看前后值差异
192
+ * ```
193
+ */
194
+ export const useHistoryRevert = (options: UseHistoryRevertOptions = {}, services?: Services) => {
195
+ // 自动捕获调用方所在组件的 appContext(在 setup 中调用时),业务方亦可显式覆盖。
196
+ const appContext = options.appContext ?? getCurrentInstance()?.appContext ?? null;
197
+ const { extendState, getPropsPanelFormState, dialogWidth } = options;
198
+
199
+ /** 目标数据已被删除、无法回滚时的统一提示。 */
200
+ const showRevertTargetMissing = () => {
201
+ tMagicMessage.error('回滚失败:该记录对应的数据已被删除');
202
+ };
203
+
204
+ /**
205
+ * 「回滚」二次确认:新增 / 删除 / 多节点更新等无法做差异对比的步骤,
206
+ * 不弹差异弹窗,改用一个普通确认框替代「确定回滚」按钮,避免点击后无任何提示直接执行。
207
+ * 用户取消时返回 false,调用方据此中止回滚。
208
+ */
209
+ const confirmRevert = (): Promise<boolean> =>
210
+ confirmHistoryAction(
211
+ '确定回滚该步骤吗?回滚会将该操作作为一条新记录反向应用(新增将被删除、删除将被还原),不影响后续历史记录。',
212
+ );
213
+
214
+ /**
215
+ * 「回滚」统一确认入口:可差异对比的步骤动态挂载 HistoryDiffDialog 走差异确认弹窗,
216
+ * 无法对比的步骤(add / remove / 多节点更新)回退到普通二次确认框。
217
+ * `extra` 供业务自有历史(如模块)透传自定义表单配置加载等渲染入参。
218
+ */
219
+ const runRevert = (payload: DiffDialogPayload | null, extra?: CustomDiffFormOptions): Promise<boolean> => {
220
+ if (payload) {
221
+ return confirmRevertWithDiffDialog(payload, {
222
+ appContext,
223
+ extendState,
224
+ services,
225
+ ...extra,
226
+ width: extra?.width ?? dialogWidth,
227
+ size: extra?.size,
228
+ });
229
+ }
230
+ return confirmRevert();
231
+ };
232
+
233
+ const buildPageDiffPayload = (index: number): DiffDialogPayload | null =>
234
+ buildDiffPayload(
235
+ {
236
+ category: 'node',
237
+ groups: () => services?.historyService.getHistoryGroups('page', services?.editorService.get('page')?.id) ?? [],
238
+ getCurrent: (id) => services?.editorService.getNodeById(id) as Record<string, any> | null,
239
+ resolveType: (n, o) => n.type || o.type || '',
240
+ resolveLabel: (n, o) => n.name || o.name || n.type || o.type || '',
241
+ },
242
+ index,
243
+ );
244
+
245
+ const buildDataSourceDiffPayload = (id: string | number, index: number): DiffDialogPayload | null =>
246
+ buildDiffPayload(
247
+ {
248
+ category: 'data-source',
249
+ groups: () => services?.historyService.getHistoryGroups('dataSource') ?? [],
250
+ getCurrent: (id) => services?.dataSourceService.getDataSourceById(`${id}`) as Record<string, any> | null,
251
+ resolveType: (n, o) => n.type || o.type || 'base',
252
+ resolveLabel: (n, o, id) => n.title || o.title || `${id}`,
253
+ },
254
+ index,
255
+ id,
256
+ );
257
+
258
+ const buildCodeBlockDiffPayload = (id: string | number, index: number): DiffDialogPayload | null =>
259
+ buildDiffPayload(
260
+ {
261
+ category: 'code-block',
262
+ groups: () => services?.historyService.getHistoryGroups('codeBlock') ?? [],
263
+ getCurrent: (id) => services?.codeBlockService.getCodeContentById(id) as Record<string, any> | null,
264
+ resolveLabel: (n, o, id) => n.name || o.name || `${id}`,
265
+ },
266
+ index,
267
+ id,
268
+ );
269
+
270
+ /**
271
+ * 回滚前置校验:若该历史步骤回滚所依赖的目标数据已被删除,则无法回滚。
272
+ * - update(把旧值写回):被修改的目标必须仍存在;
273
+ * - 页面 remove(还原被删节点):被删节点的原父容器必须仍存在,否则无处插回;
274
+ * add(回滚即删除)即使目标已不在,也已达成「删除」目的,不视为失败。
275
+ */
276
+ const isPageRevertTargetMissing = (index: number): boolean => {
277
+ const step = services?.historyService.getStepList('page', services?.editorService.get('page')?.id)[index]?.step;
278
+ if (!step) return false;
279
+ if (step.opType === 'update') {
280
+ return (step.diff ?? []).some((item) => {
281
+ const id = item.newSchema?.id ?? item.oldSchema?.id;
282
+ return id !== undefined && !services?.editorService.getNodeById(id, false);
283
+ });
284
+ }
285
+ if (step.opType === 'remove') {
286
+ return (step.diff ?? []).some(
287
+ (item) => item.parentId !== undefined && !services?.editorService.getNodeById(item.parentId, false),
288
+ );
289
+ }
290
+ return false;
291
+ };
292
+
293
+ /** 数据源 update 步骤回滚时,对应数据源必须仍存在(已删除则无处写回旧值)。 */
294
+ const isDataSourceRevertTargetMissing = (id: string | number, index: number): boolean => {
295
+ const step = services?.historyService.getStepList('dataSource', id)[index]?.step;
296
+ return Boolean(step?.opType === 'update' && !services?.dataSourceService.getDataSourceById(`${id}`));
297
+ };
298
+
299
+ /** 代码块 update 步骤回滚时,对应代码块必须仍存在(已删除则无处写回旧值)。 */
300
+ const isCodeBlockRevertTargetMissing = (id: string | number, index: number): boolean => {
301
+ const step = services?.historyService.getStepList('codeBlock', id)[index]?.step;
302
+ return Boolean(step?.opType === 'update' && !services?.codeBlockService.getCodeContentById(id));
303
+ };
304
+
305
+ const onPageRevert = (index: number) => {
306
+ if (isPageRevertTargetMissing(index)) {
307
+ showRevertTargetMissing();
308
+ return Promise.resolve(null);
309
+ }
310
+ return runRevert(buildPageDiffPayload(index), { compareFormState: getPropsPanelFormState?.() }).then((result) =>
311
+ result ? services?.editorService.revertPageStep(index) : null,
312
+ );
313
+ };
314
+
315
+ const onDataSourceRevert = (id: string | number, index: number) => {
316
+ if (isDataSourceRevertTargetMissing(id, index)) {
317
+ showRevertTargetMissing();
318
+ return Promise.resolve(null);
319
+ }
320
+ return runRevert(buildDataSourceDiffPayload(id, index)).then((result) =>
321
+ result ? services?.dataSourceService.revert(id, index) : null,
322
+ );
323
+ };
324
+
325
+ const onCodeBlockRevert = (id: string | number, index: number) => {
326
+ if (isCodeBlockRevertTargetMissing(id, index)) {
327
+ showRevertTargetMissing();
328
+ return Promise.resolve(null);
329
+ }
330
+ return runRevert(buildCodeBlockDiffPayload(id, index)).then((result) =>
331
+ result ? services?.codeBlockService.revert(id, index) : null,
332
+ );
333
+ };
334
+
335
+ /**
336
+ * 「查看差异」:可差异对比的步骤(单实体 update)动态挂载只读 HistoryDiffDialog 展示前后值差异,
337
+ * 无可对比内容(add / remove / 多节点更新)时不弹窗、静默返回。
338
+ */
339
+ const onPageDiff = (index: number): Promise<void> | void => {
340
+ const payload = buildPageDiffPayload(index);
341
+ if (payload) {
342
+ return viewHistoryDiffDialog(payload, {
343
+ appContext,
344
+ extendState,
345
+ services,
346
+ width: dialogWidth,
347
+ compareFormState: getPropsPanelFormState?.(),
348
+ });
349
+ }
350
+ };
351
+
352
+ const onDataSourceDiff = (id: string | number, index: number): Promise<void> | void => {
353
+ const payload = buildDataSourceDiffPayload(id, index);
354
+ if (payload) return viewHistoryDiffDialog(payload, { appContext, extendState, services, width: dialogWidth });
355
+ };
356
+
357
+ const onCodeBlockDiff = (id: string | number, index: number): Promise<void> | void => {
358
+ const payload = buildCodeBlockDiffPayload(id, index);
359
+ if (payload) return viewHistoryDiffDialog(payload, { appContext, extendState, services, width: dialogWidth });
360
+ };
361
+
362
+ /**
363
+ * 业务自有历史(如管理台「模块」)的「单步回滚」统一入口:
364
+ * 复用与页面/数据源/代码块一致的「目标校验 → 差异/二次确认弹窗 → 反向回滚」流程,
365
+ * 内置三类只是它的特例(各自预置了 buildXxxDiffPayload + service.revert)。
366
+ *
367
+ * 用户取消或前置校验未过时返回 null,确认并执行回滚后返回 `revert()` 的结果。
368
+ *
369
+ * ```ts
370
+ * await confirmAndRevert({
371
+ * diffPayload: oldSchema && newSchema ? { category: 'module', lastValue: oldSchema, value: newSchema, ... } : null,
372
+ * loadConfig: (ctx) => loadModFormConfig(modTypeId, values),
373
+ * selfDiffFieldTypes: ['mod-cond'],
374
+ * revert: () => moduleHistoryStore.revert(id, index),
375
+ * });
376
+ * ```
377
+ */
378
+ const confirmAndRevert = async <T = unknown>(revertOptions: ConfirmAndRevertOptions<T>): Promise<T | null> => {
379
+ if (revertOptions.isTargetMissing?.()) {
380
+ showRevertTargetMissing();
381
+ return null;
382
+ }
383
+ const confirmed = await runRevert(revertOptions.diffPayload ?? null, {
384
+ loadConfig: revertOptions.loadConfig,
385
+ selfDiffFieldTypes: revertOptions.selfDiffFieldTypes,
386
+ width: revertOptions.width,
387
+ size: revertOptions.size,
388
+ });
389
+ if (!confirmed) return null;
390
+ return await revertOptions.revert();
391
+ };
392
+
393
+ /**
394
+ * 业务自有历史的「查看差异」统一入口:传入自行构造的差异入参即弹出只读差异弹窗,
395
+ * 可透传 `loadConfig` / `selfDiffFieldTypes`。payload 为 null(不可对比)时静默返回。
396
+ */
397
+ const viewDiff = (payload: DiffDialogPayload | null, extra?: CustomDiffFormOptions): Promise<void> | void => {
398
+ if (payload)
399
+ return viewHistoryDiffDialog(payload, {
400
+ appContext,
401
+ extendState,
402
+ services,
403
+ ...extra,
404
+ width: extra?.width ?? dialogWidth,
405
+ size: extra?.size,
406
+ });
407
+ };
408
+
409
+ return {
410
+ onPageRevert,
411
+ onDataSourceRevert,
412
+ onCodeBlockRevert,
413
+ onPageDiff,
414
+ onDataSourceDiff,
415
+ onCodeBlockDiff,
416
+ buildPageDiffPayload,
417
+ buildDataSourceDiffPayload,
418
+ buildCodeBlockDiffPayload,
419
+ isPageRevertTargetMissing,
420
+ isDataSourceRevertTargetMissing,
421
+ isCodeBlockRevertTargetMissing,
422
+ confirmAndRevert,
423
+ viewDiff,
424
+ };
425
+ };
@@ -51,6 +51,7 @@
51
51
  type: 'button',
52
52
  text: '删除',
53
53
  icon: Delete,
54
+ buttonProps: { type: 'danger' },
54
55
  handler: () => remove(item),
55
56
  }"
56
57
  ></ToolButton>
@@ -72,7 +73,7 @@ import { computed, ref, useTemplateRef, watch } from 'vue';
72
73
  import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
73
74
 
74
75
  import { type Id, type MPage, type MPageFragment, NodeType } from '@tmagic/core';
75
- import { TMagicIcon, TMagicPopover } from '@tmagic/design';
76
+ import { TMagicIcon, tMagicMessageBox, TMagicPopover } from '@tmagic/design';
76
77
 
77
78
  import ToolButton from '@editor/components/ToolButton.vue';
78
79
  import { useServices } from '@editor/hooks/use-services';
@@ -140,7 +141,8 @@ const copy = (node: MPage | MPageFragment) => {
140
141
  });
141
142
  };
142
143
 
143
- const remove = (node: MPage | MPageFragment) => {
144
+ const remove = async (node: MPage | MPageFragment) => {
145
+ await tMagicMessageBox.confirm('确定删除该页面吗?');
144
146
  editorService.remove(node);
145
147
  };
146
148
 
@@ -12,7 +12,9 @@
12
12
  :size="propsPanelSize"
13
13
  :init-values="values"
14
14
  :config="config"
15
+ :type-match-valid="true"
15
16
  :extend-state="extendState"
17
+ :validate-on-init="true"
16
18
  @change="submit"
17
19
  @error="errorHandler"
18
20
  ></MForm>
@@ -46,11 +48,13 @@
46
48
  import { computed, getCurrentInstance, inject, onMounted, onUnmounted, ref, useTemplateRef, watchEffect } from 'vue';
47
49
  import { Document as DocumentIcon } from '@element-plus/icons-vue';
48
50
 
49
- import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
51
+ import { TMagicButton, tMagicMessage, TMagicScrollbar } from '@tmagic/design';
50
52
  import type { ContainerChangeEventData, FormConfig, FormState, FormValue } from '@tmagic/form';
51
- import { MForm } from '@tmagic/form';
53
+ import { MForm, validateForm } from '@tmagic/form';
54
+ import { filterXSS } from '@tmagic/utils';
52
55
 
53
56
  import MIcon from '@editor/components/Icon.vue';
57
+ import { ENABLE_PROPS_FORM_VALIDATE } from '@editor/editorProps';
54
58
  import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
55
59
  import { useServices } from '@editor/hooks/use-services';
56
60
 
@@ -75,13 +79,15 @@ const props = defineProps<{
75
79
  }>();
76
80
 
77
81
  const emit = defineEmits<{
78
- submit: [values: any, eventData?: ContainerChangeEventData];
82
+ submit: [values: any, eventData?: ContainerChangeEventData, error?: any];
79
83
  'submit-error': [e: any];
80
84
  'form-error': [e: any];
81
85
  mounted: [internalInstance: any];
82
86
  unmounted: [];
83
87
  }>();
84
88
 
89
+ const enablePropsFormValidate = inject(ENABLE_PROPS_FORM_VALIDATE, false);
90
+
85
91
  const services = useServices();
86
92
  const { editorService, uiService } = services;
87
93
 
@@ -113,9 +119,16 @@ onUnmounted(() => {
113
119
  const submit = async (v: FormValue, eventData: ContainerChangeEventData) => {
114
120
  try {
115
121
  const values = await configFormRef.value?.submitForm();
122
+ // 校验成功:正常更新节点(第三个参数 error 为空,表示清除该来源的错误记录)
116
123
  emit('submit', values, eventData);
117
124
  } catch (e: any) {
118
- emit('submit-error', e);
125
+ if (enablePropsFormValidate) {
126
+ // 启用校验联动:校验失败时仍以当前表单值更新节点,并把错误信息一并抛给上层记录
127
+ emit('submit', v, eventData, e);
128
+ } else {
129
+ // 未启用:保持原行为,校验失败丢弃本次改动
130
+ emit('submit-error', e);
131
+ }
119
132
  }
120
133
  };
121
134
 
@@ -123,8 +136,61 @@ const errorHandler = (e: any) => {
123
136
  emit('form-error', e);
124
137
  };
125
138
 
126
- const saveCode = (values: any) => {
127
- emit('submit', props.codeValueKey ? { [props.codeValueKey]: values } : values);
139
+ /**
140
+ * 将校验错误文案安全地转为用于弹窗展示的 HTML。
141
+ *
142
+ * 文案形如 `字段 -> 主文案\n\n建议`,多条以结构性 `<br>` 拼接。为在保留换行排版的同时
143
+ * 避免 `${value}`/字段名中可能夹带的 HTML 造成 XSS:先按结构性 `<br>` 拆分,对每段做 HTML
144
+ * 转义后再把段内换行 `\n` 替换为 `<br>`,最后用 `<br>` 拼回。
145
+ */
146
+ const formatValidateErrorHtml = (error: string): string =>
147
+ error
148
+ .split(/<br\s*\/?>/i)
149
+ .map((segment) => filterXSS(segment).replaceAll('\n', '<br>'))
150
+ .join('<br>');
151
+
152
+ const saveCode = async (values: any) => {
153
+ const newValues = props.codeValueKey ? { [props.codeValueKey]: values } : values;
154
+
155
+ if (!enablePropsFormValidate) {
156
+ // 未启用校验联动:保持原行为,直接提交源码保存的值
157
+ emit('submit', newValues);
158
+ return;
159
+ }
160
+
161
+ // 启用校验联动:源码编辑器保存的值未经过表单交互,这里另建一个独立的 MForm 实例对最新配置
162
+ // 做一次静默校验(不复用、也不污染页面上正在展示的表单),并将校验结果(错误信息)随提交
163
+ // 一并抛给上层记录,使源码保存的错误状态与表单编辑保持一致。
164
+ try {
165
+ const error = await validateForm({
166
+ config: props.config,
167
+ typeMatchValid: true,
168
+ initValues: newValues,
169
+ // 将当前组件实例的 provides(含 Editor 顶层的 services / codeOptions 等组件级 provide)
170
+ // 合入 appContext,使临时 MForm 中的编辑器字段组件(DataSourceInput 等)能正常 inject
171
+ appContext: internalInstance?.appContext ? { ...internalInstance?.appContext, provides: { services } } : null,
172
+ extendState: (state) => {
173
+ if (configFormRef.value?.formState) {
174
+ return { ...(configFormRef.value?.formState || {}) };
175
+ }
176
+ return props.extendState?.(state) || {};
177
+ },
178
+ });
179
+
180
+ if (error) {
181
+ tMagicMessage({
182
+ type: 'error',
183
+ message: formatValidateErrorHtml(error),
184
+ dangerouslyUseHTMLString: true,
185
+ });
186
+ }
187
+
188
+ emit('submit', newValues, undefined, error ? new Error(error) : undefined);
189
+ } catch (e: any) {
190
+ console.log('validateForm error', e);
191
+ // 静默校验本身出现异常(如初始化超时)时,退回到不携带错误信息的提交,避免阻塞源码保存
192
+ emit('submit', newValues);
193
+ }
128
194
  };
129
195
 
130
196
  defineExpose({ configForm: configFormRef, submit });