@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.
Files changed (52) hide show
  1. package/.eslintrc.js +42 -0
  2. package/README.md +7 -0
  3. package/canvas.html +212 -0
  4. package/dist/index.js +48919 -0
  5. package/index.html +13 -0
  6. package/package.json +30 -0
  7. package/public/favicon.ico +0 -0
  8. package/src/Design.vue +53 -0
  9. package/src/assets/logo.png +0 -0
  10. package/src/canvas.js +34 -0
  11. package/src/components/builtin/CanvasBox.vue +22 -0
  12. package/src/components/builtin/CanvasCol.vue +89 -0
  13. package/src/components/builtin/CanvasCollection.js +278 -0
  14. package/src/components/builtin/CanvasCollection.vue +106 -0
  15. package/src/components/builtin/CanvasIcon.vue +30 -0
  16. package/src/components/builtin/CanvasImg.vue +18 -0
  17. package/src/components/builtin/CanvasPlaceholder.vue +26 -0
  18. package/src/components/builtin/CanvasRow.vue +67 -0
  19. package/src/components/builtin/CanvasRowColContainer.vue +42 -0
  20. package/src/components/builtin/CanvasSlot.vue +22 -0
  21. package/src/components/builtin/CanvasText.vue +18 -0
  22. package/src/components/builtin/builtin.json +955 -0
  23. package/src/components/builtin/helper.js +46 -0
  24. package/src/components/builtin/index.js +33 -0
  25. package/src/components/common/index.js +158 -0
  26. package/src/components/container/CanvasAction.vue +554 -0
  27. package/src/components/container/CanvasContainer.vue +244 -0
  28. package/src/components/container/CanvasDivider.vue +246 -0
  29. package/src/components/container/CanvasDragItem.vue +38 -0
  30. package/src/components/container/CanvasFooter.vue +86 -0
  31. package/src/components/container/CanvasMenu.vue +214 -0
  32. package/src/components/container/CanvasResize.vue +195 -0
  33. package/src/components/container/CanvasResizeBorder.vue +219 -0
  34. package/src/components/container/container.js +791 -0
  35. package/src/components/container/keyboard.js +147 -0
  36. package/src/components/container/shortCutPopover.vue +181 -0
  37. package/src/components/render/CanvasEmpty.vue +14 -0
  38. package/src/components/render/RenderMain.js +408 -0
  39. package/src/components/render/context.js +53 -0
  40. package/src/components/render/render.js +689 -0
  41. package/src/components/render/runner.js +140 -0
  42. package/src/i18n/en.json +5 -0
  43. package/src/i18n/zh.json +5 -0
  44. package/src/i18n.js +21 -0
  45. package/src/index.js +96 -0
  46. package/src/locale.js +19 -0
  47. package/src/lowcode.js +104 -0
  48. package/src/main.js +17 -0
  49. package/test/form.json +690 -0
  50. package/test/group.json +99 -0
  51. package/test/jsslot.json +427 -0
  52. package/vite.config.js +73 -0
@@ -0,0 +1,244 @@
1
+ <template>
2
+ <canvas-action
3
+ :hoverState="hoverState"
4
+ :selectState="selectState"
5
+ :lineState="lineState"
6
+ :windowGetClickEventTarget="target"
7
+ :resize="canvasState.type === 'absolute'"
8
+ @select-slot="selectSlot"
9
+ @setting="settingModle"
10
+ ></canvas-action>
11
+ <canvas-divider :selectState="selectState"></canvas-divider>
12
+ <canvas-resize-border :iframe="iframe"></canvas-resize-border>
13
+ <canvas-resize>
14
+ <iframe
15
+ v-if="!loading"
16
+ id="canvas"
17
+ ref="iframe"
18
+ :src="canvasSrc"
19
+ style="border: none; width: 100%; height: 100%"
20
+ ></iframe>
21
+ <div v-else class="datainit-tip">应用数据初始化中...</div>
22
+ </canvas-resize>
23
+ <canvas-menu @insert="insertComponent"></canvas-menu>
24
+ <!-- 快捷选择物料面板 -->
25
+ <div v-if="insertPosition" ref="insertPanel" class="insert-panel">
26
+ <component :is="materialsPanel" :shortcut="insertPosition" @close="insertPosition = false"></component>
27
+ </div>
28
+ </template>
29
+
30
+ <script>
31
+ import { onMounted, ref, computed, onUnmounted } from 'vue'
32
+ import { iframeMonitoring } from '@opentiny/tiny-engine-common/js/monitor'
33
+ import { useTranslate, useCanvas, useResource } from '@opentiny/tiny-engine-controller'
34
+ import CanvasAction from './CanvasAction.vue'
35
+ import CanvasResize from './CanvasResize.vue'
36
+ import CanvasMenu, { closeMenu, openMenu } from './CanvasMenu.vue'
37
+ import { NODE_UID, NODE_LOOP } from '../common'
38
+ import { registerHostkeyEvent, removeHostkeyEvent } from './keyboard'
39
+ import CanvasDivider from './CanvasDivider.vue'
40
+ import CanvasResizeBorder from './CanvasResizeBorder.vue'
41
+ import {
42
+ canvasState,
43
+ onMouseUp,
44
+ dragMove,
45
+ dragState,
46
+ hoverState,
47
+ selectState,
48
+ lineState,
49
+ removeNodeById,
50
+ updateRect,
51
+ getElement,
52
+ dragStart,
53
+ getOffset,
54
+ selectNode,
55
+ initCanvas,
56
+ clearLineState,
57
+ querySelectById,
58
+ getCurrent
59
+ } from './container'
60
+
61
+ export default {
62
+ components: { CanvasAction, CanvasResize, CanvasMenu, CanvasDivider, CanvasResizeBorder },
63
+ props: {
64
+ controller: Object,
65
+ canvasSrc: String,
66
+ materialsPanel: Object
67
+ },
68
+ emits: ['selected', 'remove'],
69
+ setup(props, { emit }) {
70
+ const iframe = ref(null)
71
+ const insertPanel = ref(null)
72
+ const insertPosition = ref(false)
73
+ const loading = computed(() => useCanvas().isLoading())
74
+ let showSettingModle = ref(false)
75
+ let target = ref(null)
76
+
77
+ const setCurrentNode = async (event) => {
78
+ const { clientX, clientY } = event
79
+ const element = getElement(event.target)
80
+ closeMenu()
81
+ let node = getCurrent().schema
82
+
83
+ if (element) {
84
+ const currentElement = querySelectById(getCurrent().schema?.id)
85
+
86
+ if (!currentElement?.contains(element) || event.button === 0) {
87
+ const loopId = element.getAttribute(NODE_LOOP)
88
+ if (loopId) {
89
+ node = await selectNode(element.getAttribute(NODE_UID), `loop-id=${loopId}`)
90
+ } else {
91
+ node = await selectNode(element.getAttribute(NODE_UID))
92
+ }
93
+ }
94
+
95
+ if (event.button === 0 && element !== element.ownerDocument.body) {
96
+ const { x, y } = element.getBoundingClientRect()
97
+
98
+ dragStart(node, element, { offsetX: clientX - x, offsetY: clientY - y })
99
+ }
100
+
101
+ // 如果是点击右键则打开右键菜单
102
+ if (event.button === 2) {
103
+ openMenu(getOffset(event.target), event)
104
+ }
105
+ }
106
+ }
107
+
108
+ const beforeCanvasReady = () => {
109
+ if (iframe.value) {
110
+ const win = iframe.value.contentWindow
111
+
112
+ win.thirdPartyDeps = useResource().resState.thirdPartyDeps
113
+ }
114
+ }
115
+
116
+ const canvasReady = ({ detail }) => {
117
+ if (iframe.value) {
118
+ // 设置monitor报错埋点
119
+ iframeMonitoring()
120
+
121
+ initCanvas({ emit, renderer: detail, iframe: iframe.value, controller: props.controller })
122
+
123
+ const doc = iframe.value.contentDocument
124
+ const win = iframe.value.contentWindow
125
+
126
+ // 以下是内部iframe监听的事件
127
+ win.addEventListener('mousedown', (event) => {
128
+ insertPosition.value = false
129
+ setCurrentNode(event)
130
+ target.value = event.target
131
+ })
132
+
133
+ win.addEventListener('dragover', (ev) => {
134
+ ev.dataTransfer.dropEffect = 'move'
135
+ ev.preventDefault()
136
+ dragMove(ev)
137
+ })
138
+
139
+ win.addEventListener('drop', (ev) => {
140
+ ev.preventDefault()
141
+ onMouseUp(ev)
142
+ })
143
+
144
+ win.addEventListener('mousemove', (ev) => {
145
+ dragMove(ev, true)
146
+ })
147
+
148
+ // 阻止浏览器默认的右键菜单功能
149
+ win.oncontextmenu = (e) => {
150
+ e.preventDefault()
151
+ }
152
+
153
+ registerHostkeyEvent(doc)
154
+
155
+ win.addEventListener('scroll', updateRect, true)
156
+ }
157
+ }
158
+ // 设置弹窗
159
+ const settingModle = () => {
160
+ showSettingModle.value = true
161
+ }
162
+
163
+ const updateI18n = (message) => {
164
+ if (message?.data?.isI18n) {
165
+ const data = message.data.data || {}
166
+ const ensureI18n = useTranslate().ensureI18n
167
+ Object.keys(data).forEach((key) => {
168
+ ensureI18n(data[key], false)
169
+ })
170
+ }
171
+ }
172
+ const run = () => {
173
+ // 以下是外部window需要监听的事件
174
+ window.addEventListener('mousedown', (e) => {
175
+ insertPosition.value = insertPanel.value?.contains(e.target)
176
+ target.value = e.target
177
+ })
178
+
179
+ window.addEventListener('dragenter', () => {
180
+ // 如果拖拽范围超出了iframe范围,则清空拖拽位置数据
181
+ clearLineState()
182
+ })
183
+
184
+ window.addEventListener('message', updateI18n)
185
+ }
186
+
187
+ const insertComponent = (position) => {
188
+ insertPosition.value = position
189
+ }
190
+
191
+ const selectSlot = (slotName) => {
192
+ hoverState.slot = slotName
193
+ }
194
+
195
+ onMounted(() => run(iframe))
196
+ onUnmounted(() => {
197
+ removeHostkeyEvent(iframe.value.contentDocument)
198
+ window.removeEventListener('message', updateI18n, false)
199
+ })
200
+
201
+ document.addEventListener('beforeCanvasReady', beforeCanvasReady)
202
+ document.addEventListener('canvasReady', canvasReady)
203
+
204
+ return {
205
+ iframe,
206
+ dragState,
207
+ hoverState,
208
+ selectState,
209
+ lineState,
210
+ removeNodeById,
211
+ selectSlot,
212
+ canvasState,
213
+ insertComponent,
214
+ insertPanel,
215
+ settingModle,
216
+ target,
217
+ showSettingModle,
218
+ insertPosition,
219
+ loading
220
+ }
221
+ }
222
+ }
223
+ </script>
224
+ <style lang="less" scoped>
225
+ .insert-panel {
226
+ z-index: 1;
227
+ position: fixed;
228
+ top: 200px;
229
+ left: 400px;
230
+ width: 480px;
231
+ :deep(.components-wrap) {
232
+ & > .tiny-collapse {
233
+ height: 300px;
234
+ }
235
+ }
236
+ }
237
+ .datainit-tip {
238
+ display: flex;
239
+ height: 100%;
240
+ justify-content: center;
241
+ align-items: center;
242
+ color: #1890ff;
243
+ }
244
+ </style>
@@ -0,0 +1,246 @@
1
+ <template>
2
+ <div
3
+ v-if="state.showVerticalDivider"
4
+ class="divider-wrapper divider-vertical"
5
+ @click="handleSplit"
6
+ @mouseenter="handleMouseEnter('vertical')"
7
+ @mouseleave="handleMouseLeave"
8
+ >
9
+ <div class="divider"></div>
10
+ </div>
11
+ <div
12
+ v-if="state.showHorizontalDivider"
13
+ class="divider-wrapper divider-horizontal"
14
+ @click="handleSplitRow"
15
+ @mouseenter="handleMouseEnter('horizontal')"
16
+ @mouseleave="handleMouseLeave"
17
+ >
18
+ <div class="divider"></div>
19
+ </div>
20
+
21
+ <div v-if="state.showDividerLine" class="divider-line" :style="state.dividerStyle"></div>
22
+ </template>
23
+
24
+ <script>
25
+ import { reactive, watch } from 'vue'
26
+ import { extend } from '@opentiny/vue-renderless/common/object'
27
+ import { getCurrent, updateRect } from './container'
28
+
29
+ const LEGAL_DIVIDER_COMPONENT = ['CanvasRow', 'CanvasCol']
30
+
31
+ const ROW_SNIPPET = {
32
+ componentName: 'CanvasRow',
33
+ props: {
34
+ rowGap: '20px',
35
+ colGap: '20px'
36
+ },
37
+ children: [
38
+ {
39
+ componentName: 'CanvasCol',
40
+ props: {
41
+ rowGap: '20px',
42
+ colGap: '20px',
43
+ grow: true,
44
+ shrink: true,
45
+ widthType: 'auto'
46
+ }
47
+ }
48
+ ]
49
+ }
50
+
51
+ const COL_SNIPPET = {
52
+ componentName: 'CanvasCol',
53
+ props: {
54
+ rowGap: '20px',
55
+ colGap: '20px',
56
+ grow: true,
57
+ shrink: true,
58
+ widthType: 'auto'
59
+ },
60
+ children: []
61
+ }
62
+
63
+ export default {
64
+ props: {
65
+ selectState: {
66
+ type: Object,
67
+ default: () => ({})
68
+ }
69
+ },
70
+ setup(props) {
71
+ const state = reactive({
72
+ horizontalLeft: 0,
73
+ horizontalTop: 0,
74
+ verLeft: 0,
75
+ verTop: 0,
76
+ showVerticalDivider: false,
77
+ showHorizontalDivider: false,
78
+ showDividerLine: false,
79
+ dividerStyle: {}
80
+ })
81
+
82
+ const handleSplit = () => {
83
+ const { parent, schema } = getCurrent()
84
+
85
+ const index = parent.children.findIndex(({ id }) => id === schema.id)
86
+
87
+ parent.children.splice(index + 1, 0, extend(true, {}, COL_SNIPPET))
88
+ updateRect()
89
+ }
90
+
91
+ const handleSplitRow = () => {
92
+ const { parent, schema } = getCurrent()
93
+
94
+ const index = parent.children.findIndex(({ id }) => id === schema.id)
95
+
96
+ if (schema.componentName === 'CanvasRow') {
97
+ parent.children.splice(index + 1, 0, extend(true, {}, ROW_SNIPPET))
98
+
99
+ return
100
+ }
101
+
102
+ // 当前选中组件是 CanvasCol 横向切割
103
+ if (schema.componentName === 'CanvasCol') {
104
+ // 当前组件为空组件,直接切成两行
105
+ if (!schema.children?.length) {
106
+ schema.children = [
107
+ {
108
+ ...extend(true, {}, ROW_SNIPPET),
109
+ children: [{ ...extend(true, {}, COL_SNIPPET) }]
110
+ },
111
+ {
112
+ ...extend(true, {}, ROW_SNIPPET),
113
+ children: [{ ...extend(true, {}, COL_SNIPPET) }]
114
+ }
115
+ ]
116
+ } else if (schema.children[0].componentName !== 'CanvasRow') {
117
+ // 当前组件不为空组件且第一个孩子不为 row,则是第一次切割,切割成两行,需要将原来有的 children 放置到第一个 row 的 col
118
+ schema.children = [
119
+ {
120
+ ...extend(true, {}, ROW_SNIPPET),
121
+ children: [
122
+ {
123
+ ...extend(true, {}, COL_SNIPPET),
124
+ children: [...(schema.children || [])]
125
+ }
126
+ ]
127
+ },
128
+ extend(true, {}, ROW_SNIPPET)
129
+ ]
130
+ } else {
131
+ // 已经切割过了,直接加一行
132
+ schema.children.push(...extend(true, {}, ROW_SNIPPET))
133
+ }
134
+ }
135
+
136
+ updateRect()
137
+ }
138
+
139
+ watch(
140
+ () => props.selectState,
141
+ (selectState) => {
142
+ const { width, height, left, top, componentName } = selectState
143
+
144
+ if (!LEGAL_DIVIDER_COMPONENT.includes(componentName)) {
145
+ state.showHorizontalDivider = false
146
+ state.showVerticalDivider = false
147
+
148
+ return
149
+ }
150
+
151
+ state.showHorizontalDivider = true
152
+ state.showVerticalDivider = 'CanvasRow' !== componentName
153
+
154
+ const centerLeft = left + width / 2
155
+ const centerTop = top + height / 2
156
+
157
+ state.verLeft = `${centerLeft}px`
158
+ state.verTop = `${top}px`
159
+
160
+ state.horizontalLeft = `${left}px`
161
+ state.horizontalTop = `${centerTop}px`
162
+ },
163
+ { deep: true }
164
+ )
165
+
166
+ // 鼠标进入剪刀按钮,出现剪刀线
167
+ const handleMouseEnter = (type) => {
168
+ const { width, height, left, top } = props.selectState
169
+ if (type === 'vertical') {
170
+ state.dividerStyle = {
171
+ width: '1px',
172
+ height: `${height}px`,
173
+ left: `${state.verLeft}px`,
174
+ top: `${top}px`
175
+ }
176
+ } else {
177
+ state.dividerStyle = {
178
+ width: `${width}px`,
179
+ height: '1px',
180
+ left: `${left}px`,
181
+ top: `${state.horizontalTop}px`
182
+ }
183
+ }
184
+ state.showDividerLine = true
185
+ }
186
+
187
+ const handleMouseLeave = () => {
188
+ state.showDividerLine = false
189
+ state.dividerStyle = {}
190
+ }
191
+
192
+ return {
193
+ state,
194
+ handleSplit,
195
+ handleSplitRow,
196
+ handleMouseEnter,
197
+ handleMouseLeave
198
+ }
199
+ }
200
+ }
201
+ </script>
202
+
203
+ <style lang="less" scoped>
204
+ .divider-wrapper {
205
+ position: fixed;
206
+ display: flex;
207
+ align-items: center;
208
+ justify-content: center;
209
+ width: 24px;
210
+ height: 24px;
211
+ border-radius: 50%;
212
+ background-color: var(--ti-lowcode-common-text-color-3);
213
+ cursor: pointer;
214
+ z-index: 3;
215
+ &.divider-vertical {
216
+ transform: translateX(-50%);
217
+ top: v-bind('state.verTop');
218
+ left: v-bind('state.verLeft');
219
+ }
220
+ &.divider-horizontal {
221
+ transform: translate(10px, -50%) rotate(270deg);
222
+ top: v-bind('state.horizontalTop');
223
+ left: v-bind('state.horizontalLeft');
224
+ }
225
+ &:hover {
226
+ background-color: var(--ti-lowcode-common-primary-color);
227
+ .divider {
228
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAGKADAAQAAAABAAAAGAAAAADiNXWtAAACbElEQVRIDZ2Vu2sVURCH9yZgbCxMsFFjKQkExScREgQtLISAXbS4GDu10fgHiL1gpxKDCLFRfICCCSpCwEiCYiNWFkYsImIRH4jgY/x+xzNy7t29614HvsycmfnN3j1nd5Nl0cysDrPwEV7DRVjn9Vaent7Y+wYvrWbUG/pJTIHsCzyBd1pg8v0NzcmC2kDswdkizIFmyKZCK4F+uWwGelxPfAx+wFPPpZ58JzyHX3DEa8TdoFmyesYf3ZKu+nd40nyJvGzEc+7JjYWK2aTn3JPXRTRzVhdYhjkvpp78KMieNeW7yGnPZYfSmsfktV3LHSSWoNVhro+CbTTvdzH+KGyI61batdSXdAcXQDYWBcGx1m2+ha/wHRZUwK+C9/ABtA3q6W7S+vadl6AH9LT8hAk4CKdAQtlJuBIis3340zE+gR+PsXqlkVYzNEsz/5wrQR/MQ2qfWYzHX72RWE+UnppPoP3vijUNVm9qCyz60rvSnXTAJtBjOwKr0wbWV8GtaDulkVYzdLbtGaLdINPed1ZVt3Mlf/21r8NVL1Cpj1+sM9IZvAK9uY8qCas2MfAGyPbC9RCZDVXVl/YxbHsc+FCNxDpE3cWDUmHVIoPug2yna4hvhYzZLs/9l2fInjjoZjqA3JaYn07zbccM0Qujw839TyB3B2Q72h4sAcIDQW52uWiABsf63aJ6aQ6h3uqX8A38q5nTULsHsq25YlkCweEgMzv3j77B2He7rK+hhmAFLII+YGsaigULevSU6bHdXFDOp2g8DrIz+Wo+Q99Q6Da7lq9mWdG3aCWNL+BskaA5V6vVHpPT8FpzTevfkrgJcwZ0OJIAAAAASUVORK5CYII=);
229
+ }
230
+ }
231
+ .divider {
232
+ width: 12px;
233
+ height: 12px;
234
+ color: var(--ti-lowcode-common-text-color-3);
235
+ background-size: contain;
236
+ background-repeat: no-repeat;
237
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAGKADAAQAAAABAAAAGAAAAADiNXWtAAACO0lEQVRIDa2Uy6tNURzHN7cwMUAmHncot0QeV5QiBgZKmWEgzDDx+ANu5soMcUkxIY+iEFIKEZnIyMC9GZAMrke6hXt9Pvus1VnntPbZ53B/9dnrt36P7zpr7bVPUTRtD+5j+Arv4QwshDpbTIG1o2CvGmq12CVmk/ADnsGnMHccgCpbRiLWjuA/BTXUUrM0VzNwD+aVkcbjIMNveJnEUrePyWuYgP1JYi6+WmqWO3FLrpqKMy3tHE8LtzemLc99ITfcEm1MXERNtYsxcGs520nQBV61JWcyHw253W25OFVzbDqPj1D1MheF6tWM24LvcAD6w7yqdwF5tYvT4K90y6m5zQ/wE37BC9Bmw2f4Ah6DNdamFo/vlEHP3pvwB87CLjgGNrrwEbgY/K2MQ8E/zHg0+NbaY68aaqmpdmlLeT4HBSPf8RXQloA3ylvzDTx/34OmsLWxz9Hdqtlivo/l4NXy1syB1C4ziSK547THXjXU6tk20uECnn1fz91dNJynJu5gUxf1PZV4nr6DdzABj2BK7Rpq/votcDX4GxinxNagovjDoOZLdBcPwvy/h/souMDaROlGiK1PYv/kbg5C19u6V4b43bZ4z1M/GF/uQKbzFjF3NpjJdRXaEQQuVFQr7AK3K/Idw36Jb2Ec+jtU3iHnIqs61GRTe4naeDKbbQbXhbqbzVC9N4OSEfAPbD7UmbfMa7uirjDmD+H464/HQM3oB2f9lVxd7l9vFoVv4ESuIRN7QkzxaZlc8RcdnI7tMHZBlAAAAABJRU5ErkJggg==);
238
+ }
239
+ }
240
+
241
+ .divider-line {
242
+ position: fixed;
243
+ border: 1px dashed var(--ti-lowcode-common-primary-color);
244
+ z-index: 2;
245
+ }
246
+ </style>
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <div draggable="true" class="drag-item" @dragstart="dragstart">
3
+ <slot></slot>
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ import { dragStart } from './container'
9
+ export default {
10
+ props: {
11
+ data: Object
12
+ },
13
+ emits: ['click'],
14
+ setup(props, { emit }) {
15
+ const dragstart = (e) => {
16
+ if (props.data && e.button === 0) {
17
+ const data = JSON.parse(JSON.stringify(props.data))
18
+ emit('click', data)
19
+ dragStart(data)
20
+
21
+ // 设置拖拽鼠标样式和设置拖拽预览图
22
+ const target = e.target.querySelector('.component-item-component')
23
+ e.dataTransfer.effectAllowed = 'move'
24
+ e.dataTransfer.setDragImage(target, 10, 10)
25
+ }
26
+ }
27
+ return {
28
+ dragstart
29
+ }
30
+ }
31
+ }
32
+ </script>
33
+ <style lang="less">
34
+ .drag-item {
35
+ user-select: none;
36
+ cursor: move;
37
+ }
38
+ </style>
@@ -0,0 +1,86 @@
1
+ <template>
2
+ <div id="tiny-bottom-panel">
3
+ <div class="content">
4
+ <tiny-steps v-show="data.length > 0" :data="data" @click="(index, node) => $emit('click', node)"></tiny-steps>
5
+ <div v-show="data.length <= 0" class="tip">没有选中的节点</div>
6
+ </div>
7
+ </div>
8
+ </template>
9
+
10
+ <script>
11
+ import { Steps } from '@opentiny/vue'
12
+
13
+ export default {
14
+ components: {
15
+ TinySteps: Steps
16
+ },
17
+ props: {
18
+ data: {
19
+ type: Array,
20
+ default: []
21
+ }
22
+ },
23
+ emits: ['click']
24
+ }
25
+ </script>
26
+
27
+ <style lang="less" scoped>
28
+ #tiny-bottom-panel {
29
+ width: 100%;
30
+ height: var(--base-bottom-panel-height, 30px);
31
+ bottom: 0;
32
+ position: absolute;
33
+ background-color: var(--ti-lowcode-breadcrumb-bg);
34
+ z-index: 90;
35
+ border-top: 1px solid var(--ti-lowcode-canvas-footer-border-top-color);
36
+ .content {
37
+ .tip {
38
+ color: var(--ti-lowcode-breadcrumb-color);
39
+ line-height: 30px;
40
+ height: 30px;
41
+ padding-left: 10px;
42
+ }
43
+ :deep(.tiny-steps-advanced) {
44
+ li {
45
+ width: unset !important;
46
+ background: var(--ti-lowcode-breadcrumb-bg);
47
+ a {
48
+ padding: 0 3px 0 16px;
49
+ border-top: 0;
50
+ color: var(--ti-lowcode-breadcrumb-color);
51
+ transition: 0.3s;
52
+ &::after {
53
+ display: inline-block;
54
+ right: -6px;
55
+ border-left: 6px solid var(--ti-lowcode-breadcrumb-bg);
56
+ transition: 0.3s;
57
+ }
58
+ &::before {
59
+ display: inline-block;
60
+ right: -7px;
61
+ left: unset;
62
+ border-left: 6px solid var(--ti-lowcode-breadcrumb-icon-color);
63
+ transition: 0.3s;
64
+ z-index: 1;
65
+ }
66
+ &:hover {
67
+ background: var(--ti-lowcode-breadcrumb-hover-bg);
68
+ &::after {
69
+ border-left-color: var(--ti-lowcode-breadcrumb-hover-bg);
70
+ }
71
+ }
72
+ }
73
+ &:last-child a {
74
+ border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
75
+ border-radius: 0;
76
+ }
77
+ &:first-child a {
78
+ border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
79
+ border-radius: 0;
80
+ border-left: unset;
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ </style>