@epxing/designer 5.3.0-beta.2 → 5.3.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 (33) hide show
  1. package/API.md +1 -1
  2. package/README.md +46 -46
  3. package/dist/src/page-designer/PageDesigner.js +11 -0
  4. package/dist/src/page-designer/designer.js +21 -0
  5. package/dist/src/page-designer/previews/add-form/Preview.js +1 -1
  6. package/dist/src/page-designer/previews/add-form/Preview.vue.js +19 -8
  7. package/dist/src/page-designer/setting-panel/Setting.js +46 -10
  8. package/dist/src/visual/config-page/ComponentLibraryDialog.js +8 -2
  9. package/dist/src/visual/config-page/DashboardMobile.js +1 -1
  10. package/dist/style.css +116 -112
  11. package/package.json +8 -8
  12. package/src/index.js +27 -27
  13. package/src/page-designer/PageDesigner.vue +11 -0
  14. package/src/page-designer/designer.js +1322 -1299
  15. package/src/page-designer/previews/add-form/Preview.vue +43 -44
  16. package/src/page-designer/setting-panel/Setting.vue +24 -2
  17. package/src/page-designer/widget-preview/Mixin.js +285 -285
  18. package/src/page-designer/widget-preview/Preview.vue +322 -322
  19. package/src/page-designer/widget-preview/WidgetWrapper.vue +197 -197
  20. package/src/page-designer/widget-preview/directive.js +258 -258
  21. package/src/visual/CategoryList.vue +563 -563
  22. package/src/visual/ComponentList.vue +280 -280
  23. package/src/visual/CreatePage.vue +389 -389
  24. package/src/visual/EntityCardList.vue +449 -449
  25. package/src/visual/EntityList.vue +838 -838
  26. package/src/visual/config-page/ComponentLibraryDialog.vue +7 -1
  27. package/src/visual/config-page/DashboardMobile.vue +934 -934
  28. package/src/visual/widgets/ProcessFormItemVisual.vue +58 -58
  29. package/types/index.d.ts +22 -22
  30. package/types/page-designer.d.ts +3 -3
  31. package/types/visual-component.d.ts +1 -1
  32. package/types/visual-entity-list.d.ts +1 -1
  33. package/types/visual-entity-sub-list.d.ts +1 -1
