forked from opentiny/tiny-engine
Compare commits
1 Commits
develop
...
atom-2024/
| Author | SHA1 | Date |
|---|---|---|
|
|
9a12bf3c2f |
|
|
@ -8,4 +8,4 @@ tool_params:
|
|||
secsolar:
|
||||
source_dir: ./
|
||||
cmetrics:
|
||||
exclude: vite.config.js|package.json|index.js|mockServer/assets|packages/vue-generator/test
|
||||
exclude: vite.config.js|package.json|index.js|mockServer/assets
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
name: AI Code Review
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: anc95/ChatGPT-CodeReview@main
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
LANGUAGE: Chinese
|
||||
OPENAI_API_ENDPOINT: https://api.openai.com/v1
|
||||
MODEL: gpt-3.5-turbo
|
||||
MAX_TOKENS: 4096
|
||||
MAX_PATCH_LENGTH: 10000
|
||||
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 16
|
||||
|
||||
- name: Install pnpm
|
||||
run: npm i -g pnpm
|
||||
|
|
@ -24,7 +24,7 @@ jobs:
|
|||
|
||||
- name: Get changed files
|
||||
id: get_changed_files
|
||||
uses: tj-actions/changed-files@v41
|
||||
uses: tj-actions/changed-files@v40
|
||||
with:
|
||||
files: |
|
||||
**.js
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ package-lock.json
|
|||
yarn.lock
|
||||
pnpm-lock.yaml
|
||||
lerna-debug.log
|
||||
packages/design-core/bundle-deps
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
|
|
|
|||
|
|
@ -769,7 +769,7 @@
|
|||
{
|
||||
"componentName": "TinyTimeLine",
|
||||
"props": {
|
||||
"active": 2,
|
||||
"active": "2",
|
||||
"data": [
|
||||
{
|
||||
"name": "基础配置"
|
||||
|
|
@ -1525,7 +1525,7 @@
|
|||
{
|
||||
"componentName": "div",
|
||||
"props": {
|
||||
"style": "border-width: 1px;z-index:1;border-style: solid; border-color: #ffffff; padding-top: 10px; padding-left: 10px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px; background-color: #fff; position: fixed; inset: auto 0% 0% 0%; height: 80px; line-height: 80px; border-radius: 0px;"
|
||||
"style": "border-width: 1px; border-style: solid; border-color: #ffffff; padding-top: 10px; padding-left: 10px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px; background-color: #fff; position: fixed; inset: auto 0% 0% 0%; height: 80px; line-height: 80px; border-radius: 0px;"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
|
|
@ -1550,7 +1550,7 @@
|
|||
{
|
||||
"componentName": "TinyCol",
|
||||
"props": {
|
||||
"span": 8
|
||||
"span": "8"
|
||||
},
|
||||
"id": "b9d051a5",
|
||||
"children": [
|
||||
|
|
@ -1563,7 +1563,7 @@
|
|||
{
|
||||
"componentName": "TinyCol",
|
||||
"props": {
|
||||
"span": 5,
|
||||
"span": "5",
|
||||
"style": "display: flex;"
|
||||
},
|
||||
"id": "02352776",
|
||||
|
|
@ -1597,7 +1597,7 @@
|
|||
{
|
||||
"componentName": "TinyCol",
|
||||
"props": {
|
||||
"span": 7
|
||||
"span": "7"
|
||||
},
|
||||
"id": "82b6c659",
|
||||
"children": [
|
||||
|
|
@ -1665,7 +1665,7 @@
|
|||
{
|
||||
"componentName": "TinyCol",
|
||||
"props": {
|
||||
"span": 4,
|
||||
"span": "4",
|
||||
"style": "display: flex; flex-direction: row-reverse; border-radius: 0px; height: 100%; justify-content: flex-start; align-items: center;"
|
||||
},
|
||||
"id": "10b73009",
|
||||
|
|
@ -1690,7 +1690,7 @@
|
|||
],
|
||||
"fileName": "createVm",
|
||||
"meta": {
|
||||
"id": "NTJ4MjvqoVj8OVsc",
|
||||
"id": 1977,
|
||||
"parentId": "0",
|
||||
"group": "staticPages",
|
||||
"occupier": {
|
||||
|
|
@ -1787,7 +1787,7 @@
|
|||
},
|
||||
{
|
||||
"componentName": "TinyPlusFrozenPage",
|
||||
"package": "@opentiny/vue",
|
||||
"package": "@opentiny/vuee",
|
||||
"exportName": "FrozenPage",
|
||||
"destructuring": true,
|
||||
"version": "3.4.1"
|
||||
|
|
@ -1925,13 +1925,6 @@
|
|||
"destructuring": true,
|
||||
"version": "0.1.16"
|
||||
},
|
||||
{
|
||||
"componentName": "TinyButton",
|
||||
"package": "@opentiny/vue",
|
||||
"exportName": "Button",
|
||||
"destructuring": true,
|
||||
"version": "0.1.16"
|
||||
},
|
||||
{
|
||||
"componentName": "TinyButtonGroup",
|
||||
"package": "@opentiny/vue",
|
||||
|
|
@ -1974,48 +1967,6 @@
|
|||
"destructuring": true,
|
||||
"version": "0.1.16"
|
||||
},
|
||||
{
|
||||
"componentName": "ElInput",
|
||||
"package": "element-plus",
|
||||
"exportName": "ElInput",
|
||||
"destructuring": true,
|
||||
"version": "2.4.2"
|
||||
},
|
||||
{
|
||||
"componentName": "ElButton",
|
||||
"package": "element-plus",
|
||||
"exportName": "ElButton",
|
||||
"destructuring": true,
|
||||
"version": "2.4.2"
|
||||
},
|
||||
{
|
||||
"componentName": "ElForm",
|
||||
"package": "element-plus",
|
||||
"exportName": "ElForm",
|
||||
"destructuring": true,
|
||||
"version": "2.4.2"
|
||||
},
|
||||
{
|
||||
"componentName": "ElFormItem",
|
||||
"package": "element-plus",
|
||||
"exportName": "ElFormItem",
|
||||
"destructuring": true,
|
||||
"version": "2.4.2"
|
||||
},
|
||||
{
|
||||
"componentName": "ElTable",
|
||||
"package": "element-plus",
|
||||
"exportName": "ElTable",
|
||||
"destructuring": true,
|
||||
"version": "2.4.2"
|
||||
},
|
||||
{
|
||||
"componentName": "ElTableColumn",
|
||||
"package": "element-plus",
|
||||
"exportName": "ElTableColumn",
|
||||
"destructuring": true,
|
||||
"version": "2.4.2"
|
||||
},
|
||||
{
|
||||
"componentName": "PortalHome",
|
||||
"main": "common/components/home",
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@
|
|||
"dependencies": {
|
||||
"@opentiny/tiny-engine-i18n-host": "workspace:*",
|
||||
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
|
||||
"@opentiny/vue": "~3.14.0",
|
||||
"@opentiny/vue-icon": "~3.14.0",
|
||||
"@opentiny/vue-theme": "~3.14.0",
|
||||
"@opentiny/vue": "~3.11.0",
|
||||
"@opentiny/vue-icon": "~3.11.0",
|
||||
"@opentiny/vue-theme": "~3.11.0",
|
||||
"@vue/shared": "^3.3.11",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^9.9.0"
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export default defineConfig(({ command, mode }) => {
|
|||
}
|
||||
|
||||
const vuePluginConfig = {}
|
||||
const styleLinks = ['https://npm.onmicrosoft.cn/@opentiny/vue-theme@3.14/index.css']
|
||||
const styleLinks = ['https://npm.onmicrosoft.cn/@opentiny/vue-theme@3.11/index.css']
|
||||
|
||||
config.publicDir = false
|
||||
|
||||
|
|
|
|||
|
|
@ -20,14 +20,12 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@opentiny/vue": "~3.10.0",
|
||||
"vite-plugin-css-injected-by-js": "^3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"vite": "^4.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "^3.4.15"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,12 @@
|
|||
"@opentiny/tiny-engine-builtin-component": "workspace:*",
|
||||
"@opentiny/tiny-engine-controller": "workspace:*",
|
||||
"@opentiny/tiny-engine-i18n-host": "workspace:*",
|
||||
"@opentiny/tiny-engine-live-component": "workspace:*",
|
||||
"@opentiny/tiny-engine-utils": "workspace:*",
|
||||
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
|
||||
"@opentiny/vue": "~3.10.0",
|
||||
"@opentiny/vue-icon": "~3.10.0",
|
||||
"@opentiny/vue-renderless": "~3.10.0",
|
||||
"@vue/babel-plugin-jsx": "1.1.1",
|
||||
"@vue/shared": "^3.3.4",
|
||||
"@vueuse/core": "^9.6.0"
|
||||
|
|
@ -43,9 +47,6 @@
|
|||
"vite": "^4.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentiny/vue": "^3.14.0",
|
||||
"@opentiny/vue-icon": "^3.14.0",
|
||||
"@opentiny/vue-renderless": "^3.14.0",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^9.9.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -432,6 +432,61 @@
|
|||
"configure": {
|
||||
"loop": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"icon": "Image",
|
||||
"name": {
|
||||
"zh_CN": "Live"
|
||||
},
|
||||
"component": "Live",
|
||||
"container": false,
|
||||
"schema": {
|
||||
"properties": [
|
||||
{
|
||||
"label": {
|
||||
"zh_CN": "基础信息"
|
||||
},
|
||||
"description": {
|
||||
"zh_CN": "基础信息"
|
||||
},
|
||||
"collapse": {
|
||||
"number": 6,
|
||||
"text": {
|
||||
"zh_CN": "显示更多"
|
||||
}
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"property": "code",
|
||||
"type": "String",
|
||||
"defaultValue": "",
|
||||
"bindState": true,
|
||||
"label": {
|
||||
"text": {
|
||||
"zh_CN": "代码逻辑"
|
||||
}
|
||||
},
|
||||
"cols": 12,
|
||||
"rules": [],
|
||||
"widget": {
|
||||
"component": "MetaCodeEditor",
|
||||
"props": {
|
||||
"language": "vue"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"events": {},
|
||||
"shortcuts": {},
|
||||
"contentMenu": {
|
||||
"actions": []
|
||||
}
|
||||
},
|
||||
"configure": {
|
||||
"loop": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -517,6 +572,20 @@
|
|||
"src": "https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/designer-default-icon.jpg"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": {
|
||||
"zh_CN": "Live"
|
||||
},
|
||||
"screenshot": "",
|
||||
"snippetName": "Live",
|
||||
"icon": "Image",
|
||||
"schema": {
|
||||
"componentName": "Live",
|
||||
"props": {
|
||||
"code": "<script setup lang='tsx'>import { ref } from 'vue';const msg = ref('Hello World!')</script><template><h1>{{ msg }}</h1><input v-model='msg' /></template>"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
/**
|
||||
* Copyright (c) 2023 - present TinyEngine Authors.
|
||||
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license.
|
||||
*
|
||||
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
||||
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
* Copyright (c) 2023 - present TinyEngine Authors.
|
||||
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license.
|
||||
*
|
||||
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
||||
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
|
||||
export const NODE_UID = 'data-uid'
|
||||
export const NODE_TAG = 'data-tag'
|
||||
|
|
@ -125,10 +125,9 @@ export const getClipboardSchema = (event) => translateStringToSchame(event.clipb
|
|||
*/
|
||||
export const dynamicImportComponents = async ({ package: pkg, script, components }) => {
|
||||
if (!script) return
|
||||
const scriptUrl = script.startsWith('.') ? new URL(script, location.href).href : script
|
||||
|
||||
if (!window.TinyComponentLibs[pkg]) {
|
||||
const modules = await import(/* @vite-ignore */ scriptUrl)
|
||||
const modules = await import(/* @vite-ignore */ script)
|
||||
|
||||
window.TinyComponentLibs[pkg] = modules
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,46 +27,14 @@
|
|||
</div>
|
||||
<!-- 绝对定位画布时调节元素大小 -->
|
||||
<template v-else>
|
||||
<div
|
||||
class="drag-resize resize-top"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'center', 'start')"
|
||||
></div>
|
||||
<div
|
||||
class="drag-resize resize-bottom"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'center', 'end')"
|
||||
></div>
|
||||
<div
|
||||
class="drag-resize resize-left"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'start', 'center')"
|
||||
></div>
|
||||
<div
|
||||
class="drag-resize resize-right"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'end', 'center')"
|
||||
></div>
|
||||
<div
|
||||
class="drag-resize resize-top-left"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'start', 'start')"
|
||||
></div>
|
||||
<div
|
||||
class="drag-resize resize-top-right"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'end', 'start')"
|
||||
></div>
|
||||
<div
|
||||
class="drag-resize resize-bottom-left"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'start', 'end')"
|
||||
></div>
|
||||
<div
|
||||
class="drag-resize resize-bottom-right"
|
||||
draggable="true"
|
||||
@mousedown.stop="onMousedown($event, 'end', 'end')"
|
||||
></div>
|
||||
<div class="drag-resize resize-top" @mousedown.stop="onMousedown($event, 'center', 'start')"></div>
|
||||
<div class="drag-resize resize-bottom" @mousedown.stop="onMousedown($event, 'center', 'end')"></div>
|
||||
<div class="drag-resize resize-left" @mousedown.stop="onMousedown($event, 'start', 'center')"></div>
|
||||
<div class="drag-resize resize-right" @mousedown.stop="onMousedown($event, 'end', 'center')"></div>
|
||||
<div class="drag-resize resize-top-left" @mousedown.stop="onMousedown($event, 'start', 'start')"></div>
|
||||
<div class="drag-resize resize-top-right" @mousedown.stop="onMousedown($event, 'end', 'start')"></div>
|
||||
<div class="drag-resize resize-bottom-left" @mousedown.stop="onMousedown($event, 'start', 'end')"></div>
|
||||
<div class="drag-resize resize-bottom-right" @mousedown.stop="onMousedown($event, 'end', 'end')"></div>
|
||||
</template>
|
||||
<div v-if="showAction" ref="optionRef" class="corner-mark-right" :style="fixStyle">
|
||||
<template v-if="!isModal">
|
||||
|
|
@ -100,7 +68,7 @@
|
|||
<div v-show="hoverState.configure?.isContainer" class="corner-mark-bottom-right">拖放元素到容器内</div>
|
||||
</div>
|
||||
<div v-show="lineState.height && lineState.width" class="canvas-rect line">
|
||||
<div :class="['hover-line', lineState.position, { forbidden: lineState.forbidden }]">
|
||||
<div :class="['hover-line', lineState.position]">
|
||||
<div v-if="lineState.position === 'in' && hoverState.configure" class="choose-slots"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -117,7 +85,6 @@ import {
|
|||
IconEyeclose
|
||||
} from '@opentiny/vue-icon'
|
||||
import {
|
||||
canvasState,
|
||||
getCurrent,
|
||||
removeNodeById,
|
||||
selectNode,
|
||||
|
|
@ -380,14 +347,15 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
const getStyleValues = (selectState, canvasSize, labelWidth, optionWidth) => {
|
||||
const getStyleValues = (selectState, siteCanvasWidth, siteCanvasHeight) => {
|
||||
const { left, top, width, height, doc } = selectState
|
||||
const { width: canvasWidth, height: canvasHeight } = canvasSize
|
||||
const labelRect = labelRef.value.getBoundingClientRect()
|
||||
const optionRect = optionRef.value.getBoundingClientRect()
|
||||
// 标签宽度和工具操作条宽度之和加上间距
|
||||
const fullRectWidth = labelWidth + optionWidth + OPTION_SPACE
|
||||
const fullRectWidth = labelRect.width + optionRect.width + OPTION_SPACE
|
||||
|
||||
// 是否 将label 标签放置到底部,判断 top 距离
|
||||
const isLabelAtBottom = top < LABEL_HEIGHT
|
||||
const isLabelAtBottom = top <= LABEL_HEIGHT
|
||||
const labelAlign = new Align({
|
||||
alignLeft: true,
|
||||
horizontalValue: 0,
|
||||
|
|
@ -396,7 +364,7 @@ export default {
|
|||
})
|
||||
|
||||
// 是否将操作栏放置到底部,判断当前选中组件底部与页面底部的距离。
|
||||
const isOptionAtBottom = canvasHeight - top - height >= OPTION_BAR_HEIGHT
|
||||
const isOptionAtBottom = siteCanvasHeight - top - height > OPTION_BAR_HEIGHT
|
||||
const optionAlign = new Align({
|
||||
alignLeft: false,
|
||||
horizontalValue: 0,
|
||||
|
|
@ -410,13 +378,13 @@ export default {
|
|||
// 选中框宽度小于标签宽度和工具操作条宽度之和加上间距
|
||||
|
||||
// 如果labe宽度大于选中框宽度,并且label右侧已经超出画布,则label对齐右侧
|
||||
const isLabelAlignRight = labelWidth > width && left + labelWidth + scrollBarWidth > canvasWidth
|
||||
const isLabelAlignRight = labelRect.width > width && left + labelRect.width + scrollBarWidth > siteCanvasWidth
|
||||
if (isLabelAlignRight) {
|
||||
labelAlign.align(positions.RIGHT)
|
||||
}
|
||||
|
||||
// 如果option宽度大于选中框宽度,并且option左侧已经超出画布,则option对齐左侧
|
||||
const isOptionAlignLeft = optionWidth > width && left + width - optionWidth < 0
|
||||
const isOptionAlignLeft = optionRect.width > width && left + width - optionRect.width < 0
|
||||
if (isOptionAlignLeft) {
|
||||
optionAlign.align(positions.LEFT)
|
||||
}
|
||||
|
|
@ -424,14 +392,14 @@ export default {
|
|||
if (isLabelAtBottom === isOptionAtBottom) {
|
||||
// 标签框和工具操作框都在顶部或者都在底部
|
||||
|
||||
if (left + fullRectWidth < canvasWidth) {
|
||||
if (left + fullRectWidth < siteCanvasWidth) {
|
||||
// 都放在左侧
|
||||
labelAlign.align(positions.LEFT)
|
||||
optionAlign.align(positions.LEFT, labelWidth + OPTION_SPACE)
|
||||
optionAlign.align(positions.LEFT, labelRect.width + OPTION_SPACE)
|
||||
} else {
|
||||
// 都放在右侧
|
||||
optionAlign.align(positions.RIGHT)
|
||||
labelAlign.align(positions.RIGHT, optionWidth + OPTION_SPACE)
|
||||
labelAlign.align(positions.RIGHT, optionRect.width + OPTION_SPACE)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -439,10 +407,10 @@ export default {
|
|||
labelAlign.align(positions.LEFT, Math.min(-left, width - fullRectWidth))
|
||||
}
|
||||
|
||||
if (left + width + scrollBarWidth > canvasWidth) {
|
||||
if (left + width + scrollBarWidth > siteCanvasWidth) {
|
||||
optionAlign.align(
|
||||
positions.RIGHT,
|
||||
Math.min(left + width + scrollBarWidth - canvasWidth, width - fullRectWidth)
|
||||
Math.min(left + width + scrollBarWidth - siteCanvasWidth, width - fullRectWidth)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -454,7 +422,8 @@ export default {
|
|||
}
|
||||
|
||||
watchPostEffect(async () => {
|
||||
const { left, top, width, height, doc } = props.selectState
|
||||
let { left } = props.selectState
|
||||
const { top, width, height, doc } = props.selectState
|
||||
|
||||
// nextTick后ref才能获取到元素。需要把监听的依赖放在await之前,否则无法监听变化
|
||||
await nextTick()
|
||||
|
|
@ -469,17 +438,15 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
|
||||
const scale = useLayout().getScale()
|
||||
const canvasRect = canvasState.iframe.getBoundingClientRect()
|
||||
const { width: labelWidth } = labelRef.value.getBoundingClientRect()
|
||||
const { width: optionWidth } = optionRef.value.getBoundingClientRect()
|
||||
|
||||
// canvas容器中,iframe以及iframe之外的元素clientRect的尺寸都是缩放过的,除以scale得到原始大小
|
||||
left -= ((1 - scale) / 2) * siteCanvasRect.width
|
||||
|
||||
const { labelStyleValue, optionStyleValue } = getStyleValues(
|
||||
{ left, top, width, height, doc },
|
||||
{ width: canvasRect.width / scale, height: canvasRect.height / scale },
|
||||
labelWidth / scale,
|
||||
optionWidth / scale
|
||||
siteCanvasRect.width * scale,
|
||||
siteCanvasRect.height
|
||||
)
|
||||
|
||||
labelStyle.value = labelStyleValue
|
||||
|
|
@ -573,12 +540,11 @@ export default {
|
|||
height: 100%;
|
||||
background: var(--ti-lowcode-canvas-hover-line-in-bg-color);
|
||||
}
|
||||
&.forbidden:not(.in) {
|
||||
&.forbid {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--ti-lowcode-canvas-hover-line-forbid-bg-color);
|
||||
}
|
||||
&.forbidden.in {
|
||||
background: var(--ti-lowcode-canvas-hover-line-in-forbid-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.choose-slots {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
:windowGetClickEventTarget="target"
|
||||
:resize="canvasState.type === 'absolute'"
|
||||
@select-slot="selectSlot"
|
||||
@setting="settingModel"
|
||||
@setting="settingModle"
|
||||
></canvas-action>
|
||||
<canvas-divider :selectState="selectState"></canvas-divider>
|
||||
<canvas-resize-border :iframe="iframe"></canvas-resize-border>
|
||||
|
|
@ -50,12 +50,12 @@ import {
|
|||
updateRect,
|
||||
getElement,
|
||||
dragStart,
|
||||
getOffset,
|
||||
selectNode,
|
||||
initCanvas,
|
||||
clearLineState,
|
||||
querySelectById,
|
||||
getCurrent,
|
||||
canvasApi
|
||||
getCurrent
|
||||
} from './container'
|
||||
|
||||
export default {
|
||||
|
|
@ -71,7 +71,7 @@ export default {
|
|||
const insertPanel = ref(null)
|
||||
const insertPosition = ref(false)
|
||||
const loading = computed(() => useCanvas().isLoading())
|
||||
let showSettingModel = ref(false)
|
||||
let showSettingModle = ref(false)
|
||||
let target = ref(null)
|
||||
|
||||
const setCurrentNode = async (event) => {
|
||||
|
|
@ -100,13 +100,11 @@ export default {
|
|||
|
||||
// 如果是点击右键则打开右键菜单
|
||||
if (event.button === 2) {
|
||||
openMenu(event)
|
||||
openMenu(getOffset(event.target), event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useCanvas().initCanvasApi(canvasApi)
|
||||
|
||||
const beforeCanvasReady = () => {
|
||||
if (iframe.value) {
|
||||
const win = iframe.value.contentWindow
|
||||
|
|
@ -121,6 +119,7 @@ export default {
|
|||
iframeMonitoring()
|
||||
|
||||
initCanvas({ emit, renderer: detail, iframe: iframe.value, controller: props.controller })
|
||||
useCanvas().renderer.value = { ...detail, ...window.canvasApi }
|
||||
|
||||
const doc = iframe.value.contentDocument
|
||||
const win = iframe.value.contentWindow
|
||||
|
|
@ -180,8 +179,8 @@ export default {
|
|||
}
|
||||
}
|
||||
// 设置弹窗
|
||||
const settingModel = () => {
|
||||
showSettingModel.value = true
|
||||
const settingModle = () => {
|
||||
showSettingModle.value = true
|
||||
}
|
||||
|
||||
const updateI18n = (message) => {
|
||||
|
|
@ -238,9 +237,9 @@ export default {
|
|||
canvasState,
|
||||
insertComponent,
|
||||
insertPanel,
|
||||
settingModel,
|
||||
settingModle,
|
||||
target,
|
||||
showSettingModel,
|
||||
showSettingModle,
|
||||
insertPosition,
|
||||
loading
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ const ROW_SNIPPET = {
|
|||
grow: true,
|
||||
shrink: true,
|
||||
widthType: 'auto'
|
||||
},
|
||||
children: []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -104,7 +103,16 @@ export default {
|
|||
if (schema.componentName === 'CanvasCol') {
|
||||
// 当前组件为空组件,直接切成两行
|
||||
if (!schema.children?.length) {
|
||||
schema.children = [extend(true, {}, ROW_SNIPPET), extend(true, {}, ROW_SNIPPET)]
|
||||
schema.children = [
|
||||
{
|
||||
...extend(true, {}, ROW_SNIPPET),
|
||||
children: [{ ...extend(true, {}, COL_SNIPPET) }]
|
||||
},
|
||||
{
|
||||
...extend(true, {}, ROW_SNIPPET),
|
||||
children: [{ ...extend(true, {}, COL_SNIPPET) }]
|
||||
}
|
||||
]
|
||||
} else if (schema.children[0].componentName !== 'CanvasRow') {
|
||||
// 当前组件不为空组件且第一个孩子不为 row,则是第一次切割,切割成两行,需要将原来有的 children 放置到第一个 row 的 col
|
||||
schema.children = [
|
||||
|
|
@ -121,7 +129,7 @@ export default {
|
|||
]
|
||||
} else {
|
||||
// 已经切割过了,直接加一行
|
||||
schema.children.push(extend(true, {}, ROW_SNIPPET))
|
||||
schema.children.push(...extend(true, {}, ROW_SNIPPET))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -194,7 +202,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.divider-wrapper {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
@ -231,7 +239,7 @@ export default {
|
|||
}
|
||||
|
||||
.divider-line {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
border: 1px dashed var(--ti-lowcode-common-primary-color);
|
||||
z-index: 2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export default {
|
|||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: []
|
||||
}
|
||||
},
|
||||
emits: ['click']
|
||||
|
|
@ -44,23 +44,37 @@ export default {
|
|||
li {
|
||||
width: unset !important;
|
||||
background: var(--ti-lowcode-breadcrumb-bg);
|
||||
.label {
|
||||
a {
|
||||
padding: 0 3px 0 16px;
|
||||
border-top: 0;
|
||||
color: var(--ti-lowcode-breadcrumb-color);
|
||||
transition: 0.3s;
|
||||
&::after {
|
||||
display: inline-block;
|
||||
right: -6px;
|
||||
border-left: 6px solid var(--ti-lowcode-breadcrumb-bg);
|
||||
transition: 0.3s;
|
||||
}
|
||||
&::before {
|
||||
display: inline-block;
|
||||
right: -7px;
|
||||
left: unset;
|
||||
border-left: 6px solid var(--ti-lowcode-breadcrumb-icon-color);
|
||||
transition: 0.3s;
|
||||
z-index: 1;
|
||||
}
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background: var(--ti-lowcode-breadcrumb-hover-bg);
|
||||
&::after {
|
||||
border-left-color: var(--ti-steps-advanced-li-hover-bg-color);
|
||||
border-left-color: var(--ti-lowcode-breadcrumb-hover-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-child .label {
|
||||
&:last-child a {
|
||||
border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
|
||||
border-radius: 0;
|
||||
}
|
||||
&:first-child .label {
|
||||
&:first-child a {
|
||||
border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
|
||||
border-radius: 0;
|
||||
border-left: unset;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<span>{{ item.name }}</span>
|
||||
<span v-if="item.items"><icon-right></icon-right></span>
|
||||
</div>
|
||||
<ul v-if="item.items && current === item" class="sub-menu menu-item" :style="subMenuStyles">
|
||||
<ul v-if="item.items && current === item" class="sub-menu menu-item">
|
||||
<template v-for="(subItem, subIndex) in item.items" :key="subIndex">
|
||||
<li
|
||||
:class="[{ 'menu-item-disabled': subItem.check && !subItem.check?.() }]"
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
<script lang="jsx">
|
||||
import { ref, reactive, nextTick } from 'vue'
|
||||
import { canvasState, getConfigure, getController, getCurrent, copyNode, removeNodeById } from './container'
|
||||
import { getConfigure, getController, getCurrent, copyNode, removeNodeById } from './container'
|
||||
import { useLayout, useModal, useCanvas } from '@opentiny/tiny-engine-controller'
|
||||
import { iconRight } from '@opentiny/vue-icon'
|
||||
|
||||
|
|
@ -45,35 +45,27 @@ const menuState = reactive({
|
|||
|
||||
const current = ref(null)
|
||||
const menuDom = ref(null)
|
||||
const subMenuStyles = ref(null)
|
||||
|
||||
export const closeMenu = () => {
|
||||
menuState.show = false
|
||||
current.value = null
|
||||
}
|
||||
|
||||
export const openMenu = (event) => {
|
||||
export const openMenu = (offset, event) => {
|
||||
const { x, y } = offset
|
||||
const { getScale } = useLayout()
|
||||
menuState.position = {
|
||||
left: event.clientX + 2 + 'px',
|
||||
top: event.clientY + 'px'
|
||||
// 位置处于画布右侧边缘时需要调整显示方向 TODO
|
||||
left: event.clientX * getScale() + x + 2 + 'px',
|
||||
top: event.clientY * getScale() + y + 'px'
|
||||
}
|
||||
menuState.show = sessionStorage.getItem('pageInfo') ? true : false
|
||||
|
||||
nextTick(() => {
|
||||
if (menuDom.value) {
|
||||
const { right, bottom, width, height } = menuDom.value.getBoundingClientRect()
|
||||
const canvasRect = canvasState.iframe.getBoundingClientRect()
|
||||
if (bottom > canvasRect.bottom) {
|
||||
menuState.position.top = `${parseInt(menuState.position.top) - height}px`
|
||||
}
|
||||
if (right > canvasRect.right) {
|
||||
menuState.position.left = `${parseInt(menuState.position.left) - width - 2}px`
|
||||
}
|
||||
// sub-menu样式width为100px,少于100宽度的空白区域则放置到左侧
|
||||
if (right + 100 < canvasRect.right) {
|
||||
subMenuStyles.value = { right: '-100px' }
|
||||
} else {
|
||||
subMenuStyles.value = { left: '-100px' }
|
||||
const { bottom, height, top } = menuDom.value.getBoundingClientRect()
|
||||
if (bottom > document.body?.clientHeight) {
|
||||
menuState.position.top = top - height + 'px'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -234,7 +226,6 @@ export default {
|
|||
close,
|
||||
current,
|
||||
menuDom,
|
||||
subMenuStyles,
|
||||
actionDisabled,
|
||||
onShowChildrenMenu
|
||||
}
|
||||
|
|
@ -244,7 +235,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.context-menu {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
}
|
||||
.menu-item {
|
||||
|
|
@ -285,6 +276,7 @@ export default {
|
|||
&.sub-menu {
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
right: -100px;
|
||||
top: -2px;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,12 +17,13 @@ import { canvasState } from './container'
|
|||
export default {
|
||||
setup() {
|
||||
const sizeStyle = computed(() => {
|
||||
const { width, height, maxWidth, minWidth } = useLayout().getDimension()
|
||||
const { width, maxWidth, minWidth, scale } = useLayout().getDimension()
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
maxWidth,
|
||||
minWidth
|
||||
minWidth,
|
||||
height: `${100 / scale}%`,
|
||||
transform: `scale(${scale}) translateY(-${(100 / scale - 100) / 2}%)`
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
<script>
|
||||
import { reactive, watch } from 'vue'
|
||||
import { useLayout } from '@opentiny/tiny-engine-controller'
|
||||
import { getCurrent, updateRect, selectState, querySelectById } from './container'
|
||||
|
||||
export default {
|
||||
|
|
@ -39,17 +38,12 @@ export default {
|
|||
}
|
||||
|
||||
const handleResize = (event, type) => {
|
||||
if (!props.iframe) {
|
||||
return
|
||||
}
|
||||
|
||||
let { clientX, clientY } = event
|
||||
const iframeRect = props.iframe.getBoundingClientRect()
|
||||
const scale = useLayout().getScale()
|
||||
|
||||
if (type !== 'iframe') {
|
||||
clientX = (clientX - iframeRect.left) / scale
|
||||
clientY = (clientY - iframeRect.top) / scale
|
||||
if (type === 'iframe' && props.iframe) {
|
||||
const iframeRect = props.iframe.getBoundingClientRect()
|
||||
clientX += iframeRect.left
|
||||
clientY += iframeRect.top
|
||||
}
|
||||
|
||||
const { parent, schema } = getCurrent()
|
||||
|
|
@ -71,9 +65,14 @@ export default {
|
|||
const parentWidth = parseInt(window.getComputedStyle(parentDomNode).width, 10)
|
||||
|
||||
// 最大宽度不能大于父组件宽度
|
||||
newWidth = Math.min(newWidth, parentWidth)
|
||||
if (newWidth >= parentWidth) {
|
||||
newWidth = parentWidth
|
||||
}
|
||||
|
||||
// 最小宽度32
|
||||
newWidth = Math.max(newWidth, 32)
|
||||
if (newWidth <= 32) {
|
||||
newWidth = 32
|
||||
}
|
||||
|
||||
schema.props.flexBasis = `${newWidth}px`
|
||||
schema.props.widthType = 'fixed'
|
||||
|
|
@ -187,7 +186,7 @@ export default {
|
|||
|
||||
<style lang="less" scoped>
|
||||
.resize-border {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -20,16 +20,15 @@ import {
|
|||
NODE_LOOP
|
||||
} from '../common'
|
||||
import { useCanvas, useLayout, useResource, useTranslate } from '@opentiny/tiny-engine-controller'
|
||||
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
|
||||
import Builtin from '../builtin/builtin.json'
|
||||
|
||||
export const POSITION = Object.freeze({
|
||||
TOP: 'top',
|
||||
BOTTOM: 'bottom',
|
||||
LEFT: 'left',
|
||||
RIGHT: 'right',
|
||||
IN: 'in'
|
||||
IN: 'in',
|
||||
FORBID: 'forbid'
|
||||
})
|
||||
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
|
||||
|
||||
const initialDragState = {
|
||||
keydown: false,
|
||||
|
|
@ -99,7 +98,6 @@ const initialLineState = {
|
|||
width: 0,
|
||||
left: 0,
|
||||
position: '',
|
||||
forbidden: false,
|
||||
id: '',
|
||||
config: null,
|
||||
doc: null
|
||||
|
|
@ -322,19 +320,19 @@ export const scrollToNode = (element) => {
|
|||
if (element) {
|
||||
const container = getDocument().documentElement
|
||||
const { clientWidth, clientHeight } = container
|
||||
const { left, right, top, bottom, width, height } = element.getBoundingClientRect()
|
||||
const { x, y, width, height } = element.getBoundingClientRect()
|
||||
const option = {}
|
||||
|
||||
if (right < 0) {
|
||||
option.left = container.scrollLeft + left - SCROLL_MARGIN
|
||||
} else if (left > clientWidth) {
|
||||
option.left = container.scrollLeft + left - clientWidth + width + SCROLL_MARGIN
|
||||
if (x < 0) {
|
||||
option.left = container.scrollLeft + x - SCROLL_MARGIN
|
||||
} else if (x > clientWidth) {
|
||||
option.left = x + width - clientWidth + SCROLL_MARGIN
|
||||
}
|
||||
|
||||
if (bottom < 0) {
|
||||
option.top = container.scrollTop + top - SCROLL_MARGIN
|
||||
} else if (top > clientHeight) {
|
||||
option.top = container.scrollTop + top - clientHeight + height + SCROLL_MARGIN
|
||||
if (y < 0) {
|
||||
option.top = container.scrollTop + y - SCROLL_MARGIN
|
||||
} else if (y > clientHeight) {
|
||||
option.top = y + height - clientHeight + SCROLL_MARGIN
|
||||
}
|
||||
|
||||
if (typeof option.left === 'number' || typeof option.top === 'number') {
|
||||
|
|
@ -348,13 +346,16 @@ const setSelectRect = (element) => {
|
|||
element = element || getDocument().body
|
||||
|
||||
const { left, height, top, width } = getRect(element)
|
||||
const { x, y } = getOffset(element)
|
||||
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
|
||||
const componentName = getCurrent().schema?.componentName || ''
|
||||
const scale = useLayout().getScale()
|
||||
clearHover()
|
||||
Object.assign(selectState, {
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
componentName,
|
||||
doc: getDocument()
|
||||
})
|
||||
|
|
@ -415,23 +416,6 @@ export const allowInsert = (configure = hoverState.configure || {}, data = dragS
|
|||
return flag
|
||||
}
|
||||
|
||||
const isAncestor = (ancestor, descendant) => {
|
||||
const ancestorId = typeof ancestor === 'string' ? ancestor : ancestor.id
|
||||
let descendantId = typeof descendant === 'string' ? descendant : descendant.id
|
||||
|
||||
while (descendantId) {
|
||||
const { parent } = getNode(descendantId, true) || {}
|
||||
|
||||
if (parent.id === ancestorId) {
|
||||
return true
|
||||
}
|
||||
|
||||
descendantId = parent.id
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 获取位置信息,返回状态
|
||||
const lineAbs = 20
|
||||
const getPosLine = (rect, configure) => {
|
||||
|
|
@ -439,7 +423,6 @@ const getPosLine = (rect, configure) => {
|
|||
const yAbs = Math.min(lineAbs, rect.height / 3)
|
||||
const xAbs = Math.min(lineAbs, rect.width / 3)
|
||||
let type
|
||||
let forbidden = false
|
||||
|
||||
if (mousePos.y < rect.top + yAbs) {
|
||||
type = POSITION.TOP
|
||||
|
|
@ -450,21 +433,12 @@ const getPosLine = (rect, configure) => {
|
|||
} else if (mousePos.x > rect.right - xAbs) {
|
||||
type = POSITION.RIGHT
|
||||
} else if (configure.isContainer) {
|
||||
type = POSITION.IN
|
||||
if (!allowInsert()) {
|
||||
forbidden = true
|
||||
}
|
||||
type = allowInsert() ? POSITION.IN : POSITION.FORBID
|
||||
} else {
|
||||
type = POSITION.BOTTOM
|
||||
}
|
||||
|
||||
// 如果被拖拽的节点不是新增的,并且是放置的节点的祖先节点,则禁止插入
|
||||
const draggedId = dragState.data?.id
|
||||
if (draggedId && isAncestor(draggedId, lineState.id)) {
|
||||
forbidden = true
|
||||
}
|
||||
|
||||
return { type, forbidden }
|
||||
return { type }
|
||||
}
|
||||
|
||||
const isBodyEl = (element) => element.nodeName === 'BODY'
|
||||
|
|
@ -478,6 +452,9 @@ const setHoverRect = (element, data) => {
|
|||
const configure = getConfigure(componentName)
|
||||
const rect = getRect(element)
|
||||
const { left, height, top, width } = rect
|
||||
const { x, y } = getOffset(element)
|
||||
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
|
||||
const scale = useLayout().getScale()
|
||||
|
||||
hoverState.configure = configure
|
||||
|
||||
|
|
@ -506,24 +483,21 @@ const setHoverRect = (element, data) => {
|
|||
if (childEle) {
|
||||
const childRect = getRect(childEle)
|
||||
const { left, height, top, width } = childRect
|
||||
const posLine = getPosLine(childRect, lineState.configure)
|
||||
const { x, y } = getOffset(childEle)
|
||||
Object.assign(lineState, {
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
position: canvasState.type === 'absolute' || posLine.type,
|
||||
forbidden: posLine.forbidden
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
position: canvasState.type === 'absolute' || getPosLine(childRect, lineState.configure).type
|
||||
})
|
||||
} else {
|
||||
const posLine = getPosLine(rect, configure)
|
||||
Object.assign(lineState, {
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
position: canvasState.type === 'absolute' || posLine.type,
|
||||
forbidden: posLine.forbidden
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
position: canvasState.type === 'absolute' || getPosLine(rect, configure).type
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -532,10 +506,10 @@ const setHoverRect = (element, data) => {
|
|||
|
||||
// 设置元素hover状态
|
||||
Object.assign(hoverState, {
|
||||
width,
|
||||
height,
|
||||
top,
|
||||
left,
|
||||
width: width * scale,
|
||||
height: height * scale,
|
||||
top: top * scale + y - siteCanvasRect.y,
|
||||
left: left * scale + x - siteCanvasRect.x,
|
||||
componentName
|
||||
})
|
||||
return undefined
|
||||
|
|
@ -696,12 +670,13 @@ export const copyNode = (id) => {
|
|||
|
||||
export const onMouseUp = () => {
|
||||
const { draging, data } = dragState
|
||||
const { position, forbidden } = lineState
|
||||
const { position } = lineState
|
||||
const absolute = canvasState.type === 'absolute'
|
||||
const sourceId = data?.id
|
||||
const lineId = lineState.id
|
||||
const allowInsert = position !== POSITION.FORBID
|
||||
|
||||
if (draging && !forbidden) {
|
||||
if (draging && allowInsert) {
|
||||
const { parent, node } = getNode(lineId, true) || {} // target
|
||||
const targetNode = { parent, node, data: toRaw(data) }
|
||||
|
||||
|
|
@ -848,49 +823,6 @@ export const canvasDispatch = (name, data, doc = getDocument()) => {
|
|||
doc.dispatchEvent(new CustomEvent(name, data))
|
||||
}
|
||||
|
||||
export const canvasApi = {
|
||||
dragStart,
|
||||
updateRect,
|
||||
getContext,
|
||||
getNodePath,
|
||||
dragMove,
|
||||
setLocales,
|
||||
setState,
|
||||
deleteState,
|
||||
getRenderer,
|
||||
clearSelect,
|
||||
selectNode,
|
||||
hoverNode,
|
||||
insertNode,
|
||||
removeNode,
|
||||
addComponent,
|
||||
setPageCss,
|
||||
addScript,
|
||||
addStyle,
|
||||
getNode,
|
||||
getCurrent,
|
||||
setSchema,
|
||||
setUtils,
|
||||
updateUtils,
|
||||
deleteUtils,
|
||||
getSchema,
|
||||
setI18n,
|
||||
getCanvasType,
|
||||
setCanvasType,
|
||||
setProps,
|
||||
setGlobalState,
|
||||
getGlobalState,
|
||||
getDocument,
|
||||
canvasDispatch,
|
||||
Builtin,
|
||||
setDataSourceMap: (...args) => {
|
||||
return canvasState.renderer.setDataSourceMap(...args)
|
||||
},
|
||||
getDataSourceMap: (...args) => {
|
||||
return canvasState.renderer.getDataSourceMap(...args)
|
||||
}
|
||||
}
|
||||
|
||||
export const initCanvas = ({ renderer, iframe, emit, controller }) => {
|
||||
const currentSchema = getSchema()
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import { generateFunction } from '@opentiny/tiny-engine-controller/utils'
|
|||
import renderer, { parseData, setConfigure, setController, globalNotify, isStateAccessor } from './render'
|
||||
import { getNode as getNodeById, clearNodes, getRoot, setContext, getContext, setCondition, context } from './context'
|
||||
import CanvasEmpty from './CanvasEmpty.vue'
|
||||
import { getCurrent, setLocales, updateRect, addStyle, addScript, canvasDispatch } from '../container/container'
|
||||
import Builtin from '../builtin/builtin.json'
|
||||
|
||||
const { BROADCAST_CHANNEL } = constants
|
||||
|
||||
|
|
@ -424,3 +426,17 @@ export const api = {
|
|||
setDataSourceMap,
|
||||
setGlobalState
|
||||
}
|
||||
|
||||
const canvasApi = {
|
||||
getCurrent,
|
||||
setLocales,
|
||||
getNodeById: getNode,
|
||||
updateRect,
|
||||
addStyle,
|
||||
addScript,
|
||||
canvasDispatch
|
||||
}
|
||||
|
||||
window.api = api
|
||||
window.canvasApi = canvasApi
|
||||
window.Builtin = Builtin
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import babelPluginJSX from '@vue/babel-plugin-jsx'
|
|||
import { transformSync } from '@babel/core'
|
||||
import i18nHost from '@opentiny/tiny-engine-i18n-host'
|
||||
import { CanvasRow, CanvasCol, CanvasRowColContainer } from '@opentiny/tiny-engine-builtin-component'
|
||||
import { CanvasLive } from '@opentiny/tiny-engine-live-component'
|
||||
import { context, conditions, setNode } from './context'
|
||||
import {
|
||||
CanvasBox,
|
||||
|
|
@ -65,6 +66,7 @@ const Mapper = {
|
|||
slot: CanvasSlot,
|
||||
Template: CanvasBox,
|
||||
Img: CanvasImg,
|
||||
Live: CanvasLive,
|
||||
CanvasRow,
|
||||
CanvasCol,
|
||||
CanvasRowColContainer,
|
||||
|
|
@ -598,20 +600,6 @@ const getLoopScope = ({ scope, index, item, loopArgs }) => {
|
|||
}
|
||||
}
|
||||
|
||||
const injectPlaceHolder = (componentName, children) => {
|
||||
const isEmptyArr = Array.isArray(children) && !children.length
|
||||
|
||||
if (configure[componentName]?.isContainer && (!children || isEmptyArr)) {
|
||||
return [
|
||||
{
|
||||
componentName: 'CanvasPlaceholder'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
const renderGroup = (children, scope, parent) => {
|
||||
return children.map?.((schema) => {
|
||||
const { componentName, children, loop, loopArgs, condition, id } = schema
|
||||
|
|
@ -631,14 +619,10 @@ const renderGroup = (children, scope, parent) => {
|
|||
return null
|
||||
}
|
||||
|
||||
const renderChildren = injectPlaceHolder(componentName, children)
|
||||
|
||||
return h(
|
||||
getComponent(componentName),
|
||||
getBindProps(schema, mergeScope),
|
||||
Array.isArray(renderChildren)
|
||||
? renderSlot(renderChildren, mergeScope, schema)
|
||||
: parseData(renderChildren, mergeScope)
|
||||
Array.isArray(children) ? renderSlot(children, mergeScope, schema) : parseData(children, mergeScope)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -646,9 +630,19 @@ const renderGroup = (children, scope, parent) => {
|
|||
})
|
||||
}
|
||||
|
||||
const ContainerComponent = ['CanvasCol', 'CanvasRow', 'CanvasRowColContainer']
|
||||
|
||||
const getChildren = (schema, mergeScope) => {
|
||||
const { componentName, children } = schema
|
||||
const renderChildren = injectPlaceHolder(componentName, children)
|
||||
let renderChildren = children
|
||||
|
||||
if (ContainerComponent.includes(componentName) && !renderChildren?.length) {
|
||||
renderChildren = [
|
||||
{
|
||||
componentName: 'CanvasPlaceholder'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const component = getComponent(componentName)
|
||||
const isNative = typeof component === 'string'
|
||||
|
|
@ -696,11 +690,11 @@ export const renderer = {
|
|||
const renderElement = (item, index) => {
|
||||
let mergeScope = item
|
||||
? getLoopScope({
|
||||
item,
|
||||
index,
|
||||
loopArgs,
|
||||
scope
|
||||
})
|
||||
item,
|
||||
index,
|
||||
loopArgs,
|
||||
scope
|
||||
})
|
||||
: scope
|
||||
|
||||
// 如果是区块,并且使用了区块的作用域插槽,则需要将作用域插槽的数据传递下去
|
||||
|
|
|
|||
|
|
@ -26,77 +26,73 @@ const dispatch = (name, data) => {
|
|||
window.parent.document.dispatchEvent(new CustomEvent(name, data))
|
||||
}
|
||||
|
||||
const initRenderContext = () => {
|
||||
dispatch('beforeCanvasReady')
|
||||
dispatch('beforeCanvasReady')
|
||||
|
||||
TinyI18nHost.lowcode = lowcode
|
||||
TinyI18nHost.lowcode = lowcode
|
||||
|
||||
// 目前先兼容老区块发布的代码,后期区块发布整改后再删除
|
||||
window.React = {}
|
||||
window.React.createElement = Vue.h
|
||||
// 目前先兼容老区块发布的代码,后期区块发布整改后再删除
|
||||
window.React = {}
|
||||
window.React.createElement = Vue.h
|
||||
|
||||
// 不能采用new Proxy代理Vue的方案,在编译后的vue会报错警告,采用一下方案扩展用于注入一些区块加载逻辑
|
||||
window.Vue = {
|
||||
...Vue,
|
||||
resolveComponent(...args) {
|
||||
// 此处先执行vue内部的解析组件的方法,如果可以拿到组件对象则直接返回,反之则去注册区块
|
||||
const component = Vue.resolveComponent(args[0])
|
||||
if (component && typeof component === 'string') {
|
||||
return getComponent(capitalize(camelize(args[0])))
|
||||
// 不能采用new Proxy代理Vue的方案,在编译后的vue会报错警告,采用一下方案扩展用于注入一些区块加载逻辑
|
||||
window.Vue = {
|
||||
...Vue,
|
||||
resolveComponent(...args) {
|
||||
// 此处先执行vue内部的解析组件的方法,如果可以拿到组件对象则直接返回,反之则去注册区块
|
||||
const component = Vue.resolveComponent(args[0])
|
||||
if (component && typeof component === 'string') {
|
||||
return getComponent(capitalize(camelize(args[0])))
|
||||
} else {
|
||||
return component
|
||||
}
|
||||
},
|
||||
// renderSlot方法第三个参数是作用域插槽传递的数据,格式{ data: vue.unref(state).componentData }
|
||||
renderSlot(...args) {
|
||||
// 获取当前vue的实例
|
||||
const instance = Vue.getCurrentInstance()
|
||||
|
||||
// 获取当前区块名称
|
||||
const blockName = instance.attrs.dataTag
|
||||
|
||||
const [, slotName, slotData] = args
|
||||
|
||||
// 如果是作用域插槽,则获取作用域插槽传递过来的参数
|
||||
if (slotData) {
|
||||
if (blockSlotDataMap[blockName]) {
|
||||
blockSlotDataMap[blockName][slotName] = slotData
|
||||
} else {
|
||||
return component
|
||||
blockSlotDataMap[blockName] = { [slotName]: slotData }
|
||||
}
|
||||
},
|
||||
// renderSlot方法第三个参数是作用域插槽传递的数据,格式{ data: vue.unref(state).componentData }
|
||||
renderSlot(...args) {
|
||||
// 获取当前vue的实例
|
||||
const instance = Vue.getCurrentInstance()
|
||||
|
||||
// 获取当前区块名称
|
||||
const blockName = instance.attrs.dataTag
|
||||
|
||||
const [, slotName, slotData] = args
|
||||
|
||||
// 如果是作用域插槽,则获取作用域插槽传递过来的参数
|
||||
if (slotData) {
|
||||
if (blockSlotDataMap[blockName]) {
|
||||
blockSlotDataMap[blockName][slotName] = slotData
|
||||
} else {
|
||||
blockSlotDataMap[blockName] = { [slotName]: slotData }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* vue源码中的renderSlot会忽略default插槽的名称,所以这里必须手动添加args第三个参数的name值
|
||||
* vue源码如右所示:if (name !== 'default') props.name = name; return createVNode('slot', props, fallback && fallback());
|
||||
**/
|
||||
if (slotName === 'default') {
|
||||
args[2] = args[2] || {}
|
||||
args[2].name = slotName
|
||||
}
|
||||
|
||||
return Vue.renderSlot(...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* vue源码中的renderSlot会忽略default插槽的名称,所以这里必须手动添加args第三个参数的name值
|
||||
* vue源码如右所示:if (name !== 'default') props.name = name; return createVNode('slot', props, fallback && fallback());
|
||||
**/
|
||||
if (slotName === 'default') {
|
||||
args[2] = args[2] || {}
|
||||
args[2].name = slotName
|
||||
}
|
||||
|
||||
return Vue.renderSlot(...args)
|
||||
}
|
||||
|
||||
window.VueI18n = VueI18n
|
||||
window.TinyVue = TinyVue
|
||||
window.TinyVueIcon = TinyVueIcon
|
||||
window.TinyWebcomponentCore = TinyWebcomponentCore
|
||||
window.TinyI18nHost = TinyI18nHost
|
||||
window.TinyLowcodeComponent = {}
|
||||
window.TinyComponentLibs = {}
|
||||
|
||||
Object.entries(TinyVue).forEach(([_key, component]) => {
|
||||
const { name } = component
|
||||
if (name) {
|
||||
window.TinyLowcodeComponent[name] = component
|
||||
}
|
||||
})
|
||||
|
||||
document.addEventListener('updateDependencies', updateDependencies)
|
||||
}
|
||||
|
||||
window.VueI18n = VueI18n
|
||||
window.TinyVue = TinyVue
|
||||
window.TinyVueIcon = TinyVueIcon
|
||||
window.TinyWebcomponentCore = TinyWebcomponentCore
|
||||
window.TinyI18nHost = TinyI18nHost
|
||||
window.TinyLowcodeComponent = {}
|
||||
window.TinyComponentLibs = {}
|
||||
|
||||
Object.entries(TinyVue).forEach(([_key, component]) => {
|
||||
const { name } = component
|
||||
if (name) {
|
||||
window.TinyLowcodeComponent[name] = component
|
||||
}
|
||||
})
|
||||
|
||||
let App = null
|
||||
|
||||
const renderer = {
|
||||
|
|
@ -133,7 +129,6 @@ const create = () => {
|
|||
}
|
||||
|
||||
export const createRender = (config) => {
|
||||
initRenderContext()
|
||||
const { dslMode, canvasOptions } = config
|
||||
const { styles = [], scripts = [] } = canvasOptions[dslMode]
|
||||
const { styles: thirdStyles = [], scripts: thirdScripts = [] } = window.thirdPartyDeps || {}
|
||||
|
|
@ -143,3 +138,5 @@ export const createRender = (config) => {
|
|||
...scripts.map((src) => addScript(src)).concat([...thirdStyles, ...styles].map((src) => addStyle(src)))
|
||||
]).finally(create)
|
||||
}
|
||||
|
||||
document.addEventListener('updateDependencies', updateDependencies)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,93 @@
|
|||
*
|
||||
*/
|
||||
|
||||
export { default as CanvasContainer } from './components/container/CanvasContainer.vue'
|
||||
export { default as CanvasDragItem } from './components/container/CanvasDragItem.vue'
|
||||
export { default as CanvasFooter } from './components/container/CanvasFooter.vue'
|
||||
export { createRender } from './components/render/runner'
|
||||
import CanvasContainer from './components/container/CanvasContainer.vue'
|
||||
import CanvasAction from './components/container/CanvasAction.vue'
|
||||
import CanvasDragItem from './components/container/CanvasDragItem.vue'
|
||||
import CanvasFooter from './components/container/CanvasFooter.vue'
|
||||
import CanvasResize from './components/container/CanvasResize.vue'
|
||||
import Builtin from './components/builtin/builtin.json'
|
||||
import RenderMain, { api as renderApi } from './components/render/RenderMain'
|
||||
import { createRender } from './components/render/runner'
|
||||
import {
|
||||
dragStart,
|
||||
updateRect,
|
||||
getContext,
|
||||
getNodePath,
|
||||
dragMove,
|
||||
setLocales,
|
||||
setState,
|
||||
deleteState,
|
||||
getRenderer,
|
||||
clearSelect,
|
||||
selectNode,
|
||||
hoverNode,
|
||||
insertNode,
|
||||
removeNode,
|
||||
addComponent,
|
||||
setPageCss,
|
||||
addScript,
|
||||
addStyle,
|
||||
getNode,
|
||||
getCurrent,
|
||||
setSchema,
|
||||
setUtils,
|
||||
updateUtils,
|
||||
deleteUtils,
|
||||
getSchema,
|
||||
setI18n,
|
||||
getCanvasType,
|
||||
setCanvasType,
|
||||
setProps,
|
||||
setGlobalState,
|
||||
getGlobalState,
|
||||
getDocument,
|
||||
canvasDispatch
|
||||
} from './components/container/container'
|
||||
|
||||
export {
|
||||
CanvasContainer,
|
||||
CanvasAction,
|
||||
CanvasFooter,
|
||||
CanvasDragItem,
|
||||
CanvasResize,
|
||||
RenderMain,
|
||||
renderApi,
|
||||
Builtin,
|
||||
dragStart,
|
||||
dragMove,
|
||||
updateRect,
|
||||
getContext,
|
||||
getNodePath,
|
||||
getNode,
|
||||
getCurrent,
|
||||
setSchema,
|
||||
setUtils,
|
||||
updateUtils,
|
||||
deleteUtils,
|
||||
getSchema,
|
||||
setLocales,
|
||||
setState,
|
||||
deleteState,
|
||||
setI18n,
|
||||
getRenderer,
|
||||
clearSelect,
|
||||
selectNode,
|
||||
insertNode,
|
||||
removeNode,
|
||||
hoverNode,
|
||||
addComponent,
|
||||
setPageCss,
|
||||
addScript,
|
||||
addStyle,
|
||||
getCanvasType,
|
||||
setCanvasType,
|
||||
setProps,
|
||||
setGlobalState,
|
||||
getGlobalState,
|
||||
getDocument,
|
||||
canvasDispatch,
|
||||
createRender
|
||||
}
|
||||
|
||||
export default CanvasContainer
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ export default defineConfig({
|
|||
'@': path.resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
define: {
|
||||
'process.env': {}
|
||||
},
|
||||
plugins: [
|
||||
vue({
|
||||
template: {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
:modal="false"
|
||||
:fullscreen="true"
|
||||
:append-to-body="true"
|
||||
title="Schema 线上与本地差异"
|
||||
title="Schema 本地与线上差异"
|
||||
>
|
||||
<vue-monaco
|
||||
v-if="state.compareVisible"
|
||||
|
|
@ -82,8 +82,8 @@ import {
|
|||
FormItem as TinyFormItem
|
||||
} from '@opentiny/vue'
|
||||
import { theme } from '@opentiny/tiny-engine-controller/adapter'
|
||||
import { useLayout, useNotify, useCanvas } from '@opentiny/tiny-engine-controller'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
import { useLayout } from '@opentiny/tiny-engine-controller'
|
||||
import { getSchema, setSchema } from '@opentiny/tiny-engine-canvas'
|
||||
import VueMonaco from './VueMonaco.vue'
|
||||
|
||||
export default {
|
||||
|
|
@ -106,7 +106,6 @@ export default {
|
|||
},
|
||||
emits: ['update:visible'],
|
||||
setup(props, { emit, attrs }) {
|
||||
const { COMPONENT_NAME } = constants
|
||||
const formState = reactive({
|
||||
deployInfo: '',
|
||||
version: '',
|
||||
|
|
@ -187,10 +186,9 @@ export default {
|
|||
const remote = await api.getBlockById(block?.id)
|
||||
const originalObj = remote?.content || {}
|
||||
state.originalCode = JSON.stringify(originalObj, null, 2)
|
||||
const getSchema = useCanvas().canvasApi.value.getSchema
|
||||
|
||||
// 转为普通对象,和线上代码顺序保持一致
|
||||
const pageSchema = getSchema?.() || {}
|
||||
const pageSchema = getSchema() || {}
|
||||
if (pageSchema.componentName === 'Block') {
|
||||
state.code = JSON.stringify(pageSchema, null, 2)
|
||||
} else {
|
||||
|
|
@ -214,21 +212,10 @@ export default {
|
|||
}
|
||||
|
||||
const save = () => {
|
||||
if (!state.newCode) {
|
||||
close()
|
||||
return
|
||||
}
|
||||
try {
|
||||
const pageSchema = JSON.parse(state.newCode)
|
||||
const setSchema = useCanvas().canvasApi.value.setSchema
|
||||
setSchema?.({ ...pageSchema, componentName: COMPONENT_NAME.Block })
|
||||
close()
|
||||
} catch (err) {
|
||||
useNotify({
|
||||
type: 'error',
|
||||
message: '代码静态检查有错误,请先修改后再保存'
|
||||
})
|
||||
}
|
||||
const pageSchema = JSON.parse(state.newCode)
|
||||
setSchema(pageSchema)
|
||||
|
||||
close()
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { capitalize } from '@vue/shared'
|
|||
import { extend } from '@opentiny/vue-renderless/common/object'
|
||||
import { Input as TinyInput, Form as TinyForm, FormItem as TinyFormItem, Popover as TinyPopover } from '@opentiny/vue'
|
||||
import { useLayout, useModal, useCanvas, useBlock } from '@opentiny/tiny-engine-controller'
|
||||
import { getSchema, getCurrent } from '@opentiny/tiny-engine-canvas'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -37,8 +38,7 @@ export default {
|
|||
const { pageState } = useCanvas()
|
||||
const { addBlockEvent, removeEventLink, getCurrentBlock, appendEventEmit } = useBlock()
|
||||
const { PLUGIN_NAME, activePlugin } = useLayout()
|
||||
|
||||
const { schema } = useCanvas().canvasApi.value?.getSchema?.() || {}
|
||||
const { schema } = getSchema()
|
||||
const events = schema?.events || []
|
||||
const eventsList = Object.entries(events).map(([key, value]) => {
|
||||
return {
|
||||
|
|
@ -88,7 +88,7 @@ export default {
|
|||
|
||||
const {
|
||||
schema: { id, componentName }
|
||||
} = useCanvas().canvasApi.value?.getCurrent?.() || {}
|
||||
} = getCurrent()
|
||||
|
||||
const newEvent = extend(true, {}, data.metaEvent, {
|
||||
linked: {
|
||||
|
|
@ -117,7 +117,7 @@ export default {
|
|||
const editEvent = (item, data) => {
|
||||
const {
|
||||
schema: { id, componentName }
|
||||
} = useCanvas().canvasApi.value?.getCurrent?.() || {}
|
||||
} = getCurrent()
|
||||
|
||||
const oldEventName = data.linkedEventName
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import { reactive, computed } from 'vue'
|
|||
import { extend } from '@opentiny/vue-renderless/common/object'
|
||||
import { Input as TinyInput, Popover as TinyPopover } from '@opentiny/vue'
|
||||
import { useLayout, useModal, useCanvas, useBlock, useHistory } from '@opentiny/tiny-engine-controller'
|
||||
import { getSchema, getCurrent } from '@opentiny/tiny-engine-canvas'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -35,10 +36,10 @@ export default {
|
|||
},
|
||||
setup(props) {
|
||||
const { confirm } = useModal()
|
||||
const { pageState, canvasApi } = useCanvas()
|
||||
const { pageState } = useCanvas()
|
||||
const { addBlockProperty, removePropertyLink, getCurrentBlock, editBlockProperty } = useBlock()
|
||||
const { PLUGIN_NAME, activePlugin } = useLayout()
|
||||
const { schema } = canvasApi.value.getSchema?.() || {}
|
||||
const { schema } = getSchema()
|
||||
|
||||
const state = reactive({
|
||||
newPropertyName: ''
|
||||
|
|
@ -67,7 +68,7 @@ export default {
|
|||
exec() {
|
||||
const {
|
||||
schema: { id, componentName }
|
||||
} = canvasApi.value?.getCurrent?.() || {}
|
||||
} = getCurrent()
|
||||
const newProperty = extend(true, {}, property, {
|
||||
property: state.newPropertyName || `${property.property}${id}`,
|
||||
linked: {
|
||||
|
|
@ -86,7 +87,7 @@ export default {
|
|||
const editProperty = (property) => {
|
||||
const {
|
||||
schema: { id, componentName }
|
||||
} = canvasApi.value?.getCurrent?.() || {}
|
||||
} = getCurrent()
|
||||
|
||||
// 如果之前有关联关系,则需要去除关联
|
||||
if (props.data?.linked) {
|
||||
|
|
|
|||
|
|
@ -78,8 +78,9 @@
|
|||
<script lang="jsx">
|
||||
import { reactive, ref, watchEffect, onBeforeUnmount } from 'vue'
|
||||
import { Button, DialogBox, Popover, Search, Alert } from '@opentiny/vue'
|
||||
import { getGlobalConfig, useModal, usePage, useNotify, useCanvas } from '@opentiny/tiny-engine-controller'
|
||||
import { getGlobalConfig, useModal, usePage, useNotify } from '@opentiny/tiny-engine-controller'
|
||||
import { theme } from '@opentiny/tiny-engine-controller/adapter'
|
||||
import { getSchema } from '@opentiny/tiny-engine-canvas'
|
||||
import MetaListItems from './MetaListItems.vue'
|
||||
import { iconYes } from '@opentiny/vue-icon'
|
||||
import VueMonaco from './VueMonaco.vue'
|
||||
|
|
@ -129,7 +130,7 @@ export default {
|
|||
})
|
||||
|
||||
watchEffect(() => {
|
||||
state.bindLifeCycles = props.bindLifeCycles || useCanvas().canvasApi.value?.getSchema()?.lifeCycles || {}
|
||||
state.bindLifeCycles = props.bindLifeCycles || getSchema()?.lifeCycles || {}
|
||||
})
|
||||
|
||||
const searchLifeCyclesList = (value) => {
|
||||
|
|
@ -141,7 +142,7 @@ export default {
|
|||
}
|
||||
|
||||
const syncLifeCycle = () => {
|
||||
const currentSchema = useCanvas().canvasApi.value?.getSchema?.()
|
||||
const currentSchema = getSchema()
|
||||
const pageContent = getPageContent()
|
||||
const { id, fileName } = pageContent
|
||||
if (id === currentSchema.id || fileName === currentSchema.fileName) {
|
||||
|
|
@ -164,8 +165,7 @@ export default {
|
|||
|
||||
const openLifeCyclesPanel = (item) => {
|
||||
state.title = item
|
||||
const bindLifeCycleSource =
|
||||
props.bindLifeCycles?.[item] || useCanvas().canvasApi.value?.getSchema?.()?.lifeCycles?.[item]
|
||||
const bindLifeCycleSource = props.bindLifeCycles?.[item] || getSchema().lifeCycles?.[item]
|
||||
state.editorValue =
|
||||
bindLifeCycleSource?.value ||
|
||||
`function ${item} (${item === 'setup' ? '{ props, state, watch, onMounted }' : ''}) {} `
|
||||
|
|
|
|||
|
|
@ -124,6 +124,7 @@ import { reactive, ref, computed, nextTick, watch } from 'vue'
|
|||
import { camelize, capitalize } from '@vue/shared'
|
||||
import { Button, DialogBox, Search, Switch, Input, Tooltip, Alert } from '@opentiny/vue'
|
||||
import { useHttp } from '@opentiny/tiny-engine-http'
|
||||
import { getSchema, getGlobalState, setState, getNode, getCurrent } from '@opentiny/tiny-engine-canvas'
|
||||
import { useCanvas, useResource, useLayout, useApp, useProperties, useData } from '@opentiny/tiny-engine-controller'
|
||||
import { theme } from '@opentiny/tiny-engine-controller/adapter'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
|
|
@ -149,12 +150,6 @@ const CONSTANTS = {
|
|||
}
|
||||
|
||||
const getJsSlot = () => {
|
||||
const { getNode, getCurrent } = useCanvas().canvasApi.value || {}
|
||||
|
||||
if (!getNode || !getCurrent) {
|
||||
return [false, {}]
|
||||
}
|
||||
|
||||
const jsSlot = getNode(getCurrent()?.parent?.id, true)?.parent
|
||||
|
||||
return [jsSlot?.type === 'JSSlot', jsSlot]
|
||||
|
|
@ -403,9 +398,10 @@ export default {
|
|||
const confirm = () => {
|
||||
let variableContent = state.isEditorEditMode ? editor.value?.getEditor().getValue() : state.variable
|
||||
|
||||
const { setSaved, canvasApi } = useCanvas()
|
||||
// 如果新旧值不一样就显示未保存状态
|
||||
if (oldValue !== variableContent) {
|
||||
const { setSaved } = useCanvas()
|
||||
|
||||
setSaved(false)
|
||||
variableContent = formatString(variableContent, 'javascript')
|
||||
}
|
||||
|
|
@ -415,14 +411,14 @@ export default {
|
|||
|
||||
if (variableContent) {
|
||||
if (state.bindPrefix === CONSTANTS.DATASOUCEPREFIX) {
|
||||
const pageSchema = canvasApi.value.getSchema()
|
||||
const pageSchema = getSchema()
|
||||
const stateName = state.variable.replace(`${CONSTANTS.STATE}`, '')
|
||||
const staticData = state.variableContent.map(({ _id, ...other }) => other)
|
||||
|
||||
pageSchema.state[stateName] = staticData
|
||||
|
||||
// 设置画布上下文环境,让画布触发更新渲染
|
||||
canvasApi.value.setState({ [stateName]: staticData })
|
||||
setState({ [stateName]: staticData })
|
||||
|
||||
// 这里在setup生命周期函数内部处理用户真实环境中的数据源请求
|
||||
genRemoteMethodToLifeSetup(stateName, state.dataSouce, pageSchema)
|
||||
|
|
@ -457,7 +453,7 @@ export default {
|
|||
state.isVisible = true
|
||||
state.variableName = bindKey.value
|
||||
state.variable = getInitVariable()
|
||||
state.variables = useCanvas().canvasApi.value.getSchema()?.state || {}
|
||||
state.variables = getSchema()?.state || {}
|
||||
state.bindPrefix = CONSTANTS.STATE
|
||||
state.variableContent = state.variables[bindKey.value]
|
||||
nextTick(() => window.dispatchEvent(new Event('resize')))
|
||||
|
|
@ -465,8 +461,6 @@ export default {
|
|||
|
||||
const selectItem = (item) => {
|
||||
state.active = item.id
|
||||
const { canvasApi } = useCanvas()
|
||||
|
||||
if (item.id === 'function') {
|
||||
state.bindPrefix = CONSTANTS.THIS
|
||||
const { PLUGIN_NAME, getPluginApi } = useLayout()
|
||||
|
|
@ -482,7 +476,7 @@ export default {
|
|||
state.variables = bridge
|
||||
} else if (item.id === 'props') {
|
||||
state.bindPrefix = CONSTANTS.PROPS
|
||||
const properties = canvasApi.value.getSchema()?.schema?.properties
|
||||
const properties = getSchema()?.schema?.properties
|
||||
const bindProperties = {}
|
||||
properties?.forEach(({ content }) => {
|
||||
content.forEach(({ property }) => {
|
||||
|
|
@ -508,7 +502,7 @@ export default {
|
|||
state.bindPrefix = CONSTANTS.STORE
|
||||
state.variables = {}
|
||||
|
||||
const stores = canvasApi.value.getGlobalState()
|
||||
const stores = getGlobalState()
|
||||
stores.forEach(({ id, state: storeState = {}, getters = {} }) => {
|
||||
const loadProp = (prop) => {
|
||||
const propBinding = `${id}.${prop}`
|
||||
|
|
@ -529,7 +523,7 @@ export default {
|
|||
state.variables = params.reduce((variables, param) => ({ ...variables, [param]: param }), {})
|
||||
} else {
|
||||
state.bindPrefix = CONSTANTS.STATE
|
||||
state.variables = canvasApi.value.getSchema()?.[item.id]
|
||||
state.variables = getSchema()?.[item.id]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="head-content">
|
||||
<meta-input v-model="state.text" type="textarea" @update:modelValue="change"></meta-input>
|
||||
<meta-input v-model="state.text" type="textarea" @change="change"></meta-input>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
<script>
|
||||
import { onMounted, ref, watchEffect } from 'vue'
|
||||
import { useMessage, useCanvas } from '@opentiny/tiny-engine-controller'
|
||||
import { getSchema } from '@opentiny/tiny-engine-canvas'
|
||||
import MetaCodeEditor from './MetaCodeEditor.vue'
|
||||
import MetaBindVariable from './MetaBindVariable.vue'
|
||||
|
||||
|
|
@ -90,7 +91,7 @@ export default {
|
|||
let newValue = value
|
||||
|
||||
if (value?.type === CONSTANT.JSEXPRESSION) {
|
||||
const pageSchema = useCanvas().canvasApi.value.getSchema()
|
||||
const pageSchema = getSchema()
|
||||
const stateName = value?.value?.replace(CONSTANT.STATE, '')
|
||||
newValue = pageSchema?.state?.[stateName]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@
|
|||
<script>
|
||||
import { ref, watchEffect, nextTick } from 'vue'
|
||||
import { Popover } from '@opentiny/vue'
|
||||
import { useProperties, useResource, useModal, useCanvas } from '@opentiny/tiny-engine-controller'
|
||||
import { useProperties, useResource, useModal } from '@opentiny/tiny-engine-controller'
|
||||
import { updateRect } from '@opentiny/tiny-engine-canvas'
|
||||
import { iconHelpCircle } from '@opentiny/vue-icon'
|
||||
|
||||
export default {
|
||||
|
|
@ -120,7 +121,7 @@ export default {
|
|||
})
|
||||
}
|
||||
|
||||
useCanvas().canvasApi.value.updateRect()
|
||||
updateRect()
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
<script setup>
|
||||
import { nextTick } from 'vue'
|
||||
import { useProperties, useResource, useCanvas } from '@opentiny/tiny-engine-controller'
|
||||
import { updateRect } from '@opentiny/tiny-engine-canvas'
|
||||
import { useProperties, useResource } from '@opentiny/tiny-engine-controller'
|
||||
import MetaArrayItem from './MetaArrayItem.vue'
|
||||
|
||||
const { children: schemaChildren, componentName, props } = useProperties().getSchema()
|
||||
|
|
@ -25,7 +26,7 @@ const updateColumns = (columns) => {
|
|||
})
|
||||
|
||||
useProperties().getSchema().children = children
|
||||
nextTick(useCanvas().canvasApi.value.updateRect)
|
||||
nextTick(updateRect)
|
||||
}
|
||||
|
||||
updateColumns(props?.columns)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<template>
|
||||
<ul
|
||||
v-if="state.data.length"
|
||||
:class="['block-list', 'lowcode-scrollbar', { 'is-small-list': blockStyle === 'mini' }, { isShortcutPanel }]"
|
||||
@mouseleave="state.hover = false"
|
||||
>
|
||||
|
|
@ -123,14 +122,12 @@
|
|||
</div>
|
||||
</div>
|
||||
</ul>
|
||||
<search-empty :isShow="!state.data.length" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { computed, watch, inject, reactive } from 'vue'
|
||||
import { iconSetting, iconPlus } from '@opentiny/vue-icon'
|
||||
import { Tooltip, Progress, Popover } from '@opentiny/vue'
|
||||
import SearchEmpty from './SearchEmpty.vue'
|
||||
import SvgButton from './SvgButton.vue'
|
||||
|
||||
const defaultImg =
|
||||
|
|
@ -143,8 +140,7 @@ export default {
|
|||
IconSetting: iconSetting(),
|
||||
IconPlus: iconPlus(),
|
||||
TinyPopover: Popover,
|
||||
SvgButton,
|
||||
SearchEmpty
|
||||
SvgButton
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<tiny-dialog-box
|
||||
:append-to-body="true"
|
||||
:close-on-click-modal="false"
|
||||
:visible="visible"
|
||||
title="新建区块"
|
||||
width="500"
|
||||
@close="cancel"
|
||||
>
|
||||
<tiny-dialog-box :append-to-body="true" :visible="visible" title="新建区块" width="500" @close="cancel">
|
||||
<tiny-form
|
||||
show-message
|
||||
:model="formData"
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
<template>
|
||||
<div v-if="isShow" class="empty-wrap">
|
||||
<svg-icon class="empty-icon" name="empty"></svg-icon>
|
||||
<p class="empty-text">暂无数据</p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
/**
|
||||
* 控制搜索空数据显示
|
||||
*/
|
||||
isShow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -79,7 +79,6 @@ import BlockDeployDialog from './component/BlockDeployDialog.vue'
|
|||
import ProgressBar from './component/ProgressBar.vue'
|
||||
import MetaButtonGroup from './component/MetaButtonGroup.vue'
|
||||
import MetaTableColumns from './component/MetaTableColumns.vue'
|
||||
import SearchEmpty from './component/SearchEmpty.vue'
|
||||
|
||||
import i18n, { i18nKeyMaps } from '@opentiny/tiny-engine-controller/js/i18n'
|
||||
|
||||
|
|
@ -146,8 +145,7 @@ export const MetaComponents = {
|
|||
MetaIpSection,
|
||||
MetaRelatedEditor,
|
||||
MetaRelatedColumns,
|
||||
MetaTableColumns,
|
||||
SearchEmpty
|
||||
MetaTableColumns
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
@ -208,6 +206,5 @@ export {
|
|||
BindI18n,
|
||||
BlockDeployDialog,
|
||||
ProgressBar,
|
||||
MetaTableColumns,
|
||||
SearchEmpty
|
||||
MetaTableColumns
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@
|
|||
"@opentiny/tiny-engine-controller": "workspace:*",
|
||||
"@opentiny/tiny-engine-http": "workspace:*",
|
||||
"@opentiny/tiny-engine-utils": "workspace:*",
|
||||
"@opentiny/vue": "~3.10.0",
|
||||
"@opentiny/vue-icon": "~3.10.0",
|
||||
"@opentiny/vue-renderless": "~3.10.0",
|
||||
"@vue/shared": "^3.3.4",
|
||||
"monaco-editor": "0.33.0",
|
||||
"prettier": "2.7.1",
|
||||
|
|
@ -42,9 +45,6 @@
|
|||
"vite": "^4.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentiny/vue": "^3.14.0",
|
||||
"@opentiny/vue-icon": "^3.14.0",
|
||||
"@opentiny/vue-renderless": "^3.14.0",
|
||||
"vue": "^3.4.15"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ export default defineConfig({
|
|||
plugins: [vue(), vueJsx()],
|
||||
publicDir: false,
|
||||
resolve: {},
|
||||
define: {
|
||||
'process.env': {}
|
||||
},
|
||||
build: {
|
||||
cssCodeSplit: false,
|
||||
lib: {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import prettier from 'prettier'
|
|||
import parserHtml from 'prettier/parser-html'
|
||||
import parseCss from 'prettier/parser-postcss'
|
||||
import parserBabel from 'prettier/parser-babel'
|
||||
import prettierConfig from './config-files/prettierrc'
|
||||
|
||||
const METHOD_REGEXP = /function.*?\(/
|
||||
|
||||
|
|
@ -34,7 +33,10 @@ const formatScript = (string) => {
|
|||
const options = {
|
||||
parser: 'babel',
|
||||
plugins: [parserBabel],
|
||||
...prettierConfig
|
||||
printWidth: 120,
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
trailingComma: 'none'
|
||||
}
|
||||
try {
|
||||
// 低码中的编辑器大多只会输入js值,并不是一个完整的javascript表达式,无法格式化,因此需要特殊处理预格式化该种情形
|
||||
|
|
@ -51,21 +53,21 @@ const formatJson = (string) =>
|
|||
parser: 'json',
|
||||
plugins: [parserBabel],
|
||||
trailingComma: 'es5',
|
||||
...prettierConfig
|
||||
tabWidth: 2,
|
||||
semi: false,
|
||||
singleQuote: true
|
||||
})
|
||||
|
||||
const formatHtml = (string) =>
|
||||
prettier.format(string, {
|
||||
parser: 'html',
|
||||
plugins: [parserBabel, parserHtml],
|
||||
...prettierConfig
|
||||
plugins: [parserBabel, parserHtml]
|
||||
})
|
||||
|
||||
const formatCss = (string) =>
|
||||
prettier.format(string, {
|
||||
parser: 'css',
|
||||
plugins: [parseCss],
|
||||
...prettierConfig
|
||||
plugins: [parseCss]
|
||||
})
|
||||
|
||||
const formatterMap = {
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
import eslintRecommended from '@eslint/js/src/configs/eslint-recommended.js'
|
||||
export default {
|
||||
...eslintRecommended.rules,
|
||||
'no-console': 'error',
|
||||
'no-debugger': 'error',
|
||||
'space-before-function-paren': 'off',
|
||||
'no-use-before-define': 'error'
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
export default {
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
trailingComma: 'none',
|
||||
endOfLine: 'auto',
|
||||
tabWidth: 2
|
||||
}
|
||||
|
|
@ -24,13 +24,7 @@ const http = useHttp()
|
|||
* @param { json } params {"event_type": design_error,"url": "elit in reprehenderit enim incididunt" }
|
||||
* @returns { Promise }
|
||||
*/
|
||||
export const requestEvent = (url, params) => {
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
|
||||
return http.post(url, params).catch(() => {})
|
||||
}
|
||||
export const requestEvent = (url, params) => http.post(url, params).catch(() => {})
|
||||
|
||||
/**
|
||||
* 页面更新
|
||||
|
|
|
|||
|
|
@ -1,29 +1,27 @@
|
|||
/**
|
||||
* Copyright (c) 2023 - present TinyEngine Authors.
|
||||
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license.
|
||||
*
|
||||
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
||||
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
* Copyright (c) 2023 - present TinyEngine Authors.
|
||||
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license.
|
||||
*
|
||||
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
||||
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
|
||||
import eslintWorkerUrl from './worker-files/eslint.worker?worker&url'
|
||||
import { PROD, BASE_URL } from './environments'
|
||||
|
||||
export const initLinter = (editor, monacoInstance, state) => {
|
||||
let workerUrl = new URL(eslintWorkerUrl, import.meta.url)
|
||||
let workerUrl = `${BASE_URL}monaco-linter/eslint.worker.js`
|
||||
|
||||
// 线上环境,存在 worker 资源跨域的情况
|
||||
if (workerUrl.origin !== location.origin) {
|
||||
const workerBlob = new Blob([`import('${workerUrl}');`], {
|
||||
type: 'application/javascript'
|
||||
})
|
||||
if (PROD) {
|
||||
const workerBlob = new Blob([`importScripts('${workerUrl}');`], { type: 'application/javascript' })
|
||||
workerUrl = window.URL.createObjectURL(workerBlob)
|
||||
}
|
||||
|
||||
const worker = new Worker(workerUrl, { type: 'module' })
|
||||
const worker = new Worker(workerUrl)
|
||||
|
||||
// 监听 ESLint web worker 的返回
|
||||
worker.onmessage = function (event) {
|
||||
|
|
|
|||
|
|
@ -93,10 +93,6 @@ const promiseMonitoring = () => {
|
|||
*/
|
||||
|
||||
export const iframeMonitoring = () => {
|
||||
if (!monitorUrl) {
|
||||
return false
|
||||
}
|
||||
|
||||
window.frames[0].onerror = function (errorMessage, scriptURI, lineNo, columnNo, error) {
|
||||
requestEvent(monitorUrl, {
|
||||
event_type: 'design_iframeError',
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
/**
|
||||
* Copyright (c) 2023 - present TinyEngine Authors.
|
||||
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license.
|
||||
*
|
||||
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
||||
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
|
||||
import { Linter } from 'eslint-linter-browserify'
|
||||
import eslintRule from '../config-files/eslint-rule'
|
||||
|
||||
const defaultConfig = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
},
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module'
|
||||
}
|
||||
}
|
||||
|
||||
const config = {
|
||||
...defaultConfig,
|
||||
rules: {
|
||||
...eslintRule,
|
||||
// JS 面板中,仅定义 function,但可能不使用该方法
|
||||
'no-unused-vars': 'off',
|
||||
'no-alert': 'off',
|
||||
'no-console': 'off'
|
||||
},
|
||||
settings: {}
|
||||
}
|
||||
|
||||
// 错误的等级,ESLint 与 monaco 的存在差异,做一层映射
|
||||
const severityMap = {
|
||||
2: 'Error',
|
||||
1: 'Warning'
|
||||
}
|
||||
const linter = new Linter()
|
||||
|
||||
self.addEventListener('message', (event) => {
|
||||
const { code, version } = event.data
|
||||
|
||||
const ruleDefines = linter.getRules()
|
||||
const errs = linter.verify(code, config)
|
||||
|
||||
const markers = errs.map(({ ruleId = '', line, endLine, column, endColumn, message, severity }) => ({
|
||||
code: {
|
||||
value: ruleId || '',
|
||||
target: ruleDefines.get(ruleId)?.meta?.docs?.url || ''
|
||||
},
|
||||
startLineNumber: line,
|
||||
endLineNumber: endLine,
|
||||
startColumn: column,
|
||||
endColumn: endColumn,
|
||||
message: message,
|
||||
severity: severityMap[severity],
|
||||
source: 'ESLint'
|
||||
}))
|
||||
|
||||
// ESLint 静态检查结果,发回主线程
|
||||
self.postMessage({ markers, version })
|
||||
})
|
||||
|
|
@ -31,16 +31,18 @@
|
|||
"license": "MIT",
|
||||
"homepage": "https://opentiny.design/tiny-engine",
|
||||
"dependencies": {
|
||||
"@babel/generator": "~7.23.2",
|
||||
"@babel/parser": "~7.23.2",
|
||||
"@babel/traverse": "~7.23.2",
|
||||
"@babel/generator": "7.18.13",
|
||||
"@babel/parser": "7.18.13",
|
||||
"@babel/traverse": "7.18.13",
|
||||
"@opentiny/tiny-engine-builtin-component": "workspace:*",
|
||||
"@opentiny/tiny-engine-http": "workspace:*",
|
||||
"@opentiny/tiny-engine-i18n-host": "workspace:*",
|
||||
"@opentiny/tiny-engine-utils": "workspace:*",
|
||||
"@opentiny/vue": "~3.10.0",
|
||||
"@opentiny/vue-locale": "~3.10.0",
|
||||
"@opentiny/vue-renderless": "~3.10.0",
|
||||
"@vue/shared": "^3.3.4",
|
||||
"css-tree": "^2.3.1",
|
||||
"eslint-linter-browserify": "8.57.0",
|
||||
"prettier": "2.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -50,9 +52,6 @@
|
|||
"vite": "^4.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentiny/vue": "^3.14.0",
|
||||
"@opentiny/vue-locale": "^3.14.0",
|
||||
"@opentiny/vue-renderless": "^3.14.0",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^9.9.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ const initBlock = async (block = {}, _langs = {}, isEdit) => {
|
|||
// 把区块的schema传递给画布
|
||||
await resetBlockCanvasState({ pageSchema: getBlockPageSchema(block) })
|
||||
// 这一步操作很重要,让区块管理面板和画布共同维护同一份区块schema
|
||||
block.content = useCanvas().canvasApi.value?.getSchema()
|
||||
block.content = useCanvas().renderer.value?.getSchema()
|
||||
|
||||
setCurrentBlock(block)
|
||||
setBreadcrumbBlock([block[nameCn] || block.label], block.histories)
|
||||
|
|
@ -361,7 +361,7 @@ const createEmptyBlock = ({ name_cn, label, path, categories }) => {
|
|||
}
|
||||
|
||||
const setComponentLinkedValue = ({ propertyName, value }) => {
|
||||
const { schema } = useCanvas().canvasApi.value?.getCurrent() || {}
|
||||
const { schema } = useCanvas().renderer.value?.getCurrent() || {}
|
||||
|
||||
if (!propertyName || !schema) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -51,20 +51,14 @@ const defaultSchema = {
|
|||
outputs: []
|
||||
}
|
||||
|
||||
const canvasApi = ref({})
|
||||
const isCanvasApiReady = ref(false)
|
||||
|
||||
const initCanvasApi = (newCanvasApi) => {
|
||||
canvasApi.value = newCanvasApi
|
||||
isCanvasApiReady.value = true
|
||||
}
|
||||
const renderer = ref(null)
|
||||
|
||||
const pageState = reactive({ ...defaultPageState, loading: true })
|
||||
// 重置画布数据
|
||||
const resetCanvasState = async (state = {}) => {
|
||||
Object.assign(pageState, defaultPageState, state)
|
||||
|
||||
await canvasApi.value?.setSchema(pageState.pageSchema)
|
||||
await renderer.value?.setSchema(pageState.pageSchema)
|
||||
}
|
||||
|
||||
// 页面重置画布数据
|
||||
|
|
@ -148,6 +142,7 @@ const getCurrentPage = () => pageState.currentPage
|
|||
export default function () {
|
||||
return {
|
||||
pageState,
|
||||
renderer,
|
||||
isBlock,
|
||||
isSaved,
|
||||
isLoading,
|
||||
|
|
@ -158,11 +153,10 @@ export default function () {
|
|||
resetPageCanvasState,
|
||||
resetBlockCanvasState,
|
||||
clearCurrentState,
|
||||
getDataSourceMap: renderer.value?.getDataSourceMap,
|
||||
setDataSourceMap: renderer.value?.setDataSourceMap,
|
||||
getCurrentSchema,
|
||||
setCurrentSchema,
|
||||
getCurrentPage,
|
||||
initCanvasApi,
|
||||
canvasApi,
|
||||
isCanvasApiReady
|
||||
getCurrentPage
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ const push = (schema) => {
|
|||
|
||||
const go = (addend, valid) => {
|
||||
historyState.index = historyState.index + addend
|
||||
useCanvas().canvasApi.value?.setSchema(string2Schema(list[historyState.index]))
|
||||
useCanvas().renderer.value?.setSchema(string2Schema(list[historyState.index]))
|
||||
|
||||
// 不是锁定状态,撤销操作后,传递第二个标识位,将 list 的长度减一,置灰 undoredo 操作按钮
|
||||
if (typeof valid === 'boolean') {
|
||||
|
|
@ -95,7 +95,7 @@ const clear = () => {
|
|||
const addHistory = (schema) => {
|
||||
if (!schema) {
|
||||
useCanvas().setSaved(false)
|
||||
push(useCanvas().canvasApi.value?.getSchema())
|
||||
push(useCanvas().renderer.value?.getSchema())
|
||||
} else {
|
||||
clear()
|
||||
// 初始 schema 需要设置为第一条历史记录
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ const setProp = (name, value, type) => {
|
|||
}
|
||||
|
||||
// 没有父级,或者不在节点上面,要更新内容。就用setState
|
||||
const { getNode, setState, updateRect } = useCanvas().canvasApi.value || {}
|
||||
const { getNode, setState, updateRect } = useCanvas().renderer.value || {}
|
||||
getNode(properties.schema.id, true)?.parent || setState(useCanvas().getPageSchema().state)
|
||||
propsUpdateKey.value++
|
||||
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ const registerBlock = async (data, notFetchResouce) => {
|
|||
return block
|
||||
} else {
|
||||
if (!blockResource.get(label)) {
|
||||
const { addScript, addStyle } = useCanvas().canvasApi.value
|
||||
const { addScript, addStyle } = useCanvas().renderer.value
|
||||
const promises = scripts
|
||||
.filter((item) => item.includes('umd.js'))
|
||||
.map(addScript)
|
||||
|
|
@ -372,7 +372,7 @@ const fetchResource = async ({ isInit = true } = {}) => {
|
|||
const { id, type } = useEditorInfo().useInfo()
|
||||
useApp().appInfoState.selectedId = id
|
||||
|
||||
const { Builtin } = useCanvas().canvasApi.value
|
||||
const Builtin = window.Builtin
|
||||
Builtin.data.materials.components[0].children.map(registerComponent)
|
||||
BuiltinComponentMaterials.components[0].children.map(registerComponent)
|
||||
|
||||
|
|
@ -452,7 +452,7 @@ const updateCanvasDependencies = (blocks) => {
|
|||
getBlockDeps(block.content.dependencies)
|
||||
})
|
||||
|
||||
useCanvas().canvasApi.value?.canvasDispatch('updateDependencies', { detail: resState.thirdPartyDeps })
|
||||
useCanvas().renderer.value?.canvasDispatch('updateDependencies', { detail: resState.thirdPartyDeps })
|
||||
}
|
||||
|
||||
export default function () {
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ const ensureI18n = (obj, send) => {
|
|||
}
|
||||
})
|
||||
|
||||
useCanvas().canvasApi.value?.setLocales(messages, true)
|
||||
useCanvas().renderer.value?.setLocales(messages, true)
|
||||
} catch (e) {
|
||||
// 不需要处理,有报错的词条会在画布初始化的时候统一调setLocales这个方法
|
||||
}
|
||||
|
|
@ -171,7 +171,7 @@ const initAppI18n = async (appId) => {
|
|||
host: appId,
|
||||
hostType: HOST_TYPE.App
|
||||
})
|
||||
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
|
||||
useCanvas().renderer.value?.setLocales(i18nResource.messages)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ const initBlockI18n = async (blockId) => {
|
|||
host: blockId,
|
||||
hostType: HOST_TYPE.Block
|
||||
})
|
||||
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
|
||||
useCanvas().renderer.value?.setLocales(i18nResource.messages)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ const initBlockLocalI18n = async (langs = {}) => {
|
|||
hostType: HOST_TYPE.Block,
|
||||
local: true
|
||||
})
|
||||
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
|
||||
useCanvas().renderer.value?.setLocales(i18nResource.messages)
|
||||
}
|
||||
|
||||
const format = (str = '', params = {}) => str.replace(/\$\{(.+?)\}/g, (substr, key) => params[key] || '')
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
|
|||
import { glob } from 'glob'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const jsEntries = glob.sync('./js/**/*.js').map((file) => {
|
||||
const jsEntries = glob.sync('./js/**.js').map((file) => {
|
||||
return [file.slice(0, file.length - path.extname(file).length), fileURLToPath(new URL(file, import.meta.url))]
|
||||
})
|
||||
|
||||
|
|
@ -26,12 +26,9 @@ export default defineConfig({
|
|||
plugins: [vue(), vueJsx()],
|
||||
publicDir: false,
|
||||
resolve: {},
|
||||
base: './',
|
||||
define: {
|
||||
'import.meta': 'import.meta',
|
||||
'import.meta.env.MODE': 'import.meta.env.MODE',
|
||||
'import.meta.env.PROD': 'import.meta.env.PROD',
|
||||
'import.meta.env.BASE_URL': 'import.meta.env.BASE_URL'
|
||||
'process.env': {},
|
||||
'import.meta': 'import.meta'
|
||||
},
|
||||
build: {
|
||||
cssCodeSplit: false,
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
NODE_ENV=production
|
||||
VITE_CDN_DOMAIN=https://npm.onmicrosoft.cn
|
||||
VITE_LOCAL_IMPORT_MAPS=false
|
||||
VITE_LOCAL_BUNDLE_DEPS=false
|
||||
# VITE_ORIGIN=
|
||||
|
||||
# 错误监控上报 url
|
||||
|
|
|
|||
|
|
@ -2,7 +2,5 @@
|
|||
|
||||
NODE_ENV=development
|
||||
VITE_CDN_DOMAIN=https://npm.onmicrosoft.cn
|
||||
VITE_LOCAL_IMPORT_MAPS=false
|
||||
VITE_LOCAL_BUNDLE_DEPS=false
|
||||
# request data via alpha service
|
||||
# VITE_ORIGIN=
|
||||
# VITE_ORIGIN=
|
||||
|
|
@ -2,6 +2,4 @@
|
|||
|
||||
NODE_ENV=production
|
||||
VITE_CDN_DOMAIN=https://npm.onmicrosoft.cn
|
||||
VITE_LOCAL_IMPORT_MAPS=false
|
||||
VITE_LOCAL_BUNDLE_DEPS=false
|
||||
#VITE_ORIGIN=
|
||||
#VITE_ORIGIN=
|
||||
|
|
@ -1,265 +1,302 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<style type="text/css">
|
||||
.loading-warp {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
top: -75px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link href="%VITE_CDN_DOMAIN%/@opentiny/vue-theme@3.11/index.css" rel="stylesheet" />
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<style type="text/css">
|
||||
.loading-warp {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
top: -75px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
animation: load 3s linear infinite;
|
||||
}
|
||||
|
||||
.loading div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.loading span {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #99cc66;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -10px;
|
||||
margin-left: -10px;
|
||||
animation: changeBgColor 3s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes load {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
.loading {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
animation: load 3s linear infinite;
|
||||
}
|
||||
.loading div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
.loading span {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
background: #99cc66;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-top: -10px;
|
||||
margin-left: -10px;
|
||||
animation: changeBgColor 3s ease infinite;
|
||||
}
|
||||
@keyframes load {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
33.3% {
|
||||
transform: rotate(120deg);
|
||||
}
|
||||
66.6% {
|
||||
transform: rotate(240deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes changeBgColor {
|
||||
0%,
|
||||
100% {
|
||||
background: #99cc66;
|
||||
}
|
||||
33.3% {
|
||||
background: #ffff66;
|
||||
}
|
||||
66.6% {
|
||||
background: #ff6666;
|
||||
}
|
||||
}
|
||||
.loading div:nth-child(2) {
|
||||
|
||||
33.3% {
|
||||
transform: rotate(120deg);
|
||||
}
|
||||
.loading div:nth-child(3) {
|
||||
|
||||
66.6% {
|
||||
transform: rotate(240deg);
|
||||
}
|
||||
.loading div:nth-child(2) span {
|
||||
animation-delay: 1s;
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
.loading div:nth-child(3) span {
|
||||
animation-delay: 2s;
|
||||
}
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
span,
|
||||
object,
|
||||
iframe,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
abbr,
|
||||
address,
|
||||
cite,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
em,
|
||||
img,
|
||||
ins,
|
||||
kbd,
|
||||
q,
|
||||
samp,
|
||||
small,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
var,
|
||||
b,
|
||||
i,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
article,
|
||||
aside,
|
||||
canvas,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary,
|
||||
time,
|
||||
mark,
|
||||
audio,
|
||||
video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 100%;
|
||||
background: transparent;
|
||||
user-select: none;
|
||||
}
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
@keyframes changeBgColor {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
background: #99cc66;
|
||||
}
|
||||
|
||||
.design-canvas {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
33.3% {
|
||||
background: #ffff66;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
66.6% {
|
||||
background: #ff6666;
|
||||
}
|
||||
body::-webkit-scrollbar-track,
|
||||
body::-webkit-scrollbar-track-piece,
|
||||
body::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.loading div:nth-child(2) {
|
||||
transform: rotate(120deg);
|
||||
}
|
||||
|
||||
.loading div:nth-child(3) {
|
||||
transform: rotate(240deg);
|
||||
}
|
||||
|
||||
.loading div:nth-child(2) span {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
.loading div:nth-child(3) span {
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
div,
|
||||
span,
|
||||
object,
|
||||
iframe,
|
||||
p,
|
||||
blockquote,
|
||||
pre,
|
||||
abbr,
|
||||
address,
|
||||
cite,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
em,
|
||||
img,
|
||||
ins,
|
||||
kbd,
|
||||
q,
|
||||
samp,
|
||||
small,
|
||||
strong,
|
||||
sub,
|
||||
sup,
|
||||
var,
|
||||
b,
|
||||
i,
|
||||
dl,
|
||||
dt,
|
||||
dd,
|
||||
ol,
|
||||
ul,
|
||||
li,
|
||||
fieldset,
|
||||
form,
|
||||
label,
|
||||
legend,
|
||||
table,
|
||||
caption,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
tr,
|
||||
th,
|
||||
td,
|
||||
article,
|
||||
aside,
|
||||
canvas,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section,
|
||||
summary,
|
||||
time,
|
||||
mark,
|
||||
audio,
|
||||
video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 100%;
|
||||
background: transparent;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.design-canvas {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-track,
|
||||
body::-webkit-scrollbar-track-piece,
|
||||
body::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-thumb {
|
||||
background-color: #dbdbdb;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #c2c2c2;
|
||||
}
|
||||
|
||||
.design-page {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.design-page> :not(:defined):empty {
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 此选择器表示所有未被定义的自定义元素,用来表示区块还没加载完成,并添加加载中动画 */
|
||||
.design-page> :not(:defined):empty::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #4f77ff;
|
||||
border-top-color: transparent;
|
||||
border-radius: 100%;
|
||||
animation: circle infinite 0.75s linear;
|
||||
}
|
||||
|
||||
/* 以下代码是为了保证区块还没加载完成之前不显示内部插槽内容 */
|
||||
.design-page> :not(:defined):empty>* {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.design-page> :not(:defined):empty::after {
|
||||
margin-left: 10px;
|
||||
content: '区块加载中...';
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.canvas-container {
|
||||
background: #f1f1f1;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.canvas-container .container-box {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1px 100%, 100% 1px;
|
||||
background-position: 100% 0, 100% 100%;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background-image: linear-gradient(-90deg, #e0e0e0, #e0e0e0), linear-gradient(-180deg, #e0e0e0, #e0e0e0);
|
||||
}
|
||||
|
||||
.design-page .tiny-row .tiny-col:empty {
|
||||
min-height: 30px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.canvas-container .container-box .container-tip {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1px 100%, 100% 1px;
|
||||
background-position: initial;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: 100%;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #a7b1bd;
|
||||
background-image: linear-gradient(-90deg, #e0e0e0, #e0e0e0), linear-gradient(-180deg, #e0e0e0, #e0e0e0);
|
||||
}
|
||||
|
||||
.canvas-grid-bg {
|
||||
background-image: linear-gradient(#dadada7d 1px, transparent 0),
|
||||
linear-gradient(90deg, #dadada7d 1px, transparent 0), linear-gradient(#dadada 1px, transparent 0),
|
||||
linear-gradient(90deg, #dadada 1px, transparent 0);
|
||||
background-size: 11px 11px, 11px 11px, 55px 55px, 55px 55px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
|
||||
@keyframes circle {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
body::-webkit-scrollbar-thumb {
|
||||
background-color: #dbdbdb;
|
||||
border-radius: 4px;
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
body::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #c2c2c2;
|
||||
}
|
||||
.design-page {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
.design-page > :not(:defined):empty {
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
/* 此选择器表示所有未被定义的自定义元素,用来表示区块还没加载完成,并添加加载中动画 */
|
||||
.design-page > :not(:defined):empty::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #4f77ff;
|
||||
border-top-color: transparent;
|
||||
border-radius: 100%;
|
||||
animation: circle infinite 0.75s linear;
|
||||
}
|
||||
/* 以下代码是为了保证区块还没加载完成之前不显示内部插槽内容 */
|
||||
.design-page > :not(:defined):empty > * {
|
||||
display: none;
|
||||
}
|
||||
.design-page > :not(:defined):empty::after {
|
||||
margin-left: 10px;
|
||||
content: '区块加载中...';
|
||||
font-size: 16px;
|
||||
}
|
||||
.canvas-container {
|
||||
background: #f1f1f1;
|
||||
height: 100%;
|
||||
}
|
||||
.canvas-container .container-box {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1px 100%, 100% 1px;
|
||||
background-position: 100% 0, 100% 100%;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
background-image: linear-gradient(-90deg, #e0e0e0, #e0e0e0), linear-gradient(-180deg, #e0e0e0, #e0e0e0);
|
||||
}
|
||||
.design-page .tiny-row .tiny-col:empty {
|
||||
min-height: 30px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.canvas-container .container-box .container-tip {
|
||||
background-repeat: no-repeat;
|
||||
background-size: 1px 100%, 100% 1px;
|
||||
background-position: initial;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: 100%;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #a7b1bd;
|
||||
background-image: linear-gradient(-90deg, #e0e0e0, #e0e0e0), linear-gradient(-180deg, #e0e0e0, #e0e0e0);
|
||||
}
|
||||
.canvas-grid-bg {
|
||||
background-image: linear-gradient(#dadada7d 1px, transparent 0),
|
||||
linear-gradient(90deg, #dadada7d 1px, transparent 0), linear-gradient(#dadada 1px, transparent 0),
|
||||
linear-gradient(90deg, #dadada 1px, transparent 0);
|
||||
background-size: 11px 11px, 11px 11px, 55px 55px, 55px 55px;
|
||||
background-color: rgb(255, 255, 255);
|
||||
}
|
||||
@keyframes circle {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="design-canvas" id="app">
|
||||
<div class="loading-warp">
|
||||
<div class="loading">
|
||||
<div><span></span></div>
|
||||
<div><span></span></div>
|
||||
<div><span></span></div>
|
||||
</div>
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="design-canvas" id="app">
|
||||
<div class="loading-warp">
|
||||
<div class="loading">
|
||||
<div><span></span></div>
|
||||
<div><span></span></div>
|
||||
<div><span></span></div>
|
||||
</div>
|
||||
<script type="module" src="./src/canvas.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
<script type="module" src="./src/canvas.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -37,15 +37,20 @@ import Bridge from '@opentiny/tiny-engine-plugin-bridge'
|
|||
import Block from '@opentiny/tiny-engine-plugin-block'
|
||||
import Datasource from '@opentiny/tiny-engine-plugin-datasource'
|
||||
import Robot from '@opentiny/tiny-engine-plugin-robot'
|
||||
import { getPlugins } from '@opentiny/tiny-engine-plugin-ai'
|
||||
|
||||
import Props from '@opentiny/tiny-engine-setting-props'
|
||||
import Events from '@opentiny/tiny-engine-setting-events'
|
||||
import Styles from '@opentiny/tiny-engine-setting-styles'
|
||||
|
||||
import { useCanvas, useHistory } from '@opentiny/tiny-engine-controller'
|
||||
|
||||
import '@opentiny/tiny-engine-theme'
|
||||
|
||||
const { PageCreator, ComponentEditor } = getPlugins({ useCanvas, useHistory })
|
||||
|
||||
const addons = {
|
||||
plugins: [Materials, Tree, Page, Block, Datasource, Bridge, I18n, Script, Data, Schema, Help, Robot],
|
||||
plugins: [Materials, Tree, Page, Block, Datasource, Bridge, I18n, Script, Data, Schema, Help, Robot, PageCreator, ComponentEditor],
|
||||
toolbars: [
|
||||
Logo,
|
||||
Breadcrumb,
|
||||
|
|
|
|||
|
|
@ -30,16 +30,16 @@
|
|||
"author": "OpenTiny Team",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "~7.23.2",
|
||||
"@babel/generator": "~7.23.2",
|
||||
"@babel/parser": "~7.23.2",
|
||||
"@babel/traverse": "~7.23.2",
|
||||
"@babel/core": "7.18.13",
|
||||
"@babel/generator": "7.18.13",
|
||||
"@babel/parser": "7.18.13",
|
||||
"@babel/traverse": "7.18.13",
|
||||
"@opentiny/tiny-engine-canvas": "workspace:*",
|
||||
"@opentiny/tiny-engine-common": "workspace:*",
|
||||
"@opentiny/tiny-engine-controller": "workspace:*",
|
||||
"@opentiny/tiny-engine-dsl-vue": "workspace:*",
|
||||
"@opentiny/tiny-engine-http": "workspace:*",
|
||||
"@opentiny/tiny-engine-i18n-host": "workspace:*",
|
||||
"@opentiny/tiny-engine-plugin-ai": "workspace:*",
|
||||
"@opentiny/tiny-engine-plugin-block": "workspace:*",
|
||||
"@opentiny/tiny-engine-plugin-bridge": "workspace:*",
|
||||
"@opentiny/tiny-engine-plugin-data": "workspace:*",
|
||||
|
|
@ -78,22 +78,21 @@
|
|||
"@opentiny/tiny-engine-toolbar-setting": "workspace:*",
|
||||
"@opentiny/tiny-engine-utils": "workspace:*",
|
||||
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
|
||||
"@opentiny/vue": "~3.14.0",
|
||||
"@opentiny/vue-design-smb": "~3.14.0",
|
||||
"@opentiny/vue-renderless": "~3.14.0",
|
||||
"@opentiny/vue-theme": "~3.14.0",
|
||||
"@opentiny/vue": "~3.11.0",
|
||||
"@opentiny/vue-design-smb": "~3.11.0",
|
||||
"@opentiny/vue-renderless": "~3.11.0",
|
||||
"@opentiny/vue-theme": "~3.11.0",
|
||||
"@vue/babel-plugin-jsx": "1.1.1",
|
||||
"@vue/repl": "^2.9.0",
|
||||
"@vueuse/core": "^9.6.0",
|
||||
"element-resize-detector": "^1.2.4",
|
||||
"eslint-linter-browserify": "8.57.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"html2canvas": "^1.4.1",
|
||||
"jszip": "^3.10.1",
|
||||
"monaco-editor": "0.33.0",
|
||||
"prettier": "2.7.1",
|
||||
"sortablejs": "^1.14.0",
|
||||
"vue": "3.4.23",
|
||||
"vue": "^3.4.15",
|
||||
"vue-i18n": "^9.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -120,11 +119,9 @@
|
|||
"rollup-plugin-polyfill-node": "^0.12.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-visualizer": "^5.8.3",
|
||||
"shelljs": "^0.8.5",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"vite": "^4.3.7",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"vite-plugin-static-copy": "^0.16.0",
|
||||
"vite-plugin-monaco-editor": "^1.0.10",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-eslint-parser": "^8.0.1"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Design Core Preview</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./src/preview/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Design Core Preview</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="./src/preview/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -311,7 +311,7 @@
|
|||
"loop": true,
|
||||
"condition": true,
|
||||
"styles": true,
|
||||
"isContainer": true,
|
||||
"isContainer": false,
|
||||
"isModal": false,
|
||||
"isPopper": false,
|
||||
"nestingRule": {
|
||||
|
|
@ -632,7 +632,7 @@
|
|||
"loop": true,
|
||||
"condition": true,
|
||||
"styles": true,
|
||||
"isContainer": true,
|
||||
"isContainer": false,
|
||||
"isModal": false,
|
||||
"isPopper": false,
|
||||
"nestingRule": {
|
||||
|
|
@ -1089,7 +1089,7 @@
|
|||
"loop": true,
|
||||
"condition": true,
|
||||
"styles": true,
|
||||
"isContainer": true,
|
||||
"isContainer": false,
|
||||
"isModal": false,
|
||||
"isPopper": false,
|
||||
"nestingRule": {
|
||||
|
|
@ -4139,9 +4139,6 @@
|
|||
"contentMenu": {
|
||||
"actions": []
|
||||
}
|
||||
},
|
||||
"configure": {
|
||||
"isContainer": true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -4500,9 +4497,6 @@
|
|||
"contentMenu": {
|
||||
"actions": []
|
||||
}
|
||||
},
|
||||
"configure": {
|
||||
"isContainer": true
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -5985,7 +5979,7 @@
|
|||
"loop": true,
|
||||
"condition": true,
|
||||
"styles": true,
|
||||
"isContainer": false,
|
||||
"isContainer": true,
|
||||
"isModal": false,
|
||||
"nestingRule": {
|
||||
"childWhitelist": "",
|
||||
|
|
@ -6400,7 +6394,7 @@
|
|||
"loop": true,
|
||||
"condition": true,
|
||||
"styles": true,
|
||||
"isContainer": false,
|
||||
"isContainer": true,
|
||||
"isModal": false,
|
||||
"nestingRule": {
|
||||
"childWhitelist": "",
|
||||
|
|
@ -8189,7 +8183,7 @@
|
|||
"loop": true,
|
||||
"condition": true,
|
||||
"styles": true,
|
||||
"isContainer": false,
|
||||
"isContainer": true,
|
||||
"isModal": false,
|
||||
"nestingRule": {
|
||||
"childWhitelist": "",
|
||||
|
|
@ -8956,9 +8950,7 @@
|
|||
"cols": 12,
|
||||
"widget": {
|
||||
"component": "MetaCodeEditor",
|
||||
"props": {
|
||||
"language": "json"
|
||||
}
|
||||
"props": {}
|
||||
},
|
||||
"description": {
|
||||
"zh_CN": ""
|
||||
|
|
@ -9004,7 +8996,7 @@
|
|||
"loop": true,
|
||||
"condition": true,
|
||||
"styles": true,
|
||||
"isContainer": false,
|
||||
"isContainer": true,
|
||||
"clickCapture": false,
|
||||
"isModal": false,
|
||||
"nestingRule": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* Copyright (c) 2023 - present TinyEngine Authors.
|
||||
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license.
|
||||
*
|
||||
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
|
||||
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
|
||||
*
|
||||
*/
|
||||
|
||||
// importScripts 不支持 esm, 此处使用 umd, 对外暴露的变量名:linter,需要填写openTiny的cdn地址
|
||||
importScripts('')
|
||||
|
||||
// 根据公司的编码规范内置了 config/rules, 可以进一步定制
|
||||
const config = {
|
||||
...self.linter.config,
|
||||
rules: {
|
||||
...self.linter.config.rules,
|
||||
// JS 面板中,仅定义 function,但可能不使用该方法
|
||||
'no-unused-vars': 'off',
|
||||
'no-alert': 'off',
|
||||
'no-console': 'off'
|
||||
},
|
||||
settings: {}
|
||||
}
|
||||
|
||||
// 错误的等级,ESLint 与 monaco 的存在差异,做一层映射
|
||||
const severityMap = {
|
||||
2: 'Error',
|
||||
1: 'Warning'
|
||||
}
|
||||
|
||||
self.addEventListener('message', (event) => {
|
||||
const { code, version } = event.data
|
||||
|
||||
const ruleDefines = self.linter.esLinter.getRules()
|
||||
const errs = self.linter.esLinter.verify(code, config)
|
||||
|
||||
const markers = errs.map(({ ruleId = '', line, endLine, column, endColumn, message, severity }) => ({
|
||||
code: {
|
||||
value: ruleId,
|
||||
target: ruleDefines.get(ruleId)?.meta?.docs?.url
|
||||
},
|
||||
startLineNumber: line,
|
||||
endLineNumber: endLine,
|
||||
startColumn: column,
|
||||
endColumn: endColumn,
|
||||
message: message,
|
||||
severity: severityMap[severity],
|
||||
source: 'ESLint'
|
||||
}))
|
||||
|
||||
// ESLint 静态检查结果,发回主线程
|
||||
self.postMessage({ markers, version })
|
||||
})
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
import path from 'node:path'
|
||||
import { readJsonSync } from 'fs-extra'
|
||||
import { installPackageTemporary } from '../vite-plugins/installPackageTemporary'
|
||||
import { configServerAddProxy } from '../vite-plugins/configureServerAddProxy'
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
import {
|
||||
getCdnPathNpmInfoForSingleFile,
|
||||
getPackageNeedToInstallAndFilesUsingSameVersion,
|
||||
dedupeCopyFiles,
|
||||
copyfileToDynamicSrcMapper
|
||||
} from './locateCdnNpmInfo'
|
||||
|
||||
export function extraBundleCdnLink(filename, originCdnPrefix) {
|
||||
const result = []
|
||||
const bundle = readJsonSync(filename)
|
||||
bundle.data?.materials?.components?.forEach((component) => {
|
||||
if (component.npm) {
|
||||
const possibleUrl = [component.npm.script, component.npm.css]
|
||||
possibleUrl.forEach((url) => {
|
||||
if (url?.startsWith(originCdnPrefix) && !result.includes(url)) {
|
||||
result.push(url)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export function replaceBundleCdnLink(bundle, fileMap) {
|
||||
bundle.data?.materials?.components?.forEach((component) => {
|
||||
if (component.npm) {
|
||||
const possibleUrl = ['script', 'css']
|
||||
possibleUrl.forEach((key) => {
|
||||
const matchRule = fileMap.find((rule) => component.npm[key] === rule.originUrl)
|
||||
if (matchRule) {
|
||||
component.npm[key] = matchRule.newUrl
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function copyBundleDeps({
|
||||
bundleFile,
|
||||
targetBundleFile,
|
||||
originCdnPrefix,
|
||||
base,
|
||||
dir = 'material-static',
|
||||
bundleTempDir = 'bundle-deps/material-static'
|
||||
}) {
|
||||
const cdnFiles = extraBundleCdnLink(bundleFile, originCdnPrefix).map((url) =>
|
||||
getCdnPathNpmInfoForSingleFile(url, originCdnPrefix, base, dir, false, bundleTempDir)
|
||||
)
|
||||
const { packages: packageNeedToInstall, files } = getPackageNeedToInstallAndFilesUsingSameVersion(cdnFiles)
|
||||
|
||||
const plugin = (isDev) => {
|
||||
return [
|
||||
...(isDev ? configServerAddProxy(targetBundleFile, targetBundleFile.replace(/\.([^.]+?$)/, '-local.$1')) : []),
|
||||
...installPackageTemporary(packageNeedToInstall, bundleTempDir),
|
||||
...viteStaticCopy({
|
||||
targets: [
|
||||
...dedupeCopyFiles(files).map(copyfileToDynamicSrcMapper),
|
||||
{
|
||||
src: bundleFile,
|
||||
dest: path.dirname(targetBundleFile),
|
||||
transform: (content) => {
|
||||
const json = JSON.parse(content)
|
||||
replaceBundleCdnLink(json, files)
|
||||
return JSON.stringify(json, null, 2)
|
||||
},
|
||||
rename: (filename, fileExtension) =>
|
||||
isDev ? `${filename}-local.${fileExtension}` : path.basename(targetBundleFile),
|
||||
overwrite: true // 覆盖public的
|
||||
}
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
return {
|
||||
plugin
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
import {
|
||||
getPackageNeedToInstallAndFilesUsingSameVersion,
|
||||
copyfileToDynamicSrcMapper,
|
||||
dedupeCopyFiles,
|
||||
getCdnPathNpmInfoForPackage,
|
||||
getCdnPathNpmInfoForSingleFile
|
||||
} from './locateCdnNpmInfo'
|
||||
import { importmapPlugin } from '../externalDeps'
|
||||
import { installPackageTemporary } from '../vite-plugins/installPackageTemporary'
|
||||
|
||||
export const copyLocalImportMap = ({
|
||||
importMap,
|
||||
styleUrls,
|
||||
originCdnPrefix,
|
||||
base,
|
||||
dir = 'import-map-static',
|
||||
bundleTempDir = 'bundle-deps/design-core-import-map',
|
||||
packageCopy = [] // 值为importMap的imports的左值 (非右值的地址上的包名)
|
||||
}) => {
|
||||
const importMapFiles = Object.entries(importMap.imports)
|
||||
.filter(([_libKey, libPath]) => libPath.startsWith(originCdnPrefix))
|
||||
.map(([libKey, libPath]) => {
|
||||
if (packageCopy.includes(libKey)) {
|
||||
return getCdnPathNpmInfoForPackage(libPath, originCdnPrefix, base, dir, true, bundleTempDir)
|
||||
}
|
||||
return getCdnPathNpmInfoForSingleFile(libPath, originCdnPrefix, base, dir, false, bundleTempDir)
|
||||
})
|
||||
const styleFiles = styleUrls
|
||||
.filter((styleUrl) => styleUrl.startsWith(originCdnPrefix))
|
||||
.map((url) => getCdnPathNpmInfoForSingleFile(url, originCdnPrefix, base, dir, false), bundleTempDir)
|
||||
|
||||
const { packages: packageNeedToInstall, files } = getPackageNeedToInstallAndFilesUsingSameVersion(
|
||||
importMapFiles.concat(styleFiles)
|
||||
)
|
||||
|
||||
return [
|
||||
...installPackageTemporary(packageNeedToInstall, bundleTempDir),
|
||||
...viteStaticCopy({
|
||||
targets: dedupeCopyFiles(files).map(copyfileToDynamicSrcMapper)
|
||||
}),
|
||||
{
|
||||
config(config, { command }) {
|
||||
// 处理devAlias带CDN域名, 另外需要使得本地vue和importMap的vue是同一个实例
|
||||
if (command === 'serve') {
|
||||
config.resolve.alias = [
|
||||
...config.resolve.alias,
|
||||
{
|
||||
find: /^vue$/,
|
||||
replacement: `http://localhost:${config.server.port || 8080}/${
|
||||
files.find(({ originUrl }) => importMap.imports.vue === originUrl).newUrl
|
||||
}` // 实际端口号需要更具本地启动修改
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
importmapPlugin(
|
||||
{
|
||||
imports: Object.fromEntries(
|
||||
Object.entries(importMap.imports).map(([k, v]) => [k, files.find((f) => f.originUrl === v)?.newUrl ?? v])
|
||||
)
|
||||
},
|
||||
styleUrls.map((url) => styleFiles.find((f) => f.originUrl === url).newUrl ?? url)
|
||||
)
|
||||
]
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
import path from 'node:path'
|
||||
import { readJsonSync } from 'fs-extra'
|
||||
import {
|
||||
getPackageNeedToInstallAndFilesUsingSameVersion,
|
||||
copyfileToDynamicSrcMapper,
|
||||
dedupeCopyFiles,
|
||||
getCdnPathNpmInfoForPackage,
|
||||
getCdnPathNpmInfoForSingleFile
|
||||
} from './locateCdnNpmInfo'
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||
import { installPackageTemporary } from '../vite-plugins/installPackageTemporary'
|
||||
|
||||
export function extraPreviewImport(filename, originCdnPrefix) {
|
||||
const result = []
|
||||
const importMap = readJsonSync(filename)
|
||||
Object.entries(importMap.imports)?.forEach(([_key, location]) => {
|
||||
const url = location.replace('${VITE_CDN_DOMAIN}', originCdnPrefix).replace('${opentinyVueVersion}', '~3.14')
|
||||
if (url?.startsWith(originCdnPrefix) && !result.includes(url)) {
|
||||
result.push(url)
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
export function replacePreviewImport(importMap, fileMap, originCdnPrefix) {
|
||||
return {
|
||||
imports: Object.fromEntries(
|
||||
Object.entries(importMap.imports)?.map(([key, location]) => {
|
||||
// 这里的替换占位符规则等同于packages/design-core/src/preview/src/preview/importMap.js, 两边修改需要同步
|
||||
const url = location.replace('${VITE_CDN_DOMAIN}', originCdnPrefix).replace('${opentinyVueVersion}', '~3.14')
|
||||
const matchRule = fileMap.find((rule) => url === rule.originUrl)
|
||||
if (matchRule) {
|
||||
return [key, matchRule.newUrl]
|
||||
}
|
||||
return [key, location]
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function extraPreviewImportFile(filename, targetFileName, originCdnPrefix) {
|
||||
return (fileMap) => [
|
||||
{
|
||||
src: filename,
|
||||
dest: path.dirname(targetFileName),
|
||||
transform: (content) => {
|
||||
return JSON.stringify(replacePreviewImport(JSON.parse(content), fileMap, originCdnPrefix), null, 2)
|
||||
},
|
||||
rename: path.basename(targetFileName)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export function copyPreviewImportMap({
|
||||
importMapJson,
|
||||
targetImportMapJson,
|
||||
originCdnPrefix,
|
||||
base,
|
||||
dir = 'preview-import-map-static',
|
||||
bundleTempDir = 'bundle-deps/preview-import-map',
|
||||
packageCopyLib = [] // 值为cdn地址上的包名
|
||||
}) {
|
||||
const cdnFiles = extraPreviewImport(importMapJson, originCdnPrefix).map((url) => {
|
||||
const { packageName } = url.match(
|
||||
new RegExp(`^${originCdnPrefix}/?(?<packageName>.+?)@(?<versionDemand>[^/]+)(?<filePathInPackage>.*?)$`)
|
||||
).groups
|
||||
if (packageCopyLib.includes(packageName)) {
|
||||
return getCdnPathNpmInfoForPackage(url, originCdnPrefix, base, dir, true, bundleTempDir)
|
||||
}
|
||||
return getCdnPathNpmInfoForSingleFile(url, originCdnPrefix, base, dir, false, bundleTempDir)
|
||||
})
|
||||
const { packages: packageNeedToInstall, files } = getPackageNeedToInstallAndFilesUsingSameVersion(cdnFiles)
|
||||
return [
|
||||
...installPackageTemporary(packageNeedToInstall, bundleTempDir),
|
||||
...viteStaticCopy({
|
||||
targets: [
|
||||
...dedupeCopyFiles(files).map(copyfileToDynamicSrcMapper),
|
||||
...extraPreviewImportFile(importMapJson, targetImportMapJson, originCdnPrefix)(files)
|
||||
]
|
||||
})
|
||||
]
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
export * from './copyBundleDeps'
|
||||
export * from './copyImportMap'
|
||||
export * from './copyPreviewImportMap'
|
||||
export * from './utils'
|
||||
export * from './locateCdnNpmInfo'
|
||||
export * from './replaceImportPath.mjs'
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
import path from 'node:path'
|
||||
import fs from 'node:fs'
|
||||
import fg from 'fast-glob'
|
||||
import { normalizePath } from 'vite'
|
||||
import { readJsonSync } from 'fs-extra'
|
||||
import { babelReplaceImportPathWithCertainFileName } from './replaceImportPath.mjs'
|
||||
|
||||
function transform(content, filename) {
|
||||
if (filename.endsWith('.js')) {
|
||||
const result = babelReplaceImportPathWithCertainFileName(content, filename, console)
|
||||
return result.code || content
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
function onlyFiles(globString) {
|
||||
// viteStaticCopy 自带的glob匹配无法过滤目录, 手动过滤目录作为数组传入
|
||||
return fg.sync(globString + '/**/*', { onlyFiles: true }).map((p) => normalizePath(p))
|
||||
}
|
||||
|
||||
function replaceTailSlash(pathStr) {
|
||||
// 替换尾部的 / 可以把目录当文件复制
|
||||
return pathStr.replace(/\/$/, '')
|
||||
}
|
||||
|
||||
export function copyfileToDynamicSrcMapper({ src, dest, transform, rename, folder, ...rest }) {
|
||||
// viteStaticCopy 自带的glob匹配无法过滤目录, 手动过滤目录作为数组传入,但是不存在的包需要推迟glob的时机到安装文件后
|
||||
return {
|
||||
...rest,
|
||||
get src() {
|
||||
// 注意对象不能被解构,否则getter无法动态计算
|
||||
return src || onlyFiles(folder)
|
||||
},
|
||||
dest,
|
||||
transform,
|
||||
rename
|
||||
}
|
||||
}
|
||||
|
||||
// 生成复制单个文件所需要的信息
|
||||
export function getCdnPathNpmInfoForSingleFile(
|
||||
url, // cdn托管的npm文件地址数组
|
||||
originCdnPrefix, // cdn的前缀
|
||||
base, // build构建的base(BASE_URL)参数
|
||||
dir, // 复制到目标的文件目录
|
||||
transformIContent = false, // 是否需要转换内容, 如果传入url实际为目录则不会转会
|
||||
tempDir = 'bundle-deps' // 新安装包的安装目录
|
||||
) {
|
||||
const baseSlash = base.endsWith('/') ? '' : '/'
|
||||
const { packageName, versionDemand, filePathInPackage } = url.match(
|
||||
new RegExp(`^${originCdnPrefix}/?(?<packageName>.+?)@(?<versionDemand>[^/]+)(?<filePathInPackage>.*?)$`)
|
||||
).groups
|
||||
let version = versionDemand
|
||||
let isFolder = filePathInPackage.endsWith('/')
|
||||
let src = replaceTailSlash(`node_modules/${packageName}${filePathInPackage}`)
|
||||
const sourceExist = fs.existsSync(path.resolve(src))
|
||||
let sourceExistExternal = false
|
||||
if (sourceExist) {
|
||||
const stat = fs.statSync(path.resolve(src))
|
||||
if (stat.isDirectory()) {
|
||||
isFolder = true
|
||||
}
|
||||
const content = readJsonSync(`node_modules/${packageName}/package.json`)
|
||||
version = content.version // 忽略请求的包版本,使用本地包版本号
|
||||
} else {
|
||||
src = tempDir + '/' + src
|
||||
sourceExistExternal = fs.existsSync(path.resolve(src)) // 安装过的不重新安装, 当且仅当所有包都安装过
|
||||
if (sourceExistExternal) {
|
||||
const packageJson = readJsonSync(`${tempDir}/node_modules/${packageName}/package.json`)
|
||||
version = packageJson.version // 如果重新安装这个版本号还需要刷新
|
||||
}
|
||||
}
|
||||
const updateVersion = (version) => {
|
||||
const destPackageDir = `${dir}/${packageName}@${version}`
|
||||
const destFullPath = `${destPackageDir}${filePathInPackage}`
|
||||
const destFullPathWithoutTailSlash = replaceTailSlash(destFullPath)
|
||||
const dest = path.dirname(destFullPathWithoutTailSlash)
|
||||
const rename = dest.startsWith(destPackageDir) ? null : path.basename(destFullPathWithoutTailSlash) // 版本号被截断,需要补充回去
|
||||
return {
|
||||
version,
|
||||
newUrl: `${base}${baseSlash}${destFullPath}`,
|
||||
dest,
|
||||
rename
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
originUrl: url,
|
||||
// newUrl, // overwrite by updateVersion(version)
|
||||
src,
|
||||
// dest, // overwrite by updateVersion(version)
|
||||
packageName,
|
||||
// version, // overwrite by updateVersion(version)
|
||||
versionDemand,
|
||||
filePathInPackage,
|
||||
sourceExist,
|
||||
sourceExistExternal,
|
||||
...updateVersion(version),
|
||||
updateVersion,
|
||||
transform: transformIContent && !isFolder ? transform : null
|
||||
}
|
||||
}
|
||||
|
||||
export function getCdnPathNpmInfoForPackage(
|
||||
url, // cdn托管的npm文件地址数组
|
||||
originCdnPrefix, // cdn的前缀
|
||||
base, // build构建的base(BASE_URL)参数
|
||||
dir, // 复制到目标的文件目录
|
||||
transformIContent = false, // 是否需要转换内容
|
||||
tempDir = 'bundle-deps' // 新安装包的安装目录
|
||||
) {
|
||||
const baseSlash = base.endsWith('/') ? '' : '/'
|
||||
const { packageName, versionDemand, filePathInPackage } = url.match(
|
||||
new RegExp(`^${originCdnPrefix}/?(?<packageName>.+?)@(?<versionDemand>[^/]+)(?<filePathInPackage>.*?)$`)
|
||||
).groups
|
||||
let version = versionDemand
|
||||
let src = `node_modules/${packageName}`
|
||||
const sourceExist = fs.existsSync(path.resolve(src))
|
||||
let sourceExistExternal = false
|
||||
if (sourceExist) {
|
||||
const content = readJsonSync(`${src}/package.json`)
|
||||
version = content.version // 忽略请求的包版本,使用本地包版本号
|
||||
} else {
|
||||
src = tempDir + '/' + src
|
||||
sourceExistExternal = fs.existsSync(path.resolve(src)) // 安装过的不重新安装, 当且仅当所有包都安装过
|
||||
if (sourceExistExternal) {
|
||||
const packageJson = readJsonSync(`${src}/package.json`)
|
||||
version = packageJson.version // 如果重新安装这个版本号还需要刷新
|
||||
}
|
||||
}
|
||||
const updateVersion = (version) => {
|
||||
const packageDir = `${dir}/${packageName}@${version}`
|
||||
const packageDirBasename = path.basename(packageDir)
|
||||
return {
|
||||
version,
|
||||
newUrl: `${base}${baseSlash}${dir}/${packageName}@${version}${filePathInPackage}`,
|
||||
dest: path.dirname(packageDir),
|
||||
rename: (_filename, _fileExtension, fullPath) => `${packageDirBasename}${fullPath.replace(src, '')}`
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
folder: src,
|
||||
originUrl: url,
|
||||
// newUrl, // overwrite by updateVersion(version)
|
||||
src: sourceExist || sourceExistExternal ? onlyFiles(src) : null,
|
||||
// dest, // overwrite by updateVersion(version)
|
||||
packageName,
|
||||
// version, // overwrite by updateVersion(version)
|
||||
versionDemand,
|
||||
filePathInPackage,
|
||||
sourceExist,
|
||||
sourceExistExternal,
|
||||
...updateVersion(version),
|
||||
updateVersion,
|
||||
transform: transformIContent ? transform : null
|
||||
}
|
||||
}
|
||||
|
||||
export function dedupeCopyFiles(files) {
|
||||
return files.reduce((acc, cur) => {
|
||||
//去重,分别处理字符串和数组
|
||||
if (
|
||||
(cur.folder && !acc.some((item) => !!item.folder && item.folder === cur.folder && item.dest === cur.dest)) || // 文件夹拷贝
|
||||
(typeof cur.src === 'string' && !acc.some((item) => item.src === cur.src && item.dest === cur.dest)) // 文件拷贝
|
||||
) {
|
||||
acc.push(cur)
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
}
|
||||
|
||||
export function getPackageNeedToInstallAndFilesUsingSameVersion(files) {
|
||||
const packageNeedToInstall = files
|
||||
.filter((item) => !item.sourceExist)
|
||||
.map(({ packageName, version, sourceExistExternal: exist }) => ({ packageName, version, exist }))
|
||||
.reduce((acc, cur) => {
|
||||
// 同个包避免多个版本只保留一个版本
|
||||
if (!acc.some(({ packageName }) => cur.packageName === packageName)) {
|
||||
acc.push(cur)
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
let newFiles = null
|
||||
if (packageNeedToInstall.length) {
|
||||
// 确保同个包多个版本只能从一个版本引用文件
|
||||
newFiles = files.map((file) => {
|
||||
const samePackageDifferentVersion = packageNeedToInstall.find(
|
||||
({ packageName, version }) => packageName === file.packageName && version !== file.version
|
||||
)
|
||||
if (samePackageDifferentVersion) {
|
||||
return {
|
||||
...file,
|
||||
...file.updateVersion(samePackageDifferentVersion.version)
|
||||
}
|
||||
}
|
||||
return file
|
||||
})
|
||||
}
|
||||
return {
|
||||
packages: packageNeedToInstall,
|
||||
files: newFiles ?? files
|
||||
}
|
||||
}
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { parse } from '@babel/core'
|
||||
import traversePkg from '@babel/traverse'
|
||||
import generatePkg from '@babel/generator'
|
||||
const traverse = traversePkg.default
|
||||
const generate = generatePkg.default
|
||||
|
||||
export function relativePathPattern(relativePath) {
|
||||
return './' + (path.sep === '/' ? relativePath : relativePath.replace(/\\/g, '/'))
|
||||
}
|
||||
|
||||
export function resolvePath(importPath, currentFilePath) {
|
||||
if (['js', 'mjs'].some(suffix =>importPath.endsWith(suffix))) { // 文件名已经带有.js,.mjs后缀
|
||||
return importPath
|
||||
}
|
||||
const parentPath = path.resolve(currentFilePath, '../')
|
||||
const filePrefix = path.resolve(parentPath, importPath)
|
||||
|
||||
if (fs.existsSync(filePrefix)) {
|
||||
const stat = fs.statSync(filePrefix)
|
||||
if (stat.isDirectory()) {
|
||||
let mainFileName = 'index.js'
|
||||
|
||||
const packageFile = path.resolve(filePrefix, 'package.json')
|
||||
|
||||
if (fs.existsSync(packageFile)) {
|
||||
const packageFileContent = fs.readFileSync(packageFile, { encoding: 'utf-8' })
|
||||
const packageJson = JSON.parse(packageFileContent)
|
||||
mainFileName = packageJson.module || packageJson.main || mainFileName
|
||||
}
|
||||
|
||||
const mainFile = path.resolve(filePrefix, mainFileName)
|
||||
if (fs.existsSync(mainFile)) {
|
||||
return relativePathPattern(path.relative(parentPath, mainFile))
|
||||
}
|
||||
return null
|
||||
}
|
||||
return importPath
|
||||
}
|
||||
const possibleSuffix = ['.js', '.mjs']
|
||||
const suffix = possibleSuffix.find(suf => fs.existsSync(filePrefix + suf))
|
||||
if (suffix) {
|
||||
return relativePathPattern(path.relative(path.resolve(currentFilePath, '../'), filePrefix + suffix))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// babel 替换 js的相对地址引用为确定文件后缀
|
||||
export function babelReplaceImportPathWithCertainFileName(content, currentFilePath, logger = console) {
|
||||
let fileChangedMark = false
|
||||
let result = {
|
||||
code: null,
|
||||
success: [],
|
||||
error: []
|
||||
}
|
||||
const ast = parse(content, { sourceType: 'module' })
|
||||
traverse(ast, {
|
||||
ImportOrExportDeclaration: (astPath) => {
|
||||
const node = astPath.node
|
||||
if (!node.source) {
|
||||
return
|
||||
}
|
||||
const importPath = node.source.value
|
||||
if(importPath.startsWith('.')) {
|
||||
const certainPath = resolvePath(importPath, currentFilePath)
|
||||
if(!certainPath) {
|
||||
logger.warn(`File not found: ${importPath} used in ${currentFilePath}`)
|
||||
result.error.push(importPath)
|
||||
}
|
||||
if(certainPath !== importPath) {
|
||||
node.source.value = certainPath
|
||||
fileChangedMark = true
|
||||
result.success.push({before: importPath, after: certainPath})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
if (fileChangedMark) {
|
||||
const { code } = generate(ast, {
|
||||
jsescOption: {
|
||||
quotes: 'single'
|
||||
}
|
||||
})
|
||||
result.code = code
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
export function getBaseUrlFromCli(fallback = '') {
|
||||
// 理论上要从resolvedConfig阶段的钩子里面拿到base,由于插件嵌套插件,子插件的配置项需要在resolveConfig前传入这里,无法等resolvedConfig,故手动获取命令行base
|
||||
const index = process.argv?.indexOf('--base')
|
||||
return index > -1 ? process.argv[index + 1] || fallback : fallback
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
export function configServerAddProxy(path, target) {
|
||||
return [
|
||||
{
|
||||
name: 'vite-plugin-config-server-add-proxy',
|
||||
configureServer(server) {
|
||||
server.middlewares.use((req, _res, next) => {
|
||||
if (req.url.includes(path)) {
|
||||
req.url = req.url.replace(path, target)
|
||||
}
|
||||
next()
|
||||
})
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import shelljs from 'shelljs'
|
||||
|
||||
export function installPackageTemporary(packageNeedToInstall, tempDir, logger = console) {
|
||||
return [
|
||||
{
|
||||
name: 'vite-plugin-install-package-temporary',
|
||||
buildStart() {
|
||||
if (packageNeedToInstall.every((pkg) => pkg.exist)) {
|
||||
logger.info(`[vite-plugin-install-package-temporary]: bundle dependencies packages exist, skip install `)
|
||||
return
|
||||
}
|
||||
|
||||
let code = shelljs.mkdir('-p', tempDir).code
|
||||
if (code === 0) {
|
||||
//code 为 0 表示成功
|
||||
fs.writeFileSync(
|
||||
path.resolve(tempDir, 'package.json'),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: 'bundle-deps', // tempDir to 烤串
|
||||
dependencies: Object.fromEntries(packageNeedToInstall.map((cur) => [cur.packageName, cur.version]))
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
}
|
||||
code =
|
||||
code ||
|
||||
shelljs.cd(tempDir).code ||
|
||||
shelljs.exec(`npm install --force`).code ||
|
||||
shelljs.cd(path.relative(tempDir, '.')).code
|
||||
|
||||
if (code === 0) {
|
||||
logger.info(
|
||||
`[vite-plugin-install-package-temporary]: bundle dependencies package install success, total ${packageNeedToInstall.length} package(s)`
|
||||
)
|
||||
} else {
|
||||
logger.warn(`[vite-plugin-install-package-temporary]: bundle dependencies package install failed`)
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -21,15 +21,7 @@
|
|||
import { reactive, ref, watch, onUnmounted } from 'vue'
|
||||
import { ConfigProvider as TinyConfigProvider } from '@opentiny/vue'
|
||||
import designSmbConfig from '@opentiny/vue-design-smb'
|
||||
import {
|
||||
useResource,
|
||||
useLayout,
|
||||
useEditorInfo,
|
||||
useModal,
|
||||
useApp,
|
||||
useNotify,
|
||||
useCanvas
|
||||
} from '@opentiny/tiny-engine-controller'
|
||||
import { useResource, useLayout, useEditorInfo, useModal, useApp, useNotify } from '@opentiny/tiny-engine-controller'
|
||||
import AppManage from '@opentiny/tiny-engine-plugin-page'
|
||||
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
|
||||
import DesignToolbars from './DesignToolbars.vue'
|
||||
|
|
@ -98,18 +90,7 @@ export default {
|
|||
}
|
||||
|
||||
useEditorInfo().getUserInfo()
|
||||
|
||||
watch(
|
||||
useCanvas().isCanvasApiReady,
|
||||
(ready) => {
|
||||
if (ready) {
|
||||
useResource().fetchResource()
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
useResource().fetchResource()
|
||||
|
||||
const handlePopStateEvent = () => {
|
||||
useResource().handlePopStateEvent()
|
||||
|
|
@ -173,7 +154,6 @@ export default {
|
|||
}
|
||||
}
|
||||
.tiny-engine-right-wrap {
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
}
|
||||
:deep(.monaco-editor .suggest-widget) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="canvas-wrap" ref="canvasRef">
|
||||
<div ref="siteCanvas" class="site-canvas" :style="siteCanvasStyle">
|
||||
<div ref="siteCanvas" class="site-canvas">
|
||||
<canvas-container
|
||||
:controller="controller"
|
||||
:materials-panel="materialsPanel"
|
||||
|
|
@ -14,8 +14,15 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, watch, computed, onUnmounted } from 'vue'
|
||||
import { CanvasContainer, CanvasFooter } from '@opentiny/tiny-engine-canvas'
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import {
|
||||
CanvasContainer,
|
||||
CanvasFooter,
|
||||
selectNode,
|
||||
getNodePath,
|
||||
updateRect,
|
||||
getSchema
|
||||
} from '@opentiny/tiny-engine-canvas'
|
||||
import {
|
||||
useProperties,
|
||||
useCanvas,
|
||||
|
|
@ -55,19 +62,11 @@ export default {
|
|||
|
||||
const removeNode = (node) => {
|
||||
const { pageState } = useCanvas()
|
||||
footData.value = useCanvas().canvasApi.value.getNodePath(node?.id)
|
||||
footData.value = getNodePath(node?.id)
|
||||
pageState.currentSchema = {}
|
||||
pageState.properties = null
|
||||
}
|
||||
|
||||
const siteCanvasStyle = computed(() => {
|
||||
const { scale } = useLayout().getDimension()
|
||||
return {
|
||||
height: `calc((100% - var(--base-bottom-panel-height, 30px) - 36px) / ${scale})`,
|
||||
transform: `scale(${scale})`
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
[() => useCanvas().isSaved(), () => useLayout().layoutState.pageStatus, () => useCanvas().getPageSchema()],
|
||||
([isSaved, pageStatus, pageSchema], [oldIsSaved, _oldPageStatus, oldPageSchema]) => {
|
||||
|
|
@ -134,8 +133,6 @@ export default {
|
|||
useLayout().closePlugin()
|
||||
}
|
||||
|
||||
const { getSchema, getNodePath } = useCanvas().canvasApi.value
|
||||
|
||||
const schema = getSchema()
|
||||
// 如果选中的节点是画布,就设置成默认选中最外层schema
|
||||
useProperties().getProps(node || schema, parent)
|
||||
|
|
@ -145,31 +142,12 @@ export default {
|
|||
}
|
||||
|
||||
const selectFooterNode = ({ node }) => {
|
||||
const { selectNode } = useCanvas().canvasApi.value
|
||||
|
||||
selectNode(node)
|
||||
}
|
||||
|
||||
let canvasResizeObserver = null
|
||||
watch(
|
||||
() => [useCanvas().isCanvasApiReady.value, canvasRef.value],
|
||||
([ready]) => {
|
||||
if (!ready || !canvasRef.value) {
|
||||
return
|
||||
}
|
||||
|
||||
// 先取消监听,再增加监听事件,避免重复监听
|
||||
document.removeEventListener('canvasResize', useCanvas().canvasApi.value.updateRect)
|
||||
canvasResizeObserver?.disconnect?.()
|
||||
|
||||
document.addEventListener('canvasResize', useCanvas().canvasApi.value.updateRect)
|
||||
canvasResizeObserver = new ResizeObserver(useCanvas().canvasApi.value.updateRect).observe(canvasRef.value)
|
||||
}
|
||||
)
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('canvasResize', useCanvas().canvasApi.value.updateRect)
|
||||
canvasResizeObserver?.disconnect?.()
|
||||
onMounted(() => {
|
||||
document.addEventListener('canvasResize', updateRect)
|
||||
new ResizeObserver(updateRect).observe(canvasRef.value)
|
||||
})
|
||||
|
||||
return {
|
||||
|
|
@ -188,7 +166,6 @@ export default {
|
|||
request: useHttp(),
|
||||
ast
|
||||
},
|
||||
siteCanvasStyle,
|
||||
canvasRef
|
||||
}
|
||||
}
|
||||
|
|
@ -205,11 +182,11 @@ export default {
|
|||
position: relative;
|
||||
|
||||
.site-canvas {
|
||||
height: calc(100% - var(--base-bottom-panel-height, 30px) - 36px);
|
||||
background: var(--ti-lowcode-breadcrumb-hover-bg);
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
margin: 18px 0;
|
||||
transform-origin: top;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -68,8 +68,7 @@ export default {
|
|||
.tiny-tabs__content {
|
||||
flex: 1;
|
||||
overflow-y: scroll;
|
||||
padding: 0;
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
.tiny-tabs__item {
|
||||
color: var(--ti-lowcode-setting-panel-tabs-item-title-color);
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ import { defineComponent, computed, defineAsyncComponent } from 'vue'
|
|||
import { Repl, ReplStore } from '@vue/repl'
|
||||
import vueJsx from '@vue/babel-plugin-jsx'
|
||||
import { transformSync } from '@babel/core'
|
||||
import { genSFCWithDefaultPlugin, parseRequiredBlocks } from '@opentiny/tiny-engine-dsl-vue'
|
||||
import { Notify } from '@opentiny/vue'
|
||||
import importMap from './importMap'
|
||||
import srcFiles from './srcFiles'
|
||||
import generateMetaFiles, { processAppJsCode } from './generate'
|
||||
import { getSearchParams, fetchMetaData, fetchImportMap, fetchAppSchema, fetchBlockSchema } from './http'
|
||||
import { getSearchParams, fetchCode, fetchMetaData } from './http'
|
||||
import { PanelType, PreviewTips } from '../constant'
|
||||
import { injectDebugSwitch } from './debugSwitch'
|
||||
import '@vue/repl/style.css'
|
||||
|
|
@ -51,6 +51,8 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
store.setImportMap(importMap)
|
||||
|
||||
// 相比store.setFiles,只要少了state.activeFile = state.files[filename],因为改变activeFile会触发多余的文件解析
|
||||
const setFiles = async (newFiles, mainFileName) => {
|
||||
await store.setFiles(newFiles, mainFileName)
|
||||
|
|
@ -59,7 +61,7 @@ export default {
|
|||
store['initTsConfig']() // 触发获取组件d.ts方便调试
|
||||
}
|
||||
|
||||
const addUtilsImportMap = (importMap, utils = []) => {
|
||||
const addUtilsImportMap = (utils = []) => {
|
||||
const utilsImportMaps = {}
|
||||
utils.forEach(({ type, content: { package: packageName, cdnLink } }) => {
|
||||
if (type === 'npm' && cdnLink) {
|
||||
|
|
@ -69,81 +71,31 @@ export default {
|
|||
const newImportMap = { imports: { ...importMap.imports, ...utilsImportMaps } }
|
||||
store.setImportMap(newImportMap)
|
||||
}
|
||||
const getBlocksSchema = async (pageSchema, blockSet = new Set()) => {
|
||||
let res = []
|
||||
|
||||
const blockNames = parseRequiredBlocks(pageSchema)
|
||||
const promiseList = blockNames
|
||||
.filter((name) => {
|
||||
if (blockSet.has(name)) {
|
||||
return false
|
||||
}
|
||||
|
||||
blockSet.add(name)
|
||||
|
||||
return true
|
||||
})
|
||||
.map((name) => fetchBlockSchema(name))
|
||||
|
||||
const schemaList = await Promise.allSettled(promiseList)
|
||||
|
||||
schemaList.forEach((item) => {
|
||||
if (item.status === 'fulfilled' && item.value?.[0]?.content) {
|
||||
res.push(item.value[0].content)
|
||||
res.push(...getBlocksSchema(item.value[0].content, blockSet))
|
||||
}
|
||||
})
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
const queryParams = getSearchParams()
|
||||
const getImportMap = async () => {
|
||||
if (import.meta.env.VITE_LOCAL_BUNDLE_DEPS === 'true') {
|
||||
const mapJSON = await fetchImportMap()
|
||||
return {
|
||||
imports: {
|
||||
...mapJSON.imports,
|
||||
...getSearchParams().scripts
|
||||
}
|
||||
}
|
||||
}
|
||||
return importMap
|
||||
}
|
||||
|
||||
const promiseList = [
|
||||
fetchAppSchema(queryParams?.app),
|
||||
fetchMetaData(queryParams),
|
||||
setFiles(srcFiles, 'src/Main.vue'),
|
||||
getImportMap()
|
||||
]
|
||||
Promise.all(promiseList).then(async ([appData, metaData, _void, importMapData]) => {
|
||||
addUtilsImportMap(importMapData, metaData.utils || [])
|
||||
const promiseList = [fetchCode(queryParams), fetchMetaData(queryParams), setFiles(srcFiles, 'src/Main.vue')]
|
||||
Promise.all(promiseList).then(([codeList, metaData]) => {
|
||||
addUtilsImportMap(metaData.utils || [])
|
||||
const codeErrorMsgs = codeList
|
||||
.filter(({ errors }) => errors?.length)
|
||||
.map(({ errors }) => errors)
|
||||
.flat()
|
||||
.map(({ message }) => message)
|
||||
|
||||
const blocks = await getBlocksSchema(queryParams.pageInfo?.schema)
|
||||
|
||||
// TODO: 需要验证级联生成 block schema
|
||||
// TODO: 物料内置 block 需要如何处理?
|
||||
const pageCode = [
|
||||
{
|
||||
panelName: 'Main.vue',
|
||||
panelValue:
|
||||
genSFCWithDefaultPlugin(queryParams.pageInfo?.schema, appData?.componentsMap || [], {
|
||||
blockRelativePath: './'
|
||||
}) || '',
|
||||
panelType: 'vue',
|
||||
index: true
|
||||
},
|
||||
...(blocks || []).map((blockSchema) => {
|
||||
return {
|
||||
panelName: blockSchema.fileName,
|
||||
panelValue:
|
||||
genSFCWithDefaultPlugin(blockSchema, appData?.componentsMap || [], { blockRelativePath: './' }) || '',
|
||||
panelType: 'vue',
|
||||
index: true
|
||||
}
|
||||
if (codeErrorMsgs.length) {
|
||||
const title = PreviewTips.ERROR_WHEN_COMPILE
|
||||
Notify({
|
||||
type: 'error',
|
||||
title,
|
||||
message: codeErrorMsgs.join('\n'),
|
||||
// 不自动关闭
|
||||
duration: 0,
|
||||
position: 'top-right'
|
||||
})
|
||||
]
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
// [@vue/repl] `Only lang="ts" is supported for <script> blocks.`
|
||||
const langReg = /lang="jsx"/
|
||||
|
|
@ -191,7 +143,7 @@ export default {
|
|||
|
||||
newFiles['app.js'] = appJsCode
|
||||
|
||||
pageCode.map(fixScriptLang).forEach(assignFiles)
|
||||
codeList.map(fixScriptLang).forEach(assignFiles)
|
||||
|
||||
const metaFiles = generateMetaFiles(metaData)
|
||||
Object.assign(newFiles, metaFiles)
|
||||
|
|
|
|||
|
|
@ -50,11 +50,3 @@ export const fetchMetaData = async ({ platform, app, type, id, history, tenant }
|
|||
params: { platform, app, type, id, history }
|
||||
})
|
||||
: {}
|
||||
|
||||
export const fetchImportMap = async () => {
|
||||
const baseUrl = new URL(import.meta.env.BASE_URL, location.href)
|
||||
return fetch(new URL('./preview-import-map-static/preview-importmap.json', baseUrl).href).then((res) => res.json())
|
||||
}
|
||||
|
||||
export const fetchAppSchema = async (id) => http.get(`/app-center/v1/api/apps/schema/${id}`)
|
||||
export const fetchBlockSchema = async (blockName) => http.get(`/material-center/api/block?label=${blockName}`)
|
||||
|
|
|
|||
|
|
@ -13,19 +13,38 @@
|
|||
// import { hyphenate } from '@vue/shared'
|
||||
|
||||
import { getSearchParams } from './http'
|
||||
import importMapJSON from './importMap.json'
|
||||
import { VITE_CDN_DOMAIN } from '@opentiny/tiny-engine-controller/js/environments'
|
||||
|
||||
const importMap = {}
|
||||
|
||||
const opentinyVueVersion = '~3.14'
|
||||
const opentinyVueVersion = '~3.11'
|
||||
|
||||
function replacePlaceholder(v) {
|
||||
return v.replace('${VITE_CDN_DOMAIN}', VITE_CDN_DOMAIN).replace('${opentinyVueVersion}', opentinyVueVersion)
|
||||
const tinyVue3Imports = {
|
||||
// 推荐之后统一使用@opentiny/vue去引入依赖,兼容后续录入的组件来源于tiny-vue
|
||||
'@opentiny/vue': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue.mjs`,
|
||||
'@opentiny/vue-icon': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-icon.mjs`,
|
||||
'@opentiny/vue-common': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-common.mjs`,
|
||||
'@opentiny/vue-locale': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-locale.mjs`,
|
||||
'@opentiny/vue-renderless/': `${VITE_CDN_DOMAIN}/@opentiny/vue-renderless@${opentinyVueVersion}/`
|
||||
}
|
||||
|
||||
importMap.imports = {
|
||||
...Object.fromEntries(Object.entries(importMapJSON.imports).map(([k, v]) => [k, replacePlaceholder(v)])),
|
||||
vue: `${VITE_CDN_DOMAIN}/vue@3.2.36/dist/vue.runtime.esm-browser.js`,
|
||||
'vue/server-renderer': `${VITE_CDN_DOMAIN}/@vue/server-renderer@3.2.36/dist/server-renderer.esm-browser.js`,
|
||||
'vue-i18n': `${VITE_CDN_DOMAIN}/vue-i18n@9.2.0-beta.36/dist/vue-i18n.esm-browser.js`,
|
||||
'vue-router': `${VITE_CDN_DOMAIN}/vue-router@4.0.16/dist/vue-router.esm-browser.js`,
|
||||
'@vue/devtools-api': `${VITE_CDN_DOMAIN}/@vue/devtools-api@6.5.1/lib/esm/index.js`,
|
||||
'@vueuse/core': `${VITE_CDN_DOMAIN}/@vueuse/core@9.6.0/index.mjs`,
|
||||
'@vueuse/shared': `${VITE_CDN_DOMAIN}/@vueuse/shared@9.6.0/index.mjs`,
|
||||
axios: `${VITE_CDN_DOMAIN}/axios@1.0.0-alpha.1/dist/esm/axios.js`,
|
||||
'axios-mock-adapter': `${VITE_CDN_DOMAIN}/axios-mock-adapter@1.21.1/dist/axios-mock-adapter.js`,
|
||||
'@opentiny/tiny-engine-webcomponent-core': `${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-webcomponent-core@1/dist/tiny-engine-webcomponent-core.es.js`,
|
||||
'@opentiny/tiny-engine-i18n-host': `${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-i18n-host@1/dist/tiny-engine-i18n-host.es.js`,
|
||||
'@opentiny/tiny-engine-builtin-component': `${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-builtin-component@1/dist/index.js`,
|
||||
'@opentiny/tiny-engine-live-component': `${VITE_CDN_DOMAIN}/tiny-engine-live-component@1.0.8/dist/index.js`,
|
||||
'vue-demi': `${VITE_CDN_DOMAIN}/vue-demi@0.13.11/lib/index.mjs`,
|
||||
pinia: `${VITE_CDN_DOMAIN}/pinia@2.0.22/dist/pinia.esm-browser.js`,
|
||||
...tinyVue3Imports,
|
||||
...getSearchParams().scripts
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"imports": {
|
||||
"vue": "${VITE_CDN_DOMAIN}/vue@3.4.23/dist/vue.runtime.esm-browser.js",
|
||||
"vue/server-renderer": "${VITE_CDN_DOMAIN}/@vue/server-renderer@3.4.23/dist/server-renderer.esm-browser.js",
|
||||
"vue-i18n": "${VITE_CDN_DOMAIN}/vue-i18n@9.2.0-beta.36/dist/vue-i18n.esm-browser.js",
|
||||
"vue-router": "${VITE_CDN_DOMAIN}/vue-router@4.0.16/dist/vue-router.esm-browser.js",
|
||||
"@vue/devtools-api": "${VITE_CDN_DOMAIN}/@vue/devtools-api@6.5.1/lib/esm/index.js",
|
||||
"@vueuse/core": "${VITE_CDN_DOMAIN}/@vueuse/core@9.6.0/index.mjs",
|
||||
"@vueuse/shared": "${VITE_CDN_DOMAIN}/@vueuse/shared@9.6.0/index.mjs",
|
||||
"axios": "${VITE_CDN_DOMAIN}/axios@1.0.0/dist/esm/axios.js",
|
||||
"axios-mock-adapter": "${VITE_CDN_DOMAIN}/axios-mock-adapter@1.21.1/dist/axios-mock-adapter.js",
|
||||
"@opentiny/tiny-engine-webcomponent-core": "${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-webcomponent-core@1/dist/tiny-engine-webcomponent-core.es.js",
|
||||
"@opentiny/tiny-engine-i18n-host": "${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-i18n-host@1/dist/lowcode-design-i18n-host.es.js",
|
||||
"@opentiny/tiny-engine-builtin-component": "${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-builtin-component@1/dist/index.js",
|
||||
"vue-demi": "${VITE_CDN_DOMAIN}/vue-demi@0.13.11/lib/index.mjs",
|
||||
"pinia": "${VITE_CDN_DOMAIN}/pinia@2.0.22/dist/pinia.esm-browser.js",
|
||||
"@opentiny/vue": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue.mjs",
|
||||
"@opentiny/vue-icon": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-icon.mjs",
|
||||
"@opentiny/vue-common": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-common.mjs",
|
||||
"@opentiny/vue-locale": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-locale.mjs",
|
||||
"@opentiny/vue-renderless/": "${VITE_CDN_DOMAIN}/@opentiny/vue-renderless@${opentinyVueVersion}/"
|
||||
}
|
||||
}
|
||||
|
|
@ -40,69 +40,4 @@ srcFiles['locales.js'] = localesJS
|
|||
srcFiles['stores.js'] = storesJS
|
||||
srcFiles['storesHelper.js'] = storesHelperJS
|
||||
|
||||
export const genPreviewTemplate = () => {
|
||||
return [
|
||||
{
|
||||
fileName: 'App.vue',
|
||||
path: '',
|
||||
fileContent: appVue
|
||||
},
|
||||
{
|
||||
fileName: 'constant.js',
|
||||
path: '',
|
||||
fileContent: constantJS
|
||||
},
|
||||
{
|
||||
fileName: 'app.js',
|
||||
path: '',
|
||||
fileContent: appJS.replace(/VITE_CDN_DOMAIN/g, import.meta.env.VITE_CDN_DOMAIN)
|
||||
},
|
||||
{
|
||||
fileName: 'injectGlobal.js',
|
||||
path: '',
|
||||
fileContent: injectGlobalJS
|
||||
},
|
||||
{
|
||||
fileName: 'lowcode.js',
|
||||
path: '',
|
||||
fileContent: lowcodeJS
|
||||
},
|
||||
{
|
||||
fileName: 'dataSourceMap.js',
|
||||
path: '',
|
||||
fileContent: dataSourceMapJS
|
||||
},
|
||||
{
|
||||
fileName: 'dataSource.js',
|
||||
path: '',
|
||||
fileContent: dataSourceJS
|
||||
},
|
||||
{
|
||||
fileName: 'utils.js',
|
||||
path: '',
|
||||
fileContent: utilsJS
|
||||
},
|
||||
{
|
||||
fileName: 'bridge.js',
|
||||
path: '',
|
||||
fileContent: bridgeJS
|
||||
},
|
||||
{
|
||||
fileName: 'locales.js',
|
||||
path: '',
|
||||
fileContent: localesJS
|
||||
},
|
||||
{
|
||||
fileName: 'stores.js',
|
||||
path: '',
|
||||
fileContent: storesJS
|
||||
},
|
||||
{
|
||||
fileName: 'storesHelper.js',
|
||||
path: '',
|
||||
fileContent: storesHelperJS
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export default srcFiles
|
||||
|
|
|
|||
|
|
@ -16,5 +16,5 @@ function addCss(href) {
|
|||
link.setAttribute('href', href)
|
||||
document.head.appendChild(link)
|
||||
}
|
||||
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme@3.14/index.css')
|
||||
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme-mobile@3.14/index.css')
|
||||
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme@3.11/index.css')
|
||||
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme-mobile@3.11/index.css')
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import lowcodeConfig from './config/lowcode.config'
|
|||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
|
||||
import { importmapPlugin } from './scripts/externalDeps'
|
||||
import visualizer from 'rollup-plugin-visualizer'
|
||||
import { getBaseUrlFromCli, copyBundleDeps, copyPreviewImportMap, copyLocalImportMap } from './scripts/localCdnFile'
|
||||
|
||||
const origin = 'http://localhost:9090/'
|
||||
|
||||
|
|
@ -94,6 +93,9 @@ const config = {
|
|||
]
|
||||
}
|
||||
},
|
||||
define: {
|
||||
'process.env': {}
|
||||
},
|
||||
build: {
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
|
|
@ -131,8 +133,8 @@ const config = {
|
|||
|
||||
const importMapVersions = {
|
||||
prettier: '2.7.1',
|
||||
vue: '3.4.23',
|
||||
tinyVue: '~3.14'
|
||||
vue: '3',
|
||||
tinyVue: '~3.11'
|
||||
}
|
||||
|
||||
const devAlias = {
|
||||
|
|
@ -183,7 +185,8 @@ const devAlias = {
|
|||
'@opentiny/tiny-engine-utils': path.resolve(__dirname, '../utils/src/index.js'),
|
||||
'@opentiny/tiny-engine-webcomponent-core': path.resolve(__dirname, '../webcomponent/src/lib.js'),
|
||||
'@opentiny/tiny-engine-i18n-host': path.resolve(__dirname, '../i18n/src/lib.js'),
|
||||
'@opentiny/tiny-engine-builtin-component': path.resolve(__dirname, '../builtinComponent/index.js')
|
||||
'@opentiny/tiny-engine-builtin-component': path.resolve(__dirname, '../builtinComponent/index.js'),
|
||||
'@opentiny/tiny-engine-live-component': path.resolve(__dirname, '../live-component/src/index.js')
|
||||
}
|
||||
|
||||
const prodAlias = {
|
||||
|
|
@ -198,16 +201,14 @@ const commonAlias = {
|
|||
}
|
||||
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
const { VITE_CDN_DOMAIN, VITE_LOCAL_IMPORT_MAPS, VITE_LOCAL_BUNDLE_DEPS } = loadEnv(mode, process.cwd(), '')
|
||||
const isLocalImportMap = VITE_LOCAL_IMPORT_MAPS === 'true' // true公共依赖库使用本地打包文件,false公共依赖库使用公共CDN
|
||||
const isCopyBundleDeps = VITE_LOCAL_BUNDLE_DEPS === 'true' // true bundle里的cdn依赖处理成本地依赖, false 不处理
|
||||
const { VITE_CDN_DOMAIN } = loadEnv(mode, process.cwd(), '')
|
||||
const monacoPublicPath = {
|
||||
local: 'editor/monaco-workers',
|
||||
alpha: 'https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/monaco-assets',
|
||||
prod: 'https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/monaco-assets'
|
||||
}
|
||||
|
||||
const monacoPublicPath = 'editor/monaco-workers'
|
||||
const monacoEditorPluginInstance = monacoEditorPlugin({
|
||||
publicPath: monacoPublicPath,
|
||||
forceBuildCDN: true,
|
||||
customDistPath: (_root, outDir, _base) => path.join(outDir, monacoPublicPath)
|
||||
})
|
||||
let monacoEditorPluginInstance = monacoEditorPlugin({ publicPath: monacoPublicPath.local })
|
||||
const htmlPlugin = (mode) => {
|
||||
const upgradeHttpsMetaTags = []
|
||||
const includeHtmls = ['index.html', 'preview.html', 'previewApp.html']
|
||||
|
|
@ -244,7 +245,7 @@ export default defineConfig(({ command, mode }) => {
|
|||
}
|
||||
|
||||
config.resolve.alias = [
|
||||
...(isLocalImportMap ? [] : [devVueAlias]),
|
||||
devVueAlias,
|
||||
...Object.entries({ ...commonAlias, ...devAlias }).map(([find, replacement]) => ({
|
||||
find,
|
||||
replacement
|
||||
|
|
@ -254,6 +255,8 @@ export default defineConfig(({ command, mode }) => {
|
|||
// command === 'build'
|
||||
config.resolve.alias = { ...commonAlias, ...prodAlias }
|
||||
|
||||
monacoEditorPluginInstance = monacoEditorPlugin({ publicPath: monacoPublicPath[mode] })
|
||||
|
||||
if (mode === 'prod') {
|
||||
config.build.minify = true
|
||||
config.build.sourcemap = false
|
||||
|
|
@ -269,57 +272,21 @@ export default defineConfig(({ command, mode }) => {
|
|||
'prettier/parser-postcss': `${VITE_CDN_DOMAIN}/prettier@${importMapVersions.prettier}/esm/parser-postcss.mjs`,
|
||||
'prettier/parser-babel': `${VITE_CDN_DOMAIN}/prettier@${importMapVersions.prettier}/esm/parser-babel.mjs`,
|
||||
|
||||
vue: `${VITE_CDN_DOMAIN}/vue@${importMapVersions.vue}/dist/vue.runtime.esm-browser${
|
||||
command === 'build' ? '.prod' : ''
|
||||
}.js`,
|
||||
vue: `${VITE_CDN_DOMAIN}/vue@${importMapVersions.vue}/dist/vue.runtime.esm-browser${command === 'build' ? '.prod' : ''
|
||||
}.js`,
|
||||
'@opentiny/vue': `${VITE_CDN_DOMAIN}/@opentiny/vue@${importMapVersions.tinyVue}/runtime/tiny-vue.mjs`,
|
||||
'@opentiny/vue-icon': `${VITE_CDN_DOMAIN}/@opentiny/vue@${importMapVersions.tinyVue}/runtime/tiny-vue-icon.mjs`,
|
||||
'@opentiny/vue-common': `${VITE_CDN_DOMAIN}/@opentiny/vue@${importMapVersions.tinyVue}/runtime/tiny-vue-common.mjs`,
|
||||
'@opentiny/vue-locale': `${VITE_CDN_DOMAIN}/@opentiny/vue@${importMapVersions.tinyVue}/runtime/tiny-vue-locale.mjs`,
|
||||
'@opentiny/vue-design-smb': `${VITE_CDN_DOMAIN}/@opentiny/vue-design-smb@${importMapVersions.tinyVue}/index.js`,
|
||||
'@opentiny/vue-theme/theme-tool': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme-tool.js`,
|
||||
'@opentiny/vue-theme/theme': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme/index.js`
|
||||
'@opentiny/vue-theme/theme-tool': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme-tool`,
|
||||
'@opentiny/vue-theme/theme': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme`
|
||||
}
|
||||
}
|
||||
|
||||
const importMapStyles = [`${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/index.css`]
|
||||
|
||||
config.plugins.push(
|
||||
monacoEditorPluginInstance,
|
||||
htmlPlugin(mode),
|
||||
isLocalImportMap
|
||||
? copyLocalImportMap({
|
||||
importMap: importmap,
|
||||
styleUrls: importMapStyles,
|
||||
originCdnPrefix: VITE_CDN_DOMAIN,
|
||||
base: getBaseUrlFromCli(config.base),
|
||||
packageCopy: [
|
||||
// 这两个包的js存在相对路径引用,不能单独拷贝一个文件,需要整个包拷贝
|
||||
'@opentiny/vue-theme/theme-tool',
|
||||
'@opentiny/vue-theme/theme'
|
||||
]
|
||||
})
|
||||
: importmapPlugin(importmap, importMapStyles),
|
||||
isCopyBundleDeps
|
||||
? copyBundleDeps({
|
||||
bundleFile: 'public/mock/bundle.json',
|
||||
targetBundleFile: 'mock/bundle.json',
|
||||
originCdnPrefix: VITE_CDN_DOMAIN, // mock 中bundle的域名当前和环境的VITE_CDN_DOMAIN一致
|
||||
base: getBaseUrlFromCli(config.base)
|
||||
}).plugin(command === 'serve')
|
||||
: [],
|
||||
isLocalImportMap
|
||||
? copyPreviewImportMap({
|
||||
importMapJson: './src/preview/src/preview/importMap.json',
|
||||
targetImportMapJson: 'preview-import-map-static/preview-importmap.json',
|
||||
originCdnPrefix: VITE_CDN_DOMAIN,
|
||||
base: getBaseUrlFromCli(config.base),
|
||||
packageCopyLib: [
|
||||
// 以下的js存在相对路径引用,不能单独拷贝一个文件,需要整个包拷贝
|
||||
'@vue/devtools-api'
|
||||
]
|
||||
})
|
||||
: []
|
||||
)
|
||||
config.plugins.push(monacoEditorPluginInstance, htmlPlugin(mode), importmapPlugin(importmap, importMapStyles))
|
||||
|
||||
return config
|
||||
})
|
||||
|
|
|
|||
|
|
@ -25,8 +25,9 @@
|
|||
"homepage": "https://opentiny.design/tiny-engine",
|
||||
"dependencies": {
|
||||
"@opentiny/tiny-engine-utils": "workspace:*",
|
||||
"@opentiny/vue": "~3.10.0",
|
||||
"@vueuse/core": "^9.6.0",
|
||||
"axios": "~0.28.0",
|
||||
"axios": "^0.27.2",
|
||||
"axios-mock-adapter": "^1.21.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -35,7 +36,6 @@
|
|||
"vite": "^4.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentiny/vue": "^3.14.0",
|
||||
"vue": "^3.4.15"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"$schema": "https://shadcn-vue.com/schema.json",
|
||||
"style": "default",
|
||||
"typescript": false,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "src/index.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": false
|
||||
},
|
||||
"framework": "vite",
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "@opentiny/tiny-engine-live-component",
|
||||
"version": "1.0.8",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opentiny/tiny-engine",
|
||||
"directory": "packages/live-component"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vueuse/core": "^9.6.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"lucide-vue-next": "^0.367.0",
|
||||
"radix-vue": "^1.7.0",
|
||||
"tailwind-merge": "^2.2.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"vite-plugin-css-injected-by-js": "^3.3.1",
|
||||
"vue-live-preview-only-fork": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"vite": "^4.5.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div :style="style">
|
||||
<VueLivePreview :code="code" :components="components" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, defineProps } from 'vue'
|
||||
import { VueLivePreview } from 'vue-live-preview-only-fork'
|
||||
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from './components/ui/accordion'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from './components/ui/avatar'
|
||||
import { Badge } from './components/ui/badge'
|
||||
import { Button } from './components/ui/button'
|
||||
import { Input } from './components/ui/input'
|
||||
import { ScrollArea } from './components/ui/scroll-area'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
} from './components/ui/select'
|
||||
|
||||
defineProps({
|
||||
code: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
style: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const components = ref({
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
Badge,
|
||||
Button,
|
||||
Input,
|
||||
ScrollArea,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
})
|
||||
</script>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<script setup>
|
||||
import { AccordionRoot, useForwardPropsEmits } from "radix-vue";
|
||||
|
||||
const props = defineProps({
|
||||
collapsible: { type: Boolean, required: false },
|
||||
disabled: { type: Boolean, required: false },
|
||||
dir: { type: String, required: false },
|
||||
orientation: { type: String, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
type: { type: null, required: false },
|
||||
modelValue: { type: null, required: false },
|
||||
defaultValue: { type: null, required: false },
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</AccordionRoot>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { AccordionContent } from 'radix-vue'
|
||||
import { cn } from '../../../lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
})
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionContent
|
||||
v-bind="delegatedProps"
|
||||
class="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
>
|
||||
<div :class="cn('pb-4 pt-0', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { AccordionItem, useForwardProps } from 'radix-vue'
|
||||
import { cn } from '../../../lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
disabled: { type: Boolean, required: false },
|
||||
value: { type: String, required: true },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
})
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionItem v-bind="forwardedProps" :class="cn('border-b', props.class)">
|
||||
<slot />
|
||||
</AccordionItem>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { AccordionHeader, AccordionTrigger } from 'radix-vue'
|
||||
import { ChevronDown } from 'lucide-vue-next'
|
||||
import { cn } from '../../../lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
})
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AccordionHeader class="flex">
|
||||
<AccordionTrigger
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
<slot name="icon">
|
||||
<ChevronDown class="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
</slot>
|
||||
</AccordionTrigger>
|
||||
</AccordionHeader>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export { default as Accordion } from "./Accordion.vue";
|
||||
export { default as AccordionContent } from "./AccordionContent.vue";
|
||||
export { default as AccordionItem } from "./AccordionItem.vue";
|
||||
export { default as AccordionTrigger } from "./AccordionTrigger.vue";
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<script setup>
|
||||
import { AvatarRoot } from 'radix-vue'
|
||||
import { avatarVariant } from '.'
|
||||
import { cn } from '../../../lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
class: { type: null, required: false },
|
||||
size: { type: null, required: false, default: 'sm' },
|
||||
shape: { type: null, required: false, default: 'circle' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AvatarRoot :class="cn(avatarVariant({ size, shape }), props.class)">
|
||||
<slot />
|
||||
</AvatarRoot>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<script setup>
|
||||
import { AvatarFallback } from "radix-vue";
|
||||
|
||||
const props = defineProps({
|
||||
delayMs: { type: Number, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AvatarFallback v-bind="props">
|
||||
<slot />
|
||||
</AvatarFallback>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<script setup>
|
||||
import { AvatarImage } from "radix-vue";
|
||||
|
||||
const props = defineProps({
|
||||
src: { type: String, required: true },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false },
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AvatarImage v-bind="props" class="h-full w-full object-cover" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import { cva } from "class-variance-authority";
|
||||
|
||||
export { default as Avatar } from "./Avatar.vue";
|
||||
export { default as AvatarImage } from "./AvatarImage.vue";
|
||||
export { default as AvatarFallback } from "./AvatarFallback.vue";
|
||||
|
||||
export const avatarVariant = cva(
|
||||
"inline-flex items-center justify-center font-normal text-slate-950 select-none shrink-0 bg-slate-100 overflow-hidden dark:text-slate-50 dark:bg-slate-800",
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
sm: "h-10 w-10 text-xs",
|
||||
base: "h-16 w-16 text-2xl",
|
||||
lg: "h-32 w-32 text-5xl",
|
||||
},
|
||||
shape: {
|
||||
circle: "rounded-full",
|
||||
square: "rounded-md",
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<script setup>
|
||||
import { badgeVariants } from '.'
|
||||
import { cn } from '../../../lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
variant: { type: null, required: false },
|
||||
class: { type: null, required: false }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn(badgeVariants({ variant }), props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { cva } from "class-variance-authority";
|
||||
|
||||
export { default as Badge } from "./Badge.vue";
|
||||
|
||||
export const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border border-slate-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 dark:border-slate-800 dark:focus:ring-slate-300",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-slate-900 text-slate-50 hover:bg-slate-900/80 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/80",
|
||||
secondary:
|
||||
"border-transparent bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80",
|
||||
destructive:
|
||||
"border-transparent bg-red-500 text-slate-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/80",
|
||||
outline: "text-slate-950 dark:text-slate-50",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<script setup>
|
||||
import { Primitive } from 'radix-vue'
|
||||
import { buttonVariants } from '.'
|
||||
import { cn } from '../../../lib/utils'
|
||||
|
||||
const props = defineProps({
|
||||
variant: { type: null, required: false },
|
||||
size: { type: null, required: false },
|
||||
class: { type: null, required: false },
|
||||
asChild: { type: Boolean, required: false },
|
||||
as: { type: null, required: false, default: 'button' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive :as="as" :as-child="asChild" :class="cn(buttonVariants({ variant, size }), props.class)">
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import { cva } from "class-variance-authority";
|
||||
|
||||
export { default as Button } from "./Button.vue";
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-950 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-slate-950 dark:focus-visible:ring-slate-300",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-slate-900 text-slate-50 hover:bg-slate-900/90 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50/90",
|
||||
destructive:
|
||||
"bg-red-500 text-slate-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-slate-50 dark:hover:bg-red-900/90",
|
||||
outline:
|
||||
"border border-slate-200 bg-white hover:bg-slate-100 hover:text-slate-900 dark:border-slate-800 dark:bg-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50",
|
||||
secondary:
|
||||
"bg-slate-100 text-slate-900 hover:bg-slate-100/80 dark:bg-slate-800 dark:text-slate-50 dark:hover:bg-slate-800/80",
|
||||
ghost:
|
||||
"hover:bg-slate-100 hover:text-slate-900 dark:hover:bg-slate-800 dark:hover:text-slate-50",
|
||||
link: "text-slate-900 underline-offset-4 hover:underline dark:text-slate-50",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue