@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
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" href="/favicon.ico" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>Vite App</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.js"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opentiny/tiny-engine-canvas",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "vite",
|
|
9
|
+
"build": "vite build",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/index.js",
|
|
13
|
+
"module": "dist/index.js",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@opentiny/tiny-engine-common": "^1.0.0",
|
|
16
|
+
"@opentiny/tiny-engine-controller": "^1.0.0",
|
|
17
|
+
"@opentiny/tiny-engine-utils": "^1.0.0",
|
|
18
|
+
"@opentiny/tiny-engine-i18n-host": "^1.0.0",
|
|
19
|
+
"@opentiny/vue": "^3.8.0",
|
|
20
|
+
"@vueuse/core": "^9.6.0",
|
|
21
|
+
"vue": "3.2.45",
|
|
22
|
+
"vue-i18n": "9.2.2"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@vitejs/plugin-vue": "^4.2.3",
|
|
26
|
+
"@vitejs/plugin-vue-jsx": "^1.3.10",
|
|
27
|
+
"rollup-plugin-terser": "^7.0.2",
|
|
28
|
+
"vite": "^4.3.7"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
Binary file
|
package/src/Design.vue
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="height: calc(100vh - 100px)">
|
|
3
|
+
<canvas-container :controller="controller" @selected="nodeSelected"></canvas-container>
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup>
|
|
8
|
+
import CanvasContainer from './index'
|
|
9
|
+
|
|
10
|
+
import formJSON from '../test/form.json'
|
|
11
|
+
|
|
12
|
+
const controller = {
|
|
13
|
+
getPageSchema() {
|
|
14
|
+
return formJSON
|
|
15
|
+
},
|
|
16
|
+
getConfigureMap() {
|
|
17
|
+
return {
|
|
18
|
+
TinyTabs: {
|
|
19
|
+
nestingRule: {
|
|
20
|
+
childWhitelist: ['TinyTabItem'],
|
|
21
|
+
parentWhitelist: '',
|
|
22
|
+
descendantBlacklist: '',
|
|
23
|
+
ancestorWhitelist: ''
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
TinyForm: {
|
|
27
|
+
nestingRule: {
|
|
28
|
+
childWhitelist: ['TinyFormItem'],
|
|
29
|
+
parentWhitelist: '',
|
|
30
|
+
descendantBlacklist: '',
|
|
31
|
+
ancestorWhitelist: ''
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
getJSResource() {
|
|
37
|
+
return {
|
|
38
|
+
utils: { Select: 'select' }
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
addHistory() {},
|
|
42
|
+
closePlugin() {},
|
|
43
|
+
getMaterial() {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const nodeSelected = () => {}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
#app {
|
|
51
|
+
height: 100%;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
Binary file
|
package/src/canvas.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
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 { createApp } from 'vue'
|
|
14
|
+
import * as TinyVue from '@opentiny/vue'
|
|
15
|
+
|
|
16
|
+
import { RenderMain } from './index'
|
|
17
|
+
|
|
18
|
+
import { I18nInjectionKey, createI18n } from 'vue-i18n'
|
|
19
|
+
|
|
20
|
+
window.TinyLowcodeComponent = {}
|
|
21
|
+
|
|
22
|
+
Object.entries(TinyVue).forEach(([_key, component]) => {
|
|
23
|
+
const { name } = component
|
|
24
|
+
if (name) {
|
|
25
|
+
window.TinyLowcodeComponent[name] = component
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const i18nHost = createI18n({
|
|
30
|
+
locale: 'zh_CN',
|
|
31
|
+
messages: {}
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
createApp(RenderMain).use(i18nHost).provide(I18nInjectionKey, i18nHost).mount('#app')
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component :is="tag" v-bind="$attrs">
|
|
3
|
+
<slot>
|
|
4
|
+
<canvas-placeholder :placeholder="$attrs.placeholder"></canvas-placeholder>
|
|
5
|
+
</slot>
|
|
6
|
+
</component>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import CanvasPlaceholder from './CanvasPlaceholder.vue'
|
|
11
|
+
export default {
|
|
12
|
+
components: {
|
|
13
|
+
CanvasPlaceholder
|
|
14
|
+
},
|
|
15
|
+
props: {
|
|
16
|
+
tag: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: 'div'
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
</script>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="colRef" class="canvas-col">
|
|
3
|
+
<slot>
|
|
4
|
+
<canvas-placeholder :placeholder="$attrs.placeholder"></canvas-placeholder>
|
|
5
|
+
</slot>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import { computed } from 'vue'
|
|
11
|
+
import CanvasPlaceholder from './CanvasPlaceholder.vue'
|
|
12
|
+
import { getStyleValue, alignMap, justAlignMap } from './helper'
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
components: {
|
|
16
|
+
CanvasPlaceholder
|
|
17
|
+
},
|
|
18
|
+
props: {
|
|
19
|
+
flexBasis: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: '0px'
|
|
22
|
+
},
|
|
23
|
+
rowGap: {
|
|
24
|
+
type: [String, Number],
|
|
25
|
+
default: ''
|
|
26
|
+
},
|
|
27
|
+
colGap: {
|
|
28
|
+
type: [String, Number],
|
|
29
|
+
default: ''
|
|
30
|
+
},
|
|
31
|
+
align: {
|
|
32
|
+
type: [String, Number],
|
|
33
|
+
default: ''
|
|
34
|
+
},
|
|
35
|
+
justAlign: {
|
|
36
|
+
type: [String, Number],
|
|
37
|
+
default: ''
|
|
38
|
+
},
|
|
39
|
+
grow: {
|
|
40
|
+
type: Boolean,
|
|
41
|
+
default: true
|
|
42
|
+
},
|
|
43
|
+
shrink: {
|
|
44
|
+
type: Boolean,
|
|
45
|
+
default: true
|
|
46
|
+
},
|
|
47
|
+
widthType: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: 'auto'
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
setup(props) {
|
|
53
|
+
const getFlex = () => {
|
|
54
|
+
const { flexBasis, grow, shrink, widthType } = props
|
|
55
|
+
|
|
56
|
+
if (widthType === 'fixed') {
|
|
57
|
+
return `0 0 ${getStyleValue(flexBasis)}`
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return `${Number(grow)} ${Number(shrink)} ${getStyleValue(flexBasis)}`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const styles = computed(() => ({
|
|
64
|
+
flex: getFlex(props.flexBasis),
|
|
65
|
+
rowGap: getStyleValue(props.rowGap),
|
|
66
|
+
colGap: getStyleValue(props.colGap),
|
|
67
|
+
align: alignMap[props.align] || 'stretch',
|
|
68
|
+
justAlign: justAlignMap[props.justAlign] || 'flex-start'
|
|
69
|
+
}))
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
styles
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
</script>
|
|
77
|
+
|
|
78
|
+
<style lang="less" scoped>
|
|
79
|
+
.canvas-col {
|
|
80
|
+
display: flex;
|
|
81
|
+
flex: v-bind('styles.flex');
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
flex-wrap: nowrap;
|
|
84
|
+
row-gap: v-bind('styles.rowGap');
|
|
85
|
+
column-gap: v-bind('styles.colGap');
|
|
86
|
+
align-items: v-bind('styles.align');
|
|
87
|
+
justify-content: v-bind('styles.justAlign');
|
|
88
|
+
}
|
|
89
|
+
</style>
|
|
@@ -0,0 +1,278 @@
|
|
|
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 { getController } from '../render/render'
|
|
14
|
+
import { api } from '../render/RenderMain'
|
|
15
|
+
import { useModal } from '@opentiny/tiny-engine-controller'
|
|
16
|
+
|
|
17
|
+
const NAME_PREFIX = {
|
|
18
|
+
loop: 'loop',
|
|
19
|
+
table: 'getTableData',
|
|
20
|
+
page: 'pageConfig',
|
|
21
|
+
grid: 'tinyGrid',
|
|
22
|
+
tree: 'tinyTree',
|
|
23
|
+
select: 'tinySelect'
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const genRemoteMethodToLifeSetup = (variableName, sourceRef, pageSchema) => {
|
|
27
|
+
if (sourceRef.value.data?.option) {
|
|
28
|
+
const setupFn = pageSchema.lifeCycles?.setup?.value
|
|
29
|
+
const fetchBody = `
|
|
30
|
+
this.state.${variableName} = []
|
|
31
|
+
this.dataSourceMap.${sourceRef.value.name}.load().then(data=>{ this.state.${variableName}=data })`
|
|
32
|
+
|
|
33
|
+
if (!setupFn) {
|
|
34
|
+
pageSchema.lifeCycles = pageSchema.lifeCycles || {}
|
|
35
|
+
pageSchema.lifeCycles.setup = {
|
|
36
|
+
type: 'JSFunction',
|
|
37
|
+
value: `function setup({ props, state, watch, onMounted }) {${fetchBody}}`
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
pageSchema.lifeCycles.setup.value = setupFn.trim().replace(/\}$/, fetchBody + '}')
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const removeState = (pageSchema, variableName) => {
|
|
46
|
+
delete pageSchema.state[variableName]
|
|
47
|
+
|
|
48
|
+
const { parse, traverse, generate } = getController().ast
|
|
49
|
+
const setupFn = pageSchema.lifeCycles?.setup?.value
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const ast = parse(setupFn)
|
|
53
|
+
|
|
54
|
+
traverse(ast, {
|
|
55
|
+
ExpressionStatement(path) {
|
|
56
|
+
path.toString().includes(variableName) && path.remove()
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
pageSchema.lifeCycles.setup.value = generate(ast).code
|
|
61
|
+
} catch (error) {
|
|
62
|
+
// do nothing
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const setStateWithSourceRef = (pageSchema, variableName, sourceRef, data) => {
|
|
67
|
+
api.setState({ [variableName]: data })
|
|
68
|
+
pageSchema.state[variableName] = data
|
|
69
|
+
|
|
70
|
+
if (sourceRef.value.data?.option?.isSync) {
|
|
71
|
+
genRemoteMethodToLifeSetup(variableName, sourceRef, pageSchema)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const defaultHandlerTemplate = ({ node, sourceRef, schemaId, pageSchema }) => {
|
|
76
|
+
const genVarName = (schemaId) => `${NAME_PREFIX.loop}${schemaId}`
|
|
77
|
+
|
|
78
|
+
const updateNode = () => {
|
|
79
|
+
const { configure } = getController().getMaterial(node?.componentName)
|
|
80
|
+
|
|
81
|
+
if (!configure?.loop) {
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const variableName = genVarName(schemaId)
|
|
86
|
+
|
|
87
|
+
setStateWithSourceRef(pageSchema, variableName, sourceRef, sourceRef.value.data?.data)
|
|
88
|
+
|
|
89
|
+
node.loop = {
|
|
90
|
+
type: 'JSExpression',
|
|
91
|
+
value: `this.state.${variableName}`
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const clearBindVar = () => {
|
|
96
|
+
const variableName = genVarName(schemaId)
|
|
97
|
+
|
|
98
|
+
removeState(pageSchema, variableName)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
updateNode,
|
|
103
|
+
clearBindVar
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const generateAssginColumns = (newColumns, oldColumns) => {
|
|
108
|
+
newColumns.forEach((item) => {
|
|
109
|
+
const targetColumn = oldColumns.find((value) => value.field === item.field)
|
|
110
|
+
if (targetColumn) {
|
|
111
|
+
Object.assign(item, targetColumn)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
return newColumns
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const askShouldImportData = ({ node, sourceRef }) => {
|
|
118
|
+
useModal().confirm({
|
|
119
|
+
message: '检测到表格存在配置的数据,是否需要引入?',
|
|
120
|
+
exec() {
|
|
121
|
+
const sourceColums = sourceRef.value?.data?.columns?.map(({ title, field }) => ({ title, field })) || []
|
|
122
|
+
// 这里需要找到对应列,然后进行列合并
|
|
123
|
+
node.props.columns = generateAssginColumns(sourceColums, node.props.columns)
|
|
124
|
+
},
|
|
125
|
+
cancel() {
|
|
126
|
+
node.props.columns = [...sourceRef.value.data?.columns]
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const updateNodeHandler = ({ node, sourceRef, pageSchema, sourceName, methodName }) => {
|
|
132
|
+
if (!node || !node.props) {
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 如果使用了数据元则需要删除表格的data属性
|
|
137
|
+
delete node?.props?.data
|
|
138
|
+
|
|
139
|
+
if (node.props.columns.length) {
|
|
140
|
+
askShouldImportData({ node, sourceRef })
|
|
141
|
+
} else {
|
|
142
|
+
node.props.columns = [...sourceRef.value.data?.columns]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const pageConfig = {
|
|
146
|
+
attrs: {
|
|
147
|
+
currentPage: 1,
|
|
148
|
+
pageSize: 50,
|
|
149
|
+
pageSizes: [10, 20, 50],
|
|
150
|
+
total: 0,
|
|
151
|
+
layout: 'sizes,total, prev, pager, next, jumper'
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
node.props.pager = pageConfig
|
|
156
|
+
|
|
157
|
+
pageSchema.methods[methodName] = {
|
|
158
|
+
type: 'JSFunction',
|
|
159
|
+
value: `function ${methodName}({ page, sort, sortBy, filters}) {
|
|
160
|
+
/**
|
|
161
|
+
* @param {Object} sort 排序数据
|
|
162
|
+
* @param {Array} sortBy 排序方式
|
|
163
|
+
* @param {Object} page 分页数据
|
|
164
|
+
* @param {Array} filters 筛选数据
|
|
165
|
+
* @returns {Object} 返回一个promise对象,并且resolve格式为{ result: Array, page: { total: Number } }
|
|
166
|
+
*/
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
this.dataSourceMap.${sourceName}.load().then((res) => {
|
|
169
|
+
// 如果按照数据源面板的建议格式编写dataHandler
|
|
170
|
+
// 那么dataSourceMap的res格式应该是:{ code: string, msg: string, data: {items: any[], total: number} }
|
|
171
|
+
resolve({ result: res.data.items, page: { total: res.data.total } });
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
}`
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const extraHandlerMap = {
|
|
179
|
+
TinyGrid: ({ node, sourceRef, schemaId, pageSchema }) => {
|
|
180
|
+
const sourceName = sourceRef.value?.name
|
|
181
|
+
const methodName = `${NAME_PREFIX.table}${schemaId}`
|
|
182
|
+
|
|
183
|
+
node.props.fetchData = {
|
|
184
|
+
type: 'JSExpression',
|
|
185
|
+
value: `{ api: this.${methodName} }`
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const updateNode = () => updateNodeHandler({ node, sourceRef, pageSchema, sourceName, methodName })
|
|
189
|
+
|
|
190
|
+
const clearBindVar = () => {
|
|
191
|
+
// 当数据源组件children字段为空时,及时清空创建的methods
|
|
192
|
+
delete pageSchema.methods[methodName]
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
updateNode,
|
|
197
|
+
clearBindVar
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
TinyTree: ({ node, sourceRef, schemaId, pageSchema }) => {
|
|
201
|
+
const genVarName = (schemaId) => `${NAME_PREFIX.tree}${schemaId}`
|
|
202
|
+
|
|
203
|
+
const updateNode = () => {
|
|
204
|
+
const variableName = genVarName(schemaId)
|
|
205
|
+
|
|
206
|
+
const arrayToTree = (data) => {
|
|
207
|
+
const map = {}
|
|
208
|
+
const tree = []
|
|
209
|
+
let node = null
|
|
210
|
+
let i = 0
|
|
211
|
+
|
|
212
|
+
for (i = 0; i < data.length; i++) {
|
|
213
|
+
map[data[i].id] = data[i]
|
|
214
|
+
data[i].children = []
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for (i = 0; i < data.length; i++) {
|
|
218
|
+
node = data[i]
|
|
219
|
+
if (node.pid !== '') {
|
|
220
|
+
map[node.pid]?.children?.push(node)
|
|
221
|
+
} else {
|
|
222
|
+
tree.push(node)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return tree
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
setStateWithSourceRef(pageSchema, variableName, sourceRef, arrayToTree(sourceRef.value.data?.data))
|
|
230
|
+
|
|
231
|
+
node.props.data = {
|
|
232
|
+
type: 'JSExpression',
|
|
233
|
+
value: `this.state.${variableName}`
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const clearBindVar = () => {
|
|
238
|
+
const variableName = genVarName(schemaId)
|
|
239
|
+
|
|
240
|
+
removeState(pageSchema, variableName)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
updateNode,
|
|
245
|
+
clearBindVar
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
TinySelect: ({ node, sourceRef, schemaId, pageSchema }) => {
|
|
249
|
+
const genVarName = (schemaId) => `${NAME_PREFIX.select}${schemaId}`
|
|
250
|
+
|
|
251
|
+
const updateNode = () => {
|
|
252
|
+
const variableName = genVarName(schemaId)
|
|
253
|
+
|
|
254
|
+
setStateWithSourceRef(pageSchema, variableName, sourceRef, sourceRef.value.data?.data)
|
|
255
|
+
|
|
256
|
+
node.props.options = {
|
|
257
|
+
type: 'JSExpression',
|
|
258
|
+
value: `this.state.${variableName}`
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const clearBindVar = () => {
|
|
263
|
+
const variableName = genVarName(schemaId)
|
|
264
|
+
|
|
265
|
+
removeState(pageSchema, variableName)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
updateNode,
|
|
270
|
+
clearBindVar
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export const getHandler = ({ node, sourceRef, schemaId, pageSchema }) =>
|
|
276
|
+
extraHandlerMap[node.componentName]
|
|
277
|
+
? extraHandlerMap[node.componentName]({ node, sourceRef, schemaId, pageSchema })
|
|
278
|
+
: defaultHandlerTemplate({ node, sourceRef, schemaId, pageSchema })
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component :is="tag" v-bind="$attrs">
|
|
3
|
+
<slot>
|
|
4
|
+
<canvas-placeholder>{{ source?.name || '未选择数据源' }}</canvas-placeholder>
|
|
5
|
+
</slot>
|
|
6
|
+
</component>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script>
|
|
10
|
+
import { ref, watch, computed, inject } from 'vue'
|
|
11
|
+
import { getController } from '../render/render'
|
|
12
|
+
import CanvasPlaceholder from './CanvasPlaceholder.vue'
|
|
13
|
+
|
|
14
|
+
import { getHandler } from './CanvasCollection.js'
|
|
15
|
+
|
|
16
|
+
export const fetchDataSourceDetail = (dataSourceId) =>
|
|
17
|
+
getController().request.get(`/app-center/api/sources/detail/${dataSourceId}`)
|
|
18
|
+
|
|
19
|
+
export default {
|
|
20
|
+
components: {
|
|
21
|
+
CanvasPlaceholder
|
|
22
|
+
},
|
|
23
|
+
props: {
|
|
24
|
+
tag: {
|
|
25
|
+
type: String,
|
|
26
|
+
default: 'div'
|
|
27
|
+
},
|
|
28
|
+
schema: {
|
|
29
|
+
type: Object,
|
|
30
|
+
default: () => ({})
|
|
31
|
+
},
|
|
32
|
+
dataSource: [String, Array, Number]
|
|
33
|
+
},
|
|
34
|
+
setup(props) {
|
|
35
|
+
const source = ref(null)
|
|
36
|
+
const pageSchema = inject('rootSchema')
|
|
37
|
+
|
|
38
|
+
if (props.dataSource) {
|
|
39
|
+
fetchDataSourceDetail(props.dataSource).then((res) => {
|
|
40
|
+
source.value = res
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let handler
|
|
45
|
+
|
|
46
|
+
watch(
|
|
47
|
+
() => props.dataSource,
|
|
48
|
+
async (value) => {
|
|
49
|
+
if (value) {
|
|
50
|
+
source.value = await fetchDataSourceDetail(value)
|
|
51
|
+
if (props.schema?.children[0]) {
|
|
52
|
+
handler = getHandler({
|
|
53
|
+
sourceRef: source,
|
|
54
|
+
node: props.schema?.children[0],
|
|
55
|
+
schemaId: props.schema.id,
|
|
56
|
+
pageSchema
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
handler?.updateNode()
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const isEmpty = computed(() => {
|
|
65
|
+
const { children } = props.schema || {}
|
|
66
|
+
|
|
67
|
+
return Array.isArray(children) ? !children.length : !children
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
watch(
|
|
71
|
+
() => isEmpty.value,
|
|
72
|
+
(value) => {
|
|
73
|
+
if (value) {
|
|
74
|
+
// 清除自动创建的state,method与setup逻辑
|
|
75
|
+
if (handler) {
|
|
76
|
+
handler.clearBindVar()
|
|
77
|
+
} else {
|
|
78
|
+
const schemaId = props.schema?.id
|
|
79
|
+
|
|
80
|
+
// 当页面初始化时handler是不存在的,所以需要通过数据源的schemaId(唯一性),去删除对应的方法
|
|
81
|
+
Object.keys(pageSchema.methods || {})?.some((item) => {
|
|
82
|
+
if (item.includes(schemaId)) {
|
|
83
|
+
delete pageSchema.methods[item]
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
return false
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
if (props.schema?.children[0]) {
|
|
91
|
+
handler = getHandler({
|
|
92
|
+
sourceRef: source,
|
|
93
|
+
node: props.schema?.children[0],
|
|
94
|
+
schemaId: props.schema.id,
|
|
95
|
+
pageSchema
|
|
96
|
+
})
|
|
97
|
+
handler.updateNode()
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
return { source }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import * as SvgIcons from '@opentiny/vue-icon'
|
|
3
|
+
import { h } from 'vue'
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
props: {
|
|
7
|
+
name: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: ''
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
setup(props) {
|
|
13
|
+
return () =>
|
|
14
|
+
h('span', { class: 'iconwarp' }, [
|
|
15
|
+
h(SvgIcons[props.name]?.(), { style: { width: '100%', height: '100%' } }) || 'error.'
|
|
16
|
+
])
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<style scoped>
|
|
22
|
+
.iconwarp {
|
|
23
|
+
display: inline-flex;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
align-items: center;
|
|
26
|
+
fill: currentColor;
|
|
27
|
+
width: 1em;
|
|
28
|
+
height: 1em;
|
|
29
|
+
}
|
|
30
|
+
</style>
|