@@ -1,258 +1,258 @@
1
- // import util from '@epxing/core/src/runtime-utils/util'
2
-
3
- // function appendCss (id, css) {
4
- // const formatCss = css.replace(/:current/g, '.css_' + id).replace(/url\s*\(([\w+-/.\s]+)\)/g, (str, $1) => {
5
- // return `url(${util.getFileUrl($1)})`
6
- // })
7
- // const element = document.getElementById('style_' + id)
8
- // if (element) {
9
- // element.textContent = formatCss
10
- // } else {
11
- // const style = document.createElement('style')
12
- // style.id = 'style_' + id
13
- // style.textContent = formatCss
14
- // style.setAttribute('type', 'text/css')
15
- // document.head.appendChild(style)
16
- // }
17
- // }
18
-
19
- // function removeCss (id) {
20
- // const element = document.getElementById('style_' + id)
21
- // if (element) document.head.removeChild(element)
22
- // }
23
-
24
- // 卸掉 el 上一次 apply 绑的全部监听(beforeMount 与 mounted 会各 apply 一次,不先卸就会双份触发)
25
- function detach (el) {
26
- const targetEl = el._widgetTargetEl || el
27
- const pairs = [['mouseenter', '_widgetMouseEnter'], ['mouseleave', '_widgetMouseLeave'], ['click', '_widgetClick'], ['dragstart', '_widgetDragStart'], ['dragend', '_widgetDragEnd']]
28
- for (const [type, key] of pairs) {
29
- if (el[key]) {
30
- targetEl.removeEventListener(type, el[key])
31
- delete el[key]
32
- }
33
- }
34
- delete el._widgetTargetEl
35
- }
36
-
37
- // 全局指令:在组件上添加设计器相关属性和事件
38
- export default {
39
- beforeMount (el, binding) {
40
- const { widget, designer, parentWidget, templateInfo } = binding.value || {}
41
- if (!widget || !designer) return
42
-
43
- // 等待组件挂载后再操作(Vue 组件可能需要等待)
44
- const applyDirective = () => {
45
- detach(el)
46
- // Vue3 里指令挂在组件上时 el 就是组件根元素(等价于 Vue2 的 el.__vue__.$el),直接用。
47
- // 迁移前遗留的「el.__vue__ 不存在就取唯一子元素」启发式在 Vue3 下恒命中,会把 data-widget-id
48
- // 与点击 / 悬停 / 拖拽监听挂到内部子元素上(表单项挂在头部 <p>、占位组件挂在 <span>),
49
- // 导致点组件身体选不中、选中浮层只框住头部。
50
- if (el.nodeType !== 1) return
51
- const targetEl = el
52
-
53
- // 添加 data-widget-id 属性
54
- targetEl.setAttribute('data-widget-id', widget.id)
55
-
56
- // 使组件可拖拽(但不改变鼠标样式)
57
- targetEl.setAttribute('draggable', 'true')
58
-
59
- // 获取组件名称并添加到 data 属性
60
- const widgetInfo = designer.getWidgetByType(widget.contentType, widget.type) || {}
61
- const widgetTitle = widgetInfo.TITLE || widgetInfo.NAME || widget.title || ''
62
- if (widgetTitle) {
63
- targetEl.setAttribute('data-widget-title', widgetTitle)
64
- }
65
-
66
- // 添加 css_ 类名
67
- // if (targetEl.classList) {
68
- // targetEl.classList.add('css_' + widget.id)
69
- // } else {
70
- // targetEl.className += ' css_' + widget.id
71
- // }
72
-
73
- // 如果不是容器组件,添加布局类(el-col)
74
- if (!parentWidget) {
75
- if (targetEl.classList) {
76
- targetEl.classList.add('el-col')
77
- targetEl.classList.add(`el-col-${widget.span || 24}`)
78
- } else {
79
- targetEl.className += ` el-col el-col-${widget.span || 24}`
80
- }
81
- }
82
-
83
- // 应用组件的 CSS 样式
84
- // if (widget.css) {
85
- // appendCss(widget.id, widget.css)
86
- // }
87
-
88
- // 添加事件监听器
89
- const handleMouseEnter = (event) => {
90
- if (!templateInfo) {
91
- // 阻止事件冒泡,防止触发外层组件的悬停
92
- event.stopPropagation()
93
- // 通过 designer 统一管理悬浮组件状态
94
- if (typeof designer.setHoverById === 'function') {
95
- designer.setHoverById(widget.id)
96
- } else if (designer.emitEvent) {
97
- // 兼容旧逻辑
98
- designer.emitEvent('widget-hover', widget.id)
99
- }
100
- }
101
- }
102
-
103
- const handleMouseLeave = (event) => {
104
- if (!templateInfo) {
105
- const relatedTarget = event.relatedTarget
106
-
107
- // 1. 鼠标仍在当前组件内部(含子元素),不处理
108
- if (relatedTarget && targetEl.contains(relatedTarget)) {
109
- return
110
- }
111
-
112
- event.stopPropagation()
113
-
114
- // 2. 鼠标移动到了另一个挂了 data-widget-id 的组件上,直接把 hover 切换到该组件
115
- if (relatedTarget && relatedTarget.closest) {
116
- const nextWidgetEl = relatedTarget.closest('[data-widget-id]')
117
- if (nextWidgetEl) {
118
- const nextId = nextWidgetEl.getAttribute('data-widget-id')
119
- if (nextId && typeof designer.setHoverById === 'function') {
120
- designer.setHoverById(nextId)
121
- return
122
- } else if (nextId && designer.emitEvent) {
123
- // 兼容旧逻辑
124
- designer.emitEvent('widget-hover', nextId)
125
- return
126
- }
127
- }
128
- }
129
-
130
- // 3. 否则认为鼠标离开了所有组件区域,清理悬浮状态
131
- if (typeof designer.clearHover === 'function') {
132
- designer.clearHover()
133
- } else if (designer.emitEvent) {
134
- designer.emitEvent('widget-hover', null)
135
- }
136
- }
137
- }
138
-
139
- const handleClick = (event) => {
140
- if (!templateInfo) {
141
- const widgetInfo = designer.getWidgetByType(widget.contentType, widget.type) || {}
142
- if (widgetInfo.ID) {
143
- designer.setSelected(widget)
144
- event.stopPropagation()
145
- // 触发全局事件
146
- if (designer.emitEvent) {
147
- designer.emitEvent('widget-click', {
148
- widget: widget,
149
- element: targetEl,
150
- parentWidget: parentWidget
151
- })
152
- }
153
- }
154
- }
155
- }
156
-
157
- // 组件拖拽开始
158
- const handleDragStart = (event) => {
159
- event.stopPropagation()
160
-
161
- // 设置拖拽数据
162
- const dragData = {
163
- type: 'widget-reorder',
164
- widgetId: widget.id,
165
- parentWidgetId: parentWidget?.id || null
166
- }
167
- const dragDataStr = JSON.stringify(dragData)
168
-
169
- event.dataTransfer.setData('text/plain', dragDataStr)
170
- event.dataTransfer.effectAllowed = 'move'
171
-
172
- // 设置自定义拖拽图像(空元素),避免浏览器默认拖拽行为干扰
173
- const emptyEl = document.createElement('div')
174
- emptyEl.style.cssText = 'position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none'
175
- document.body.appendChild(emptyEl)
176
- event.dataTransfer.setDragImage(emptyEl, 0, 0)
177
- setTimeout(() => { if (emptyEl.parentNode) emptyEl.parentNode.removeChild(emptyEl) }, 0)
178
-
179
- // 添加拖拽样式和标记类
180
- targetEl.style.opacity = '0.5'
181
- targetEl.classList.add('epx-widget-dragging')
182
-
183
- // 注意:不要设置 pointer-events: none,否则会阻止 drop 事件
184
- // 使用 CSS 类来控制样式即可
185
-
186
- // 通知 designer 开始拖拽(使用统一的拖拽状态管理)
187
- if (designer.startDrag) {
188
- designer.startDrag({
189
- source: 'preview',
190
- data: dragData,
191
- title: widgetInfo.TITLE || widget.title,
192
- icon: widgetInfo.ICON,
193
- x: event.clientX,
194
- y: event.clientY,
195
- isNative: true
196
- })
197
- }
198
- }
199
-
200
- // 组件拖拽结束
201
- const handleDragEnd = (event) => {
202
- event.stopPropagation()
203
-
204
- // 恢复样式
205
- targetEl.style.opacity = '1'
206
- targetEl.classList.remove('epx-widget-dragging')
207
-
208
- // 预览区域组件拖拽结束,需要关闭悬浮窗
209
- // 与组件库不同,预览区域的组件不会在拖拽开始时关闭/销毁,
210
- // 所以可以安全地发送 'drag-end' 事件来关闭悬浮窗
211
- if (designer.emitEvent) {
212
- designer.emitEvent('drag-end')
213
- }
214
- }
215
-
216
- // 保存事件处理函数和元素引用,以便后续移除
217
- el._widgetTargetEl = targetEl
218
- el._widgetMouseEnter = handleMouseEnter
219
- el._widgetMouseLeave = handleMouseLeave
220
- el._widgetClick = handleClick
221
- el._widgetDragStart = handleDragStart
222
- el._widgetDragEnd = handleDragEnd
223
-
224
- targetEl.addEventListener('mouseenter', handleMouseEnter)
225
- targetEl.addEventListener('mouseleave', handleMouseLeave)
226
- targetEl.addEventListener('click', handleClick)
227
- targetEl.addEventListener('dragstart', handleDragStart)
228
- targetEl.addEventListener('dragend', handleDragEnd)
229
- }
230
-
231
- // 立即执行,如果组件还没挂载,在 inserted 钩子中再执行一次
232
- applyDirective()
233
- el._applyDirective = applyDirective
234
- },
235
- mounted (el, binding) {
236
- // 组件插入后再次应用,确保找到正确的根元素;
237
- // 组件在同一 tick 内挂载又卸载时 unmounted 已删掉该引用,回调里要再判一次
238
- if (el._applyDirective) {
239
- setTimeout(() => {
240
- if (typeof el._applyDirective === 'function') el._applyDirective()
241
- }, 0)
242
- }
243
- },
244
- updated (el, binding) {
245
- // console.log('update', binding.value)
246
- // const { widget } = binding.value || {}
247
- // if (!widget) return
248
-
249
- // // 更新 CSS 样式
250
- // if (widget.css) {
251
- // appendCss(widget.id, widget.css)
252
- // }
253
- },
254
- unmounted (el) {
255
- detach(el)
256
- delete el._applyDirective
257
- }
258
- }
1
+ // import util from '@epxing/core/src/runtime-utils/util'
2
+
3
+ // function appendCss (id, css) {
4
+ // const formatCss = css.replace(/:current/g, '.css_' + id).replace(/url\s*\(([\w+-/.\s]+)\)/g, (str, $1) => {
5
+ // return `url(${util.getFileUrl($1)})`
6
+ // })
7
+ // const element = document.getElementById('style_' + id)
8
+ // if (element) {
9
+ // element.textContent = formatCss
10
+ // } else {
11
+ // const style = document.createElement('style')
12
+ // style.id = 'style_' + id
13
+ // style.textContent = formatCss
14
+ // style.setAttribute('type', 'text/css')
15
+ // document.head.appendChild(style)
16
+ // }
17
+ // }
18
+
19
+ // function removeCss (id) {
20
+ // const element = document.getElementById('style_' + id)
21
+ // if (element) document.head.removeChild(element)
22
+ // }
23
+
24
+ // 卸掉 el 上一次 apply 绑的全部监听(beforeMount 与 mounted 会各 apply 一次,不先卸就会双份触发)
25
+ function detach (el) {
26
+ const targetEl = el._widgetTargetEl || el
27
+ const pairs = [['mouseenter', '_widgetMouseEnter'], ['mouseleave', '_widgetMouseLeave'], ['click', '_widgetClick'], ['dragstart', '_widgetDragStart'], ['dragend', '_widgetDragEnd']]
28
+ for (const [type, key] of pairs) {
29
+ if (el[key]) {
30
+ targetEl.removeEventListener(type, el[key])
31
+ delete el[key]
32
+ }
33
+ }
34
+ delete el._widgetTargetEl
35
+ }
36
+
37
+ // 全局指令:在组件上添加设计器相关属性和事件
38
+ export default {
39
+ beforeMount (el, binding) {
40
+ const { widget, designer, parentWidget, templateInfo } = binding.value || {}
41
+ if (!widget || !designer) return
42
+
43
+ // 等待组件挂载后再操作(Vue 组件可能需要等待)
44
+ const applyDirective = () => {
45
+ detach(el)
46
+ // Vue3 里指令挂在组件上时 el 就是组件根元素(等价于 Vue2 的 el.__vue__.$el),直接用。
47
+ // 迁移前遗留的「el.__vue__ 不存在就取唯一子元素」启发式在 Vue3 下恒命中,会把 data-widget-id
48
+ // 与点击 / 悬停 / 拖拽监听挂到内部子元素上(表单项挂在头部 <p>、占位组件挂在 <span>),
49
+ // 导致点组件身体选不中、选中浮层只框住头部。
50
+ if (el.nodeType !== 1) return
51
+ const targetEl = el
52
+
53
+ // 添加 data-widget-id 属性
54
+ targetEl.setAttribute('data-widget-id', widget.id)
55
+
56
+ // 使组件可拖拽(但不改变鼠标样式)
57
+ targetEl.setAttribute('draggable', 'true')
58
+
59
+ // 获取组件名称并添加到 data 属性
60
+ const widgetInfo = designer.getWidgetByType(widget.contentType, widget.type) || {}
61
+ const widgetTitle = widgetInfo.TITLE || widgetInfo.NAME || widget.title || ''
62
+ if (widgetTitle) {
63
+ targetEl.setAttribute('data-widget-title', widgetTitle)
64
+ }
65
+
66
+ // 添加 css_ 类名
67
+ // if (targetEl.classList) {
68
+ // targetEl.classList.add('css_' + widget.id)
69
+ // } else {
70
+ // targetEl.className += ' css_' + widget.id
71
+ // }
72
+
73
+ // 如果不是容器组件,添加布局类(el-col)
74
+ if (!parentWidget) {
75
+ if (targetEl.classList) {
76
+ targetEl.classList.add('el-col')
77
+ targetEl.classList.add(`el-col-${widget.span || 24}`)
78
+ } else {
79
+ targetEl.className += ` el-col el-col-${widget.span || 24}`
80
+ }
81
+ }
82
+
83
+ // 应用组件的 CSS 样式
84
+ // if (widget.css) {
85
+ // appendCss(widget.id, widget.css)
86
+ // }
87
+
88
+ // 添加事件监听器
89
+ const handleMouseEnter = (event) => {
90
+ if (!templateInfo) {
91
+ // 阻止事件冒泡,防止触发外层组件的悬停
92
+ event.stopPropagation()
93
+ // 通过 designer 统一管理悬浮组件状态
94
+ if (typeof designer.setHoverById === 'function') {
95
+ designer.setHoverById(widget.id)
96
+ } else if (designer.emitEvent) {
97
+ // 兼容旧逻辑
98
+ designer.emitEvent('widget-hover', widget.id)
99
+ }
100
+ }
101
+ }
102
+
103
+ const handleMouseLeave = (event) => {
104
+ if (!templateInfo) {
105
+ const relatedTarget = event.relatedTarget
106
+
107
+ // 1. 鼠标仍在当前组件内部(含子元素),不处理
108
+ if (relatedTarget && targetEl.contains(relatedTarget)) {
109
+ return
110
+ }
111
+
112
+ event.stopPropagation()
113
+
114
+ // 2. 鼠标移动到了另一个挂了 data-widget-id 的组件上,直接把 hover 切换到该组件
115
+ if (relatedTarget && relatedTarget.closest) {
116
+ const nextWidgetEl = relatedTarget.closest('[data-widget-id]')
117
+ if (nextWidgetEl) {
118
+ const nextId = nextWidgetEl.getAttribute('data-widget-id')
119
+ if (nextId && typeof designer.setHoverById === 'function') {
120
+ designer.setHoverById(nextId)
121
+ return
122
+ } else if (nextId && designer.emitEvent) {
123
+ // 兼容旧逻辑
124
+ designer.emitEvent('widget-hover', nextId)
125
+ return
126
+ }
127
+ }
128
+ }
129
+
130
+ // 3. 否则认为鼠标离开了所有组件区域,清理悬浮状态
131
+ if (typeof designer.clearHover === 'function') {
132
+ designer.clearHover()
133
+ } else if (designer.emitEvent) {
134
+ designer.emitEvent('widget-hover', null)
135
+ }
136
+ }
137
+ }
138
+
139
+ const handleClick = (event) => {
140
+ if (!templateInfo) {
141
+ const widgetInfo = designer.getWidgetByType(widget.contentType, widget.type) || {}
142
+ if (widgetInfo.ID) {
143
+ designer.setSelected(widget)
144
+ event.stopPropagation()
145
+ // 触发全局事件
146
+ if (designer.emitEvent) {
147
+ designer.emitEvent('widget-click', {
148
+ widget: widget,
149
+ element: targetEl,
150
+ parentWidget: parentWidget
151
+ })
152
+ }
153
+ }
154
+ }
155
+ }
156
+
157
+ // 组件拖拽开始
158
+ const handleDragStart = (event) => {
159
+ event.stopPropagation()
160
+
161
+ // 设置拖拽数据
162
+ const dragData = {
163
+ type: 'widget-reorder',
164
+ widgetId: widget.id,
165
+ parentWidgetId: parentWidget?.id || null
166
+ }
167
+ const dragDataStr = JSON.stringify(dragData)
168
+
169
+ event.dataTransfer.setData('text/plain', dragDataStr)
170
+ event.dataTransfer.effectAllowed = 'move'
171
+
172
+ // 设置自定义拖拽图像(空元素),避免浏览器默认拖拽行为干扰
173
+ const emptyEl = document.createElement('div')
174
+ emptyEl.style.cssText = 'position:absolute;left:-9999px;width:1px;height:1px;opacity:0;pointer-events:none'
175
+ document.body.appendChild(emptyEl)
176
+ event.dataTransfer.setDragImage(emptyEl, 0, 0)
177
+ setTimeout(() => { if (emptyEl.parentNode) emptyEl.parentNode.removeChild(emptyEl) }, 0)
178
+
179
+ // 添加拖拽样式和标记类
180
+ targetEl.style.opacity = '0.5'
181
+ targetEl.classList.add('epx-widget-dragging')
182
+
183
+ // 注意:不要设置 pointer-events: none,否则会阻止 drop 事件
184
+ // 使用 CSS 类来控制样式即可
185
+
186
+ // 通知 designer 开始拖拽(使用统一的拖拽状态管理)
187
+ if (designer.startDrag) {
188
+ designer.startDrag({
189
+ source: 'preview',
190
+ data: dragData,
191
+ title: widgetInfo.TITLE || widget.title,
192
+ icon: widgetInfo.ICON,
193
+ x: event.clientX,
194
+ y: event.clientY,
195
+ isNative: true
196
+ })
197
+ }
198
+ }
199
+
200
+ // 组件拖拽结束
201
+ const handleDragEnd = (event) => {
202
+ event.stopPropagation()
203
+
204
+ // 恢复样式
205
+ targetEl.style.opacity = '1'
206
+ targetEl.classList.remove('epx-widget-dragging')
207
+
208
+ // 预览区域组件拖拽结束,需要关闭悬浮窗
209
+ // 与组件库不同,预览区域的组件不会在拖拽开始时关闭/销毁,
210
+ // 所以可以安全地发送 'drag-end' 事件来关闭悬浮窗
211
+ if (designer.emitEvent) {
212
+ designer.emitEvent('drag-end')
213
+ }
214
+ }
215
+
216
+ // 保存事件处理函数和元素引用,以便后续移除
217
+ el._widgetTargetEl = targetEl
218
+ el._widgetMouseEnter = handleMouseEnter
219
+ el._widgetMouseLeave = handleMouseLeave
220
+ el._widgetClick = handleClick
221
+ el._widgetDragStart = handleDragStart
222
+ el._widgetDragEnd = handleDragEnd
223
+
224
+ targetEl.addEventListener('mouseenter', handleMouseEnter)
225
+ targetEl.addEventListener('mouseleave', handleMouseLeave)
226
+ targetEl.addEventListener('click', handleClick)
227
+ targetEl.addEventListener('dragstart', handleDragStart)
228
+ targetEl.addEventListener('dragend', handleDragEnd)
229
+ }
230
+
231
+ // 立即执行,如果组件还没挂载,在 inserted 钩子中再执行一次
232
+ applyDirective()
233
+ el._applyDirective = applyDirective
234
+ },
235
+ mounted (el, binding) {
236
+ // 组件插入后再次应用,确保找到正确的根元素;
237
+ // 组件在同一 tick 内挂载又卸载时 unmounted 已删掉该引用,回调里要再判一次
238
+ if (el._applyDirective) {
239
+ setTimeout(() => {
240
+ if (typeof el._applyDirective === 'function') el._applyDirective()
241
+ }, 0)
242
+ }
243
+ },
244
+ updated (el, binding) {
245
+ // console.log('update', binding.value)
246
+ // const { widget } = binding.value || {}
247
+ // if (!widget) return
248
+
249
+ // // 更新 CSS 样式
250
+ // if (widget.css) {
251
+ // appendCss(widget.id, widget.css)
252
+ // }
253
+ },
254
+ unmounted (el) {
255
+ detach(el)
256
+ delete el._applyDirective
257
+ }
258
+ }