@opentiny/tiny-engine-canvas 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +42 -0
- package/README.md +7 -0
- package/canvas.html +212 -0
- package/dist/index.js +48919 -0
- package/index.html +13 -0
- package/package.json +30 -0
- package/public/favicon.ico +0 -0
- package/src/Design.vue +53 -0
- package/src/assets/logo.png +0 -0
- package/src/canvas.js +34 -0
- package/src/components/builtin/CanvasBox.vue +22 -0
- package/src/components/builtin/CanvasCol.vue +89 -0
- package/src/components/builtin/CanvasCollection.js +278 -0
- package/src/components/builtin/CanvasCollection.vue +106 -0
- package/src/components/builtin/CanvasIcon.vue +30 -0
- package/src/components/builtin/CanvasImg.vue +18 -0
- package/src/components/builtin/CanvasPlaceholder.vue +26 -0
- package/src/components/builtin/CanvasRow.vue +67 -0
- package/src/components/builtin/CanvasRowColContainer.vue +42 -0
- package/src/components/builtin/CanvasSlot.vue +22 -0
- package/src/components/builtin/CanvasText.vue +18 -0
- package/src/components/builtin/builtin.json +955 -0
- package/src/components/builtin/helper.js +46 -0
- package/src/components/builtin/index.js +33 -0
- package/src/components/common/index.js +158 -0
- package/src/components/container/CanvasAction.vue +554 -0
- package/src/components/container/CanvasContainer.vue +244 -0
- package/src/components/container/CanvasDivider.vue +246 -0
- package/src/components/container/CanvasDragItem.vue +38 -0
- package/src/components/container/CanvasFooter.vue +86 -0
- package/src/components/container/CanvasMenu.vue +214 -0
- package/src/components/container/CanvasResize.vue +195 -0
- package/src/components/container/CanvasResizeBorder.vue +219 -0
- package/src/components/container/container.js +791 -0
- package/src/components/container/keyboard.js +147 -0
- package/src/components/container/shortCutPopover.vue +181 -0
- package/src/components/render/CanvasEmpty.vue +14 -0
- package/src/components/render/RenderMain.js +408 -0
- package/src/components/render/context.js +53 -0
- package/src/components/render/render.js +689 -0
- package/src/components/render/runner.js +140 -0
- package/src/i18n/en.json +5 -0
- package/src/i18n/zh.json +5 -0
- package/src/i18n.js +21 -0
- package/src/index.js +96 -0
- package/src/locale.js +19 -0
- package/src/lowcode.js +104 -0
- package/src/main.js +17 -0
- package/test/form.json +690 -0
- package/test/group.json +99 -0
- package/test/jsslot.json +427 -0
- package/vite.config.js +73 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2023 - present TinyEngine Authors.
|
|
3
|
+
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license.
|
|
6
|
+
*
|
|
7
|
+
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
|
8
|
+
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
|
9
|
+
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { reactive, toRaw, nextTick, shallowReactive } from 'vue'
|
|
14
|
+
import {
|
|
15
|
+
addScript as appendScript,
|
|
16
|
+
addStyle as appendStyle,
|
|
17
|
+
copyObject,
|
|
18
|
+
NODE_UID,
|
|
19
|
+
NODE_TAG,
|
|
20
|
+
NODE_LOOP
|
|
21
|
+
} from '../common'
|
|
22
|
+
import { useCanvas, useLayout, useResource, useTranslate } from '@opentiny/tiny-engine-controller'
|
|
23
|
+
export const POSITION = Object.freeze({
|
|
24
|
+
TOP: 'top',
|
|
25
|
+
BOTTOM: 'bottom',
|
|
26
|
+
LEFT: 'left',
|
|
27
|
+
RIGHT: 'right',
|
|
28
|
+
IN: 'in',
|
|
29
|
+
FORBID: 'forbid'
|
|
30
|
+
})
|
|
31
|
+
import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments'
|
|
32
|
+
|
|
33
|
+
const initialDragState = {
|
|
34
|
+
keydown: false,
|
|
35
|
+
draging: false,
|
|
36
|
+
data: null,
|
|
37
|
+
position: null, // ghost位置
|
|
38
|
+
mouse: null, // iframe里鼠标位置
|
|
39
|
+
element: null,
|
|
40
|
+
offset: {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const canvasState = shallowReactive({
|
|
44
|
+
type: 'normal',
|
|
45
|
+
schema: null,
|
|
46
|
+
renderer: null, // 存放画布内的api
|
|
47
|
+
iframe: null,
|
|
48
|
+
loading: true,
|
|
49
|
+
current: null,
|
|
50
|
+
parent: null,
|
|
51
|
+
loopId: null
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
export const getContext = () => {
|
|
55
|
+
return getRenderer().getContext()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 记录拖拽状态
|
|
59
|
+
export const dragState = reactive({
|
|
60
|
+
...initialDragState
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
export const initialRectState = {
|
|
64
|
+
top: 0,
|
|
65
|
+
height: 0,
|
|
66
|
+
width: 0,
|
|
67
|
+
left: 0,
|
|
68
|
+
schema: null,
|
|
69
|
+
configure: null,
|
|
70
|
+
componentName: ''
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const initialLineState = {
|
|
74
|
+
top: 0,
|
|
75
|
+
height: 0,
|
|
76
|
+
width: 0,
|
|
77
|
+
left: 0,
|
|
78
|
+
position: '',
|
|
79
|
+
id: '',
|
|
80
|
+
config: null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// 选中画布中元素时的状态
|
|
84
|
+
export const selectState = reactive({
|
|
85
|
+
...initialRectState
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
// 鼠标移入画布中元素时的状态
|
|
89
|
+
export const hoverState = reactive({
|
|
90
|
+
...initialRectState
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// 拖拽时的位置状态
|
|
94
|
+
export const lineState = reactive({
|
|
95
|
+
...initialLineState
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
export const dragStart = (
|
|
99
|
+
data,
|
|
100
|
+
element,
|
|
101
|
+
{ offsetX = 0, offsetY = 0, horizontal, vertical, width, height, x, y } = {}
|
|
102
|
+
) => {
|
|
103
|
+
// 表示鼠标按下开始拖拽
|
|
104
|
+
dragState.keydown = true
|
|
105
|
+
dragState.data = data || {}
|
|
106
|
+
|
|
107
|
+
// 记录上次一开始拖拽的时间
|
|
108
|
+
dragState.timer = Date.now()
|
|
109
|
+
|
|
110
|
+
// 如果element存在表示在iframe内部拖拽
|
|
111
|
+
dragState.element = element
|
|
112
|
+
dragState.offset = { offsetX, offsetY, horizontal, vertical, width, height, x, y }
|
|
113
|
+
clearHover()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export const clearLineState = () => {
|
|
117
|
+
Object.assign(lineState, initialLineState)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export const dragEnd = () => {
|
|
121
|
+
const { element, data } = dragState
|
|
122
|
+
|
|
123
|
+
if (element && canvasState.type === 'absolute') {
|
|
124
|
+
data.props = data.props || {}
|
|
125
|
+
data.props.style = element.style.cssText
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 重置拖拽状态
|
|
129
|
+
Object.assign(dragState, initialDragState)
|
|
130
|
+
|
|
131
|
+
// 重置拖拽插入位置状态
|
|
132
|
+
clearLineState()
|
|
133
|
+
smoothScroll.stop()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const getOffset = (element) => {
|
|
137
|
+
if (element.ownerDocument === document) {
|
|
138
|
+
return { x: 0, y: 0 }
|
|
139
|
+
}
|
|
140
|
+
const { x, y, bottom, top } = canvasState.iframe.getBoundingClientRect()
|
|
141
|
+
return { x, y, bottom, top }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const getElement = (element) => {
|
|
145
|
+
// 如果当前元素是body
|
|
146
|
+
if (element === element.ownerDocument.body) {
|
|
147
|
+
return element
|
|
148
|
+
}
|
|
149
|
+
if (!element || element.nodeType !== 1) {
|
|
150
|
+
return undefined
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (element.getAttribute(NODE_UID)) {
|
|
154
|
+
return element
|
|
155
|
+
} else if (element.parentElement) {
|
|
156
|
+
return getElement(element.parentElement)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return undefined
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const inserAfter = ({ parent, node, data }) => {
|
|
163
|
+
const parentChildren = parent.children
|
|
164
|
+
const index = parentChildren.indexOf(node)
|
|
165
|
+
parent.children.splice(index + 1, 0, data)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const insertBefore = ({ parent, node, data }) => {
|
|
169
|
+
const parentChildren = parent.children
|
|
170
|
+
const index = parentChildren.indexOf(node)
|
|
171
|
+
parent.children.splice(index, 0, data)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const insertInner = ({ node, data }, position) => {
|
|
175
|
+
node.children = node.children || []
|
|
176
|
+
|
|
177
|
+
if (position === POSITION.TOP || position === POSITION.LEFT) {
|
|
178
|
+
node.children.unshift(data)
|
|
179
|
+
} else {
|
|
180
|
+
node.children.push(data)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const removeNode = ({ parent, node }) => {
|
|
185
|
+
const parentChildren = parent.children || parent.value
|
|
186
|
+
const index = parentChildren.indexOf(node)
|
|
187
|
+
|
|
188
|
+
if (index > -1) {
|
|
189
|
+
parentChildren.splice(index, 1)
|
|
190
|
+
} else {
|
|
191
|
+
const templates = parentChildren.filter(({ componentName }) => componentName === 'Template')
|
|
192
|
+
|
|
193
|
+
templates.forEach((template) => {
|
|
194
|
+
const { children } = template
|
|
195
|
+
|
|
196
|
+
if (children.length) {
|
|
197
|
+
children.splice(children.indexOf(node), 1)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!children.length) {
|
|
201
|
+
parentChildren.splice(parentChildren.indexOf(template), 1)
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export const removeNodeById = (id) => {
|
|
208
|
+
removeNode(getNode(id, true))
|
|
209
|
+
clearSelect()
|
|
210
|
+
getController().addHistory()
|
|
211
|
+
canvasState.emit('remove')
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const insertNode = (node, position = POSITION.IN, select = true) => {
|
|
215
|
+
if (!node.parent) {
|
|
216
|
+
insertInner({ node: canvasState.schema, data: node.data }, position)
|
|
217
|
+
} else {
|
|
218
|
+
switch (position) {
|
|
219
|
+
case POSITION.TOP:
|
|
220
|
+
case POSITION.LEFT:
|
|
221
|
+
insertBefore(node)
|
|
222
|
+
break
|
|
223
|
+
case POSITION.BOTTOM:
|
|
224
|
+
case POSITION.RIGHT:
|
|
225
|
+
inserAfter(node)
|
|
226
|
+
break
|
|
227
|
+
case POSITION.IN:
|
|
228
|
+
insertInner(node)
|
|
229
|
+
break
|
|
230
|
+
default:
|
|
231
|
+
insertInner(node)
|
|
232
|
+
break
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
select && setTimeout(() => selectNode(node.data.id))
|
|
237
|
+
|
|
238
|
+
getController().addHistory()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export const addComponent = (data, position) => {
|
|
242
|
+
const { schema, parent } = getCurrent()
|
|
243
|
+
|
|
244
|
+
insertNode({ node: schema, parent, data }, position)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const copyNode = (id) => {
|
|
248
|
+
const { node, parent } = getNode(id, true)
|
|
249
|
+
|
|
250
|
+
inserAfter({ parent, node, data: copyObject(node) })
|
|
251
|
+
getController().addHistory()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export const onMouseUp = ({ target }) => {
|
|
255
|
+
const { draging, data } = dragState
|
|
256
|
+
const { position } = lineState
|
|
257
|
+
const absolute = canvasState.type === 'absolute'
|
|
258
|
+
const sourceId = data?.id
|
|
259
|
+
const lineId = lineState.id
|
|
260
|
+
const allowInsert = position !== POSITION.FORBID
|
|
261
|
+
|
|
262
|
+
if (draging && allowInsert) {
|
|
263
|
+
const { parent, node } = getNode(lineId, true) || {} // target
|
|
264
|
+
const targetNode = { parent, node, data: toRaw(data) }
|
|
265
|
+
|
|
266
|
+
if (sourceId) {
|
|
267
|
+
// 内部拖拽
|
|
268
|
+
if (sourceId !== lineId && !absolute) {
|
|
269
|
+
removeNode(getNode(sourceId, true))
|
|
270
|
+
insertNode(targetNode, position)
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
// 从外部拖拽进来的无ID,insert
|
|
274
|
+
if (absolute) {
|
|
275
|
+
targetNode.node = getSchema()
|
|
276
|
+
data.props = data.props || {}
|
|
277
|
+
data.props.style = {
|
|
278
|
+
position: 'absolute',
|
|
279
|
+
top: dragState.mouse.y + 'px',
|
|
280
|
+
left: dragState.mouse.x + 'px'
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
insertNode(targetNode, position)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// 重置拖拽状态
|
|
289
|
+
dragEnd()
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const smoothScroll = {
|
|
293
|
+
timmer: null,
|
|
294
|
+
/**
|
|
295
|
+
*
|
|
296
|
+
* @param {*} up 方向
|
|
297
|
+
* @param {*} step 每次滚动距离
|
|
298
|
+
* @param {*} time 滚动延时(不得大于系统滚动时长,否则可能出现卡顿效果)
|
|
299
|
+
*/
|
|
300
|
+
start(up, step = 40, time = 100) {
|
|
301
|
+
const dom = getDocument().documentElement
|
|
302
|
+
const fn = () => {
|
|
303
|
+
const top = up ? dom.scrollTop + step : dom.scrollTop - step
|
|
304
|
+
|
|
305
|
+
dom.scrollTo({ top, behavior: 'smooth' })
|
|
306
|
+
this.timmer = setTimeout(fn, time)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
this.timmer || fn()
|
|
310
|
+
},
|
|
311
|
+
stop() {
|
|
312
|
+
clearTimeout(this.timmer)
|
|
313
|
+
this.timmer = null
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 绝对布局
|
|
318
|
+
const absoluteMove = (event, element) => {
|
|
319
|
+
const { clientX, clientY } = event
|
|
320
|
+
const { offsetX, offsetY, horizontal, vertical, height, width, x, y } = dragState.offset
|
|
321
|
+
|
|
322
|
+
element.style.position = 'absolute'
|
|
323
|
+
|
|
324
|
+
if (!horizontal) {
|
|
325
|
+
// 未传方向信息时判断为移动元素位置
|
|
326
|
+
element.style.top = `${clientY - offsetY}px`
|
|
327
|
+
element.style.left = `${clientX - offsetX}px`
|
|
328
|
+
} else {
|
|
329
|
+
// 调整元素大小
|
|
330
|
+
if (horizontal === 'start') {
|
|
331
|
+
element.style.left = `${clientX}px`
|
|
332
|
+
element.style.width = `${width + (x - clientX)}px`
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (horizontal === 'end') {
|
|
336
|
+
element.style.width = `${clientX - x}px`
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (vertical === 'start') {
|
|
340
|
+
element.style.top = `${clientY}px`
|
|
341
|
+
element.style.height = `${height + (y - clientY)}px`
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (vertical === 'end') {
|
|
345
|
+
element.style.height = `${clientY - y}px`
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
updateRect()
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const setDragPosition = ({ clientX, x, clientY, y, offsetBottom, offsetTop }) => {
|
|
352
|
+
const left = clientX + x
|
|
353
|
+
const top = clientY + y
|
|
354
|
+
if (clientY < 20) {
|
|
355
|
+
smoothScroll.start(false)
|
|
356
|
+
} else if (offsetBottom - clientY - offsetTop < 20) {
|
|
357
|
+
smoothScroll.start(true)
|
|
358
|
+
} else {
|
|
359
|
+
smoothScroll.stop()
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
dragState.position = { left, top }
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export const dragMove = (event, isHover) => {
|
|
366
|
+
if (!dragState.draging && dragState.keydown && new Date().getTime() - dragState.timer < 200) {
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const { x, y, bottom: offsetBottom, top: offsetTop } = getOffset(event.target)
|
|
371
|
+
const { clientX, clientY } = event
|
|
372
|
+
const { element } = dragState
|
|
373
|
+
const absolute = canvasState.type === 'absolute'
|
|
374
|
+
|
|
375
|
+
dragState.draging = dragState.keydown
|
|
376
|
+
|
|
377
|
+
dragState.mouse = { x: clientX, y: clientY }
|
|
378
|
+
|
|
379
|
+
// 如果仅仅是mouseover事件直接return,并重置拖拽位置状态,优化性能
|
|
380
|
+
if (isHover) {
|
|
381
|
+
lineState.position = ''
|
|
382
|
+
setHoverRect(getElement(event.target), null)
|
|
383
|
+
|
|
384
|
+
return
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
setHoverRect(getElement(event.target), dragState.data)
|
|
388
|
+
|
|
389
|
+
if (dragState.draging) {
|
|
390
|
+
// 绝对布局时走的逻辑
|
|
391
|
+
if (element && absolute) {
|
|
392
|
+
absoluteMove(event, element)
|
|
393
|
+
}
|
|
394
|
+
setDragPosition({ clientX, x, clientY, y, offsetBottom, offsetTop })
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export const clearHover = () => {
|
|
399
|
+
Object.assign(hoverState, initialRectState, { slot: null })
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export const clearSelect = () => {
|
|
403
|
+
canvasState.current = null
|
|
404
|
+
canvasState.parent = null
|
|
405
|
+
Object.assign(selectState, initialRectState)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export const querySelectById = (id, type = '') => {
|
|
409
|
+
let selector = `[${NODE_UID}="${id}"]`
|
|
410
|
+
const doc = canvasState.iframe.contentDocument
|
|
411
|
+
let element = doc.querySelector(selector)
|
|
412
|
+
const loopId = element?.getAttribute('loop-id')
|
|
413
|
+
if (element && loopId) {
|
|
414
|
+
const currentLoopId = getCurrent().loopId
|
|
415
|
+
selector = `[${NODE_UID}="${id}"][${NODE_LOOP}="${currentLoopId}"]`
|
|
416
|
+
element = doc.querySelector(selector)
|
|
417
|
+
}
|
|
418
|
+
return element
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export const getCurrentElement = () => querySelectById(getCurrent().schema?.id)
|
|
422
|
+
|
|
423
|
+
export const updateRect = (id) => {
|
|
424
|
+
id = (typeof id === 'string' && id) || getCurrent().schema?.id
|
|
425
|
+
clearHover()
|
|
426
|
+
|
|
427
|
+
if (id) {
|
|
428
|
+
setTimeout(() => setSelectRect(querySelectById(id)))
|
|
429
|
+
} else {
|
|
430
|
+
clearSelect()
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// type == clickTree, 为点击大纲; type == loop-id=xxx ,为点击循环数据
|
|
434
|
+
export const selectNode = async (id, type) => {
|
|
435
|
+
if (type && type.indexOf('loop-id') > -1) {
|
|
436
|
+
const loopId = type.split('=')[1]
|
|
437
|
+
canvasState.loopId = loopId
|
|
438
|
+
}
|
|
439
|
+
const { node, parent } = getNode(id, true) || {}
|
|
440
|
+
let element = querySelectById(id, type)
|
|
441
|
+
|
|
442
|
+
if (element) {
|
|
443
|
+
const { rootSelector } = getConfigure(node.componentName)
|
|
444
|
+
element = rootSelector ? element.querySelector(rootSelector) : element
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
canvasState.current = node
|
|
448
|
+
canvasState.parent = parent
|
|
449
|
+
|
|
450
|
+
await scrollToNode(element)
|
|
451
|
+
setSelectRect(element)
|
|
452
|
+
canvasState.emit('selected', node, parent, type)
|
|
453
|
+
|
|
454
|
+
return node
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export const hoverNode = (id, data) => {
|
|
458
|
+
const element = querySelectById(id)
|
|
459
|
+
element && setHoverRect(element, data)
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export const scrollToNode = (element) => {
|
|
463
|
+
if (element) {
|
|
464
|
+
const container = getDocument().documentElement
|
|
465
|
+
const h = container?.clientHeight
|
|
466
|
+
const { y, height } = element.getBoundingClientRect()
|
|
467
|
+
|
|
468
|
+
if (y < 0) {
|
|
469
|
+
container.scrollTo({ top: container.scrollTop + y - 15 })
|
|
470
|
+
} else if (y > h) {
|
|
471
|
+
container.scrollTo({ top: y + height - h + 15 })
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return nextTick()
|
|
476
|
+
}
|
|
477
|
+
const setSelectRect = (element) => {
|
|
478
|
+
element = element || getDocument().body
|
|
479
|
+
|
|
480
|
+
const { left, height, top, width } = element.getBoundingClientRect()
|
|
481
|
+
const { x, y } = getOffset(element)
|
|
482
|
+
const componentName = getCurrent().schema?.componentName || ''
|
|
483
|
+
const scale = useLayout().getScale()
|
|
484
|
+
clearHover()
|
|
485
|
+
Object.assign(selectState, {
|
|
486
|
+
width: width * scale,
|
|
487
|
+
height: height * scale,
|
|
488
|
+
top: top * scale + y,
|
|
489
|
+
left: left * scale + x,
|
|
490
|
+
componentName
|
|
491
|
+
})
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const isBodyEl = (element) => element.nodeName === 'BODY'
|
|
495
|
+
|
|
496
|
+
const setHoverRect = (element, data) => {
|
|
497
|
+
if (!element) {
|
|
498
|
+
return clearHover()
|
|
499
|
+
}
|
|
500
|
+
const componentName = element.getAttribute(NODE_TAG)
|
|
501
|
+
const id = element.getAttribute(NODE_UID)
|
|
502
|
+
const configure = getConfigure(componentName)
|
|
503
|
+
const rect = element.getBoundingClientRect()
|
|
504
|
+
const { left, height, top, width } = rect
|
|
505
|
+
const { x, y } = getOffset(element)
|
|
506
|
+
const scale = useLayout().getScale()
|
|
507
|
+
|
|
508
|
+
hoverState.configure = configure
|
|
509
|
+
|
|
510
|
+
if (data) {
|
|
511
|
+
let childEle = null
|
|
512
|
+
lineState.id = id
|
|
513
|
+
lineState.configure = configure
|
|
514
|
+
const rectType = isBodyEl(element) ? POSITION.IN : getPosLine(rect, configure).type
|
|
515
|
+
|
|
516
|
+
// 如果拖拽经过的元素是body或者是带有容器属性的盒子,并且在元素内部插入,则需要特殊处理
|
|
517
|
+
if ((isBodyEl(element) || configure?.isContainer) && rectType === POSITION.IN) {
|
|
518
|
+
const { node } = isBodyEl(element) ? { node: getSchema() } : getNode(id, true) || {}
|
|
519
|
+
const children = node?.children || []
|
|
520
|
+
if (children.length > 0) {
|
|
521
|
+
// 如果容器盒子有子节点,则以最后一个子节点为拖拽参照物
|
|
522
|
+
const lastNode = children[children.length - 1]
|
|
523
|
+
childEle = querySelectById(lastNode.id)
|
|
524
|
+
const childComponentName = element.getAttribute(childEle)
|
|
525
|
+
const Childconfigure = getConfigure(childComponentName)
|
|
526
|
+
lineState.id = lastNode.id
|
|
527
|
+
lineState.configure = Childconfigure
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// 如果容器盒子有子元素
|
|
532
|
+
if (childEle) {
|
|
533
|
+
const childRect = childEle.getBoundingClientRect()
|
|
534
|
+
const { left, height, top, width } = childRect
|
|
535
|
+
const { x, y } = getOffset(childEle)
|
|
536
|
+
Object.assign(lineState, {
|
|
537
|
+
width: width * scale,
|
|
538
|
+
height: height * scale,
|
|
539
|
+
top: top * scale + y,
|
|
540
|
+
left: left * scale + x,
|
|
541
|
+
position: canvasState.type === 'absolute' || getPosLine(childRect, lineState.configure).type
|
|
542
|
+
})
|
|
543
|
+
} else {
|
|
544
|
+
Object.assign(lineState, {
|
|
545
|
+
width: width * scale,
|
|
546
|
+
height: height * scale,
|
|
547
|
+
top: top * scale + y,
|
|
548
|
+
left: left * scale + x,
|
|
549
|
+
position: canvasState.type === 'absolute' || getPosLine(rect, configure).type
|
|
550
|
+
})
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
useLayout().closePlugin()
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// 设置元素hover状态
|
|
557
|
+
Object.assign(hoverState, {
|
|
558
|
+
width: width * scale,
|
|
559
|
+
height: height * scale,
|
|
560
|
+
top: top * scale + y,
|
|
561
|
+
left: left * scale + x,
|
|
562
|
+
componentName
|
|
563
|
+
})
|
|
564
|
+
return undefined
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* 是否允许插入
|
|
569
|
+
* @param {*} configure 当前放置目标的 configure,比如getConfigure(componentName)
|
|
570
|
+
* @param {*} data 当前插入目标的schame数据
|
|
571
|
+
* @returns
|
|
572
|
+
*/
|
|
573
|
+
export const allowInsert = (configure = hoverState.configure || {}, data = dragState.data || {}) => {
|
|
574
|
+
const { nestingRule = {} } = configure
|
|
575
|
+
const { childWhitelist = [], descendantBlacklist = [] } = nestingRule
|
|
576
|
+
|
|
577
|
+
// 要插入的父节点必须是容器
|
|
578
|
+
if (!configure.isContainer) {
|
|
579
|
+
return false
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
let flag = true
|
|
583
|
+
// 白名单
|
|
584
|
+
flag = childWhitelist.length ? childWhitelist.includes(data?.componentName) : true
|
|
585
|
+
|
|
586
|
+
// 黑名单
|
|
587
|
+
if (descendantBlacklist.length) {
|
|
588
|
+
flag = !descendantBlacklist.includes(data?.componentName)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return flag
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export const getConfigure = (targetName) => {
|
|
595
|
+
const material = getController().getMaterial(targetName)
|
|
596
|
+
|
|
597
|
+
// 这里如果是区块插槽,则返回标识为容器的对象
|
|
598
|
+
if (targetName === 'Template') {
|
|
599
|
+
return {
|
|
600
|
+
isContainer: true
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return material?.content?.configure || material.configure || {}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// 获取位置信息,返回状态
|
|
608
|
+
const lineAbs = 20
|
|
609
|
+
const getPosLine = (rect, configure) => {
|
|
610
|
+
const mousePos = dragState.mouse
|
|
611
|
+
const yAbs = Math.min(lineAbs, rect.height / 3)
|
|
612
|
+
const xAbs = Math.min(lineAbs, rect.width / 3)
|
|
613
|
+
let type
|
|
614
|
+
|
|
615
|
+
if (mousePos.y < rect.top + yAbs) {
|
|
616
|
+
type = POSITION.TOP
|
|
617
|
+
} else if (mousePos.y > rect.bottom - yAbs) {
|
|
618
|
+
type = POSITION.BOTTOM
|
|
619
|
+
} else if (mousePos.x < rect.left + xAbs) {
|
|
620
|
+
type = POSITION.LEFT
|
|
621
|
+
} else if (mousePos.x > rect.right - xAbs) {
|
|
622
|
+
type = POSITION.RIGHT
|
|
623
|
+
} else if (configure.isContainer) {
|
|
624
|
+
type = allowInsert() ? POSITION.IN : POSITION.FORBID
|
|
625
|
+
} else {
|
|
626
|
+
type = POSITION.BOTTOM
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return { type }
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export const setPageCss = (css = '') => {
|
|
633
|
+
const id = 'page-css'
|
|
634
|
+
const document = getDocument()
|
|
635
|
+
let element = document.getElementById(id)
|
|
636
|
+
const head = document.querySelector('head')
|
|
637
|
+
|
|
638
|
+
document.body.setAttribute('style', '')
|
|
639
|
+
|
|
640
|
+
if (!element) {
|
|
641
|
+
element = document.createElement('style')
|
|
642
|
+
element.setAttribute('type', 'text/css')
|
|
643
|
+
element.setAttribute('id', id)
|
|
644
|
+
|
|
645
|
+
element.innerHTML = css
|
|
646
|
+
head.appendChild(element)
|
|
647
|
+
} else {
|
|
648
|
+
element.innerHTML = css
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
export const addStyle = (href) => appendStyle(href, getDocument())
|
|
653
|
+
|
|
654
|
+
export const addScript = (src) => appendScript(src, getDocument())
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
*
|
|
658
|
+
* @param {*} messages
|
|
659
|
+
* @param {*} merge 是否合并,默认是重置所有数据
|
|
660
|
+
*/
|
|
661
|
+
export const setLocales = (messages, merge) => {
|
|
662
|
+
const i18n = getRenderer().getI18n()
|
|
663
|
+
|
|
664
|
+
Object.keys(messages).forEach((lang) => {
|
|
665
|
+
const fn = merge ? 'mergeLocaleMessage' : 'setLocaleMessage'
|
|
666
|
+
i18n.global[fn](lang, messages[lang])
|
|
667
|
+
})
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export const setState = (state) => {
|
|
671
|
+
getRenderer().setState(state)
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export const setUtils = (utils) => {
|
|
675
|
+
getRenderer().setUtils(utils)
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export const deleteState = (variable) => {
|
|
679
|
+
getRenderer().deleteState(variable)
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
export const setGlobalState = (state) => {
|
|
683
|
+
useResource().resState.globalState = state
|
|
684
|
+
getRenderer().setGlobalState(state)
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
export const getGlobalState = () => getRenderer().getGlobalState()
|
|
688
|
+
|
|
689
|
+
export const getRenderer = () => canvasState.renderer
|
|
690
|
+
|
|
691
|
+
export const getController = () => canvasState.controller
|
|
692
|
+
|
|
693
|
+
export const getNode = (id, parent) => getRenderer()?.getNode(id, parent)
|
|
694
|
+
|
|
695
|
+
export const getDocument = () => canvasState.iframe.contentDocument
|
|
696
|
+
|
|
697
|
+
export const getWindow = () => canvasState.iframe.contentWindow
|
|
698
|
+
|
|
699
|
+
export const getCurrent = () => {
|
|
700
|
+
return {
|
|
701
|
+
schema: canvasState.current,
|
|
702
|
+
parent: canvasState.parent,
|
|
703
|
+
loopId: canvasState.loopId
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
export const getNodePath = (id, nodes = []) => {
|
|
708
|
+
const { parent, node } = getNode(id, true) || {}
|
|
709
|
+
|
|
710
|
+
node && nodes.unshift({ name: node.componentName, node: id })
|
|
711
|
+
|
|
712
|
+
if (parent) {
|
|
713
|
+
parent && getNodePath(parent.id, nodes)
|
|
714
|
+
} else {
|
|
715
|
+
nodes.unshift({ name: 'BODY', node: id })
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
return nodes
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
export const setSchema = async (schema) => {
|
|
722
|
+
clearHover()
|
|
723
|
+
clearSelect()
|
|
724
|
+
canvasState.schema = await getRenderer()?.setSchema(schema)
|
|
725
|
+
|
|
726
|
+
return canvasState.schema
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
export const getSchema = () => getRenderer()?.getSchema()
|
|
730
|
+
|
|
731
|
+
export const setConfigure = (configure) => {
|
|
732
|
+
getRenderer().setConfigure(configure)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export const setProps = (data) => getRenderer()?.setProps(data)
|
|
736
|
+
|
|
737
|
+
export const setI18n = (data) => {
|
|
738
|
+
const messages = data || useTranslate().getData()
|
|
739
|
+
const i18n = getRenderer().getI18n()
|
|
740
|
+
Object.keys(messages).forEach((lang) => {
|
|
741
|
+
i18n.global.mergeLocaleMessage(lang, messages[lang])
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
export const setCanvasType = (type) => {
|
|
746
|
+
canvasState.type = type || 'normal'
|
|
747
|
+
getDocument().body.className = type === 'absolute' ? 'canvas-grid-bg' : ''
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
export const getCanvasType = () => canvasState.type
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* 画布派发事件
|
|
754
|
+
* @param {string} name 事件名称
|
|
755
|
+
* @param {any} data 派发的数据
|
|
756
|
+
*/
|
|
757
|
+
export const canvasDispatch = (name, data, doc = getDocument()) => {
|
|
758
|
+
if (!doc) return
|
|
759
|
+
|
|
760
|
+
doc.dispatchEvent(new CustomEvent(name, data))
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export const initCanvas = ({ renderer, iframe, emit, controller }) => {
|
|
764
|
+
const currentSchema = getSchema()
|
|
765
|
+
|
|
766
|
+
// 在点击刷新按钮的情况下继续保留最新的schema.json
|
|
767
|
+
const schema = currentSchema ? currentSchema : useCanvas().getPageSchema()
|
|
768
|
+
|
|
769
|
+
canvasState.iframe = iframe
|
|
770
|
+
canvasState.emit = emit
|
|
771
|
+
// 存放画布外层传进来的插件api
|
|
772
|
+
canvasState.controller = controller
|
|
773
|
+
canvasState.renderer = renderer
|
|
774
|
+
renderer.setController(controller)
|
|
775
|
+
setLocales(useTranslate().getData(), true)
|
|
776
|
+
if (isVsCodeEnv) {
|
|
777
|
+
const parent = window.parent
|
|
778
|
+
const senterMessage = parent.postMessage
|
|
779
|
+
// 发消息给webview
|
|
780
|
+
senterMessage({ type: 'i18nReady', value: true }, '*')
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
setGlobalState(useResource().resState.globalState)
|
|
784
|
+
renderer.setDataSourceMap(useResource().resState.dataSource)
|
|
785
|
+
// 设置画布全局的utils工具类上下文环境
|
|
786
|
+
setUtils(useResource().resState.utils)
|
|
787
|
+
setSchema(schema)
|
|
788
|
+
setConfigure(useResource().getConfigureMap())
|
|
789
|
+
canvasDispatch('updateDependencies', { detail: useResource().resState.thirdPartyDeps })
|
|
790
|
+
canvasState.loading = false
|
|
791
|
+
}
|