forked from opentiny/tiny-engine
Compare commits
1 Commits
develop
...
atom-2024/
| Author | SHA1 | Date |
|---|---|---|
|
|
a5e529dab8 |
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
# 使用淘宝镜像,提升下载速度
|
||||
registry=https://registry.npmmirror.com/
|
||||
|
|
@ -769,7 +769,7 @@
|
|||
{
|
||||
"componentName": "TinyTimeLine",
|
||||
"props": {
|
||||
"active": 2,
|
||||
"active": "2",
|
||||
"data": [
|
||||
{
|
||||
"name": "基础配置"
|
||||
|
|
@ -1525,6 +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;"
|
||||
},
|
||||
"children": [
|
||||
|
|
@ -1550,7 +1551,7 @@
|
|||
{
|
||||
"componentName": "TinyCol",
|
||||
"props": {
|
||||
"span": 8
|
||||
"span": "8"
|
||||
},
|
||||
"id": "b9d051a5",
|
||||
"children": [
|
||||
|
|
@ -1563,7 +1564,7 @@
|
|||
{
|
||||
"componentName": "TinyCol",
|
||||
"props": {
|
||||
"span": 5,
|
||||
"span": "5",
|
||||
"style": "display: flex;"
|
||||
},
|
||||
"id": "02352776",
|
||||
|
|
@ -1597,7 +1598,7 @@
|
|||
{
|
||||
"componentName": "TinyCol",
|
||||
"props": {
|
||||
"span": 7
|
||||
"span": "7"
|
||||
},
|
||||
"id": "82b6c659",
|
||||
"children": [
|
||||
|
|
@ -1665,7 +1666,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 +1691,7 @@
|
|||
],
|
||||
"fileName": "createVm",
|
||||
"meta": {
|
||||
"id": "NTJ4MjvqoVj8OVsc",
|
||||
"id": 1977,
|
||||
"parentId": "0",
|
||||
"group": "staticPages",
|
||||
"occupier": {
|
||||
|
|
@ -1787,7 +1788,7 @@
|
|||
},
|
||||
{
|
||||
"componentName": "TinyPlusFrozenPage",
|
||||
"package": "@opentiny/vue",
|
||||
"package": "@opentiny/vuee",
|
||||
"exportName": "FrozenPage",
|
||||
"destructuring": true,
|
||||
"version": "3.4.1"
|
||||
|
|
@ -1974,48 +1975,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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@
|
|||
"@opentiny/tiny-engine-i18n-host": "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 +46,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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -598,20 +598,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 +617,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 +628,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'
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
<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" />
|
||||
<style type="text/css">
|
||||
.loading-warp {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -30,14 +30,13 @@
|
|||
"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-block": "workspace:*",
|
||||
|
|
@ -78,22 +77,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 +118,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"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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,37 @@
|
|||
// 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`,
|
||||
'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 = {
|
||||
|
|
@ -198,16 +200,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 +244,7 @@ export default defineConfig(({ command, mode }) => {
|
|||
}
|
||||
|
||||
config.resolve.alias = [
|
||||
...(isLocalImportMap ? [] : [devVueAlias]),
|
||||
devVueAlias,
|
||||
...Object.entries({ ...commonAlias, ...devAlias }).map(([find, replacement]) => ({
|
||||
find,
|
||||
replacement
|
||||
|
|
@ -254,6 +254,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
|
||||
|
|
@ -277,49 +279,14 @@ export default defineConfig(({ command, mode }) => {
|
|||
'@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,20 @@
|
|||
{
|
||||
"name": "material-generator",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"gen-antdv": "node ./scripts/antdvToMaterials.mjs",
|
||||
"gen-vant": "node ./scripts/vantToMaterials.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ant-design-vue": "^4.1.2",
|
||||
"vant": "^4.8.10",
|
||||
"vite": "^5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.27.2",
|
||||
"markdown-it": "^14.0.0",
|
||||
"naive-ui": "2.38.1"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
import GenerateMaterial from './common/GenerateMaterial-old.mjs';
|
||||
|
||||
function checkIsStandardTable(params, standard) {
|
||||
const { col, content } = params
|
||||
for (let i = 0; i < col; i++) {
|
||||
const item = standard[i];
|
||||
const ct = content[i]
|
||||
if (Array.isArray(item)) {
|
||||
if (!item.includes(ct)) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if (item !== ct && !!item !== !!ct) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const gm = new GenerateMaterial({
|
||||
owner: "vueComponent",
|
||||
repo: "ant-design-vue",
|
||||
path: "components",
|
||||
host: "https://api.github.com",
|
||||
version: "4.1.2",
|
||||
projectName: "ant-design-vue",
|
||||
framework: "Vue",
|
||||
script: "https://npm.onmicrosoft.cn/ant-design-vue@4.1.2/dist/antd.esm.js",
|
||||
css: "https://npm.onmicrosoft.cn/ant-design-vue@4.1.2/dist/reset.css",
|
||||
exportNamePrefix: "A",
|
||||
// github api token
|
||||
token: null,
|
||||
isAttributeTable(params) {
|
||||
const standard = [['参数', '属性', '成员', 'Property'], ['说明', 'Description'], ['类型', 'Type'], ['默认值', 'Default'], ['版本', 'Version', '']]
|
||||
return checkIsStandardTable(params, standard)
|
||||
},
|
||||
isEvent(params) {
|
||||
const standard = ['事件名称', '说明', '回调参数', '版本', undefined]
|
||||
return checkIsStandardTable(params, standard)
|
||||
},
|
||||
})
|
||||
await gm.start()
|
||||
}
|
||||
main()
|
||||
|
|
@ -0,0 +1,609 @@
|
|||
import axios from 'axios'
|
||||
import markdownit from 'markdown-it'
|
||||
import fs from 'fs'
|
||||
import LocalCache from './cache.mjs';
|
||||
|
||||
function returnFunc(param) {
|
||||
if (typeof param === "function") {
|
||||
return param
|
||||
}
|
||||
if (Array.isArray(param)) {
|
||||
return param[0]
|
||||
}
|
||||
if (Array.isArray(param.type)) {
|
||||
return param.type[0]
|
||||
}
|
||||
return param.type
|
||||
}
|
||||
|
||||
const DEFAULT_DESCRIPTION = "<empty comment>"
|
||||
|
||||
function generateType(param) {
|
||||
let type = ""
|
||||
let defaultValue = undefined
|
||||
let func = returnFunc(param)
|
||||
if (typeof func === "function") {
|
||||
type = func.name
|
||||
} else { }
|
||||
if ("defalut" in param) {
|
||||
defaultValue = param.defalut
|
||||
}
|
||||
switch (type) {
|
||||
case "Boolean":
|
||||
defaultValue = !!defaultValue
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return { type, defaultValue }
|
||||
}
|
||||
|
||||
class GenerateMaterial {
|
||||
_owner = undefined
|
||||
_repo = undefined
|
||||
_path = undefined
|
||||
_host = undefined
|
||||
_version = undefined
|
||||
_projectName = undefined
|
||||
_framework = undefined
|
||||
_requiredComponents = []
|
||||
_unnecessary = []
|
||||
_temporary_omit = []
|
||||
_script = undefined
|
||||
_css = undefined
|
||||
_exportNamePrefix = undefined
|
||||
_md = markdownit()
|
||||
_Module = undefined
|
||||
_middlewares = [];
|
||||
_lc = new LocalCache()
|
||||
|
||||
_components = []
|
||||
_blocks = []
|
||||
_snippets = []
|
||||
_cached = []
|
||||
_default_actions = ["copy", "remove", "insert", "updateAttr", "bindEevent", "createBlock"]
|
||||
_need_cache_key = ['_components', '_blocks', '_snippets', '_framework', '_cached']
|
||||
_mdParams = []
|
||||
get url() {
|
||||
return `${this._host}/repos/${this._owner}/${this._repo}/contents/${this._path}`
|
||||
}
|
||||
|
||||
constructor(params) {
|
||||
this._checkParams(params)
|
||||
this._owner = params.owner
|
||||
this._repo = params.repo
|
||||
this._path = params.path
|
||||
this._host = params.host
|
||||
this._version = params.version
|
||||
this._projectName = params.projectName
|
||||
this._framework = params.framework
|
||||
this._script = params.script
|
||||
this._css = params.css
|
||||
this._exportNamePrefix = params.exportNamePrefix
|
||||
this._Module = params.Module
|
||||
this._init()
|
||||
}
|
||||
|
||||
async start() {
|
||||
// const files = await this._requestESMFile(this._script)
|
||||
const CurrentModule = this._Module
|
||||
const moduleKeys = Object.keys(CurrentModule)
|
||||
for (const key of moduleKeys) {
|
||||
const module = CurrentModule[key]
|
||||
if (!module.name) {
|
||||
console.log(`${key} 不是vue组件`)
|
||||
continue
|
||||
}
|
||||
this._generateMaterial(module)
|
||||
}
|
||||
// 中间件处理
|
||||
await this._execute()
|
||||
// 生成数据
|
||||
const jsonData = { data: this.data }
|
||||
this._generateJSONFile(jsonData, "bundle.json", ".")
|
||||
}
|
||||
|
||||
_init() {
|
||||
|
||||
}
|
||||
|
||||
_to_save() {
|
||||
const caches = this._need_cache_key
|
||||
for (let i = 0; i < caches.length; i++) {
|
||||
const key = caches[i];
|
||||
this._lc.set(key, this[key])
|
||||
}
|
||||
}
|
||||
|
||||
get data() {
|
||||
return {
|
||||
framework: this._framework,
|
||||
materials: {
|
||||
components: this._components,
|
||||
blocks: this._blocks,
|
||||
snippets: this._snippets,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async _requestGithub(url) {
|
||||
try {
|
||||
const res = await axios({
|
||||
url,
|
||||
headers: { 'Authorization': "Bearer " + this._token }
|
||||
})
|
||||
return res?.data || {}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
async _requestESMFile(url) {
|
||||
try {
|
||||
const res = await axios({
|
||||
url
|
||||
})
|
||||
return res?.data || {}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
get baseNpm() {
|
||||
return {
|
||||
package: this._repo,
|
||||
version: this._version,
|
||||
script: this._script,
|
||||
css: this._css,
|
||||
dependencies: null,
|
||||
}
|
||||
}
|
||||
|
||||
_generateProperties(props) {
|
||||
const properties = {
|
||||
label: {
|
||||
zh_CN: "基础信息"
|
||||
},
|
||||
description: {
|
||||
zh_CN: "基础信息"
|
||||
},
|
||||
collapse: {
|
||||
number: 6,
|
||||
text: {
|
||||
zh_CN: "显示更多"
|
||||
}
|
||||
},
|
||||
content: []
|
||||
}
|
||||
let slots = {}
|
||||
const propsKeys = Object.keys(props)
|
||||
if (propsKeys.length) {
|
||||
// const res = this._chunk(attrs.content, attrs.col)
|
||||
for (const key of propsKeys) {
|
||||
const propsValue = props[key]
|
||||
if (!propsValue) {
|
||||
continue
|
||||
}
|
||||
if (!key.startsWith("on")) {
|
||||
if (propsValue === "") {
|
||||
console.log()
|
||||
}
|
||||
const typeObject = generateType(propsValue)
|
||||
const params = {
|
||||
property: key,
|
||||
description: DEFAULT_DESCRIPTION,
|
||||
type: String(typeObject.type).toLowerCase(),
|
||||
defaultValue: typeObject.defaultValue
|
||||
}
|
||||
const content_item = this._createPropertieContent(params)
|
||||
properties.content.push(content_item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return { properties, slots }
|
||||
}
|
||||
|
||||
_generateEvents(props) {
|
||||
const events = {}
|
||||
const eventList = Object.keys(props).filter((keyStr) => keyStr.startsWith("on"))
|
||||
if (eventList.length) {
|
||||
for (const key of eventList) {
|
||||
const content_item = this._createEventContent(key)
|
||||
Object.assign(events, content_item)
|
||||
}
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
_generateComponent(params) {
|
||||
let { heading = {}, name, props = {} } = params
|
||||
const description = DEFAULT_DESCRIPTION
|
||||
const npm = {
|
||||
...this.baseNpm,
|
||||
exportName: name
|
||||
}
|
||||
|
||||
const { properties, slots } = this._generateProperties(props)
|
||||
const configure = {
|
||||
loop: true,
|
||||
condition: true,
|
||||
styles: true,
|
||||
isContainer: false,
|
||||
isModal: false,
|
||||
isPopper: false,
|
||||
nestingRule: {
|
||||
childWhitelist: "",
|
||||
parentWhitelist: "",
|
||||
descendantBlacklist: "",
|
||||
ancestorWhitelist: ""
|
||||
},
|
||||
isNullNode: false,
|
||||
isLayout: false,
|
||||
rootSelector: "",
|
||||
shortcuts: {
|
||||
properties: properties.content?.map(({ property }) => property) || []
|
||||
},
|
||||
contextMenu: {
|
||||
actions: this._default_actions,
|
||||
disable: []
|
||||
},
|
||||
invalidity: [""],
|
||||
clickCapture: true,
|
||||
framework: this._framework
|
||||
}
|
||||
const events = this._generateEvents(props)
|
||||
const shortcuts = {}
|
||||
const contentMenu = {}
|
||||
const schema = { properties: [properties], events, shortcuts, slots, contentMenu }
|
||||
const component = {
|
||||
id: this._components.length + 1,
|
||||
version: this._version,
|
||||
name: { "zh_CN": heading?.subtitle },
|
||||
component: npm.exportName,
|
||||
icon: name,
|
||||
description,
|
||||
doc_url: "",
|
||||
screenshot: "",
|
||||
tags: "",
|
||||
keywords: "",
|
||||
dev_mode: "proCode",
|
||||
npm,
|
||||
group: this._repo,
|
||||
category: this._projectName,
|
||||
configure,
|
||||
schema,
|
||||
}
|
||||
|
||||
this._components.push(component)
|
||||
}
|
||||
|
||||
_generateSnippets(params) {
|
||||
const { name } = params
|
||||
let snippet = this._snippets.find(({ group }) => group === this._repo)
|
||||
if (!snippet) {
|
||||
snippet = { group: this._repo, children: [] }
|
||||
this._snippets.push(snippet)
|
||||
}
|
||||
const nowComponent = {
|
||||
name: {
|
||||
zh_CN: name
|
||||
},
|
||||
icon: name,
|
||||
screenshot: "",
|
||||
snippetName: name,
|
||||
schema: {}
|
||||
}
|
||||
snippet.children.push(nowComponent)
|
||||
}
|
||||
|
||||
async _generateMaterial(params) {
|
||||
this._generateComponent(params)
|
||||
this._generateSnippets(params)
|
||||
}
|
||||
|
||||
_createPropertieContent(params) {
|
||||
let { property, description, type, defaultValue } = params;
|
||||
const widgetContent = this._createWidget({ type, defaultValue })
|
||||
return {
|
||||
property,
|
||||
label: {
|
||||
text: {
|
||||
zh_CN: property
|
||||
}
|
||||
},
|
||||
description: {
|
||||
zh_CN: description,
|
||||
},
|
||||
required: true,
|
||||
readOnly: false,
|
||||
disabled: false,
|
||||
cols: 12,
|
||||
labelPosition: "top",
|
||||
type: widgetContent.type,
|
||||
defaultValue: widgetContent.defaultValue,
|
||||
widget: widgetContent.widget
|
||||
}
|
||||
}
|
||||
|
||||
_createEventContent(name) {
|
||||
return {
|
||||
[name]: {
|
||||
label: {
|
||||
zh_CN: DEFAULT_DESCRIPTION
|
||||
},
|
||||
description: {
|
||||
zh_CN: DEFAULT_DESCRIPTION
|
||||
},
|
||||
type: "event"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_createWidget(params) {
|
||||
let { type, defaultValue } = params
|
||||
const result = {}
|
||||
if (type.includes('|')) {
|
||||
const types = type.split('|').map((str) => str.trim())
|
||||
const isString = types.every((str) => str.startsWith('`') && str.endsWith('`'))
|
||||
if (isString) {
|
||||
type = 'string'
|
||||
result.widget = {
|
||||
component: 'MetaSelect',
|
||||
props: {
|
||||
options: types.map((item) => (
|
||||
{
|
||||
label: item.slice(1, item.length - 1),
|
||||
value: item.slice(1, item.length - 1)
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
const isBoolean = types.some((str) => str === 'boolean')
|
||||
if (isBoolean) {
|
||||
result.widget = { component: 'MetaSwitch', props: {} }
|
||||
result.defaultValue = String(defaultValue).includes("false") ? false : true
|
||||
}
|
||||
}
|
||||
result.type = type
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
result.widget = { component: 'MetaSwitch', props: {} }
|
||||
result.defaultValue = String(defaultValue).includes("false") ? false : true
|
||||
break;
|
||||
case '':
|
||||
result.widget = { component: 'MetaSwitch', props: {} }
|
||||
result.defaultValue = String(defaultValue).includes("false") ? false : true
|
||||
break;
|
||||
case 'number':
|
||||
!result.widget && (result.widget = { component: 'MetaNumber', props: {} })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'array':
|
||||
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'function':
|
||||
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'date':
|
||||
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'object':
|
||||
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'string':
|
||||
!result.widget && (result.widget = { component: 'MetaInput', props: {} })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'v-slot':
|
||||
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'slot':
|
||||
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!result.widget) {
|
||||
console.log()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
_chunk(array, size = 1) {
|
||||
size = Math.max(Number(size), 0)
|
||||
const length = array == null ? 0 : array.length
|
||||
if (!length || size < 1) {
|
||||
return []
|
||||
}
|
||||
let index = 0
|
||||
let resIndex = 0
|
||||
const result = new Array(Math.ceil(length / size))
|
||||
|
||||
while (index < length) {
|
||||
result[resIndex++] = array.slice(index, (index += size))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
_isAttributeTable(params) {
|
||||
throw new Error("需要传入 isAttributeTable")
|
||||
}
|
||||
|
||||
_isEvent(params) {
|
||||
throw new Error("需要传入 isEvent")
|
||||
}
|
||||
|
||||
_generateJSONFile(jsonData, fileName = 'output.json', filePath = './') {
|
||||
// 将 JSON 数据转换为字符串
|
||||
const jsonString = JSON.stringify(jsonData, null, 2);
|
||||
|
||||
// 拼接文件路径
|
||||
const fullFilePath = filePath.endsWith('/') ? filePath + fileName : filePath + '/' + fileName;
|
||||
|
||||
// 写入文件
|
||||
fs.writeFile(fullFilePath, jsonString, 'utf8', (err) => {
|
||||
if (err) {
|
||||
console.error('写入文件时出错:', err);
|
||||
} else {
|
||||
console.log(`JSON 数据已成功写入到文件 ${fullFilePath}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_atob_cn(base64String) {
|
||||
const bytes = Uint8Array.from(atob(base64String), c => c.charCodeAt(0))
|
||||
const decodedString = new TextDecoder('utf-8').decode(bytes)
|
||||
return decodedString
|
||||
}
|
||||
|
||||
_getBaseInfo(tokens) {
|
||||
let description = undefined
|
||||
let heading = undefined
|
||||
// let name = undefined
|
||||
// 寻找表格内容
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
if (!description && token.type === "paragraph_open") {
|
||||
description = tokens[i + 1].content
|
||||
}
|
||||
if (!heading && token.type === "heading_open") {
|
||||
const content = tokens[i + 1].content
|
||||
const heads = content.split("\n")
|
||||
heading = {}
|
||||
heads.forEach((item) => {
|
||||
const kv = item.split(": ")
|
||||
heading[kv[0]] = kv[1]
|
||||
})
|
||||
}
|
||||
}
|
||||
return { description, heading }
|
||||
}
|
||||
|
||||
_getMdTableInfo(tokens) {
|
||||
let tableContent = null
|
||||
const tableContents = []
|
||||
const titles = this._parseTitle(tokens)
|
||||
let col = 0 // 列数
|
||||
// 寻找表格内容
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
if (token.type === 'table_open') {
|
||||
col = 0
|
||||
tableContent = {
|
||||
content: [],
|
||||
row: 0,
|
||||
col,
|
||||
parents: [],
|
||||
parentMap: {}
|
||||
}
|
||||
let endIndex = undefined
|
||||
for (let t = 0; t < titles.length; t++) {
|
||||
const { index } = titles[t];
|
||||
if (index > i && endIndex === undefined) {
|
||||
endIndex = t
|
||||
break;
|
||||
}
|
||||
}
|
||||
const newTitles = titles.slice(0, endIndex).reverse()
|
||||
let first_title_index = (newTitles[0] || {}).index
|
||||
if (first_title_index) {
|
||||
tableContent.parents.push(newTitles[0])
|
||||
}
|
||||
for (let j = 0; j < newTitles.length; j++) {
|
||||
const title = newTitles[j];
|
||||
if (title.index > first_title_index) {
|
||||
tableContent.parents.push(title)
|
||||
first_title_index = title.index
|
||||
}
|
||||
}
|
||||
} else if (token.type === 'thead_close') {
|
||||
tableContent.col = col
|
||||
} else if (token.type === 'table_close') {
|
||||
tableContent.row = tableContent.content.length / tableContent.col
|
||||
tableContents.push(tableContent)
|
||||
tableContent = null
|
||||
} else if (tableContent && token.type === 'inline') {
|
||||
const content = token.content
|
||||
col++
|
||||
tableContent.content.push(content)
|
||||
}
|
||||
}
|
||||
return { tables: tableContents }
|
||||
}
|
||||
|
||||
_parseTitle(tokens) {
|
||||
const titles = []
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
if (token.type === 'heading_open' && token.markup && token.markup.includes("#")) {
|
||||
const content = tokens[i + 1].content
|
||||
const title = {
|
||||
level: String(token.markup).length,
|
||||
content,
|
||||
index: i
|
||||
}
|
||||
titles.push(title)
|
||||
}
|
||||
}
|
||||
return titles
|
||||
}
|
||||
|
||||
_parseMd(mdStrBase64) {
|
||||
const mdStr = this._atob_cn(mdStrBase64)
|
||||
let result = undefined
|
||||
try {
|
||||
result = this._md.parse(mdStr, {})
|
||||
} catch (error) {
|
||||
console.log(mdStr)
|
||||
throw new Error(error)
|
||||
}
|
||||
const tablesInfo = this._getMdTableInfo(result)
|
||||
const info = this._getBaseInfo(result)
|
||||
return { ...tablesInfo, ...info }
|
||||
}
|
||||
|
||||
_checkParams(params) {
|
||||
if (!params) {
|
||||
throw new Error('Initialization requires passing in parameters');
|
||||
}
|
||||
const requiredProperties = [
|
||||
'version',
|
||||
'Module',
|
||||
'framework',
|
||||
'script',
|
||||
'css',
|
||||
'exportNamePrefix',
|
||||
]
|
||||
for (let prop of requiredProperties) {
|
||||
if (!(prop in params)) {
|
||||
throw new Error(`Property '${prop}' is missing from the params.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(middleware) {
|
||||
this._middlewares.push(middleware);
|
||||
}
|
||||
|
||||
// 执行中间件
|
||||
async _execute() {
|
||||
let components = this._components;
|
||||
let blocks = this._blocks;
|
||||
let snippets = this._snippets;
|
||||
let result = { components, blocks, snippets }
|
||||
for (const fn of this._middlewares) {
|
||||
result = await fn(result); // 调用中间件函数,传入参数 A,并更新结果
|
||||
}
|
||||
this._components = result.components// 返回最终结果
|
||||
this._snippets = result.snippets// 返回最终结果
|
||||
this._blocks = result.blocks// 返回最终结果
|
||||
}
|
||||
}
|
||||
export default GenerateMaterial
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
import axios from 'axios'
|
||||
import markdownit from 'markdown-it'
|
||||
import fs from 'fs'
|
||||
import LocalCache from './cache.mjs';
|
||||
|
||||
|
||||
function checkIsStandardTable(params, standard) {
|
||||
const { col, content } = params
|
||||
for (let i = 0; i < col; i++) {
|
||||
const item = standard[i];
|
||||
const ct = content[i]
|
||||
if (Array.isArray(item)) {
|
||||
if (!item.includes(ct)) {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if (item !== ct && !!item !== !!ct) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function capitalizeFirstLetter(word) {
|
||||
if (typeof word !== 'string' || word.length === 0) {
|
||||
throw new Error('Input must be a non-empty string');
|
||||
}
|
||||
return word.charAt(0).toUpperCase() + word.slice(1);
|
||||
}
|
||||
|
||||
class GenerateMaterial {
|
||||
_owner = undefined
|
||||
_repo = undefined
|
||||
_path = undefined
|
||||
_host = undefined
|
||||
_version = undefined
|
||||
_projectName = undefined
|
||||
_framework = undefined
|
||||
_requiredComponents = []
|
||||
_unnecessary = []
|
||||
_temporary_omit = []
|
||||
_script = undefined
|
||||
_css = undefined
|
||||
_exportNamePrefix = undefined
|
||||
_md = markdownit()
|
||||
_lc = new LocalCache()
|
||||
|
||||
_components = []
|
||||
_blocks = []
|
||||
_snippets = []
|
||||
_cached = []
|
||||
_default_actions = ["copy", "remove", "insert", "updateAttr", "bindEevent", "createBlock"]
|
||||
_need_cache_key = ['_components', '_blocks', '_snippets', '_framework', '_cached']
|
||||
_mdParams = []
|
||||
get url() {
|
||||
return `${this._host}/repos/${this._owner}/${this._repo}/contents/${this._path}`
|
||||
}
|
||||
|
||||
constructor(params) {
|
||||
this._checkParams(params)
|
||||
this._owner = params.owner
|
||||
this._repo = params.repo
|
||||
this._path = params.path
|
||||
this._host = params.host
|
||||
this._version = params.version
|
||||
this._projectName = params.projectName
|
||||
this._framework = params.framework
|
||||
this._script = params.script
|
||||
this._css = params.css
|
||||
this._exportNamePrefix = params.exportNamePrefix
|
||||
if (!params.token) {
|
||||
throw new Error("请在系统中内置环境变量 GITHUB_TOKEN_PAT")
|
||||
}
|
||||
this._token = params.token
|
||||
if (params.temporary_omit) {
|
||||
this._temporary_omit = params.temporary_omit
|
||||
}
|
||||
if (params.isAttributeTable) {
|
||||
this._isAttributeTable = params.isAttributeTable
|
||||
}
|
||||
if (params.isEvent) {
|
||||
this._isEvent = params.isEvent
|
||||
}
|
||||
this._init()
|
||||
}
|
||||
|
||||
async start() {
|
||||
try {
|
||||
const files = await this._requestGithub(this.url)
|
||||
if (files && Array.isArray(files)) {
|
||||
files.forEach((item) => {
|
||||
const { name = "", type = "" } = item
|
||||
if (
|
||||
!name.startsWith("_")
|
||||
&& !name.startsWith("vc-")
|
||||
&& !this._unnecessary.includes(name)
|
||||
&& type === "dir"
|
||||
) {
|
||||
if (this._temporary_omit.length) {
|
||||
if (this._temporary_omit.includes(name)) {
|
||||
this._requiredComponents.push(item)
|
||||
}
|
||||
} else {
|
||||
this._requiredComponents.push(item)
|
||||
}
|
||||
}
|
||||
})
|
||||
this._requiredComponents = this._requiredComponents.filter(({ name }) => !this._cached.includes(name))
|
||||
console.log(`files lenght: ${this._requiredComponents.length}`)
|
||||
if (this._requiredComponents.length) {
|
||||
for (const item of this._requiredComponents) {
|
||||
const { git_url } = item
|
||||
const data = await this._requestGithub(git_url)
|
||||
if (data && Array.isArray(data.tree)) {
|
||||
const mdObj = data.tree.find(({ path }) => path === "index.zh-CN.md")
|
||||
console.log(item.name, mdObj)
|
||||
if (!mdObj) {
|
||||
console.log(`${item.name} 不存在文档已跳过`)
|
||||
continue
|
||||
}
|
||||
const { content } = await this._requestGithub(mdObj.url)
|
||||
let mdParam = this._parseMd(content)
|
||||
this._mdParams.push(mdParam)
|
||||
await this._generateMaterial(mdParam)
|
||||
this._cached.push(item.name)
|
||||
}
|
||||
}
|
||||
this._generateJSONFile({ data: this.data }, "bundle.json", "./packages/design-core/public/mock")
|
||||
this._generateJSONFile({ data: this._mdParams }, "mdParams.json", "./packages/design-core/public/mock")
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this._to_save()
|
||||
throw new Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
_init() {
|
||||
const caches = this._need_cache_key
|
||||
for (let i = 0; i < caches.length; i++) {
|
||||
const key = caches[i];
|
||||
const saveValue = this._lc.get(key)
|
||||
if (saveValue) {
|
||||
this[key] = saveValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_to_save() {
|
||||
const caches = this._need_cache_key
|
||||
for (let i = 0; i < caches.length; i++) {
|
||||
const key = caches[i];
|
||||
this._lc.set(key, this[key])
|
||||
}
|
||||
}
|
||||
|
||||
get data() {
|
||||
return {
|
||||
framework: this._framework,
|
||||
materials: {
|
||||
components: this._components,
|
||||
blocks: this._blocks,
|
||||
snippets: this._snippets,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async _requestGithub(url) {
|
||||
try {
|
||||
const res = await axios({
|
||||
url,
|
||||
headers: { 'Authorization': "Bearer " + this._token }
|
||||
})
|
||||
return res?.data || {}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
get baseNpm() {
|
||||
return {
|
||||
package: this._repo,
|
||||
version: this._version,
|
||||
script: this._script,
|
||||
css: this._css,
|
||||
dependencies: null,
|
||||
}
|
||||
}
|
||||
|
||||
_generateProperties(tables) {
|
||||
const properties = {
|
||||
label: {
|
||||
zh_CN: "基础信息"
|
||||
},
|
||||
description: {
|
||||
zh_CN: "基础信息"
|
||||
},
|
||||
collapse: {
|
||||
number: 6,
|
||||
text: {
|
||||
zh_CN: "显示更多"
|
||||
}
|
||||
},
|
||||
}
|
||||
const slots = {}
|
||||
const attrs = tables.find(this._isAttributeTable)
|
||||
if (attrs) {
|
||||
const res = this._chunk(attrs.content, attrs.col)
|
||||
for (let i = 1; i < res.length; i++) {
|
||||
const rows = res[i];
|
||||
if (!Array.isArray(properties.content)) {
|
||||
properties.content = []
|
||||
}
|
||||
const params = {
|
||||
property: rows[0],
|
||||
description: rows[1],
|
||||
type: rows[2],
|
||||
defaultValue: rows[3]
|
||||
}
|
||||
if (params.property.includes('(')) {
|
||||
let newproperty = params.property.split("(").shift()
|
||||
params.property = newproperty.trim()
|
||||
}
|
||||
const content_item = this._createPropertieContent(params)
|
||||
const types = params.type.split('|').map((str) => str.trim())
|
||||
const isSlot = types.some((item) => item.includes('slot'))
|
||||
if (isSlot) {
|
||||
slots[params.property] = {
|
||||
label: { zh_CN: params.property },
|
||||
description: { zh_CN: params.description },
|
||||
}
|
||||
}
|
||||
properties.content.push(content_item)
|
||||
}
|
||||
}
|
||||
return { properties, slots }
|
||||
}
|
||||
|
||||
_generateEvents(tables) {
|
||||
const events = {}
|
||||
const event = tables.find(this._isEvent)
|
||||
if (event) {
|
||||
const res = this._chunk(event.content, event.col)
|
||||
for (let i = 1; i < res.length; i++) {
|
||||
const rows = res[i];
|
||||
const content_item = this._createEventContent(rows)
|
||||
Object.assign(events, content_item)
|
||||
}
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
_generateComponent(params) {
|
||||
const { tables, heading = {}, description } = params
|
||||
const npm = {
|
||||
...this.baseNpm,
|
||||
exportName: `${this._exportNamePrefix}${heading?.title}`
|
||||
}
|
||||
|
||||
const { properties, slots } = this._generateProperties(tables)
|
||||
const configure = {
|
||||
loop: true,
|
||||
condition: true,
|
||||
styles: true,
|
||||
isContainer: false,
|
||||
isModal: false,
|
||||
isPopper: false,
|
||||
nestingRule: {
|
||||
childWhitelist: "",
|
||||
parentWhitelist: "",
|
||||
descendantBlacklist: "",
|
||||
ancestorWhitelist: ""
|
||||
},
|
||||
isNullNode: false,
|
||||
isLayout: false,
|
||||
rootSelector: "",
|
||||
shortcuts: {
|
||||
properties: properties.content?.map(({ property }) => property) || []
|
||||
},
|
||||
contextMenu: {
|
||||
actions: this._default_actions,
|
||||
disable: []
|
||||
},
|
||||
invalidity: [""],
|
||||
clickCapture: true,
|
||||
framework: this._framework
|
||||
}
|
||||
const events = this._generateEvents(tables)
|
||||
const shortcuts = {}
|
||||
const contentMenu = {}
|
||||
const schema = { properties: [properties], events, shortcuts, slots, contentMenu }
|
||||
const component = {
|
||||
id: this._components.length + 1,
|
||||
version: this._version,
|
||||
name: { "zh_CN": heading?.subtitle },
|
||||
component: npm.exportName,
|
||||
icon: heading?.title,
|
||||
description,
|
||||
doc_url: "",
|
||||
screenshot: "",
|
||||
tags: "",
|
||||
keywords: "",
|
||||
dev_mode: "proCode",
|
||||
npm,
|
||||
group: heading.type,
|
||||
category: this._projectName,
|
||||
configure,
|
||||
schema,
|
||||
}
|
||||
|
||||
this._components.push(component)
|
||||
}
|
||||
|
||||
_generateSnippets(params) {
|
||||
const { heading = {}, description } = params
|
||||
let snippet = this._snippets.find(({ group }) => group === heading.type)
|
||||
if (!snippet) {
|
||||
snippet = { group: heading.type, children: [] }
|
||||
this._snippets.push(snippet)
|
||||
}
|
||||
const nowComponent = {
|
||||
name: {
|
||||
zh_CN: heading?.subtitle
|
||||
},
|
||||
icon: heading?.title,
|
||||
screenshot: "",
|
||||
snippetName: `${this._exportNamePrefix}${heading?.title}`,
|
||||
schema: {}
|
||||
}
|
||||
snippet.children.push(nowComponent)
|
||||
}
|
||||
|
||||
async _generateMaterial(params) {
|
||||
this._generateComponent(params)
|
||||
this._generateSnippets(params)
|
||||
}
|
||||
|
||||
_createPropertieContent(params) {
|
||||
let { property, description, type, defaultValue } = params;
|
||||
const widgetContent = this._createWidget({ type, defaultValue })
|
||||
return {
|
||||
property,
|
||||
label: {
|
||||
text: {
|
||||
zh_CN: property
|
||||
}
|
||||
},
|
||||
description: {
|
||||
zh_CN: description,
|
||||
},
|
||||
required: true,
|
||||
readOnly: false,
|
||||
disabled: false,
|
||||
cols: 12,
|
||||
labelPosition: "top",
|
||||
type: widgetContent.type,
|
||||
defaultValue: widgetContent.defaultValue,
|
||||
widget: widgetContent.widget
|
||||
}
|
||||
}
|
||||
|
||||
_createEventContent(params) {
|
||||
let [property, description] = params;
|
||||
if (property.includes("(")) {
|
||||
property = property.split("(").shift()
|
||||
property = property.trim()
|
||||
}
|
||||
return {
|
||||
[`on${capitalizeFirstLetter(property)}`]: {
|
||||
label: {
|
||||
zh_CN: description
|
||||
},
|
||||
description: {
|
||||
zh_CN: description
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_createWidget(params) {
|
||||
let { type, defaultValue } = params
|
||||
const result = {}
|
||||
if (type.includes('|')) {
|
||||
const types = type.split('|').map((str) => str.trim())
|
||||
const isString = types.every((str) => str.startsWith('`') && str.endsWith('`'))
|
||||
if (isString) {
|
||||
type = 'string'
|
||||
result.widget = {
|
||||
component: 'MetaSelect',
|
||||
props: {
|
||||
options: types.map((item) => (
|
||||
{
|
||||
label: item.slice(1, item.length - 1),
|
||||
value: item.slice(1, item.length - 1)
|
||||
}
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
const isBoolean = types.some((str) => str === 'boolean')
|
||||
if (isBoolean) {
|
||||
result.widget = { component: 'MetaSwitch', props: {} }
|
||||
result.defaultValue = String(defaultValue).includes("false") ? false : true
|
||||
}
|
||||
}
|
||||
result.type = type
|
||||
switch (type) {
|
||||
case 'boolean':
|
||||
result.widget = { component: 'MetaSwitch', props: {} }
|
||||
result.defaultValue = String(defaultValue).includes("false") ? false : true
|
||||
break;
|
||||
case 'string':
|
||||
!result.widget && (result.widget = { component: 'MetaInput', props: {} })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'v-slot':
|
||||
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
case 'slot':
|
||||
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
|
||||
result.defaultValue = defaultValue
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
_chunk(array, size = 1) {
|
||||
size = Math.max(Number(size), 0)
|
||||
const length = array == null ? 0 : array.length
|
||||
if (!length || size < 1) {
|
||||
return []
|
||||
}
|
||||
let index = 0
|
||||
let resIndex = 0
|
||||
const result = new Array(Math.ceil(length / size))
|
||||
|
||||
while (index < length) {
|
||||
result[resIndex++] = array.slice(index, (index += size))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
_isAttributeTable(params) {
|
||||
throw new Error("需要传入 isAttributeTable")
|
||||
}
|
||||
|
||||
_isEvent(params) {
|
||||
throw new Error("需要传入 isEvent")
|
||||
}
|
||||
|
||||
_generateJSONFile(jsonData, fileName = 'output.json', filePath = './') {
|
||||
// 将 JSON 数据转换为字符串
|
||||
const jsonString = JSON.stringify(jsonData, null, 2);
|
||||
|
||||
// 拼接文件路径
|
||||
const fullFilePath = filePath.endsWith('/') ? filePath + fileName : filePath + '/' + fileName;
|
||||
|
||||
// 写入文件
|
||||
fs.writeFile(fullFilePath, jsonString, 'utf8', (err) => {
|
||||
if (err) {
|
||||
console.error('写入文件时出错:', err);
|
||||
} else {
|
||||
console.log(`JSON 数据已成功写入到文件 ${fullFilePath}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_atob_cn(base64String) {
|
||||
const bytes = Uint8Array.from(atob(base64String), c => c.charCodeAt(0))
|
||||
const decodedString = new TextDecoder('utf-8').decode(bytes)
|
||||
return decodedString
|
||||
}
|
||||
|
||||
_getBaseInfo(tokens) {
|
||||
let description = undefined
|
||||
let heading = undefined
|
||||
// let name = undefined
|
||||
// 寻找表格内容
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
if (!description && token.type === "paragraph_open") {
|
||||
description = tokens[i + 1].content
|
||||
}
|
||||
if (!heading && token.type === "heading_open") {
|
||||
const content = tokens[i + 1].content
|
||||
const heads = content.split("\n")
|
||||
heading = {}
|
||||
heads.forEach((item) => {
|
||||
const kv = item.split(": ")
|
||||
heading[kv[0]] = kv[1]
|
||||
})
|
||||
}
|
||||
}
|
||||
return { description, heading }
|
||||
}
|
||||
|
||||
_getMdTableInfo(tokens) {
|
||||
let tableContent = null
|
||||
const tableContents = []
|
||||
const titles = this._parseTitle(tokens)
|
||||
let col = 0 // 列数
|
||||
// 寻找表格内容
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
if (token.type === 'table_open') {
|
||||
col = 0
|
||||
tableContent = {
|
||||
content: [],
|
||||
row: 0,
|
||||
col,
|
||||
parents: [],
|
||||
parentMap: {}
|
||||
}
|
||||
let endIndex = undefined
|
||||
for (let t = 0; t < titles.length; t++) {
|
||||
const { index } = titles[t];
|
||||
if (index > i && endIndex === undefined) {
|
||||
endIndex = t
|
||||
break;
|
||||
}
|
||||
}
|
||||
const newTitles = titles.slice(0, endIndex).reverse()
|
||||
let first_title_index = (newTitles[0] || {}).index
|
||||
if (first_title_index) {
|
||||
tableContent.parents.push(newTitles[0])
|
||||
}
|
||||
for (let j = 0; j < newTitles.length; j++) {
|
||||
const title = newTitles[j];
|
||||
if (title.index > first_title_index) {
|
||||
tableContent.parents.push(title)
|
||||
first_title_index = title.index
|
||||
}
|
||||
}
|
||||
} else if (token.type === 'thead_close') {
|
||||
tableContent.col = col
|
||||
} else if (token.type === 'table_close') {
|
||||
tableContent.row = tableContent.content.length / tableContent.col
|
||||
tableContents.push(tableContent)
|
||||
tableContent = null
|
||||
} else if (tableContent && token.type === 'inline') {
|
||||
const content = token.content
|
||||
col++
|
||||
tableContent.content.push(content)
|
||||
}
|
||||
}
|
||||
return { tables: tableContents }
|
||||
}
|
||||
|
||||
_parseTitle(tokens) {
|
||||
const titles = []
|
||||
for (let i = 0; i < tokens.length; i++) {
|
||||
const token = tokens[i]
|
||||
if (token.type === 'heading_open' && token.markup && token.markup.includes("#")) {
|
||||
const content = tokens[i + 1].content
|
||||
const title = {
|
||||
level: String(token.markup).length,
|
||||
content,
|
||||
index: i
|
||||
}
|
||||
titles.push(title)
|
||||
}
|
||||
}
|
||||
return titles
|
||||
}
|
||||
|
||||
_parseMd(mdStrBase64) {
|
||||
const mdStr = this._atob_cn(mdStrBase64)
|
||||
let result = undefined
|
||||
try {
|
||||
result = this._md.parse(mdStr, {})
|
||||
} catch (error) {
|
||||
console.log(mdStr)
|
||||
throw new Error(error)
|
||||
}
|
||||
const tablesInfo = this._getMdTableInfo(result)
|
||||
const info = this._getBaseInfo(result)
|
||||
return { ...tablesInfo, ...info }
|
||||
}
|
||||
|
||||
_checkParams(params) {
|
||||
if (!params) {
|
||||
throw new Error('Initialization requires passing in parameters');
|
||||
}
|
||||
const requiredProperties = [
|
||||
'owner',
|
||||
'repo',
|
||||
'path',
|
||||
'host',
|
||||
'version',
|
||||
'projectName',
|
||||
'framework',
|
||||
'script',
|
||||
'css',
|
||||
'exportNamePrefix',
|
||||
]
|
||||
for (let prop of requiredProperties) {
|
||||
if (!(prop in params)) {
|
||||
throw new Error(`Property '${prop}' is missing from the params.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export default GenerateMaterial
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
class LocalCache {
|
||||
constructor(cacheDir = './cache') {
|
||||
this.cacheDir = path.resolve(cacheDir);
|
||||
if (!fs.existsSync(this.cacheDir)) {
|
||||
fs.mkdirSync(this.cacheDir);
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
const filePath = path.join(this.cacheDir, `${key}.json`);
|
||||
try {
|
||||
const data = fs.readFileSync(filePath, 'utf8');
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
// 如果文件不存在或者解析出错,返回null或者抛出自定义错误
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
set(key, value, ttl = 0) {
|
||||
const filePath = path.join(this.cacheDir, `${key}.json`);
|
||||
const data = JSON.stringify(value, null, 2);
|
||||
fs.writeFileSync(filePath, data);
|
||||
|
||||
// 如果设置了ttl,则使用setTimeout在ttl后删除缓存
|
||||
if (ttl > 0) {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (error) {
|
||||
// 忽略删除失败的情况,可能文件已经被其他操作删除了
|
||||
}
|
||||
}, ttl);
|
||||
}
|
||||
}
|
||||
}
|
||||
export default LocalCache
|
||||
// // 使用示例:
|
||||
// const cache = new LocalCache();
|
||||
|
||||
// // 设置缓存
|
||||
// cache.set('myKey', { myData: 'some data' }, 60000); // 缓存60秒
|
||||
|
||||
// // 获取缓存
|
||||
// const data = cache.get('myKey');
|
||||
// console.log(data); // 输出:{ myData: 'some data' }
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
import NaiveUI from 'naive-ui';
|
||||
|
||||
import GenerateMaterial from './common/GenerateMaterial-components.mjs';
|
||||
async function main() {
|
||||
const NewNaiveUIModule = {}
|
||||
Object.keys(NaiveUI).forEach((key) => {
|
||||
if (String(key).startsWith("N")) {
|
||||
NewNaiveUIModule[key] = NaiveUI[key]
|
||||
}
|
||||
})
|
||||
const genMaterial = new GenerateMaterial({
|
||||
version: "2.38.1",
|
||||
Module: NewNaiveUIModule,
|
||||
framework: "Vue",
|
||||
script: "https://unpkg.com/naive-ui@2.38.1/dist/index.mjs",
|
||||
css: "",
|
||||
exportNamePrefix: "N",
|
||||
repo: "naive-ui",
|
||||
})
|
||||
genMaterial.add((data) => {
|
||||
// data.snippets = data.snippets.map((item) => {
|
||||
// item.children = item.children.map((children_item) => {
|
||||
// const changeProps = {}
|
||||
// if (String(children_item.icon).startsWith("van-")) {
|
||||
// changeProps.icon = children_item.icon.slice(4)
|
||||
// }
|
||||
// return {
|
||||
// ...children_item,
|
||||
// ...changeProps
|
||||
// }
|
||||
// }).filter(({ snippetName }) => {
|
||||
// return String(snippetName).startsWith("van-")
|
||||
// })
|
||||
// return item
|
||||
// })
|
||||
data.components = data.components.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
component: `N${item.component}`
|
||||
}
|
||||
})
|
||||
return data;
|
||||
})
|
||||
// genMaterial.add((data) => {
|
||||
// data.snippets = [
|
||||
// {
|
||||
// "group": "vant",
|
||||
// "children": [
|
||||
// {
|
||||
// "name": {
|
||||
// "zh_CN": "van-button"
|
||||
// },
|
||||
// "icon": "button",
|
||||
// "screenshot": "",
|
||||
// "snippetName": "VanButton",
|
||||
// "schema": {
|
||||
// "children": [
|
||||
// {
|
||||
// "componentName": "Template",
|
||||
// "children": [
|
||||
// {
|
||||
// "componentName": "Text",
|
||||
// "props": {
|
||||
// "text": "van-button"
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// "props": {
|
||||
// "slot": {
|
||||
// "name": "default"
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// return data;
|
||||
// })
|
||||
genMaterial.start()
|
||||
}
|
||||
|
||||
main()
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import VantUI from 'vant';
|
||||
|
||||
import GenerateMaterial from './common/GenerateMaterial-components.mjs';
|
||||
async function main() {
|
||||
const genMaterial = new GenerateMaterial({
|
||||
version: "4.8.10",
|
||||
Module: VantUI,
|
||||
framework: "Vue",
|
||||
script: "https://unpkg.com/vant@4.8.10/es/index.mjs",
|
||||
css: "https://unpkg.com/vant@4.8.10/lib/index.css",
|
||||
exportNamePrefix: "Van",
|
||||
repo: "vant",
|
||||
})
|
||||
genMaterial.add((data) => {
|
||||
data.snippets = data.snippets.map((item) => {
|
||||
item.children = item.children.map((children_item) => {
|
||||
const changeProps = {}
|
||||
if (String(children_item.icon).startsWith("van-")) {
|
||||
changeProps.icon = children_item.icon.slice(4)
|
||||
}
|
||||
return {
|
||||
...children_item,
|
||||
...changeProps
|
||||
}
|
||||
}).filter(({ snippetName }) => {
|
||||
return String(snippetName).startsWith("van-")
|
||||
})
|
||||
return item
|
||||
})
|
||||
return data;
|
||||
})
|
||||
genMaterial.add((data) => {
|
||||
data.snippets = [
|
||||
{
|
||||
"group": "vant",
|
||||
"children": [
|
||||
{
|
||||
"name": {
|
||||
"zh_CN": "van-button"
|
||||
},
|
||||
"icon": "button",
|
||||
"screenshot": "",
|
||||
"snippetName": "VanButton",
|
||||
"schema": {
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Template",
|
||||
"children": [
|
||||
{
|
||||
"componentName": "Text",
|
||||
"props": {
|
||||
"text": "van-button"
|
||||
}
|
||||
}
|
||||
],
|
||||
"props": {
|
||||
"slot": {
|
||||
"name": "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
return data;
|
||||
})
|
||||
genMaterial.start()
|
||||
}
|
||||
|
||||
main()
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* 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 { defineConfig } from 'vite'
|
||||
import path from 'path'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, './src/index.js'),
|
||||
formats: ['es']
|
||||
},
|
||||
sourcemap: true
|
||||
}
|
||||
})
|
||||
|
|
@ -29,6 +29,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",
|
||||
"html2canvas": "^1.4.1",
|
||||
"pinyin-pro": "^3.13.0"
|
||||
},
|
||||
|
|
@ -38,9 +41,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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@
|
|||
<tiny-dropdown trigger="click" @item-click="changeType">
|
||||
<span>
|
||||
<span>{{ state.sortTypeLabel }}</span>
|
||||
<icon-chevron-down class="ml8"></icon-chevron-down>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<tiny-dropdown-menu
|
||||
|
|
@ -117,6 +118,7 @@ import {
|
|||
import { PluginPanel, PluginBlockList, SvgButton, SaveNewBlock, LinkButton } from '@opentiny/tiny-engine-common'
|
||||
import { useBlock, useModal, useLayout, useCanvas, useHelp } from '@opentiny/tiny-engine-controller'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
import { iconChevronDown } from '@opentiny/vue-icon'
|
||||
import BlockSetting, { openPanel, closePanel } from './BlockSetting.vue'
|
||||
import BlockGroupArrange from './BlockGroupArrange.vue'
|
||||
import CategoryEdit from './CategoryEdit.vue'
|
||||
|
|
@ -186,7 +188,8 @@ export default {
|
|||
CategoryEdit,
|
||||
PluginBlockList,
|
||||
TinyPopover,
|
||||
TinyButton
|
||||
TinyButton,
|
||||
IconChevronDown: iconChevronDown()
|
||||
},
|
||||
|
||||
setup() {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,9 @@
|
|||
"@opentiny/tiny-engine-canvas": "workspace:*",
|
||||
"@opentiny/tiny-engine-common": "workspace:*",
|
||||
"@opentiny/tiny-engine-controller": "workspace:*",
|
||||
"@opentiny/tiny-engine-http": "workspace:*"
|
||||
"@opentiny/tiny-engine-http": "workspace:*",
|
||||
"@opentiny/vue": "~3.10.0",
|
||||
"@opentiny/vue-icon": "~3.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
|
|
@ -35,8 +37,6 @@
|
|||
"vite": "^4.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentiny/vue": "^3.14.0",
|
||||
"@opentiny/vue-icon": "^3.14.0",
|
||||
"vue": "^3.4.15"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,7 @@
|
|||
clearable
|
||||
placeholder="请输入关键字搜索"
|
||||
@update:modelValue="searchBridgeData"
|
||||
>
|
||||
<template #prefix>
|
||||
<tiny-icon-search />
|
||||
</template>
|
||||
</tiny-search>
|
||||
></tiny-search>
|
||||
</div>
|
||||
<div class="list">
|
||||
<div
|
||||
|
|
@ -23,7 +19,10 @@
|
|||
<div class="item-label">{{ item.name }}</div>
|
||||
<svg-icon class="setting-icon" name="setting" @click.stop="openEdit(item, index)"></svg-icon>
|
||||
</div>
|
||||
<search-empty :isShow="!list.length" />
|
||||
<div v-if="!list.length" class="empty-wrap">
|
||||
<svg-icon class="empty-icon" name="empty"></svg-icon>
|
||||
<p class="empty-text">暂无数据</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -31,8 +30,6 @@
|
|||
<script>
|
||||
import { watchEffect, ref, reactive } from 'vue'
|
||||
import { Search } from '@opentiny/vue'
|
||||
import { iconSearch } from '@opentiny/vue-icon'
|
||||
import { SearchEmpty } from '@opentiny/tiny-engine-common'
|
||||
import {
|
||||
RESOURCE_TYPE,
|
||||
ACTION_TYPE,
|
||||
|
|
@ -47,9 +44,7 @@ import {
|
|||
|
||||
export default {
|
||||
components: {
|
||||
TinySearch: Search,
|
||||
TinyIconSearch: iconSearch(),
|
||||
SearchEmpty
|
||||
TinySearch: Search
|
||||
},
|
||||
props: {
|
||||
name: {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,9 @@
|
|||
*/
|
||||
|
||||
import { reactive } from 'vue'
|
||||
import { useApp, useResource, useNotify, useCanvas } from '@opentiny/tiny-engine-controller'
|
||||
import { useApp, useResource, useNotify } from '@opentiny/tiny-engine-controller'
|
||||
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
|
||||
import { updateUtils, deleteUtils } from '@opentiny/tiny-engine-canvas'
|
||||
import {
|
||||
fetchResourceList,
|
||||
requestDeleteReSource,
|
||||
|
|
@ -174,8 +175,6 @@ const generateBridgeUtil = (...args) => {
|
|||
}
|
||||
|
||||
export const saveResource = (data, callback, emit) => {
|
||||
const { updateUtils } = useCanvas().canvasApi.value
|
||||
|
||||
if (getActionType() === ACTION_TYPE.Edit) {
|
||||
data.id = state.resource.id
|
||||
requestUpdateReSource(data).then((result) => {
|
||||
|
|
@ -219,8 +218,6 @@ export const saveResource = (data, callback, emit) => {
|
|||
|
||||
export const deleteData = (name, callback, emit) => {
|
||||
const params = `app=${useApp().appInfoState.selectedId}&id=${state.resource?.id}`
|
||||
const { deleteUtils } = useCanvas().canvasApi.value
|
||||
|
||||
requestDeleteReSource(params).then((data) => {
|
||||
if (data) {
|
||||
const index = useResource().resState[state.type].findIndex((item) => item.name === data.name)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
"@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",
|
||||
"monaco-editor": "0.33.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -37,8 +39,6 @@
|
|||
"vite": "^4.3.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentiny/vue": "^3.14.0",
|
||||
"@opentiny/vue-icon": "^3.14.0",
|
||||
"vue": "^3.4.15"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<search-empty :isShow="!filteredKey.length" />
|
||||
<div v-if="!filteredKey.length" class="data-source-list-blank"><span>查询结果为空</span></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -25,15 +25,14 @@ import { useModal } from '@opentiny/tiny-engine-controller'
|
|||
import { useResource } from '@opentiny/tiny-engine-controller'
|
||||
import { findExpressionInAppSchema } from '@opentiny/tiny-engine-controller/js/ast'
|
||||
import { constants } from '@opentiny/tiny-engine-utils'
|
||||
import { SvgButton, SearchEmpty } from '@opentiny/tiny-engine-common'
|
||||
import { SvgButton } from '@opentiny/tiny-engine-common'
|
||||
import { STATE, OPTION_TYPE } from './js/constants'
|
||||
|
||||
const { COMPONENT_NAME } = constants
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SvgButton,
|
||||
SearchEmpty
|
||||
SvgButton
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
|
|
|
|||
|
|
@ -7,20 +7,16 @@
|
|||
<close-icon @close="closePanel"></close-icon>
|
||||
</div>
|
||||
<tiny-tabs v-model="activeName" @click="tabClick" tab-style="button-card">
|
||||
<tiny-tab-item :name="STATE.CURRENT_STATE" :title="isBlock ? '区块状态' : '页面状态'"></tiny-tab-item>
|
||||
<tiny-tab-item v-if="isBlock" :name="STATE.CURRENT_STATE" title="区块状态"></tiny-tab-item>
|
||||
<tiny-tab-item v-else :name="STATE.CURRENT_STATE" title="页面状态"></tiny-tab-item>
|
||||
<tiny-tab-item :name="STATE.GLOBAL_STATE" title="应用状态"></tiny-tab-item>
|
||||
</tiny-tabs>
|
||||
<tiny-search
|
||||
:modelValue="query"
|
||||
class="left-filter"
|
||||
placeholder="请输入搜索条件"
|
||||
clearable
|
||||
@update:modelValue="search"
|
||||
>
|
||||
<template #prefix>
|
||||
<tiny-icon-search />
|
||||
</template>
|
||||
</tiny-search>
|
||||
></tiny-search>
|
||||
<div class="add-btn">
|
||||
<tiny-button @click="openPanel(OPTION_TYPE.ADD)">{{
|
||||
activeName === STATE.CURRENT_STATE ? '添加变量' : '添加全局变量'
|
||||
|
|
@ -79,7 +75,7 @@ import {
|
|||
useLayout,
|
||||
useHelp
|
||||
} from '@opentiny/tiny-engine-controller'
|
||||
import { iconSearch } from '@opentiny/vue-icon'
|
||||
import { setState, getSchema, deleteState, setGlobalState, getGlobalState } from '@opentiny/tiny-engine-canvas'
|
||||
import { CloseIcon, LinkButton } from '@opentiny/tiny-engine-common'
|
||||
import DataSourceList from './DataSourceList.vue'
|
||||
import CreateVariable from './CreateVariable.vue'
|
||||
|
|
@ -98,8 +94,7 @@ export default {
|
|||
TinyTabs: Tabs,
|
||||
TinyTabItem: TabItem,
|
||||
CreateStore,
|
||||
LinkButton,
|
||||
TinyIconSearch: iconSearch()
|
||||
LinkButton
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const variableRef = ref(null)
|
||||
|
|
@ -160,8 +155,6 @@ export default {
|
|||
}
|
||||
|
||||
const add = (name, variable) => {
|
||||
const { getSchema } = useCanvas().canvasApi.value
|
||||
|
||||
if (getSchema()) {
|
||||
if (updateKey.value !== name && flag.value === OPTION_TYPE.UPDATE) {
|
||||
delete state.dataSource[updateKey.value]
|
||||
|
|
@ -188,7 +181,6 @@ export default {
|
|||
|
||||
const confirm = () => {
|
||||
const { name } = state.createData
|
||||
const { setState, setGlobalState } = useCanvas().canvasApi.value
|
||||
|
||||
if (!name || errorMessage.value) {
|
||||
notifySaveError('变量名未填写或名称不符合规范,请按照提示修改后重试。')
|
||||
|
|
@ -258,8 +250,6 @@ export default {
|
|||
}
|
||||
|
||||
const remove = (key) => {
|
||||
const { deleteState, getSchema } = useCanvas().canvasApi.value
|
||||
|
||||
delete state.dataSource[key]
|
||||
// 删除变量也需要同步触发画布渲染
|
||||
deleteState(key)
|
||||
|
|
@ -288,7 +278,6 @@ export default {
|
|||
}
|
||||
|
||||
const setGlobalStateToDataSource = () => {
|
||||
const { getGlobalState } = useCanvas().canvasApi.value
|
||||
const globalState = getGlobalState()
|
||||
|
||||
if (!globalState) {
|
||||
|
|
@ -303,7 +292,6 @@ export default {
|
|||
const removeStore = (key) => {
|
||||
const storeListt = [...useResource().resState.globalState] || []
|
||||
const index = storeListt.findIndex((store) => store.id === key)
|
||||
const { setGlobalState } = useCanvas().canvasApi.value
|
||||
|
||||
if (index !== -1) {
|
||||
const { id } = useEditorInfo().useInfo()
|
||||
|
|
@ -326,8 +314,6 @@ export default {
|
|||
}
|
||||
|
||||
const initDataSource = (tabsName = activeName.value) => {
|
||||
const { getSchema } = useCanvas().canvasApi.value
|
||||
|
||||
if (tabsName === STATE.GLOBAL_STATE) {
|
||||
setGlobalStateToDataSource()
|
||||
} else {
|
||||
|
|
@ -489,8 +475,11 @@ export default {
|
|||
}
|
||||
|
||||
:deep(.tiny-tabs__content) {
|
||||
margin: 0;
|
||||
height: calc(100% - 48px);
|
||||
padding: 0;
|
||||
& > div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
:deep(.help-box) {
|
||||
position: absolute;
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@
|
|||
"@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",
|
||||
"axios": "~0.28.0",
|
||||
"axios": "^0.27.2",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
|
|
@ -40,9 +43,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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,11 +13,16 @@
|
|||
</span>
|
||||
<div class="section">
|
||||
<div class="field-list">
|
||||
<!-- 字段列表 -->
|
||||
<data-source-field-list
|
||||
ref="fieldList"
|
||||
v-model="state.fields"
|
||||
@update:modelValue="$emit('update:modelValue', state.fields)"
|
||||
></data-source-field-list>
|
||||
<div v-if="state.showNewFieldForm" class="new-field-form">
|
||||
<data-source-field-type @cancel="closeFieldType" @select="selectFieldType"></data-source-field-type>
|
||||
<data-source-field-form
|
||||
ref="fieldForm"
|
||||
:modelValue="modelValue"
|
||||
:field="state.field"
|
||||
:editable="true"
|
||||
:is-open="false"
|
||||
|
|
@ -25,12 +30,6 @@
|
|||
@save="saveFieldForm"
|
||||
></data-source-field-form>
|
||||
</div>
|
||||
<!-- 字段列表 -->
|
||||
<data-source-field-list
|
||||
ref="fieldList"
|
||||
v-model="state.fields"
|
||||
@update:modelValue="$emit('update:modelValue', state.fields)"
|
||||
></data-source-field-list>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -114,7 +113,7 @@ export default {
|
|||
|
||||
if (name && title) {
|
||||
field.type = state.field.type
|
||||
state.fields.unshift(field)
|
||||
state.fields.push(field)
|
||||
emit('update:modelValue', state.fields)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,10 +66,6 @@ export default {
|
|||
isOpen: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
emits: ['save', 'cancel'],
|
||||
|
|
@ -102,9 +98,7 @@ export default {
|
|||
const handleCancel = () => {
|
||||
emit('cancel')
|
||||
}
|
||||
const uniqueName = () => {
|
||||
return props.modelValue.some((item) => item.name === state.field.name)
|
||||
}
|
||||
|
||||
const saveField = () => {
|
||||
form.value.validate((valid) => {
|
||||
if (valid) {
|
||||
|
|
@ -122,10 +116,6 @@ export default {
|
|||
|
||||
return
|
||||
}
|
||||
if (uniqueName() && rule.field === 'name') {
|
||||
callback(new Error('该字段已存在,请重新输入'))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue