Compare commits

..

3 Commits

Author SHA1 Message Date
chilingling 93f1b6980d
chore(release): publish
- @opentiny/tiny-engine-mock@1.0.1-beta.0
 - @opentiny/tiny-engine-canvas@1.0.3-beta.0
 - @opentiny/tiny-engine-common@1.0.1-beta.0
 - @opentiny/tiny-engine-controller@1.0.1-beta.0
 - @opentiny/tiny-engine@1.0.1-beta.0
 - @opentiny/tiny-engine-http@1.0.1-beta.0
 - @opentiny/tiny-engine-i18n-host@1.0.1-beta.0
 - @opentiny/tiny-engine-plugin-block@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-bridge@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-data@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-datasource@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-help@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-i18n@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-materials@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-page@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-robot@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-schema@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-script@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-tree@1.0.2-beta.0
 - @opentiny/tiny-engine-plugin-tutorial@1.0.2-beta.0
 - @opentiny/tiny-engine-setting-design@1.0.2-beta.0
 - @opentiny/tiny-engine-setting-events@1.0.2-beta.0
 - @opentiny/tiny-engine-setting-props@1.0.2-beta.0
 - @opentiny/tiny-engine-setting-styles@1.0.2-beta.0
 - @opentiny/tiny-engine-svgs@1.0.2-beta.0
 - @opentiny/tiny-engine-theme-dark@1.0.2-beta.0
 - @opentiny/tiny-engine-theme-light@1.0.3-beta.0
 - @opentiny/tiny-engine-toolbar-breadcrumb@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-clean@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-collaboration@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-fullscreen@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-generate-vue@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-lang@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-layout@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-checkinout@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-logo@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-logout@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-media@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-preview@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-redoundo@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-refresh@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-save@1.0.2-beta.0
 - @opentiny/tiny-engine-toolbar-setting@1.0.2-beta.0
 - @opentiny/tiny-engine-utils@1.0.2-beta.0
 - @opentiny/tiny-engine-dsl-vue@1.0.2-beta.0
 - @opentiny/tiny-engine-webcomponent-core@1.0.2-beta.0
2023-10-27 23:28:52 -07:00
chilingling 6ba427b205
fix(version): update version 2023-10-27 23:22:59 -07:00
chilingling 97a8ed89c9
fix(build): fix buildError 2023-10-27 23:15:19 -07:00
465 changed files with 16760 additions and 37464 deletions

View File

@ -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

View File

@ -1,5 +0,0 @@
SQL_HOST=localhost
SQL_PORT=3306
SQL_USER=root
SQL_PASSWORD=admin
SQL_DATABASE=tiny_engine

View File

@ -5,5 +5,3 @@ package-lock.json
**/node_modules/**
tmp
temp
mockServer
packages/vue-generator/**/output/**

View File

@ -64,14 +64,6 @@ body:
attributes:
label: What is actually happening
placeholder: Please Input
- type: input
id: project-name
attributes:
label: What is your project name
description: We also welcome you to fill in more detailed project information in the following issue [#334](https://github.com/opentiny/tiny-engine/issues/334).
placeholder: Please Input
validations:
required: true
- type: textarea
id: additional-comments
attributes:

View File

@ -2,4 +2,4 @@ blank_issues_enabled: true
contact_links:
- name: Questions or need help
url: https://github.com/opentiny/tiny-engine/discussions
about: Add this WeChat(opentiny-official), we will invite you to the WeChat discussion group later.
about: Add this WeChat(opentiny), we will invite you to the WeChat discussion group later.

View File

@ -21,11 +21,3 @@ body:
placeholder: Please Input
validations:
required: true
- type: input
id: project-name
attributes:
label: What is your project name
description: We also welcome you to fill in more detailed project information in the following issue [#334](https://github.com/opentiny/tiny-engine/issues/334).
placeholder: Please Input
validations:
required: true

View File

@ -1,5 +1,3 @@
English | [简体中文](https://github.com/opentiny/tiny-engine/blob/develop/.github/PULL_REQUEST_TEMPLATE/PULL_REQUEST_TEMPLATE.zh-CN.md)
# PR
## PR Checklist
@ -9,7 +7,6 @@ Please check if your PR fulfills the following requirements:
- [ ] The commit message follows our [Commit Message Guidelines](https://github.com/opentiny/tiny-engine/blob/develop/CONTRIBUTING.md)
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been added / updated (for bug fixes / features)
- [ ] Built its own designer, fully self-validated
## PR Type
@ -26,21 +23,13 @@ What kind of change does this PR introduce?
- [ ] Documentation content changes
- [ ] Other... Please describe:
## Background and solution
<!--
1. Describe the problem and the scenario.
2. New features need to be described and attached with renderings.
3. Screenshots or GIFs involving UI/Interaction changes/Bugfix before and after modification are required.
-->
### What is the current behavior?
## What is the current behavior?
<!-- Please describe the current behavior that you are modifying, or link to a relevant issue. -->
Issue Number: N/A
### What is the new behavior?
## What is the new behavior?
## Does this PR introduce a breaking change?

View File

@ -1,52 +0,0 @@
[English](https://github.com/opentiny/tiny-engine/blob/develop/.github/PULL_REQUEST_TEMPLATE.md) | 简体中文
# PR
## PR Checklist
请检查您的 PR 是否满足以下要求:
- [ ] commit message遵循我们的[提交贡献指南](https://github.com/opentiny/tiny-engine/blob/develop/CONTRIBUTING.md)
- [ ] 添加了更改内容的测试用例用于bugfix/功能)
- [ ] 文档已添加/更新用于bugfix/功能)
- [ ] 是否构建了自己的设计器,经过了充分的自验证
## PR 类型
这个PR的类型是
- [ ] 日常 bug 修复
- [ ] 新特性支持
- [ ] 代码风格优化
- [ ] 重构
- [ ] 构建优化
- [ ] 测试用例
- [ ] 文档更新
- [ ] 分支合并
- [ ] 其他改动(请补充)
## 需求背景和解决方案
<!--
1. 要解决的具体问题。
2. 新增特性,需要进行功能描述,并附上效果图。
3. 涉及UI/交互变动/Bugfix需要有修改前&修改后截图或 GIF。
-->
Issue Number: N/A
### 修改前
### 修改后
## 此PR是否含有 breaking change?
- [ ] 是
- [ ] 否
<!-- 如果此 PR 包含breaking change请在下面从用户角度描述具体变化和其他风险。-->
## Other information

View File

@ -1,36 +0,0 @@
name: Push And Create PR Check
on:
push:
branches: []
pull_request:
branches: [develop,main]
jobs:
push-check:
runs-on: ubuntu-latest # windows-latest || macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- name: Install pnpm
run: npm i -g pnpm
- name: Install dependencies
run: pnpm i
- name: Get changed files
id: get_changed_files
uses: tj-actions/changed-files@v41
with:
files: |
**.js
**.vue
**.jsx
- name: Run ESLint
run: npx eslint ${{steps.get_changed_files.outputs.all_changed_files}}
- name: Run Build
run: pnpm run build:plugin && pnpm run build:alpha

1
.gitignore vendored
View File

@ -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

View File

@ -51,11 +51,14 @@ Local startup steps:
git clone git@github.com:username/tiny-engine.git
cd tiny-engine
git remote add upstream git@github.com:opentiny/tiny-engine.git
pnpm i
npm i
# Start the project.
$ pnpm dev
$ npm run serve
# start another terminal
$ cd mockServer
$ npm run dev
```
To submit a PR:
@ -64,7 +67,7 @@ To submit a PR:
- Local coding.
- Submit according to [Commit Message Format](https://www.conventionalcommits.org/zh-hans/v1.0.0/) specification. PR that do not conform to the submission specification will not be merged.
- Submit to remote repository: `git push origin branchName`.
- (Optional) Synchronize upstream repository dev branch latest code: `git pull upstream develop`.
- (Optional) Synchronize upstream repository dev branch latest code: `git pull upstream dev`.
- Open the [Pull requests](https://github.com/opentiny/tiny-engine/pulls) link of the TinyEngine code repository and click the New pull request button to submit the PR.
- Project Committer conducts Code Review and makes comments.
- The PR author adjusts the code according to the opinion. Please note that when a branch initiates PR, the subsequent commit will be synchronized automatically, and there is no need to resubmit the PR.

View File

@ -42,18 +42,23 @@
- 点击 [TinyEngine](https://github.com/opentiny/tiny-engine) 代码仓库右上角的 Fork 按钮,将上游仓库 Fork 到个人仓库
- Clone 个人仓库到本地
- 在 TinyEngine 根目录下运行 `pnpm i`, 安装依赖
- 在 TinyEngine 根目录下运行 `pnpm dev`,启动本地开发
- 在 TinyEngine 根目录下运行 `npm install`, 安装依赖
- 在 TinyEngine mockServer 运行 `npm install`, 安装依赖
- 在 TinyEngine 根目录下运行 `npm run serve`,再到 mockServer 目录下运行 `npm run dev`,启动本地开发
```shell
# username 为用户名,执行前请替换
git clone git@github.com:username/tiny-engine.git
cd tiny-engine
git remote add upstream git@github.com:opentiny/tiny-engine.git
pnpm i
npm i
# 启动项目
$ pnpm dev
$ npm run serve
# start another terminal
$ cd mockServer
$ npm run dev
```
@ -63,7 +68,7 @@ $ pnpm dev
- 本地编码
- 遵循 Commit Message Format 规范进行提交,不符合提交规范的 PR 将不会被合并
- 提交到远程仓库git push origin branchName
- (可选)同步上游仓库 develop 分支最新代码git pull upstream develop
- (可选)同步上游仓库 dev 分支最新代码git pull upstream dev
- 打开 TinyEngine 代码仓库的 [Pull requests](https://github.com/opentiny/tiny-engine/pulls) 链接,点击 New pull request 按钮提交 PR
- 项目 Committer 进行 Code Review并提出意见
- PR 作者根据意见调整代码,请注意一个分支发起了 PR 后,后续的 commit 会自动同步,无需重新提交 PR
@ -76,4 +81,4 @@ $ pnpm dev
如果你对我们的开源项目感兴趣,欢迎通过以下方式加入我们的开源社区。
- 添加官方小助手微信opentiny-official加入我们的技术交流群
- 加入邮件列表opentiny@googlegroups.com
- 加入邮件列表opentiny@googlegroups.com

64
README.en-US.md Normal file
View File

@ -0,0 +1,64 @@
<p align="center">
<a href="https://opentiny.design/tiny-engine" target="_blank" rel="noopener noreferrer">
<img alt="OpenTiny Logo" src="logo.svg" height="100" style="max-width:100%;">
</a>
</p>
<p align="center">TinyEngine enables developers to customize low-code platforms, build low-bit platforms online in real time, and support secondary development or integration of low-bit platform capabilities.</p>
English | [简体中文](README.zh-CN.md)
🌈 Features:
- Cross-end cross-frame front-end components
- Supports online real-time construction, secondary development, or being integrated.
- Directly generate deployable source code without engine support.
- Allows access to third-party components and customized extension plug-ins.
- Supports high-code and low-code, and hybrid development and deployment of applications.
- The platform accesses AI big model capabilities to help developers build applications.
## Development
### Dependencies required for installation
```sh
$ pnpm install
```
### Local development: Start the local mock server and use the mock data of the local mock server.
```sh
$ pnpm dev
```
Open a browser: `http://localhost:8080/?type=app&id=918&tenant=1&pageid=NTJ4MjvqoVj8OVsc`
`url search` Parameters:
- `type=app` Application type
- `id=xxx` Application ID
- `tenant=xxx` Organization ID
- `pageid=xxx` Page ID
## Build
```sh
# Build all plug-ins first
pnpm build:plugin
# Build Designer
pnpm build:alpha or build:prod
```
## 🤝 Participation and Contribution
If you are interested in our open source project, please join us! 🎉
Please read the [Contribution Guide](CONTRIBUTING.md) before participating in the contribution.
- Add official assistant WeChat opentiny-official and join the technical exchange group
- Join the mailing list opentiny@googlegroups.com
## License
[MIT](LICENSE)

View File

@ -17,12 +17,6 @@ English | [简体中文](README.zh-CN.md)
- Supports high-code and low-code, and hybrid development and deployment of applications.
- The platform accesses AI big model capabilities to help developers build applications.
## Documentation
- introhttps://opentiny.design/tiny-engine#/home
- tutorialhttps://opentiny.design/tiny-engine#/help-center/course/engine
- playgroundhttps://opentiny.design/tiny-engine#/tiny-engine-editor
## Development
### Dependencies required for installation
@ -37,27 +31,6 @@ $ pnpm install
$ pnpm dev
```
## Local development, directly connected to the local tiny-engine-webservice server
1. Start <a href="https://github.com/opentiny/tiny-engine-data-center/blob/main/README.md" target="_blank">tiny-engine-data-center</a>
2. Start <a href="https://github.com/opentiny/tiny-engine-webservice/blob/main/README.md" target="_blank">tiny-engine-webservice</a>
3. Modify the origin value in `vite.config.js` in the `packages/design-core/` directory of the tiny-engine project to be the address port of your local webService project (the webService port defaults to 7011), such as:
<img alt="Modify port" src="https://res.hc-cdn.com/lowcode-portal/1.1.55/img/docimg/backend_deploy_5.png">
### Materials Synchronization [Solution](https://opentiny.design/tiny-engine#/help-center/course/engine/56)
```sh
$ pnpm splitMaterials
```
```sh
$ pnpm buildMaterials
```
Open a browser: `http://localhost:8080/?type=app&id=918&tenant=1&pageid=NTJ4MjvqoVj8OVsc`
`url search` Parameters:
@ -75,23 +48,6 @@ pnpm build:plugin
# Build Designer
pnpm build:alpha or build:prod
```
The folder where the product is located after building
```
tiny-engine/packages/design-core/dist/
```
## Milestones
```mermaid
gantt
dateFormat YYYY-MM-DD
axisFormat %Y-%m-%d
1.0.0-beta.x version :active,2023-09-25, 2024-03-31
1.0.0-rc version : 2024-04-01, 2024-06-30
1.0.0 version : 2024-07-01, 2024-07-31
```
## 🤝 Participation and Contribution

View File

@ -17,12 +17,6 @@
- 支持高代码与低代码,混合开发部署应用
- 平台接入 AI 大模型能力,辅助开发者构建应用
## 文档
- 介绍https://opentiny.design/tiny-engine#/home
- 使用文档https://opentiny.design/tiny-engine#/help-center/course/engine
- 演示应用https://opentiny.design/tiny-engine#/tiny-engine-editor
## 开发
### 安装所需的依赖
@ -37,27 +31,6 @@ $ pnpm install
$ pnpm dev
```
### 本地开发直连本地的tiny-engine-webservice服务端
1. 启动 <a href="https://github.com/opentiny/tiny-engine-data-center/blob/main/README.md" target="_blank">tiny-engine-data-center</a>
2. 启动 <a href="https://github.com/opentiny/tiny-engine-webservice/blob/main/README.md" target="_blank">tiny-engine-webservice</a>
3. 修改 tiny-engine 项目 `packages/design-core/` 目录下 `vite.config.js` 中origin的值为自己本地webService项目的地址端口webService端口默认为7011
<img alt="修改端口" src="https://res.hc-cdn.com/lowcode-portal/1.1.55/img/docimg/backend_deploy_5.png">
### 物料同步[方案](https://opentiny.design/tiny-engine#/help-center/course/engine/56)
```sh
$ pnpm splitMaterials
```
```sh
$ pnpm buildMaterials
```
浏览器打开:`http://localhost:8080/?type=app&id=918&tenant=1&pageid=NTJ4MjvqoVj8OVsc`
`url search`参数:
@ -75,23 +48,6 @@ pnpm run build:plugin
# 构建设计器
pnpm run build:alpha 或 build:prod
```
构建后产物所在文件夹
```
tiny-engine/packages/design-core/dist/
```
## 里程碑
```mermaid
gantt
dateFormat YYYY-MM-DD
axisFormat %Y-%m-%d
1.0.0-beta.x version :active,2023-09-25, 2024-03-31
1.0.0-rc version : 2024-04-01, 2024-06-30
1.0.0 version : 2024-07-01, 2024-07-31
```
## 🤝 参与贡献

View File

@ -26,7 +26,7 @@ const cacheStringFunction = (fn) => {
}
const hyphenateRE = /\B([A-Z])/g
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase())
const _export_sfc = (sfc, props) => {
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc
for (const [key, val] of props) {
target[key] = val
@ -73,7 +73,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
])
)
}
const block = /* @__PURE__ */ _export_sfc(_sfc_main, [
var block = /* @__PURE__ */ _export_sfc(_sfc_main, [
['render', _sfc_render],
['__file', 'D:/tmp/buildground/buildground_1673597935715/src/block/generated/components/PortalBlock.vue']
])

View File

@ -11,27 +11,25 @@
*/
;(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory(
require('@opentiny/tiny-engine-webcomponent-core'),
require('vue'),
require('vue-i18n'),
require('@opentiny/vue-icon')
)
} else if (typeof define === 'function' && define.amd) {
define(['@opentiny/tiny-engine-webcomponent-core', 'vue', 'vue-i18n', '@opentiny/vue-icon'], factory)
} else {
;(global = typeof globalThis !== 'undefined' ? globalThis : global || self),
(global.TinyVueBlock = factory(global.TinyWebcomponentCore, global.Vue, global.VueI18n, global.TinyVueIcon))
}
})(this, (tinyWebcomponentCore, vue, vueI18n, tinyVue3Icon) => {
typeof exports === 'object' && typeof module !== 'undefined'
? (module.exports = factory(
require('@opentiny/tiny-engine-webcomponent-core'),
require('vue'),
require('vue-i18n'),
require('@opentiny/vue-icon')
))
: typeof define === 'function' && define.amd
? define(['@opentiny/tiny-engine-webcomponent-core', 'vue', 'vue-i18n', '@opentiny/vue-icon'], factory)
: ((global = typeof globalThis !== 'undefined' ? globalThis : global || self),
(global.TinyVueBlock = factory(global.TinyWebcomponentCore, global.Vue, global.VueI18n, global.TinyVueIcon)))
})(this, function (tinyWebcomponentCore, vue, vueI18n, tinyVue3Icon) {
function _interopNamespace(e) {
if (e && e.__esModule) return e
const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } })
var n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } })
if (e) {
Object.keys(e).forEach((k) => {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
const d = Object.getOwnPropertyDescriptor(e, k)
var d = Object.getOwnPropertyDescriptor(e, k)
Object.defineProperty(
n,
k,
@ -47,10 +45,10 @@
}
})
}
n.default = e
n['default'] = e
return Object.freeze(n)
}
const vue__namespace = /* @__PURE__ */ _interopNamespace(vue)
var vue__namespace = /* @__PURE__ */ _interopNamespace(vue)
Object.freeze({})
Object.freeze([])
const cacheStringFunction = (fn) => {
@ -62,7 +60,7 @@
}
const hyphenateRE = /\B([A-Z])/g
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase())
const _export_sfc = (sfc, props) => {
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc
for (const [key, val] of props) {
target[key] = val
@ -114,7 +112,7 @@
])
)
}
const block = /* @__PURE__ */ _export_sfc(_sfc_main, [
var block = /* @__PURE__ */ _export_sfc(_sfc_main, [
['render', _sfc_render],
['__file', 'D:/tmp/buildground/buildground_1673597935715/src/block/generated/components/PortalBlock.vue']
])

View File

@ -76,6 +76,6 @@ const Mapper = {
TinyDropdown: Dropdown,
TinyChartHistogram: ChartHistogram
}
Mapper.TinyTabs.isGroup = true
Mapper.TinyGrid.isGroup = true
Mapper['TinyTabs'].isGroup = true
Mapper['TinyGrid'].isGroup = true
export { Mapper as default }

View File

@ -1,25 +1,23 @@
/**
* 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.
*
*/
;(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory(require('@opentiny/vue'))
} else if (typeof define === 'function' && define.amd) {
define(['@opentiny/vue'], factory)
} else {
;(global = typeof globalThis !== 'undefined' ? globalThis : global || self),
(global.TinyLowcodeComponent = factory(global.TinyVue))
}
})(this, (tinyVue3) => {
typeof exports === 'object' && typeof module !== 'undefined'
? (module.exports = factory(require('@opentiny/vue')))
: typeof define === 'function' && define.amd
? define(['@opentiny/vue'], factory)
: ((global = typeof globalThis !== 'undefined' ? globalThis : global || self),
(global.TinyLowcodeComponent = factory(global.TinyVue)))
})(this, function (tinyVue3) {
'use strict'
const Mapper = {
TinyCarouselItem: tinyVue3.CarouselItem,
@ -54,7 +52,7 @@
TinyDropdown: tinyVue3.Dropdown,
TinyChartHistogram: tinyVue3.ChartHistogram
}
Mapper.TinyTabs.isGroup = true
Mapper.TinyGrid.isGroup = true
Mapper['TinyTabs'].isGroup = true
Mapper['TinyGrid'].isGroup = true
return Mapper
})

View File

@ -42,9 +42,9 @@ const cacheStringFunction = (fn) => {
}
const hyphenateRE = /\B([A-Z])/g
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1').toLowerCase())
const _style_0 =
var _style_0 =
'\n.team-list-item.active[data-v-b66e3972] {\r\n border: 1px solid #38acff;\n}\n.toolbars-item[data-v-b66e3972]:hover {\r\n cursor: pointer;\r\n background-color: #f1f2f3;\n}\n.toolbars-item.active[data-v-b66e3972] {\r\n background-color: #e5e6e8;\n}\n'
const _export_sfc = (sfc, props) => {
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc
for (const [key, val] of props) {
target[key] = val
@ -406,7 +406,7 @@ const _hoisted_22 = /* @__PURE__ */ _withScopeId(() =>
)
)
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
let _a
var _a
const _component_tiny_icon_setting = resolveComponent('tiny-icon-setting')
const _component_tiny_tooltip = resolveComponent('tiny-tooltip')
const _component_tiny_icon_check_out = resolveComponent('tiny-icon-check-out')
@ -640,7 +640,7 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
])
)
}
const block = /* @__PURE__ */ _export_sfc(_sfc_main, [
var block = /* @__PURE__ */ _export_sfc(_sfc_main, [
['render', _sfc_render],
['styles', [_style_0]],
['__scopeId', 'data-v-b66e3972'],

View File

@ -11,41 +11,36 @@
*/
;(function (global, factory) {
if (typeof exports === 'object' && typeof module !== 'undefined') {
module.exports = factory(
require('@opentiny/tiny-engine-webcomponent-core'),
require('vue'),
require('vue-i18n'),
require('@opentiny/vue-icon'),
require('@opentiny/vue')
)
} else if (typeof define === 'function ' && define.amd) {
define([
'@opentiny/tiny-engine-webcomponent-core',
'vue',
'vue-i18n',
'@opentiny/vue-icon',
'@opentiny/vue'
], factory)
} else {
;(global = typeof globalThis !== 'undefined' ? globalThis : global || self),
typeof exports === 'object ' && typeof module !== 'undefined '
? (module.exports = factory(
require('@opentiny/tiny-engine-webcomponent-core '),
require('vue '),
require('vue-i18n '),
require('@opentiny/vue-icon '),
require('@opentiny/vue ')
))
: typeof define === 'function ' && define.amd
? define(
['@opentiny/tiny-engine-webcomponent-core ', 'vue ', 'vue-i18n ', '@opentiny/vue-icon ', '@opentiny/vue '],
factory
)
: ((global = typeof globalThis !== 'undefined ' ? globalThis : global || self),
(global.TinyVueBlock = factory(
global.TinyWebcomponentCore,
global.Vue,
global.VueI18n,
global.TinyVueIcon,
global.TinyVue
))
}
})(this, (tinyWebcomponentCore, vue, vueI18n, tinyVue3Icon, tinyVue3) => {
)))
})(this, function (tinyWebcomponentCore, vue, vueI18n, tinyVue3Icon, tinyVue3) {
'use strict '
function _interopNamespace(e) {
if (e && e.__esModule) return e
const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module ' } })
var n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module ' } })
if (e) {
Object.keys(e).forEach((k) => {
Object.keys(e).forEach(function (k) {
if (k !== 'default ') {
const d = Object.getOwnPropertyDescriptor(e, k)
var d = Object.getOwnPropertyDescriptor(e, k)
Object.defineProperty(
n,
k,
@ -64,7 +59,7 @@
n['default '] = e
return Object.freeze(n)
}
const vue__namespace = /* @__PURE__ */ _interopNamespace(vue)
var vue__namespace = /* @__PURE__ */ _interopNamespace(vue)
Object.freeze({})
Object.freeze([])
const cacheStringFunction = (fn) => {
@ -76,9 +71,9 @@
}
const hyphenateRE = /\B([A-Z])/g
const hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, '-$1 ').toLowerCase())
const _style_0 =
var _style_0 =
'\n.team-list-item.active[data-v-b66e3972] {\r\n border: 1px solid #38acff;\n}\n.toolbars-item[data-v-b66e3972]:hover {\r\n cursor: pointer;\r\n background-color: #f1f2f3;\n}\n.toolbars-item.active[data-v-b66e3972] {\r\n background-color: #e5e6e8;\n}\n '
const _export_sfc = (sfc, props) => {
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc
for (const [key, val] of props) {
target[key] = val
@ -451,7 +446,7 @@
)
)
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
let _a
var _a
const _component_tiny_icon_setting = vue.resolveComponent('tiny-icon-setting ')
const _component_tiny_tooltip = vue.resolveComponent('tiny-tooltip ')
const _component_tiny_icon_check_out = vue.resolveComponent('tiny-icon-check-out ')
@ -693,7 +688,7 @@
])
)
}
const block = /* @__PURE__ */ _export_sfc(_sfc_main, [
var block = /* @__PURE__ */ _export_sfc(_sfc_main, [
['render ', _sfc_render],
['styles ', [_style_0]],
['__scopeId ', 'data-v-b66e3972 '],

View File

@ -6065,7 +6065,7 @@
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "TabItem",
"exportName": "TinyTabItem",
"version": "",
"destructuring": true
},
@ -6314,7 +6314,7 @@
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "BreadcrumbItem",
"exportName": "TinyBreadcrumbItem",
"version": "",
"destructuring": true
},

View File

@ -1,16 +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.
*
*/
module.exports = {
port: process.env.MOCK_PORT || 9090,
env: process.env.NODE_ENV || 'development' // Current mode
}

View File

@ -1,21 +1,12 @@
{
"name": "@opentiny/tiny-engine-mock",
"version": "1.0.3",
"version": "1.0.1-beta.0",
"publishConfig": {
"access": "public"
},
"description": "mock服务",
"repository": {
"type": "git",
"url": "https://github.com/opentiny/tiny-engine",
"directory": "mockServer"
},
"bugs": {
"url": "https://github.com/opentiny/tiny-engine/issues"
},
"author": "OpenTiny Team",
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"author": "opentiny",
"license": "WTFPL",
"scripts": {
"start": "gulp nodemon",
"dev": "gulp",
@ -25,7 +16,7 @@
"lint": "eslint --fix ."
},
"dependencies": {
"@opentiny/tiny-engine-dsl-vue": "^1.0.3",
"@opentiny/tiny-engine-dsl-vue": "^1.0.2-beta.0",
"@seald-io/nedb": "^4.0.2",
"fs-extra": "^11.1.1",
"glob": "^10.3.4",

View File

@ -14,12 +14,13 @@ import Koa2 from 'koa'
import KoaBody from 'koa-body'
import KoaStatic from 'koa-static2'
import path from 'path'
import { env, port } from '../config/config'
import MainRoutes from './routes/main-routes'
import ErrorRoutesCatch from './middleware/ErrorRoutesCatch'
import ErrorRoutes from './routes/error-routes'
import MainRoutes from './routes/main-routes'
const app = new Koa2()
const env = process.env.NODE_ENV || 'development' // Current mode
const PORT = 9090
app
.use((ctx, next) => {
ctx.set('Access-Control-Allow-Origin', '*')
@ -56,6 +57,6 @@ if (env === 'development') {
})
}
app.listen(port)
app.listen(PORT)
export default app

View File

@ -102,7 +102,7 @@
"type": "function",
"content": {
"type": "JSFunction",
"value": "function test() {\r\n return 'test'\r\n}"
"value": "function util() {\r\n console.log(3211)\r\n}"
},
"app": 918,
"category": "utils",

View File

@ -15,674 +15,7 @@
"gmt_modified": "2023-08-23 10:22:28"
},
"dataSource": {
"list": [
{
"id": 132,
"name": "getAllComponent",
"data": {
"data": [],
"type": "array"
},
"tpl": null,
"app": "918",
"desc": null,
"created_at": "2022-06-28T06:26:26.000Z",
"updated_at": "2022-06-28T07:02:30.000Z"
},
{
"id": 133,
"name": "getAllList",
"data": {
"columns": [
{
"name": "test",
"title": "测试",
"field": "test",
"type": "string",
"format": {}
},
{
"name": "test1",
"title": "测试1",
"field": "test1",
"type": "string",
"format": {}
}
],
"type": "array",
"data": [
{
"test": "test1",
"test1": "test1",
"_id": "341efc48"
},
{
"test": "test2",
"test1": "test1",
"_id": "b86b516c"
},
{
"test": "test3",
"test1": "test1",
"_id": "f680cd78"
}
],
"options": {
"uri": "",
"method": "GET"
},
"dataHandler": {
"type": "JSFunction",
"value": "function dataHandler(data) { \n return data \n}"
},
"willFetch": {
"type": "JSFunction",
"value": "function willFetch(option) {\n return option \n}"
},
"shouldFetch": {
"type": "JSFunction",
"value": "function shouldFetch(option) {\n return true \n}"
},
"errorHandler": {
"type": "JSFunction",
"value": "function errorHandler(err) {}"
}
},
"tpl": null,
"app": "918",
"desc": null,
"created_at": "2022-06-28T07:32:16.000Z",
"updated_at": "2023-01-19T03:29:11.000Z"
},
{
"id": 135,
"name": "getAllMaterialList",
"data": {
"columns": [
{
"name": "id",
"title": "id",
"field": "id",
"type": "string",
"format": {}
},
{
"name": "name",
"title": "name",
"field": "name",
"type": "string",
"format": {}
},
{
"name": "framework",
"title": "framework",
"field": "framework",
"type": "string",
"format": {
"required": true
}
},
{
"name": "components",
"title": "components",
"field": "components",
"type": "string",
"format": {}
},
{
"name": "content",
"title": "content",
"field": "content",
"type": "string",
"format": {}
},
{
"name": "url",
"title": "url",
"field": "url",
"type": "string",
"format": {}
},
{
"name": "published_at",
"title": "published_at",
"field": "published_at",
"type": "string",
"format": {}
},
{
"name": "created_at",
"title": "created_at",
"field": "created_at",
"type": "string",
"format": {}
},
{
"name": "updated_at",
"title": "updated_at",
"field": "updated_at",
"type": "string",
"format": {}
},
{
"name": "published",
"title": "published",
"field": "published",
"type": "string",
"format": {}
},
{
"name": "last_build_info",
"title": "last_build_info",
"field": "last_build_info",
"type": "string",
"format": {}
},
{
"name": "tenant",
"title": "tenant",
"field": "tenant",
"type": "string",
"format": {}
},
{
"name": "version",
"title": "version",
"field": "version",
"type": "string",
"format": {}
},
{
"name": "description",
"title": "description",
"field": "description",
"type": "string",
"format": {}
}
],
"type": "array",
"data": [
{
"id": "f37123ec",
"url": "",
"name": "ng-material",
"tenant": "",
"content": "",
"version": "1.0.0",
"framework": "Angular",
"published": "",
"components": "",
"created_at": "2021-11-02T11:32:22.000Z",
"updated_at": "2021-11-02T11:32:22.000Z",
"description": "angular组件库物料",
"published_at": "2021-11-02T11:32:22.000Z",
"last_build_info": "",
"_id": "2a23e653"
},
{
"id": "f37123ec",
"url": "",
"name": "ng-material",
"tenant": "",
"content": "",
"version": "1.0.0",
"framework": "Angular",
"published": "",
"components": "",
"created_at": "2021-11-02T11:32:22.000Z",
"updated_at": "2021-11-02T11:32:22.000Z",
"description": "angular组件库物料",
"published_at": "2021-11-02T11:32:22.000Z",
"last_build_info": "",
"_id": "06b253be"
},
{
"id": "f37123ec",
"url": "",
"name": "ng-material",
"tenant": "",
"content": "",
"version": "1.0.0",
"framework": "Angular",
"published": "",
"components": "",
"created_at": "2021-11-02T11:32:22.000Z",
"updated_at": "2021-11-02T11:32:22.000Z",
"description": "angular组件库物料",
"published_at": "2021-11-02T11:32:22.000Z",
"last_build_info": "",
"_id": "c55a41ed"
},
{
"id": "f37123ec",
"url": "",
"name": "ng-material",
"tenant": "",
"content": "",
"version": "1.0.0",
"framework": "Angular",
"published": "",
"components": "",
"created_at": "2021-11-02T11:32:22.000Z",
"updated_at": "2021-11-02T11:32:22.000Z",
"description": "angular组件库物料",
"published_at": "2021-11-02T11:32:22.000Z",
"last_build_info": "",
"_id": "f37123ec"
},
{
"id": "7a63c1a2",
"url": "",
"name": "tiny-vue",
"tenant": "",
"content": "Tiny Vue物料",
"version": "1.0.0",
"framework": "Vue",
"published": "",
"components": "",
"created_at": "",
"updated_at": "",
"description": "Tiny Vue物料",
"published_at": "",
"last_build_info": "",
"_id": "7a63c1a2"
}
],
"options": {
"uri": "",
"method": "GET"
},
"willFetch": {
"type": "JSFunction",
"value": "function willFetch(option) {\n return option \n}"
},
"dataHandler": {
"type": "JSFunction",
"value": "function dataHandler(data) { \n return data \n}"
},
"shouldFetch": {
"type": "JSFunction",
"value": "function shouldFetch(option) {\n return true \n}"
},
"errorHandler": {
"type": "JSFunction",
"value": "function errorHandler(err) {}"
}
},
"tpl": null,
"app": "918",
"desc": null,
"created_at": "2022-06-29T00:57:50.000Z",
"updated_at": "2023-05-15T02:37:12.000Z"
},
{
"id": 139,
"name": "treedata",
"data": {
"data": [
{
"label": "level111",
"value": "111",
"id": "f6609643",
"pid": "",
"_RID": "row_4"
},
{
"label": "level1-son",
"value": "111-1",
"id": "af1f937f",
"pid": "f6609643",
"_RID": "row_5"
},
{
"label": "level222",
"value": "222",
"id": "28e3709c",
"pid": "",
"_RID": "row_6"
},
{
"label": "level2-son",
"value": "222-1",
"id": "6b571bef",
"pid": "28e3709c",
"_RID": "row_5"
},
{
"id": "6317c2cc",
"pid": "fdfa",
"label": "fsdfaa",
"value": "fsadf",
"_RID": "row_6"
},
{
"id": "9cce369f",
"pid": "test",
"label": "test1",
"value": "001"
}
],
"type": "tree"
},
"tpl": null,
"app": "918",
"desc": null,
"created_at": "2022-06-30T06:13:57.000Z",
"updated_at": "2022-07-29T03:14:55.000Z"
},
{
"id": 150,
"name": "componentList",
"data": {
"data": [
{
"_RID": "row_1",
"name": "表单",
"isSelected": "true",
"description": "由按钮、输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据"
},
{
"name": "按钮",
"isSelected": "false",
"description": "常用的操作按钮,提供包括默认按钮、图标按钮、图片按钮、下拉按钮等类型"
},
{
"id": "490f8a00",
"_RID": "row_3",
"name": "表单项",
"framework": "",
"materials": "",
"description": "Form 组件下的 FormItem 配置"
},
{
"id": "c259b8b3",
"_RID": "row_4",
"name": "开关",
"framework": "",
"materials": "",
"description": "关闭或打开"
},
{
"id": "083ed9c7",
"_RID": "row_5",
"name": "互斥按钮组",
"framework": "",
"materials": "",
"description": "以按钮组的方式出现,常用于多项类似操作"
},
{
"id": "09136cea",
"_RID": "row_6",
"name": "提示框",
"framework": "",
"materials": "",
"description": "Popover可通过对一个触发源操作触发弹出框,支持自定义弹出内容,延迟触发和渐变动画"
},
{
"id": "a63b57d5",
"_RID": "row_7",
"name": "文字提示框",
"framework": "",
"materials": "",
"description": "动态显示提示信息,一般通过鼠标事件进行响应;提供 warning、error、info、success 四种类型显示不同类别的信"
},
{
"id": "a0f6e8a3",
"_RID": "row_8",
"name": "树",
"framework": "",
"materials": "",
"description": "可进行展示有父子层级的数据,支持选择,异步加载等功能。但不推荐用它来展示菜单,展示菜单推荐使用树菜单"
},
{
"id": "d1aa18fc",
"_RID": "row_9",
"name": "分页",
"framework": "",
"materials": "",
"description": "当数据量过多时,使用分页分解数据,常用于 Grid 和 Repeater 组件"
},
{
"id": "ca49cc52",
"_RID": "row_10",
"name": "表格",
"framework": "",
"materials": "",
"description": "提供了非常强大数据表格功能,可以展示数据列表,可以对数据列表进行选择、编辑等"
},
{
"id": "4e20ecc9",
"name": "搜索框",
"framework": "",
"materials": "",
"description": "指定条件对象进行搜索数据"
},
{
"id": "6b093ee5",
"name": "折叠面板",
"framework": "",
"materials": "",
"description": "内容区可指定动态页面或自定义 html 等,支持展开收起操作"
},
{
"id": "0a09abc0",
"name": "对话框",
"framework": "",
"materials": "",
"description": "模态对话框,在浮层中显示,引导用户进行相关操作"
},
{
"id": "f814b901",
"name": "标签页签项",
"framework": "",
"materials": "",
"description": "tab页签"
},
{
"id": "c5ae797c",
"name": "单选",
"framework": "",
"materials": "",
"description": "用于配置不同场景的选项,在一组备选项中进行单选"
},
{
"id": "33d0c590",
"_RID": "row_13",
"name": "弹出编辑",
"framework": "",
"materials": "",
"description": "该组件只能在弹出的面板中选择数据,不能手动输入数据;弹出面板中显示为 Tree 组件或者 Grid 组件"
},
{
"id": "16711dfa",
"_RID": "row_14",
"name": "下拉框",
"framework": "",
"materials": "",
"description": "Select 选择器是一种通过点击弹出下拉列表展示数据并进行选择的 UI 组件"
},
{
"id": "a9fd190a",
"_RID": "row_15",
"name": "折叠面板项",
"framework": "",
"materials": "",
"description": "内容区可指定动态页面或自定义 html 等,支持展开收起操作"
},
{
"id": "a7dfa9ec",
"_RID": "row_16",
"name": "复选框",
"framework": "",
"materials": "",
"description": "用于配置不同场景的选项,提供用户可在一组选项中进行多选"
},
{
"id": "d4bb8330",
"name": "输入框",
"framework": "",
"materials": "",
"description": "通过鼠标或键盘输入字符"
},
{
"id": "ced3dc83",
"name": "时间线",
"framework": "",
"materials": "",
"description": "时间线"
}
],
"type": "array",
"columns": [
{
"name": "name",
"type": "string",
"field": "name",
"title": "name",
"format": {
"max": 0,
"min": 0,
"dateTime": false,
"required": false,
"stringType": ""
}
},
{
"name": "description",
"type": "string",
"field": "description",
"title": "description",
"format": {
"max": 0,
"min": 0,
"dateTime": false,
"required": false,
"stringType": ""
}
},
{
"name": "isSelected",
"type": "string",
"field": "isSelected",
"title": "isSelected",
"format": {
"max": 0,
"min": 0,
"dateTime": false,
"required": false,
"stringType": ""
}
}
],
"options": {
"uri": "http://localhost:9090/assets/json/bundle.json",
"method": "GET"
},
"willFetch": {
"type": "JSFunction",
"value": "function willFetch(option) {\n return option \n}"
},
"dataHandler": {
"type": "JSFunction",
"value": "function dataHandler(data) { \n return data \n}"
},
"shouldFetch": {
"type": "JSFunction",
"value": "function shouldFetch(option) {\n return true \n}"
},
"errorHandler": {
"type": "JSFunction",
"value": "function errorHandler(err) {}"
}
},
"tpl": null,
"app": "918",
"desc": null,
"created_at": "2022-07-04T02:20:07.000Z",
"updated_at": "2022-07-04T06:25:29.000Z"
},
{
"id": 151,
"name": "selectedComponents",
"data": {
"columns": [
{
"name": "name",
"title": "name",
"field": "name",
"type": "string",
"format": {
"required": false,
"stringType": "",
"min": 0,
"max": 0,
"dateTime": false
}
},
{
"name": "description",
"title": "description",
"field": "description",
"type": "string",
"format": {
"required": false,
"stringType": "",
"min": 0,
"max": 0,
"dateTime": false
}
},
{
"name": "isSelected",
"title": "isSelected",
"field": "isSelected",
"type": "string",
"format": {
"required": false,
"stringType": "",
"min": 0,
"max": 0,
"dateTime": false
}
}
],
"type": "array",
"data": [
{
"name": "标签页",
"description": "分隔内容上有关联但属于不同类别的数据集合",
"isSelected": "true",
"_RID": "row_2"
},
{
"name": "布局列",
"description": "列配置信息",
"isSelected": "true",
"id": "76a7080a",
"_RID": "row_4"
},
{
"name": "日期选择器",
"description": "用于设置/选择日期,包括年月/年月日/年月日时分/年月日时分秒日期格式",
"isSelected": "true",
"id": "76b20d73",
"_RID": "row_1"
},
{
"name": "走马灯",
"description": "常用于一组图片或卡片轮播,当内容空间不足时,可以用走马灯的形式进行收纳,进行轮播展现",
"isSelected": "true",
"id": "4c884c3d"
}
]
},
"tpl": null,
"app": "918",
"desc": null,
"created_at": "2022-07-04T03:04:05.000Z",
"updated_at": "2022-07-04T03:43:40.000Z"
}
],
"list": [],
"dataHandler": {
"type": "JSFunction",
"value": "function dataHanlder(res){\n return res;\n}"
@ -769,7 +102,7 @@
{
"componentName": "TinyTimeLine",
"props": {
"active": 2,
"active": "2",
"data": [
{
"name": "基础配置"
@ -1525,7 +858,7 @@
{
"componentName": "div",
"props": {
"style": "border-width: 1px;z-index:1;border-style: solid; border-color: #ffffff; padding-top: 10px; padding-left: 10px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px; background-color: #fff; position: fixed; inset: auto 0% 0% 0%; height: 80px; line-height: 80px; border-radius: 0px;"
"style": "border-width: 1px; border-style: solid; border-color: #ffffff; padding-top: 10px; padding-left: 10px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px; background-color: #fff; position: fixed; inset: auto 0% 0% 0%; height: 80px; line-height: 80px; border-radius: 0px;"
},
"children": [
{
@ -1550,7 +883,7 @@
{
"componentName": "TinyCol",
"props": {
"span": 8
"span": "8"
},
"id": "b9d051a5",
"children": [
@ -1563,7 +896,7 @@
{
"componentName": "TinyCol",
"props": {
"span": 5,
"span": "5",
"style": "display: flex;"
},
"id": "02352776",
@ -1597,7 +930,7 @@
{
"componentName": "TinyCol",
"props": {
"span": 7
"span": "7"
},
"id": "82b6c659",
"children": [
@ -1665,7 +998,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 +1023,7 @@
],
"fileName": "createVm",
"meta": {
"id": "NTJ4MjvqoVj8OVsc",
"id": 1977,
"parentId": "0",
"group": "staticPages",
"occupier": {
@ -1787,7 +1120,7 @@
},
{
"componentName": "TinyPlusFrozenPage",
"package": "@opentiny/vue",
"package": "@opentiny/vuee",
"exportName": "FrozenPage",
"destructuring": true,
"version": "3.4.1"
@ -1925,13 +1258,6 @@
"destructuring": true,
"version": "0.1.16"
},
{
"componentName": "TinyButton",
"package": "@opentiny/vue",
"exportName": "Button",
"destructuring": true,
"version": "0.1.16"
},
{
"componentName": "TinyButtonGroup",
"package": "@opentiny/vue",
@ -1974,48 +1300,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",
@ -2120,7 +1404,7 @@
"type": "function",
"content": {
"type": "JSFunction",
"value": "function test() {\r\n return 'test'\r\n}"
"value": "function util() {\r\n console.log(3211)\r\n}"
}
},
{

View File

@ -10,13 +10,13 @@
*
*/
import fs from 'fs-extra'
import * as glob from 'glob'
import KoaRouter from 'koa-router'
import * as glob from 'glob'
import path from 'path'
import fs from 'fs-extra'
import MockService from '../services/mockService'
import { getResponseData } from '../tool/Common'
import { getResponseData } from '../tool/Common'
const router = new KoaRouter()
export const mockService = new MockService()
const getJsonPathData = (jpath, method = 'get') => {
@ -140,9 +140,7 @@ router.get('/material-center/api/block-groups/delete/:id', async (ctx) => {
router.get('/material-center/api/block-groups', async (ctx) => {
const result = await mockService.blockGroupService.find(ctx.query)
let blockGroup
if (result.data.length === 0) {
ctx.body = result
} else if (result.data.length > 1) {
if (result.data.length > 1) {
blockGroup = await Promise.all(
result.data.map(async (group) => {
group.blocks = await Promise.all(
@ -155,7 +153,7 @@ router.get('/material-center/api/block-groups', async (ctx) => {
})
)
ctx.body = getResponseData(blockGroup)
} else if (result.data.length === 1) {
} else {
blockGroup = result.data[0]
const blocks = await Promise.all(
blockGroup.blocks.map(async (item) => {

View File

@ -31,7 +31,7 @@ export default class AppService {
// 拼装数据源
const dataSource = {
list: source,
dataHandler: app.data_handler
dataHandler: app['data_handler']
}
// 拼装工具类
const utils = []
@ -47,7 +47,7 @@ export default class AppService {
const i18n = this.formatI18nEntrites(entriesData)
return getResponseData({
dataSource,
globalState: app.global_state,
globalState: app['global_state'],
utils,
i18n
})

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,7 @@ export default class BlockCategoryService {
const categories = await this.db.findOneAsync({ _id: id })
categories.blocks.push(params._id)
await this.db.updateAsync({ _id: id }, { $set: categories })
return getResponseData(categories)
return
}
params.app = appinfo.app
await this.db.updateAsync({ _id: id }, { $set: params })

View File

@ -84,7 +84,7 @@ export default class Schema2CodeServcice {
return components.map((component) => {
const {
component: componentName,
npm: { package: packageName, exportName, version, destructuring } = {}
npm: { package: packageName, exportName, version, destructuring }
} = component
return {
componentName,

View File

@ -3,7 +3,7 @@
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "pnpm run setup && concurrently 'pnpm:serve:backend' 'pnpm:serve:frontend'",
"dev": "concurrently 'pnpm:serve:backend' 'pnpm:serve:frontend'",
"dev:mock": "pnpm --filter @opentiny/tiny-engine dev",
"serve:frontend": "pnpm --filter @opentiny/tiny-engine serve",
"serve:backend": "pnpm --filter @opentiny/tiny-engine-mock dev",
@ -18,10 +18,7 @@
"pub:premajor": "pnpm run build:plugin && pnpm run build:alpha && pnpm lerna version premajor --preid beta --no-push --yes && lerna publish from-package --pre-dist-tag beta --yes",
"pub:preminor": "pnpm run build:plugin && pnpm run build:alpha && pnpm lerna version preminor --preid beta --no-push --yes && lerna publish from-package --pre-dist-tag beta --yes",
"pub:prepatch": "pnpm run build:plugin && pnpm run build:alpha && pnpm lerna version prepatch --preid beta --no-push --yes && lerna publish from-package --pre-dist-tag beta --yes",
"pub:prerelease": "pnpm run build:plugin && pnpm run build:alpha && pnpm lerna version prerelease --preid beta --no-push --yes && lerna publish from-package --pre-dist-tag beta --yes",
"setup": "node ./scripts/setup.js",
"splitMaterials": "node ./scripts/splitMaterials.mjs",
"buildMaterials": "node ./scripts/buildMaterials.mjs"
"pub:prerelease": "pnpm run build:plugin && pnpm run build:alpha && pnpm lerna version prerelease --preid beta --no-push --yes && lerna publish from-package --pre-dist-tag beta --yes"
},
"devDependencies": {
"@babel/eslint-parser": "^7.21.3",
@ -29,24 +26,19 @@
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@types/node": "^18.0.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vitejs/plugin-vue-jsx": "^1.3.2",
"assert": "^2.0.0",
"buffer": "^6.0.3",
"chokidar": "^3.5.3",
"concurrently": "^8.2.0",
"cross-env": "^7.0.3",
"dotenv": "^16.3.1",
"eslint": "^8.38.0",
"eslint-plugin-vue": "^8.0.0",
"fast-glob": "^3.3.2",
"fs-extra": "^10.1.0",
"husky": "^8.0.0",
"concurrently": "^8.2.0",
"lerna": "^7.2.0",
"less": "^4.1.2",
"lint-staged": "^13.2.0",
"mysql": "^2.18.1",
"path": "^0.12.7",
"picocolors": "^1.0.0",
"rimraf": "^3.0.2",
"rollup-plugin-polyfill-node": "^0.12.0",
"rollup-plugin-terser": "^7.0.2",
@ -65,10 +57,5 @@
"engines": {
"node": ">=14",
"pnpm": ">=7"
},
"pnpm": {
"patchedDependencies": {
"@vue/repl@2.9.0": "patches/@vue__repl@2.9.0.patch"
}
}
}

View File

@ -1,12 +0,0 @@
# @opentiny/tiny-engine-blockToWebComponentTemplate
将区块转换成 webComponent使得不同技术栈的区块可以统一在 vue 的画布上面运行
## 使用
- 后端拉取 template
- 将区块 schema 转换成 高代码,并写入 src 文件夹中
- 写入 lib.js替换 BlockFileName 为实际出码的文件名
- 执行 `pnpm install` 安装依赖
- 运行 `pnpm run build:block` 命令
- 得到 webcomponent 转换产物

View File

@ -1,47 +0,0 @@
{
"name": "@opentiny/tiny-engine-block-build",
"version": "1.0.2",
"description": "translate block to webcomponent template",
"main": "./dist/web-components.es.js",
"scripts": {
"build:block": "vite build --mode block"
},
"keywords": [
"vue",
"vue3",
"frontend",
"opentiny",
"lowcode",
"tiny-engine",
"webComponent"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/opentiny/tiny-engine",
"directory": "packages/builtinComponent"
},
"bugs": {
"url": "https://github.com/opentiny/tiny-engine/issues"
},
"author": "OpenTiny Team",
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"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",
"@vue/shared": "^3.3.11",
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.5.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"vite": "^4.3.7"
}
}

View File

@ -1,46 +0,0 @@
<template>
<div>
<tiny-popover class="block-link-field" popper-class="option-popper block-new-attr-popover">
<tiny-input placeholder="请输入字段名称"></tiny-input>
</tiny-popover>
<div>
<slot name="menu" :title="state.title">
<span>TinyEngine 前端可视化设计器为设计器开发者提供定制服务在线构建出自己专属的设计器</span>
</slot>
</div>
</div>
</template>
<script setup>
import * as vue from 'vue'
import { defineProps, defineEmits } from 'vue'
import { I18nInjectionKey } from 'vue-i18n'
import { Input as TinyInput, Popover as TinyPopover } from '@opentiny/vue'
const props = defineProps({ testSlot: { type: Object, default: () => ({}) } })
const emit = defineEmits([])
const { t, lowcodeWrap, stores } = vue.inject(I18nInjectionKey).lowcode()
const wrap = lowcodeWrap(props, { emit }, t)
const state = vue.reactive({
title: 'test slot params'
})
wrap({
stores,
state
})
</script>
<style scoped>
body {
background-color: #fff;
}
.test {
width: 100px;
padding: 10px;
margin: 10px;
color: #191919;
}
</style>

View File

@ -1,20 +0,0 @@
import { hyphenate } from '@vue/shared'
import { defineCustomElement } from '@opentiny/tiny-engine-webcomponent-core'
import block from './BlockFileName.vue'
window.TinyLowcodeResource = window.TinyLowcodeResource || {}
const blockName = hyphenate('BlockFileName')
if (customElements.get(blockName)) {
if (window.TinyLowcodeResource[blockName]) {
Object.assign(window.TinyLowcodeResource[blockName], block)
}
} else {
block.links = process.env.VUE_APP_UI_LIB_FULL_STYLE_FILE_URL
block.styles = ['svg { width: 10px; height: 10px;}', ...(block.styles || [])]
window.TinyLowcodeResource[blockName] = block
customElements.define(blockName, defineCustomElement(block))
}
export default block

View File

@ -1,66 +0,0 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import path from 'path'
const config = {
define: {},
resolve: {
alias: {}
},
build: {
cssCodeSplit: false,
minify: false,
commonjsOptions: {
transformMixedEsModules: true
},
rollupOptions: {
external: [
'vue',
'vue-i18n',
'@opentiny/tiny-engine-i18n-host',
'@opentiny/tiny-engine-webcomponent-core',
'@opentiny/vue',
'@opentiny/vue-icon'
],
output: {
globals: {
vue: 'Vue',
'vue-i18n': 'VueI18n',
'@opentiny/tiny-engine-i18n-host': 'TinyI18nHost',
'@opentiny/tiny-engine-webcomponent-core': 'TinyWebcomponentCore',
'@opentiny/vue': 'TinyVue',
'@opentiny/vue-icon': 'TinyVueIcon'
}
}
}
}
}
export default defineConfig(({ command, mode }) => {
if (command !== 'build' || mode !== 'block') {
return config
}
const vuePluginConfig = {}
const styleLinks = ['https://npm.onmicrosoft.cn/@opentiny/vue-theme@3.14/index.css']
config.publicDir = false
config.build.lib = {
entry: path.resolve(__dirname, './src/lib.js'),
name: 'TinyVueBlock',
formats: ['umd', 'es'],
fileName: (format) => `js/web-component.${format}.js`
}
vuePluginConfig.customElement = true
config.plugins = [vue(vuePluginConfig), vueJsx()]
config.define['process.env'] = {
VUE_APP_UI_LIB_FULL_STYLE_FILE_URL: styleLinks
}
return config
})

View File

@ -1,9 +0,0 @@
# 内置组件
## 目前内置的组件
### CanvasRow
### CanvasCol
### CanvasRowColContainer

View File

@ -1,4 +0,0 @@
export { default as CanvasCol } from './src/components/CanvasCol.vue'
export { default as CanvasRow } from './src/components/CanvasRow.vue'
export { default as CanvasRowColContainer } from './src/components/CanvasRowColContainer.vue'
export { default as meta } from './src/meta'

View File

@ -1,33 +0,0 @@
{
"name": "@opentiny/tiny-engine-builtin-component",
"version": "1.0.1",
"description": "",
"main": "dist/index.js",
"module": "dist/index.js",
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/opentiny/tiny-engine",
"directory": "packages/builtinComponent"
},
"scripts": {
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"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"
}
}

View File

@ -1,76 +0,0 @@
<template>
<div ref="colRef" class="canvas-col">
<slot></slot>
</div>
</template>
<script setup>
import { computed, defineProps } from 'vue'
import { getStyleValue, alignMap, justAlignMap } from './helper'
const props = defineProps({
flexBasis: {
type: String,
default: '0px'
},
rowGap: {
type: [String, Number],
default: ''
},
colGap: {
type: [String, Number],
default: ''
},
align: {
type: [String, Number],
default: ''
},
justAlign: {
type: [String, Number],
default: ''
},
grow: {
type: Boolean,
default: true
},
shrink: {
type: Boolean,
default: true
},
widthType: {
type: String,
default: 'auto'
}
})
const getFlex = () => {
const { flexBasis, grow, shrink, widthType } = props
if (widthType === 'fixed') {
return `0 0 ${getStyleValue(flexBasis)}`
}
return `${Number(grow)} ${Number(shrink)} ${getStyleValue(flexBasis)}`
}
const styles = computed(() => ({
flex: getFlex(props.flexBasis),
rowGap: getStyleValue(props.rowGap),
colGap: getStyleValue(props.colGap),
align: alignMap[props.align] || 'stretch',
justAlign: justAlignMap[props.justAlign] || 'flex-start'
}))
</script>
<style lang="less" scoped>
.canvas-col {
display: flex;
flex: v-bind('styles.flex');
flex-direction: column;
flex-wrap: nowrap;
row-gap: v-bind('styles.rowGap');
column-gap: v-bind('styles.colGap');
align-items: v-bind('styles.align');
justify-content: v-bind('styles.justAlign');
}
</style>

View File

@ -1,54 +0,0 @@
<template>
<div class="canvas-row">
<slot></slot>
</div>
</template>
<script setup>
import { computed, defineProps } from 'vue'
import { getStyleValue, alignMap, justAlignMap } from './helper'
const props = defineProps({
minHeight: {
type: [String, Number],
default: ''
},
rowGap: {
type: [String, Number],
default: ''
},
colGap: {
type: [String, Number],
default: ''
},
align: {
type: [String, Number],
default: ''
},
justAlign: {
type: [String, Number],
default: ''
}
})
const styles = computed(() => ({
minHeight: getStyleValue(props.minHeight),
rowGap: getStyleValue(props.rowGap),
colGap: getStyleValue(props.colGap),
align: alignMap[props.align] || 'stretch',
justAlign: justAlignMap[props.justAlign] || 'flex-start'
}))
</script>
<style lang="less" scoped>
.canvas-row {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: v-bind('styles.justAlign');
align-items: v-bind('styles.align');
column-gap: v-bind('styles.rowGap');
row-gap: v-bind('styles.colGap');
min-height: v-bind('styles.minHeight');
}
</style>

View File

@ -1,29 +0,0 @@
<template>
<div class="row-col-container">
<slot> </slot>
</div>
</template>
<script setup>
import { computed, defineProps } from 'vue'
import { getStyleValue } from './helper'
const props = defineProps({
rowGap: {
type: [String, Number],
default: ''
}
})
const styles = computed(() => ({
rowGap: getStyleValue(props.rowGap)
}))
</script>
<style lang="less" scoped>
.row-col-container {
display: flex;
flex-direction: column;
row-gap: v-bind('styles.rowGap');
}
</style>

View File

@ -1,46 +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.
*
*/
export const getStyleValue = (value) => {
if (typeof value === 'number' || /^\d+\.?\d*$/.test(value)) {
return `${value}px`
}
if (/^\d+\.?\d*(px|%|pt|em|rem|vw|vh)$/.test(value)) {
return value
}
return ''
}
export const alignMap = {
'flex-start': 'flex-start',
'flex-end': 'flex-end',
center: 'center',
stretch: 'stretch',
start: 'start',
end: 'end'
}
export const justAlignMap = {
'space-between': 'space-between',
'space-around': 'space-around',
'space-evenly': 'space-evenly',
'flex-start': 'flex-start',
'flex-end': 'flex-end',
stretch: 'stretch',
center: 'center',
start: 'start',
end: 'end',
left: 'left',
right: 'right'
}

View File

@ -1,196 +0,0 @@
{
"components": {
"icon": "Box",
"name": {
"zh_CN": "CanvasCol"
},
"component": "CanvasCol",
"schema": {
"slots": {},
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "widthType",
"type": "String",
"defaultValue": "auto",
"label": {
"text": {
"zh_CN": "宽度类型"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaRadioGroup",
"props": {
"options": [
{ "label": "auto", "text": "自适应" },
{ "label": "fixed", "text": "固定" }
],
"type": "button"
}
}
},
{
"property": "flexBasis",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "固定宽度"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "rowGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "行间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "colGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "列间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "align",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "副轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "stretch", "label": "拉伸对齐" }
],
"type": "button"
}
}
},
{
"property": "justAlign",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "主轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "space-between", "label": "两端对齐" },
{ "value": "space-evenly", "label": "均分对齐" }
],
"type": "button"
}
}
},
{
"property": "grow",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "增长"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSwitch",
"props": {}
}
},
{
"property": "shrink",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "收缩"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSwitch",
"props": {}
}
}
]
}
],
"events": {},
"shortcuts": {
"properties": []
},
"contentMenu": {
"actions": []
}
},
"configure": {
"loop": true,
"isContainer": true,
"nestingRule": {
"childWhitelist": [],
"descendantBlacklist": []
}
}
}
}

View File

@ -1,142 +0,0 @@
{
"components": {
"icon": "Box",
"name": {
"zh_CN": "CanvasRow"
},
"component": "CanvasRow",
"schema": {
"slots": {},
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "minHeight",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "最小高度"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "rowGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "行间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "colGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "列间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "align",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "副轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "stretch", "label": "拉伸对齐" }
],
"type": "button"
}
}
},
{
"property": "justAlign",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "主轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "space-between", "label": "两端对齐" },
{ "value": "space-evenly", "label": "均分对齐" }
],
"type": "button"
}
}
}
]
}
],
"events": {},
"shortcuts": {
"properties": []
},
"contentMenu": {
"actions": []
}
},
"configure": {
"loop": true,
"isContainer": true,
"nestingRule": {
"childWhitelist": [],
"descendantBlacklist": []
}
}
}
}

View File

@ -1,96 +0,0 @@
{
"components": {
"icon": "Box",
"name": {
"zh_CN": "行列容器"
},
"component": "CanvasRowColContainer",
"schema": {
"slots": {},
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "rowGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "行间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
}
]
}
],
"events": {},
"shortcuts": {
"properties": []
},
"contentMenu": {
"actions": []
}
},
"configure": {
"loop": true,
"isContainer": true,
"nestingRule": {
"childWhitelist": [],
"descendantBlacklist": []
}
}
},
"snippets": {
"name": {
"zh_CN": "行列容器"
},
"screenshot": "",
"snippetName": "CanvasRowColContainer",
"icon": "Box",
"schema": {
"componentName": "CanvasRowColContainer",
"props": {
"rowGap": "20px"
},
"children": [
{
"componentName": "CanvasRow",
"props": {
"rowGap": "20px",
"colGap": "20px"
},
"children": [
{
"componentName": "CanvasCol",
"props": {
"rowGap": "20px",
"colGap": "20px",
"grow": true,
"shrink": true,
"widthType": "auto"
}
}
]
}
]
}
}
}

View File

@ -1,18 +0,0 @@
import CanvasCol from './CanvasCol.json'
import CanvasRow from './CanvasRow.json'
import CanvasRowColContainer from './CanvasRowColContainer.json'
export default {
components: [
{
group: '内置组件',
children: [{ ...CanvasCol.components }, { ...CanvasRow.components }, { ...CanvasRowColContainer.components }]
}
],
snippets: [
{
group: '内置组件',
children: [{ ...CanvasRowColContainer.snippets }]
}
]
}

View File

@ -1,33 +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 { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vitePluginCssInjectedByJs from 'vite-plugin-css-injected-by-js'
export default defineConfig({
plugins: [vue(), vueJsx(), vitePluginCssInjectedByJs()],
publicDir: false,
build: {
cssCodeSplit: false,
lib: {
entry: path.resolve(__dirname, './index.js'),
name: 'builtinComponent',
fileName: () => 'index.js',
formats: ['es']
},
rollupOptions: {
external: ['vue']
}
}
})

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-canvas",
"version": "1.0.3",
"version": "1.0.3-beta.0",
"publishConfig": {
"access": "public"
},
@ -14,39 +14,26 @@
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/opentiny/tiny-engine",
"directory": "packages/canvas"
},
"bugs": {
"url": "https://github.com/opentiny/tiny-engine/issues"
},
"author": "OpenTiny Team",
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@babel/core": "7.18.13",
"@opentiny/tiny-engine-builtin-component": "workspace:*",
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:^1.0.1-beta.0",
"@opentiny/tiny-engine-controller": "workspace:^1.0.1-beta.0",
"@opentiny/tiny-engine-i18n-host": "workspace:^1.0.1-beta.0",
"@opentiny/tiny-engine-utils": "workspace:^1.0.2-beta.0",
"@opentiny/tiny-engine-webcomponent-core": "workspace:^1.0.2-beta.0",
"@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"
"@vueuse/core": "^9.6.0",
"vue": "3.2.45",
"vue-i18n": "9.2.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"rollup-plugin-terser": "^7.0.2",
"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"
}
}

View File

@ -432,6 +432,399 @@
"configure": {
"loop": true
}
},
{
"icon": "Box",
"name": {
"zh_CN": "CanvasRow"
},
"component": "CanvasRow",
"schema": {
"slots": {},
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "minHeight",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "最小高度"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "rowGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "行间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "colGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "列间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "align",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "副轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "stretch", "label": "拉伸对齐" }
],
"type": "button"
}
}
},
{
"property": "justAlign",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "主轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "space-between", "label": "两端对齐" },
{ "value": "space-evenly", "label": "均分对齐" }
],
"type": "button"
}
}
}
]
}
],
"events": {},
"shortcuts": {
"properties": []
},
"contentMenu": {
"actions": []
}
},
"configure": {
"loop": true,
"isContainer": true,
"nestingRule": {
"childWhitelist": [],
"descendantBlacklist": []
}
}
},
{
"icon": "Box",
"name": {
"zh_CN": "CanvasCol"
},
"component": "CanvasCol",
"schema": {
"slots": {},
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "widthType",
"type": "String",
"defaultValue": "auto",
"label": {
"text": {
"zh_CN": "宽度类型"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaRadioGroup",
"props": {
"options": [
{ "label": "auto", "text": "自适应" },
{ "label": "fixed", "text": "固定" }
],
"type": "button"
}
}
},
{
"property": "flexBasis",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "固定宽度"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "rowGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "行间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "colGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "列间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
},
{
"property": "align",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "副轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "stretch", "label": "拉伸对齐" }
],
"type": "button"
}
}
},
{
"property": "justAlign",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "主轴对齐"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{ "value": "flex-start", "label": "头部对齐" },
{ "value": "center", "label": "中间对齐" },
{ "value": "flex-end", "label": "尾端对齐" },
{ "value": "space-between", "label": "两端对齐" },
{ "value": "space-evenly", "label": "均分对齐" }
],
"type": "button"
}
}
},
{
"property": "grow",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "增长"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSwitch",
"props": {}
}
},
{
"property": "shrink",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "收缩"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaSwitch",
"props": {}
}
}
]
}
],
"events": {},
"shortcuts": {
"properties": []
},
"contentMenu": {
"actions": []
}
},
"configure": {
"loop": true,
"isContainer": true,
"nestingRule": {
"childWhitelist": [],
"descendantBlacklist": []
}
}
},
{
"icon": "Box",
"name": {
"zh_CN": "行列容器"
},
"component": "CanvasRowColContainer",
"schema": {
"slots": {},
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "rowGap",
"type": "String",
"defaultValue": "",
"label": {
"text": {
"zh_CN": "行间距"
}
},
"cols": 12,
"rules": [],
"widget": {
"component": "MetaInput",
"props": {}
}
}
]
}
],
"events": {},
"shortcuts": {
"properties": []
},
"contentMenu": {
"actions": []
}
},
"configure": {
"loop": true,
"isContainer": true,
"nestingRule": {
"childWhitelist": [],
"descendantBlacklist": []
}
}
}
]
}
@ -514,9 +907,45 @@
"schema": {
"componentName": "Img",
"props": {
"src": "https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/designer-default-icon.jpg"
"src": "img/webNova.jpg",
"style": "width: 200px; height: 200px"
}
}
},
{
"name": {
"zh_CN": "行列容器"
},
"screenshot": "",
"snippetName": "CanvasRowColContainer",
"icon": "Box",
"schema": {
"componentName": "CanvasRowColContainer",
"props": {
"rowGap": "20px"
},
"children": [
{
"componentName": "CanvasRow",
"props": {
"rowGap": "20px",
"colGap": "20px"
},
"children": [
{
"componentName": "CanvasCol",
"props": {
"rowGap": "20px",
"colGap": "20px",
"grow": true,
"shrink": true,
"widthType": "auto"
}
}
]
}
]
}
}
]
}

View File

@ -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.
*
*/
import CanvasText from './CanvasText.vue'
import CanvasBox from './CanvasBox.vue'
@ -16,6 +16,18 @@ import CanvasCollection from './CanvasCollection.vue'
import CanvasIcon from './CanvasIcon.vue'
import CanvasSlot from './CanvasSlot.vue'
import CanvasImg from './CanvasImg.vue'
import CanvasPlaceholder from './CanvasPlaceholder.vue'
import CanvasRow from './CanvasRow.vue'
import CanvasCol from './CanvasCol.vue'
import CanvasRowColContainer from './CanvasRowColContainer.vue'
export { CanvasText, CanvasBox, CanvasCollection, CanvasIcon, CanvasSlot, CanvasImg, CanvasPlaceholder }
export {
CanvasText,
CanvasBox,
CanvasCollection,
CanvasIcon,
CanvasSlot,
CanvasImg,
CanvasRow,
CanvasCol,
CanvasRowColContainer
}

View File

@ -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
}

View File

@ -27,48 +27,16 @@
</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">
<div v-if="showAction" class="corner-mark-right" :style="fixStyle">
<template v-if="!isModal">
<div v-if="showToParent" title="选择父级">
<icon-chevron-left @click.stop="selectParent"></icon-chevron-left>
@ -100,13 +68,13 @@
<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>
</template>
<script>
import { watchPostEffect, ref, watch, computed, nextTick } from 'vue'
import { watchPostEffect, ref, watch, computed } from 'vue'
import {
IconDel,
IconSetting,
@ -117,7 +85,6 @@ import {
IconEyeclose
} from '@opentiny/vue-icon'
import {
canvasState,
getCurrent,
removeNodeById,
selectNode,
@ -128,7 +95,7 @@ import {
dragStart,
getCurrentElement
} from './container'
import { useLayout, useResource } from '@opentiny/tiny-engine-controller'
import { useResource } from '@opentiny/tiny-engine-controller'
import { Popover } from '@opentiny/vue'
import shortCutPopover from './shortCutPopover.vue'
@ -136,6 +103,8 @@ import shortCutPopover from './shortCutPopover.vue'
const OPTION_BAR_HEIGHT = 24
//
const LABEL_HEIGHT = 24
//
const MAX_OPTION_WIDTH = 110
//
const SCROLL_BAR_WIDTH = 8
@ -143,11 +112,6 @@ const SCROLL_BAR_WIDTH = 8
// 6px
const OPTION_SPACE = 6
//
const SELECTION_BORDER_WIDTH = 2
const STYLE_UNSET = 'unset'
export default {
components: {
IconDel: IconDel(),
@ -240,7 +204,6 @@ export default {
return config?.configure?.isModal
})
const optionRef = ref(null)
const fixStyle = ref('')
let showPopover = ref(false)
@ -302,188 +265,45 @@ export default {
const labelRef = ref(null)
const labelStyle = ref('')
const positions = {
LEFT: 'left',
RIGHT: 'right',
TOP: 'top',
BOTTOM: 'bottom',
isHorizontal(position) {
return [this.LEFT, this.RIGHT].includes(position)
},
isVertical(position) {
return [this.TOP, this.BOTTOM].includes(position)
const bottomPanelHeight = ref(0)
const topToolbarHeight = ref(0)
watchPostEffect(() => {
if (!bottomPanelHeight.value) {
bottomPanelHeight.value = document.querySelector('#tiny-bottom-panel')?.offsetHeight
}
}
class Align {
alignLeft = false
horizontalValue = 0
alignTop = false
verticalValue = 0
constructor({ alignLeft, horizontalValue, alignTop, verticalValue }) {
this.alignLeft = alignLeft
this.horizontalValue = horizontalValue
this.alignTop = alignTop
this.verticalValue = verticalValue
if (!topToolbarHeight.value) {
topToolbarHeight.value = document.querySelector('.tiny-engine-toolbar')?.offsetHeight
}
align(position, value = 0) {
if (positions.isHorizontal(position)) {
this.alignLeft = position === positions.LEFT
this.horizontalValue = value
return this
}
if (positions.isVertical(position)) {
this.alignTop = position === positions.TOP
this.horizontalValue = value
return this
}
return this
}
toStyleValue() {
const styleObj = {}
if (this.alignLeft) {
styleObj.left = this.horizontalValue
styleObj.right = STYLE_UNSET
} else {
styleObj.right = this.horizontalValue
styleObj.left = STYLE_UNSET
}
if (this.alignTop) {
styleObj.top = this.verticalValue
styleObj.bottom = STYLE_UNSET
} else {
styleObj.bottom = this.verticalValue
styleObj.top = STYLE_UNSET
}
return this.styleObj2Str(styleObj)
}
styleObj2Str = (styleObj) => {
return Object.entries(styleObj)
.map(([key, value]) => {
const num = Number(value)
if (Number.isNaN(num)) {
return `${key}:${value}`
}
const val = positions.isHorizontal(key) ? num - SELECTION_BORDER_WIDTH : num
return `${key}:${val}px`
})
.join(';')
}
}
const getStyleValues = (selectState, canvasSize, labelWidth, optionWidth) => {
const { left, top, width, height, doc } = selectState
const { width: canvasWidth, height: canvasHeight } = canvasSize
//
const fullRectWidth = labelWidth + optionWidth + OPTION_SPACE
const { left, top, width, height } = props.selectState
// label top
const isLabelAtBottom = top < LABEL_HEIGHT
const labelAlign = new Align({
alignLeft: true,
horizontalValue: 0,
alignTop: !isLabelAtBottom,
verticalValue: -LABEL_HEIGHT
})
const isLabelAtBottom = top <= topToolbarHeight.value + LABEL_HEIGHT
//
const isOptionAtBottom = canvasHeight - top - height >= OPTION_BAR_HEIGHT
const optionAlign = new Align({
alignLeft: false,
horizontalValue: 0,
alignTop: !isOptionAtBottom,
verticalValue: -OPTION_BAR_HEIGHT
})
const isOptionAtBottom = window.innerHeight - top - height - bottomPanelHeight.value > OPTION_BAR_HEIGHT
const scrollBarWidth = doc.documentElement.scrollHeight > doc.documentElement.clientHeight ? SCROLL_BAR_WIDTH : 0
// label option labelRef.value.clientWidthlabel
const minWidth = MAX_OPTION_WIDTH + labelRef.value?.clientWidth || 0
let translateXDis = 0
if (width < fullRectWidth) {
//
const siteCanvas = document.querySelector('.site-canvas')
const right = siteCanvas.getBoundingClientRect().right
// label option
const isOverRight = right <= left + width + SCROLL_BAR_WIDTH
// labelabellabel
const isLabelAlignRight = labelWidth > width && left + labelWidth + scrollBarWidth > canvasWidth
if (isLabelAlignRight) {
labelAlign.align(positions.RIGHT)
}
// optionoptionoption
const isOptionAlignLeft = optionWidth > width && left + width - optionWidth < 0
if (isOptionAlignLeft) {
optionAlign.align(positions.LEFT)
}
if (isLabelAtBottom === isOptionAtBottom) {
//
if (left + fullRectWidth < canvasWidth) {
//
labelAlign.align(positions.LEFT)
optionAlign.align(positions.LEFT, labelWidth + OPTION_SPACE)
} else {
//
optionAlign.align(positions.RIGHT)
labelAlign.align(positions.RIGHT, optionWidth + OPTION_SPACE)
}
}
} else {
if (left < 0) {
labelAlign.align(positions.LEFT, Math.min(-left, width - fullRectWidth))
}
if (left + width + scrollBarWidth > canvasWidth) {
optionAlign.align(
positions.RIGHT,
Math.min(left + width + scrollBarWidth - canvasWidth, width - fullRectWidth)
)
}
//
if (width < minWidth) {
translateXDis = MAX_OPTION_WIDTH - width + (labelRef.value?.clientWidth || 0) + OPTION_SPACE
}
return {
labelStyleValue: labelAlign.toStyleValue(),
optionStyleValue: optionAlign.toStyleValue()
}
}
labelStyle.value = `top: unset; ${isLabelAtBottom ? 'bottom' : 'top'}: -${LABEL_HEIGHT}px; ${
isOverRight && isLabelAtBottom === isOptionAtBottom && `left: -${translateXDis}px;`
}`
watchPostEffect(async () => {
const { left, top, width, height, doc } = props.selectState
// nextTickrefawait
await nextTick()
if (labelRef.value && !optionRef.value) {
// body
labelStyle.value = `left: 0; right: unset; top: unset; bottom: 0`
return
}
if (!labelRef.value || !optionRef.value) {
return
}
const scale = useLayout().getScale()
const canvasRect = canvasState.iframe.getBoundingClientRect()
const { width: labelWidth } = labelRef.value.getBoundingClientRect()
const { width: optionWidth } = optionRef.value.getBoundingClientRect()
// canvasiframeiframeclientRectscale
const { labelStyleValue, optionStyleValue } = getStyleValues(
{ left, top, width, height, doc },
{ width: canvasRect.width / scale, height: canvasRect.height / scale },
labelWidth / scale,
optionWidth / scale
)
labelStyle.value = labelStyleValue
fixStyle.value = optionStyleValue
fixStyle.value = `
${translateXDis && !isOverRight ? `transform: translateX(${translateXDis}px);` : ''}
${isOptionAtBottom ? 'bottom' : 'top'}: -${OPTION_BAR_HEIGHT}px;`
})
return {
@ -493,7 +313,6 @@ export default {
copy,
hide,
selectParent,
optionRef,
fixStyle,
showAction,
showPopover,
@ -510,7 +329,7 @@ export default {
<style lang="less">
.canvas-rect {
position: absolute;
position: fixed;
box-sizing: border-box;
pointer-events: none;
border: 1px solid var(--ti-lowcode-canvas-rect-border-color);
@ -573,12 +392,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 {
@ -636,6 +454,7 @@ export default {
display: flex;
align-items: center;
position: absolute;
right: -1px;
height: 24px;
padding: 0 4px;
color: var(--ti-lowcode-canvas-corner-mark-right-color);
@ -660,6 +479,7 @@ export default {
.corner-mark-left {
white-space: nowrap;
pointer-events: all;
left: -2px;
color: var(--ti-lowcode-canvas-select-corner-mark-left-color);
background: var(--ti-lowcode-canvas-select-corner-mark-left-bg-color);
svg {

View File

@ -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>
@ -29,7 +29,7 @@
<script>
import { onMounted, ref, computed, onUnmounted } from 'vue'
import { iframeMonitoring } from '@opentiny/tiny-engine-controller/js/monitor'
import { iframeMonitoring } from '@opentiny/tiny-engine-common/js/monitor'
import { useTranslate, useCanvas, useResource } from '@opentiny/tiny-engine-controller'
import CanvasAction from './CanvasAction.vue'
import CanvasResize from './CanvasResize.vue'
@ -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
@ -125,30 +123,8 @@ export default {
const doc = iframe.value.contentDocument
const win = iframe.value.contentWindow
let isScrolling = false
// iframe
win.addEventListener('mousedown', (event) => {
// html使scrollmouseup
if (event.target === doc.documentElement) {
isScrolling = false
return
}
insertPosition.value = false
setCurrentNode(event)
target.value = event.target
})
win.addEventListener('scroll', () => {
isScrolling = true
})
win.addEventListener('mouseup', (event) => {
if (event.target !== doc.documentElement || isScrolling) {
return
}
insertPosition.value = false
setCurrentNode(event)
target.value = event.target
@ -180,8 +156,8 @@ export default {
}
}
//
const settingModel = () => {
showSettingModel.value = true
const settingModle = () => {
showSettingModle.value = true
}
const updateI18n = (message) => {
@ -218,9 +194,7 @@ export default {
onMounted(() => run(iframe))
onUnmounted(() => {
if (iframe.value?.contentDocument) {
removeHostkeyEvent(iframe.value.contentDocument)
}
removeHostkeyEvent(iframe.value.contentDocument)
window.removeEventListener('message', updateI18n, false)
})
@ -238,9 +212,9 @@ export default {
canvasState,
insertComponent,
insertPanel,
settingModel,
settingModle,
target,
showSettingModel,
showSettingModle,
insertPosition,
loading
}
@ -249,19 +223,16 @@ export default {
</script>
<style lang="less" scoped>
.insert-panel {
z-index: 4;
z-index: 1;
position: fixed;
top: 200px;
left: 400px;
width: 480px;
:deep(.components-wrap) {
& > .tiny-collapse {
max-height: 300px;
height: 300px;
}
}
:deep(#pane-blocks) {
max-height: 400px;
}
}
.datainit-tip {
display: flex;

View File

@ -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;
}

View File

@ -1,15 +1,11 @@
<template>
<div draggable="true" class="drag-item" @dragstart="dragstart" @click="handleClick">
<div draggable="true" class="drag-item" @dragstart="dragstart">
<slot></slot>
</div>
</template>
<script>
import { utils } from '@opentiny/tiny-engine-utils'
import { dragStart } from './container'
const { deepClone } = utils
export default {
props: {
data: Object
@ -17,8 +13,9 @@ export default {
emits: ['click'],
setup(props, { emit }) {
const dragstart = (e) => {
if (props.data) {
const data = deepClone(props.data)
if (props.data && e.button === 0) {
const data = JSON.parse(JSON.stringify(props.data))
emit('click', data)
dragStart(data)
//
@ -27,18 +24,8 @@ export default {
e.dataTransfer.setDragImage(target, 10, 10)
}
}
const handleClick = () => {
if (props.data) {
const data = deepClone(props.data)
emit('click', data)
}
}
return {
dragstart,
handleClick
dragstart
}
}
}

View File

@ -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;

View File

@ -4,37 +4,32 @@
<li
v-for="(item, index) in menus"
:key="index"
:class="{
'li-item': item.items,
'li-item-disabled': actionDisabled(item)
}"
:class="item.items ? 'li-item' : ''"
@click="doOperation(item)"
@mouseover="onShowChildrenMenu(item)"
@mouseover="current = item"
>
<div>
<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?.() }]"
@click.stop="doOperation(subItem)"
>
<li v-if="subItem?.check?.() !== false" @click.stop="doOperation(subItem)">
{{ subItem.name }}
</li>
</template>
</ul>
</li>
</ul>
<SaveNewBlock :boxVisibility="boxVisibility" fromCanvas @close="close"></SaveNewBlock>
<save-new-block :boxVisibility="boxVisibility" fromCanvas @close="close"></save-new-block>
</div>
</template>
<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 { SaveNewBlock } from '@opentiny/tiny-engine-common'
import { iconRight } from '@opentiny/vue-icon'
const menuState = reactive({
@ -45,35 +40,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-menuwidth100px100
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'
}
}
})
@ -81,6 +68,7 @@ export const openMenu = (event) => {
export default {
components: {
SaveNewBlock,
IconRight: iconRight()
},
setup(props, { emit }) {
@ -107,13 +95,11 @@ export default {
items: [
{ name: '文字提示', code: 'wrap', value: 'TinyTooltip' },
{ name: '弹出框', code: 'wrap', value: 'TinyPopover' }
],
code: 'addParent'
]
},
{ name: '删除', code: 'del' },
{ name: '复制', code: 'copy' },
{ name: '绑定事件', code: 'bindEvent' },
{ name: '新建区块', code: 'createBlock' }
{ name: '绑定事件', code: 'bindEvent' }
])
const boxVisibility = ref(false)
@ -122,10 +108,10 @@ export default {
const operations = {
del() {
removeNodeById(getCurrent().schema?.id)
removeNodeById(getCurrent().schema.id)
},
copy() {
copyNode(getCurrent().schema?.id)
copyNode(getCurrent().schema.id)
},
config() {
useLayout().activeSetting('props')
@ -139,56 +125,16 @@ export default {
wrap({ value, name }) {
const componentName = value || name
const { schema, parent } = getCurrent()
if (!schema || !parent) {
return
}
const index = parent.children.findIndex(({ id }) => schema.id === id)
let wrapSchema = {
const index = parent.children.indexOf(schema)
const wrapSchema = {
componentName,
id: null,
props: {
content: '提示信息'
},
props: {},
children: [schema]
}
// popover
if (value === 'TinyPopover') {
wrapSchema = {
componentName,
props: {
width: 200,
title: '弹框标题',
trigger: 'manual',
modelValue: true
},
children: [
{
componentName: 'Template',
props: {
slot: 'reference'
},
children: [schema]
},
{
componentName: 'Template',
props: {
slot: 'default'
},
children: [
{
componentName: 'div',
props: {
placeholder: '提示内容'
}
}
]
}
]
}
}
parent.children.splice(index, 1, wrapSchema)
getController().addHistory()
},
createBlock() {
@ -203,25 +149,12 @@ export default {
}
}
const actionDisabled = (actionItem) => {
const actions = ['del', 'copy', 'addParent']
return actions.includes(actionItem.code) && !getCurrent().schema?.id
}
const onShowChildrenMenu = (menuItem) => {
current.value = !actionDisabled(menuItem) ? menuItem : null
}
const close = () => {
boxVisibility.value = false
}
const doOperation = (item) => {
if ((item.check && !item.check?.()) || actionDisabled(item)) {
return
}
if (item?.code) {
operations[item.code]?.(item)
operations[item.code](item)
closeMenu()
}
}
@ -233,10 +166,7 @@ export default {
boxVisibility,
close,
current,
menuDom,
subMenuStyles,
actionDisabled,
onShowChildrenMenu
menuDom
}
}
}
@ -244,7 +174,7 @@ export default {
<style lang="less" scoped>
.context-menu {
position: absolute;
position: fixed;
z-index: 10;
}
.menu-item {
@ -259,10 +189,6 @@ export default {
.li-item {
border-bottom: 1px solid var(--ti-lowcode-canvas-menu-border-color);
}
.li-item-disabled {
cursor: not-allowed;
color: var(--ti-lowcode-canvas-menu-item-disabled-color);
}
li {
& > div {
display: flex;
@ -270,21 +196,18 @@ export default {
justify-content: space-between;
}
font-size: 12px;
color: var(--ti-lowcode-canvas-menu-item-color);
color: var(--ti-lowcode-toolbar-breadcrumb-color);
padding: 6px 15px;
&:not(.menu-item-disabled):hover {
background: var(--ti-lowcode-canvas-menu-item-hover-bg-color);
&:hover {
color: var(--ti-lowcode-toolbar-icon-color);
background: var(--ti-lowcode-canvas-menu-hover-color);
}
position: relative;
&.menu-item-disabled {
cursor: not-allowed;
color: var(--ti-lowcode-canvas-menu-item-disabled-color);
}
}
&.sub-menu {
width: 100px;
position: absolute;
right: -100px;
top: -2px;
}
}

View File

@ -17,18 +17,26 @@ 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}%)`
}
})
const mouseDown = ref(false)
const resizeDom = ref(null)
const onMouseMove = (event) => {
if (mouseDown.value) {
event.preventDefault()
calculateSize(event)
}
}
const calculateSize = ({ movementX }) => {
const dimension = useLayout().getDimension()
const { maxWidth, minWidth, width } = dimension
@ -40,30 +48,6 @@ export default {
})
}
const onMouseMove = (event) => {
if (mouseDown.value) {
event.preventDefault()
calculateSize(event)
}
}
const onMouseUp = () => {
const iframe = canvasState.iframe
if (iframe) {
iframe.style['pointer-events'] = 'auto'
mouseDown.value = false
document.removeEventListener('mousemove', onMouseMove, { passive: false })
document.removeEventListener('mouseup', onMouseUp)
}
}
const bindEvents = () => {
document.addEventListener('mousemove', onMouseMove, { passive: false })
document.addEventListener('mouseup', onMouseUp)
}
const onMouseDown = () => {
const iframe = canvasState.iframe
@ -74,35 +58,44 @@ export default {
}
}
const onMouseUp = () => {
const iframe = canvasState.iframe
if (iframe) {
iframe.style['pointer-events'] = 'auto'
mouseDown.value = false
unbindEvents()
}
}
const bindEvents = () => {
document.addEventListener('mousemove', onMouseMove, { passive: false })
document.addEventListener('mouseup', onMouseUp)
}
const unbindEvents = () => {
document.removeEventListener('mousemove', onMouseMove, { passive: false })
document.removeEventListener('mouseup', onMouseUp)
}
const setScale = () => {
useLayout().setDimension({ scale: 1 })
nextTick(() => {
const canvasWrap = document.querySelector('#canvas-wrap')
if (!canvasWrap) {
return
}
const rate = canvasWrap.offsetWidth / resizeDom.value.offsetWidth
useLayout().setDimension({
scale: Math.min(rate, 1)
scale: rate > 1 ? 1 : rate
})
})
}
watch(() => useLayout().getDimension().width, setScale, { flush: 'post', immediate: true })
watch(() => useLayout().getDimension().width, setScale, { flush: 'post' })
watch(() => useLayout().getPluginState().fixedPanels, setScale, { flush: 'post' })
watch(
() => useLayout().getPluginState().render,
(value) => {
const currentFixed = useLayout().getPluginState().fixedPanels.includes(value)
if (!value || currentFixed) {
setScale()
}
},
(value) => !value && setScale(),
{ flush: 'post' }
)

View File

@ -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;

View File

@ -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-common/js/environments'
const initialDragState = {
keydown: false,
@ -52,28 +51,6 @@ export const canvasState = shallowReactive({
loopId: null
})
export const getRenderer = () => canvasState.renderer
export const getController = () => canvasState.controller
export const getDocument = () => canvasState.iframe.contentDocument
export const getWindow = () => canvasState.iframe.contentWindow
export const getCurrent = () => {
return {
schema: canvasState.current,
parent: canvasState.parent,
loopId: canvasState.loopId
}
}
export const getGlobalState = () => getRenderer().getGlobalState()
export const getNode = (id, parent) => getRenderer()?.getNode(id, parent)
export const getSchema = () => getRenderer()?.getSchema()
export const getContext = () => {
return getRenderer().getContext()
}
@ -99,10 +76,8 @@ const initialLineState = {
width: 0,
left: 0,
position: '',
forbidden: false,
id: '',
config: null,
doc: null
config: null
}
// 选中画布中元素时的状态
@ -120,43 +95,6 @@ export const lineState = reactive({
...initialLineState
})
export const clearHover = () => {
Object.assign(hoverState, initialRectState, { slot: null })
}
export const clearSelect = () => {
canvasState.current = null
canvasState.parent = null
Object.assign(selectState, initialRectState)
// 临时借用 remote 事件出发 currentSchema 更新
canvasState?.emit?.('remove')
}
const smoothScroll = {
timmer: null,
/**
*
* @param {*} up 方向
* @param {*} step 每次滚动距离
* @param {*} time 滚动延时不得大于系统滚动时长否则可能出现卡顿效果
*/
start(up, step = 40, time = 100) {
const dom = getDocument().documentElement
const fn = () => {
const top = up ? dom.scrollTop + step : dom.scrollTop - step
dom.scrollTo({ top, behavior: 'smooth' })
this.timmer = setTimeout(fn, time)
}
this.timmer || fn()
},
stop() {
clearTimeout(this.timmer)
this.timmer = null
}
}
export const dragStart = (
data,
element,
@ -208,12 +146,6 @@ export const getElement = (element) => {
if (element === element.ownerDocument.body) {
return element
}
// 如果当前元素是画布的html返回画布的body
if (element === element.ownerDocument.documentElement) {
return element.ownerDocument.body
}
if (!element || element.nodeType !== 1) {
return undefined
}
@ -227,23 +159,6 @@ export const getElement = (element) => {
return undefined
}
const getRect = (element) => {
if (element === getDocument().body) {
const { innerWidth: width, innerHeight: height } = getWindow()
return {
left: 0,
top: 0,
right: width,
bottom: height,
width,
height,
x: 0,
y: 0
}
}
return element.getBoundingClientRect()
}
const inserAfter = ({ parent, node, data }) => {
const parentChildren = parent.children
const index = parentChildren.indexOf(node)
@ -290,255 +205,113 @@ export const removeNode = ({ parent, node }) => {
}
export const removeNodeById = (id) => {
if (!id) {
return
}
removeNode(getNode(id, true))
clearSelect()
getController().addHistory()
canvasState.emit('remove')
}
export const querySelectById = (id) => {
let selector = `[${NODE_UID}="${id}"]`
const doc = canvasState.iframe.contentDocument
let element = doc.querySelector(selector)
const loopId = element?.getAttribute('loop-id')
if (element && loopId) {
const currentLoopId = getCurrent().loopId
selector = `[${NODE_UID}="${id}"][${NODE_LOOP}="${currentLoopId}"]`
element = doc.querySelector(selector)
}
return element
}
export const getCurrentElement = () => querySelectById(getCurrent().schema?.id)
// 滚动页面后,目标元素与页面边界至少保留的边距
const SCROLL_MARGIN = 15
export const scrollToNode = (element) => {
if (element) {
const container = getDocument().documentElement
const { clientWidth, clientHeight } = container
const { left, right, top, bottom, 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 (bottom < 0) {
option.top = container.scrollTop + top - SCROLL_MARGIN
} else if (top > clientHeight) {
option.top = container.scrollTop + top - clientHeight + height + SCROLL_MARGIN
}
if (typeof option.left === 'number' || typeof option.top === 'number') {
container.scrollTo(option)
}
}
return nextTick()
}
const setSelectRect = (element) => {
element = element || getDocument().body
const { left, height, top, width } = getRect(element)
const componentName = getCurrent().schema?.componentName || ''
clearHover()
Object.assign(selectState, {
width,
height,
top,
left,
componentName,
doc: getDocument()
})
}
export const updateRect = (id) => {
id = (typeof id === 'string' && id) || getCurrent().schema?.id
clearHover()
if (id) {
setTimeout(() => setSelectRect(querySelectById(id)))
export const insertNode = (node, position = POSITION.IN, select = true) => {
if (!node.parent) {
insertInner({ node: canvasState.schema, data: node.data }, position)
} else {
// 如果选中的是body不清除选中框
if (!selectState.componentName && selectState.width > 0) {
return
}
clearSelect()
}
}
export const getConfigure = (targetName) => {
const material = getController().getMaterial(targetName)
// 这里如果是区块插槽,则返回标识为容器的对象
if (targetName === 'Template') {
return {
isContainer: true
switch (position) {
case POSITION.TOP:
case POSITION.LEFT:
insertBefore(node)
break
case POSITION.BOTTOM:
case POSITION.RIGHT:
inserAfter(node)
break
case POSITION.IN:
insertInner(node)
break
default:
insertInner(node)
break
}
}
return material?.content?.configure || material.configure || {}
select && setTimeout(() => selectNode(node.data.id))
getController().addHistory()
}
/**
* 是否允许插入
* @param {*} configure 当前放置目标的 configure比如getConfigure(componentName)
* @param {*} data 当前插入目标的schame数据
* @returns
*/
export const allowInsert = (configure = hoverState.configure || {}, data = dragState.data || {}) => {
const { nestingRule = {} } = configure
const { childWhitelist = [], descendantBlacklist = [] } = nestingRule
export const addComponent = (data, position) => {
const { schema, parent } = getCurrent()
// 要插入的父节点必须是容器
if (!configure.isContainer) {
return false
}
let flag = true
// 白名单
flag = childWhitelist.length ? childWhitelist.includes(data?.componentName) : true
// 黑名单
if (descendantBlacklist.length) {
flag = !descendantBlacklist.includes(data?.componentName)
}
return flag
insertNode({ node: schema, parent, data }, position)
}
const isAncestor = (ancestor, descendant) => {
const ancestorId = typeof ancestor === 'string' ? ancestor : ancestor.id
let descendantId = typeof descendant === 'string' ? descendant : descendant.id
export const copyNode = (id) => {
const { node, parent } = getNode(id, true)
while (descendantId) {
const { parent } = getNode(descendantId, true) || {}
if (parent.id === ancestorId) {
return true
}
descendantId = parent.id
}
return false
inserAfter({ parent, node, data: copyObject(node) })
getController().addHistory()
}
// 获取位置信息,返回状态
const lineAbs = 20
const getPosLine = (rect, configure) => {
const mousePos = dragState.mouse
const yAbs = Math.min(lineAbs, rect.height / 3)
const xAbs = Math.min(lineAbs, rect.width / 3)
let type
let forbidden = false
export const onMouseUp = ({ target }) => {
const { draging, data } = dragState
const { position } = lineState
const absolute = canvasState.type === 'absolute'
const sourceId = data?.id
const lineId = lineState.id
const allowInsert = position !== POSITION.FORBID
if (mousePos.y < rect.top + yAbs) {
type = POSITION.TOP
} else if (mousePos.y > rect.bottom - yAbs) {
type = POSITION.BOTTOM
} else if (mousePos.x < rect.left + xAbs) {
type = POSITION.LEFT
} else if (mousePos.x > rect.right - xAbs) {
type = POSITION.RIGHT
} else if (configure.isContainer) {
type = POSITION.IN
if (!allowInsert()) {
forbidden = true
}
} else {
type = POSITION.BOTTOM
}
if (draging && allowInsert) {
const { parent, node } = getNode(lineId, true) || {} // target
const targetNode = { parent, node, data: toRaw(data) }
// 如果被拖拽的节点不是新增的,并且是放置的节点的祖先节点,则禁止插入
const draggedId = dragState.data?.id
if (draggedId && isAncestor(draggedId, lineState.id)) {
forbidden = true
}
return { type, forbidden }
}
const isBodyEl = (element) => element.nodeName === 'BODY'
const setHoverRect = (element, data) => {
if (!element) {
return clearHover()
}
const componentName = element.getAttribute(NODE_TAG)
const id = element.getAttribute(NODE_UID)
const configure = getConfigure(componentName)
const rect = getRect(element)
const { left, height, top, width } = rect
hoverState.configure = configure
if (data) {
let childEle = null
lineState.id = id
lineState.configure = configure
const rectType = isBodyEl(element) ? POSITION.IN : getPosLine(rect, configure).type
// 如果拖拽经过的元素是body或者是带有容器属性的盒子并且在元素内部插入,则需要特殊处理
if ((isBodyEl(element) || configure?.isContainer) && rectType === POSITION.IN) {
const { node } = isBodyEl(element) ? { node: getSchema() } : getNode(id, true) || {}
const children = node?.children || []
if (children.length > 0) {
// 如果容器盒子有子节点,则以最后一个子节点为拖拽参照物
const lastNode = children[children.length - 1]
childEle = querySelectById(lastNode.id)
const childComponentName = element.getAttribute(childEle)
const Childconfigure = getConfigure(childComponentName)
lineState.id = lastNode.id
lineState.configure = Childconfigure
if (sourceId) {
// 内部拖拽
if (sourceId !== lineId && !absolute) {
removeNode(getNode(sourceId, true))
insertNode(targetNode, position)
}
}
// 如果容器盒子有子元素
if (childEle) {
const childRect = getRect(childEle)
const { left, height, top, width } = childRect
const posLine = getPosLine(childRect, lineState.configure)
Object.assign(lineState, {
width,
height,
top,
left,
position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden
})
} else {
const posLine = getPosLine(rect, configure)
Object.assign(lineState, {
width,
height,
top,
left,
position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden
})
}
// 从外部拖拽进来的无IDinsert
if (absolute) {
targetNode.node = getSchema()
data.props = data.props || {}
data.props.style = {
position: 'absolute',
top: dragState.mouse.y + 'px',
left: dragState.mouse.x + 'px'
}
}
useLayout().closePlugin()
insertNode(targetNode, position)
}
}
// 设置元素hover状态
Object.assign(hoverState, {
width,
height,
top,
left,
componentName
})
return undefined
// 重置拖拽状态
dragEnd()
}
const smoothScroll = {
timmer: null,
/**
*
* @param {*} up 方向
* @param {*} step 每次滚动距离
* @param {*} time 滚动延时不得大于系统滚动时长否则可能出现卡顿效果
*/
start(up, step = 40, time = 100) {
const dom = getDocument().documentElement
const fn = () => {
const top = up ? dom.scrollTop + step : dom.scrollTop - step
dom.scrollTo({ top, behavior: 'smooth' })
this.timmer = setTimeout(fn, time)
}
this.timmer || fn()
},
stop() {
clearTimeout(this.timmer)
this.timmer = null
}
}
// 绝对布局
@ -622,6 +395,41 @@ export const dragMove = (event, isHover) => {
}
}
export const clearHover = () => {
Object.assign(hoverState, initialRectState, { slot: null })
}
export const clearSelect = () => {
canvasState.current = null
canvasState.parent = null
Object.assign(selectState, initialRectState)
}
export const querySelectById = (id, type = '') => {
let selector = `[${NODE_UID}="${id}"]`
const doc = canvasState.iframe.contentDocument
let element = doc.querySelector(selector)
const loopId = element?.getAttribute('loop-id')
if (element && loopId) {
const currentLoopId = getCurrent().loopId
selector = `[${NODE_UID}="${id}"][${NODE_LOOP}="${currentLoopId}"]`
element = doc.querySelector(selector)
}
return element
}
export const getCurrentElement = () => querySelectById(getCurrent().schema?.id)
export const updateRect = (id) => {
id = (typeof id === 'string' && id) || getCurrent().schema?.id
clearHover()
if (id) {
setTimeout(() => setSelectRect(querySelectById(id)))
} else {
clearSelect()
}
}
// type == clickTree, 为点击大纲; type == loop-id=xxx ,为点击循环数据
export const selectNode = async (id, type) => {
if (type && type.indexOf('loop-id') > -1) {
@ -651,84 +459,174 @@ export const hoverNode = (id, data) => {
element && setHoverRect(element, data)
}
export const insertNode = (node, position = POSITION.IN, select = true) => {
if (!node.parent) {
insertInner({ node: canvasState.schema, data: node.data }, position)
} else {
switch (position) {
case POSITION.TOP:
case POSITION.LEFT:
insertBefore(node)
break
case POSITION.BOTTOM:
case POSITION.RIGHT:
inserAfter(node)
break
case POSITION.IN:
insertInner(node)
break
default:
insertInner(node)
break
export const scrollToNode = (element) => {
if (element) {
const container = getDocument().documentElement
const h = container?.clientHeight
const { y, height } = element.getBoundingClientRect()
if (y < 0) {
container.scrollTo({ top: container.scrollTop + y - 15 })
} else if (y > h) {
container.scrollTo({ top: y + height - h + 15 })
}
}
select && setTimeout(() => selectNode(node.data.id))
return nextTick()
}
const setSelectRect = (element) => {
element = element || getDocument().body
getController().addHistory()
const { left, height, top, width } = element.getBoundingClientRect()
const { x, y } = getOffset(element)
const componentName = getCurrent().schema?.componentName || ''
const scale = useLayout().getScale()
clearHover()
Object.assign(selectState, {
width: width * scale,
height: height * scale,
top: top * scale + y,
left: left * scale + x,
componentName
})
}
export const addComponent = (data, position) => {
const { schema, parent } = getCurrent()
const isBodyEl = (element) => element.nodeName === 'BODY'
insertNode({ node: schema, parent, data }, position)
}
export const copyNode = (id) => {
if (!id) {
return
const setHoverRect = (element, data) => {
if (!element) {
return clearHover()
}
const { node, parent } = getNode(id, true)
const componentName = element.getAttribute(NODE_TAG)
const id = element.getAttribute(NODE_UID)
const configure = getConfigure(componentName)
const rect = element.getBoundingClientRect()
const { left, height, top, width } = rect
const { x, y } = getOffset(element)
const scale = useLayout().getScale()
inserAfter({ parent, node, data: copyObject(node) })
getController().addHistory()
}
hoverState.configure = configure
export const onMouseUp = () => {
const { draging, data } = dragState
const { position, forbidden } = lineState
const absolute = canvasState.type === 'absolute'
const sourceId = data?.id
const lineId = lineState.id
if (data) {
let childEle = null
lineState.id = id
lineState.configure = configure
const rectType = isBodyEl(element) ? POSITION.IN : getPosLine(rect, configure).type
if (draging && !forbidden) {
const { parent, node } = getNode(lineId, true) || {} // target
const targetNode = { parent, node, data: toRaw(data) }
if (sourceId) {
// 内部拖拽
if (sourceId !== lineId && !absolute) {
removeNode(getNode(sourceId, true))
insertNode(targetNode, position)
// 如果拖拽经过的元素是body或者是带有容器属性的盒子并且在元素内部插入,则需要特殊处理
if ((isBodyEl(element) || configure?.isContainer) && rectType === POSITION.IN) {
const { node } = isBodyEl(element) ? { node: getSchema() } : getNode(id, true) || {}
const children = node?.children || []
if (children.length > 0) {
// 如果容器盒子有子节点,则以最后一个子节点为拖拽参照物
const lastNode = children[children.length - 1]
childEle = querySelectById(lastNode.id)
const childComponentName = element.getAttribute(childEle)
const Childconfigure = getConfigure(childComponentName)
lineState.id = lastNode.id
lineState.configure = Childconfigure
}
}
// 如果容器盒子有子元素
if (childEle) {
const childRect = childEle.getBoundingClientRect()
const { left, height, top, width } = childRect
const { x, y } = getOffset(childEle)
Object.assign(lineState, {
width: width * scale,
height: height * scale,
top: top * scale + y,
left: left * scale + x,
position: canvasState.type === 'absolute' || getPosLine(childRect, lineState.configure).type
})
} else {
// 从外部拖拽进来的无IDinsert
if (absolute) {
targetNode.node = getSchema()
data.props = data.props || {}
data.props.style = {
position: 'absolute',
top: dragState.mouse.y + 'px',
left: dragState.mouse.x + 'px'
}
}
Object.assign(lineState, {
width: width * scale,
height: height * scale,
top: top * scale + y,
left: left * scale + x,
position: canvasState.type === 'absolute' || getPosLine(rect, configure).type
})
}
insertNode(targetNode, position)
useLayout().closePlugin()
}
// 设置元素hover状态
Object.assign(hoverState, {
width: width * scale,
height: height * scale,
top: top * scale + y,
left: left * scale + x,
componentName
})
return undefined
}
/**
* 是否允许插入
* @param {*} configure 当前放置目标的 configure比如getConfigure(componentName)
* @param {*} data 当前插入目标的schame数据
* @returns
*/
export const allowInsert = (configure = hoverState.configure || {}, data = dragState.data || {}) => {
const { nestingRule = {} } = configure
const { childWhitelist = [], descendantBlacklist = [] } = nestingRule
// 要插入的父节点必须是容器
if (!configure.isContainer) {
return false
}
let flag = true
// 白名单
flag = childWhitelist.length ? childWhitelist.includes(data?.componentName) : true
// 黑名单
if (descendantBlacklist.length) {
flag = !descendantBlacklist.includes(data?.componentName)
}
return flag
}
export const getConfigure = (targetName) => {
const material = getController().getMaterial(targetName)
// 这里如果是区块插槽,则返回标识为容器的对象
if (targetName === 'Template') {
return {
isContainer: true
}
}
// 重置拖拽状态
dragEnd()
return material?.content?.configure || material.configure || {}
}
// 获取位置信息,返回状态
const lineAbs = 20
const getPosLine = (rect, configure) => {
const mousePos = dragState.mouse
const yAbs = Math.min(lineAbs, rect.height / 3)
const xAbs = Math.min(lineAbs, rect.width / 3)
let type
if (mousePos.y < rect.top + yAbs) {
type = POSITION.TOP
} else if (mousePos.y > rect.bottom - yAbs) {
type = POSITION.BOTTOM
} else if (mousePos.x < rect.left + xAbs) {
type = POSITION.LEFT
} else if (mousePos.x > rect.right - xAbs) {
type = POSITION.RIGHT
} else if (configure.isContainer) {
type = allowInsert() ? POSITION.IN : POSITION.FORBID
} else {
type = POSITION.BOTTOM
}
return { type }
}
export const setPageCss = (css = '') => {
@ -773,16 +671,8 @@ export const setState = (state) => {
getRenderer().setState(state)
}
export const setUtils = (utils, clear, isForceRefresh) => {
getRenderer().setUtils(utils, clear, isForceRefresh)
}
export const updateUtils = (utils) => {
getRenderer().updateUtils(utils)
}
export const deleteUtils = (utils) => {
getRenderer().deleteUtils(utils)
export const setUtils = (utils) => {
getRenderer().setUtils(utils)
}
export const deleteState = (variable) => {
@ -794,6 +684,26 @@ export const setGlobalState = (state) => {
getRenderer().setGlobalState(state)
}
export const getGlobalState = () => getRenderer().getGlobalState()
export const getRenderer = () => canvasState.renderer
export const getController = () => canvasState.controller
export const getNode = (id, parent) => getRenderer()?.getNode(id, parent)
export const getDocument = () => canvasState.iframe.contentDocument
export const getWindow = () => canvasState.iframe.contentWindow
export const getCurrent = () => {
return {
schema: canvasState.current,
parent: canvasState.parent,
loopId: canvasState.loopId
}
}
export const getNodePath = (id, nodes = []) => {
const { parent, node } = getNode(id, true) || {}
@ -816,6 +726,8 @@ export const setSchema = async (schema) => {
return canvasState.schema
}
export const getSchema = () => getRenderer()?.getSchema()
export const setConfigure = (configure) => {
getRenderer().setConfigure(configure)
}
@ -848,49 +760,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()

View File

@ -7,9 +7,9 @@
<icon-close class="icon-close" @click="closePanel"></icon-close>
</span>
</div>
<ConfigGroup :group="propertiesList" :index="0">
<config-group :group="propertiesList" :index="0">
<template #item="{ data, propIndex }">
<ConfigItem
<config-item
:key="propIndex"
:property="data"
:data-prop-index="propIndex"
@ -22,19 +22,22 @@
<template v-if="!data.noBinding" #suffix>
<slot name="suffix" :data="data"></slot>
</template>
</ConfigItem>
</config-item>
</template>
</ConfigGroup>
</config-group>
</div>
</template>
<script>
import { computed, ref } from 'vue'
import { useResource, useProperties, useCanvas, useProperty } from '@opentiny/tiny-engine-controller'
import { ConfigGroup, ConfigItem } from '@opentiny/tiny-engine-common'
import { iconSetting, iconClose } from '@opentiny/vue-icon'
export default {
components: {
ConfigGroup,
ConfigItem,
IconSetting: iconSetting(),
IconClose: iconClose()
},

View File

@ -63,13 +63,13 @@ const setUtils = (data, clear, isForceRefresh) => {
const utilsCollection = {}
// 目前画布还不具备远程加载utils工具类的功能目前只能加载TinyVue组件库中的组件工具
data?.forEach((item) => {
const util = window.TinyVue[item.content.exportName]
const util = window.TinyVue[item.name]
if (util) {
utilsCollection[item.name] = util
}
// 此处需要把工具类中的icon图标也加入utils上下文环境
const utilIcon = window.TinyVueIcon[item.content.exportName]
const utilIcon = window.TinyVueIcon[item.name]
if (utilIcon) {
utilsCollection[item.name] = utilIcon
}
@ -88,19 +88,6 @@ const setUtils = (data, clear, isForceRefresh) => {
}
}
const updateUtils = (data) => {
setUtils(data, false, true)
}
const deleteUtils = (data) => {
data?.forEach((item) => {
if (utils[item.name]) {
delete utils[item.name]
}
})
setUtils([], false, true)
}
const setBridge = (data, clear) => {
clear && reset(bridge)
Object.assign(bridge, data)
@ -376,13 +363,6 @@ export default {
)
},
render() {
// 渲染画布增加根节点,与出码和预览保持一致
const rootChildrenSchema = {
componentName: 'div',
props: schema.props,
children: schema.children
}
return h(
'tiny-i18n-host',
{
@ -391,7 +371,9 @@ export default {
ref: 'page',
className: 'design-page'
},
schema.children?.length ? h(renderer, { schema: rootChildrenSchema, parent: schema }) : [h(CanvasEmpty)]
schema.children?.length
? schema.children.map((child) => h(renderer, { schema: child, parent: schema }))
: [h(CanvasEmpty)]
)
}
}
@ -399,8 +381,6 @@ export default {
export const api = {
getUtils,
setUtils,
updateUtils,
deleteUtils,
getBridge,
setBridge,
getMethods,
@ -424,3 +404,5 @@ export const api = {
setDataSourceMap,
setGlobalState
}
window.api = api

View File

@ -17,7 +17,6 @@ import { constants, utils } from '@opentiny/tiny-engine-utils'
import babelPluginJSX from '@vue/babel-plugin-jsx'
import { transformSync } from '@babel/core'
import i18nHost from '@opentiny/tiny-engine-i18n-host'
import { CanvasRow, CanvasCol, CanvasRowColContainer } from '@opentiny/tiny-engine-builtin-component'
import { context, conditions, setNode } from './context'
import {
CanvasBox,
@ -26,33 +25,27 @@ import {
CanvasText,
CanvasSlot,
CanvasImg,
CanvasPlaceholder
CanvasRow,
CanvasCol,
CanvasRowColContainer
} from '../builtin'
import { NODE_UID as DESIGN_UIDKEY, NODE_TAG as DESIGN_TAGKEY, NODE_LOOP as DESIGN_LOOPID } from '../common'
const { BROADCAST_CHANNEL } = constants
const { hyphenateRE } = utils
const customElements = {}
const transformJSX = (code) => {
const res = transformSync(code, {
const transformJSX = (code) =>
transformSync(code, {
plugins: [
[
babelPluginJSX,
{
pragma: 'h',
isCustomElement: (name) => customElements[name]
isCustomElement: (name) => custElements[name]
}
]
]
})
return (res.code || '')
.replace(/import \{.+\} from "vue";/, '')
.replace(/h\(_?resolveComponent\((.*?)\)/g, `h(this.getComponent($1)`)
.replace(/_?resolveComponent/g, 'h')
.replace(/_?createTextVNode\((.*?)\)/g, '$1')
.trim()
}
export const blockSlotDataMap = reactive({})
@ -67,8 +60,7 @@ const Mapper = {
Img: CanvasImg,
CanvasRow,
CanvasCol,
CanvasRowColContainer,
CanvasPlaceholder
CanvasRowColContainer
}
const { post } = useBroadcastChannel({ name: BROADCAST_CHANNEL.Notify })
@ -139,13 +131,18 @@ const isObject = (data) => {
export const isStateAccessor = (stateData) =>
stateData?.accessor?.getter?.type === 'JSFunction' || stateData?.accessor?.setter?.type === 'JSFunction'
// 规避创建function eslint报错
export const newFn = (...argv) => {
const Fn = Function
return new Fn(...argv)
const parseI18n = (i18n, scope, ctx) => {
return parseExpression(
{
type: 'JSExpression',
value: `this.i18n('${i18n.key}')`
},
scope,
{ i18n: i18nHost.global.t, ...ctx }
)
}
const parseExpression = (data, scope, ctx, isJsx = false) => {
const parseExpression = (data, scope, ctx) => {
try {
if (data.value.indexOf('this.i18n') > -1) {
ctx.i18n = i18nHost.global.t
@ -153,42 +150,16 @@ const parseExpression = (data, scope, ctx, isJsx = false) => {
ctx.t = i18nHost.global.t
}
const expression = isJsx ? transformJSX(data.value) : data.value
return newFn('$scope', `with($scope || {}) { return ${expression} }`).call(ctx, {
return newFn('$scope', `with($scope || {}) { return ${data.value} }`).call(ctx, {
...ctx,
...scope,
slotScope: scope
})
} catch (err) {
// 解析抛出异常,则再尝试解析 JSX 语法。如果解析 JSX 语法仍然出现错误isJsx 变量会确保不会再次递归执行解析
if (!isJsx) {
return parseExpression(data, scope, ctx, true)
}
return undefined
}
}
const parseI18n = (i18n, scope, ctx) => {
return parseExpression(
{
type: 'JSExpression',
value: `this.i18n('${i18n.key}', ${JSON.stringify(i18n.params)})`
},
scope,
{ i18n: i18nHost.global.t, ...ctx }
)
}
const renderDefault = (children, scope, parent) =>
children.map?.((child) =>
// eslint-disable-next-line no-use-before-define
h(renderer, {
schema: child,
scope,
parent
})
)
const parseJSSlot = (data, scope) => {
return ($scope) => renderDefault(data.value, { ...scope, ...$scope }, data)
}
@ -254,111 +225,20 @@ const parseFunctionString = (fnStr) => {
return null
}
const getPlainProps = (object = {}) => {
const { slot, ...rest } = object
const props = {}
if (slot) {
rest.slot = slot.name || slot
}
Object.entries(rest).forEach(([key, value]) => {
let renderKey = key
// html 标签属性会忽略大小写,所以传递包含大写的 props 需要转换为 kebab 形式的 props
if (!/on[A-Z]/.test(renderKey) && hyphenateRE.test(renderKey)) {
renderKey = hyphenate(renderKey)
}
if (['boolean', 'string', 'number'].includes(typeof value)) {
props[renderKey] = value
} else {
// 如果传给webcomponent标签的是对象或者数组需要使用.prop修饰符转化成h函数就是如下写法
props[`.${renderKey}`] = value
}
})
return props
}
const generateCollection = (schema) => {
if (schema.componentName === 'Collection' && schema.props?.dataSource && schema.children) {
schema.children.forEach((item) => {
const fetchData = item.props?.fetchData
const methodMatch = fetchData?.value?.match(/this\.(.+?)}/)
if (fetchData && methodMatch?.[1]) {
const methodName = methodMatch[1].trim()
// 缓存表格fetchData对应的数据源信息
collectionMethodsMap[methodName] = schema.props.dataSource
}
})
}
}
const generateBlockContent = (schema) => {
if (schema?.componentName === 'Collection') {
generateCollection(schema)
}
if (Array.isArray(schema?.children)) {
schema.children.forEach((item) => {
generateBlockContent(item)
})
}
}
const registerBlock = (componentName) => {
getController()
.registerBlock?.(componentName)
.then((res) => {
const blockSchema = res.content
// 拿到区块数据,建立区块中数据源的映射关系
generateBlockContent(blockSchema)
// 如果区块的根节点有百分比高度,则需要特殊处理,把高度百分比传递下去,适配大屏应用
if (/height:\s*?[\d|.]+?%/.test(blockSchema?.props?.style)) {
const blockDoms = document.querySelectorAll(hyphenate(componentName))
blockDoms.forEach((item) => {
item.style.height = '100%'
})
}
})
}
export const wrapCustomElement = (componentName) => {
const material = getController().getMaterial(componentName)
if (!Object.keys(material).length) {
registerBlock(componentName)
}
customElements[componentName] = {
name: componentName + '.ce',
render() {
return h(
hyphenate(componentName),
window.parent.TinyGlobalConfig.dslMode === 'Vue' ? getPlainProps(this.$attrs) : this.$attrs,
this.$slots.default?.()
)
}
}
return customElements[componentName]
}
export const getComponent = (name) => {
return (
Mapper[name] ||
getNative(name) ||
getBlock(name) ||
customElements[name] ||
(isHTMLTag(name) ? name : wrapCustomElement(name))
)
// 规避创建function eslint报错
export const newFn = (...argv) => {
const Fn = Function
return new Fn(...argv)
}
// 解析JSX字符串为可执行函数
const parseJSXFunction = (data, ctx) => {
try {
const newValue = transformJSX(data.value)
.code.replace(/import \{.+\} from "vue";/, '')
.replace(/h\(_?resolveComponent\((.*?)\)/g, `h(this.getComponent($1)`)
.replace(/_?resolveComponent/g, 'h')
.replace(/_?createTextVNode\((.*?)\)/g, '$1')
const fnInfo = parseFunctionString(newValue)
if (!fnInfo) throw Error('函数解析失败请检查格式。示例function fnName() { }')
@ -417,8 +297,6 @@ const parseLoopArgs = (_loop) => {
return undefined
}
export const getIcon = (name) => window.TinyVueIcon?.[name]?.() || ''
const parseObjectData = (data, scope, ctx) => {
if (!data) {
return data
@ -504,36 +382,124 @@ const stopEvent = (event) => {
return false
}
const generateSlotGroup = (children, isCustomElm, schema) => {
const slotGroup = {}
const getPlainProps = (object = {}) => {
const { slot, ...rest } = object
const props = {}
if (slot) {
rest.slot = slot.name || slot
}
Object.entries(rest).forEach(([key, value]) => {
let renderKey = key
// html 标签属性会忽略大小写,所以传递包含大写的 props 需要转换为 kebab 形式的 props
if (!/on[A-Z]/.test(renderKey) && hyphenateRE.test(renderKey)) {
renderKey = hyphenate(renderKey)
}
if (['boolean', 'string', 'number'].includes(typeof value)) {
props[renderKey] = value
} else {
// 如果传给webcomponent标签的是对象或者数组需要使用.prop修饰符转化成h函数就是如下写法
props[`.${renderKey}`] = value
}
})
return props
}
const custElements = {}
const generateCollection = (schema) => {
if (schema.componentName === 'Collection' && schema.props?.dataSource && schema.children) {
schema.children.forEach((item) => {
const fetchData = item.props?.fetchData
const methodMatch = fetchData?.value?.match(/this\.(.+?)}/)
if (fetchData && methodMatch?.[1]) {
const methodName = methodMatch[1].trim()
// 缓存表格fetchData对应的数据源信息
collectionMethodsMap[methodName] = schema.props.dataSource
}
})
}
}
const generateBlockContent = (schema) => {
if (schema?.componentName === 'Collection') {
generateCollection(schema)
}
if (Array.isArray(schema?.children)) {
schema.children.forEach((item) => {
generateBlockContent(item)
})
}
}
const registerBlock = (componentName) => {
getController()
.registerBlock?.(componentName)
.then((res) => {
const blockSchema = res.content
// 拿到区块数据,建立区块中数据源的映射关系
generateBlockContent(blockSchema)
// 如果区块的根节点有百分比高度,则需要特殊处理,把高度百分比传递下去,适配大屏应用
if (/height:\s*?[\d|.]+?%/.test(blockSchema?.props?.style)) {
const blockDoms = document.querySelectorAll(hyphenate(componentName))
blockDoms.forEach((item) => {
item.style.height = '100%'
})
}
})
}
export const wrapCustomElement = (componentName) => {
const material = getController().getMaterial(componentName)
if (!Object.keys(material).length) {
registerBlock(componentName)
}
custElements[componentName] = {
name: componentName + '.ce',
render() {
return h(
hyphenate(componentName),
window.parent.TinyGlobalConfig.dslMode === 'Vue' ? getPlainProps(this.$attrs) : this.$attrs,
this.$slots.default?.()
)
}
}
return custElements[componentName]
}
const goupSlot = (children, isCustElm) => {
const slotGrup = {}
children.forEach((child) => {
const { componentName, children, params = [], props } = child
const slot = child.slot || props?.slot?.name || props?.slot || 'default'
const isNotEmptyTemplate = componentName === 'Template' && children.length
const slot = child.slot || props?.slot || 'default'
isCustomElm && (child.props.slot = 'slot') // CE下需要给子节点加上slot标识
slotGroup[slot] = slotGroup[slot] || {
isCustElm && (child.props.slot = 'slot') // CE下需要给子节点加上slot标识
slotGrup[slot] = slotGrup[slot] || {
value: [],
params,
parent: isNotEmptyTemplate ? child : schema
params
}
slotGroup[slot].value.push(...(isNotEmptyTemplate ? children : [child])) // template 标签直接过滤掉
slotGrup[slot].value.push(...(componentName === 'Template' ? children : [child])) // template 标签直接过滤掉
})
return slotGroup
return slotGrup
}
const renderSlot = (children, scope, schema, isCustomElm) => {
const renderSlot = (children, scope, schema, isCustElm) => {
if (children.some((a) => a.componentName === 'Template')) {
const slotGroup = generateSlotGroup(children, isCustomElm, schema)
const slotGrup = goupSlot(children, isCustElm)
const slots = {}
Object.keys(slotGroup).forEach((slotName) => {
const currentSlot = slotGroup[slotName]
slots[slotName] = ($scope) => renderDefault(currentSlot.value, { ...scope, ...$scope }, currentSlot.parent)
Object.keys(slotGrup).forEach((slotName) => {
slots[slotName] = ($scope) => renderDefault(slotGrup[slotName].value, { ...scope, ...$scope }, schema)
})
return slots
@ -542,6 +508,18 @@ const renderSlot = (children, scope, schema, isCustomElm) => {
return { default: () => renderDefault(children, scope, schema) }
}
export const getComponent = (name) => {
return (
Mapper[name] ||
getNative(name) ||
getBlock(name) ||
custElements[name] ||
(isHTMLTag(name) ? name : wrapCustomElement(name))
)
}
export const getIcon = (name) => window.TinyVueIcon?.[name]?.() || ''
const checkGroup = (componentName) => configure[componentName]?.nestingRule?.childWhitelist?.length
const clickCapture = (componentName) => configure[componentName]?.clickCapture !== false
@ -550,10 +528,6 @@ const getBindProps = (schema, scope) => {
const { id, componentName } = schema
const invalidity = configure[componentName]?.invalidity || []
if (componentName === 'CanvasPlaceholder') {
return {}
}
const bindProps = {
...parseData(schema.props, scope),
[DESIGN_UIDKEY]: id,
@ -587,30 +561,14 @@ const getBindProps = (schema, scope) => {
return bindProps
}
const getLoopScope = ({ scope, index, item, loopArgs }) => {
return {
...scope,
...(parseLoopArgs({
item,
index,
loopArgs
}) || {})
}
}
const injectPlaceHolder = (componentName, children) => {
const isEmptyArr = Array.isArray(children) && !children.length
if (configure[componentName]?.isContainer && (!children || isEmptyArr)) {
return [
{
componentName: 'CanvasPlaceholder'
}
]
}
return children
}
const renderDefault = (children, scope, parent) =>
children.map?.((child) =>
h(renderer, {
schema: child,
scope,
parent
})
)
const renderGroup = (children, scope, parent) => {
return children.map?.((schema) => {
@ -631,14 +589,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,25 +600,32 @@ const renderGroup = (children, scope, parent) => {
})
}
const getLoopScope = ({ scope, index, item, loopArgs }) => {
return {
...scope,
...(parseLoopArgs({
item,
index,
loopArgs
}) || {})
}
}
const getChildren = (schema, mergeScope) => {
const { componentName, children } = schema
const renderChildren = injectPlaceHolder(componentName, children)
const component = getComponent(componentName)
const isNative = typeof component === 'string'
const isCustomElm = customElements[componentName]
const isCustElm = custElements[componentName]
const isGroup = checkGroup(componentName)
if (Array.isArray(renderChildren)) {
if (isNative || isCustomElm) {
return renderDefault(renderChildren, mergeScope, schema)
if (Array.isArray(children)) {
if (isNative || isCustElm) {
return renderDefault(children, mergeScope, schema)
} else {
return isGroup
? renderGroup(renderChildren, mergeScope, schema)
: renderSlot(renderChildren, mergeScope, schema, isCustomElm)
return isGroup ? renderGroup(children, mergeScope, schema) : renderSlot(children, mergeScope, schema, isCustElm)
}
} else {
return parseData(renderChildren, mergeScope)
return parseData(children, mergeScope)
}
}

View File

@ -13,7 +13,7 @@
import * as Vue from 'vue'
import * as VueI18n from 'vue-i18n'
import { addScript, addStyle, dynamicImportComponents, updateDependencies } from '../common'
import TinyI18nHost, { I18nInjectionKey } from '@opentiny/tiny-engine-controller/js/i18n'
import TinyI18nHost, { I18nInjectionKey } from '@opentiny/tiny-engine-common/js/i18n'
import * as TinyWebcomponentCore from '@opentiny/tiny-engine-webcomponent-core'
import TinyVue from '@opentiny/vue'
import * as TinyVueIcon from '@opentiny/vue-icon'
@ -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 = {
@ -115,15 +111,11 @@ const create = () => {
document.body.remove()
document.body = document.createElement('body')
const app = document.createElement('div')
app.setAttribute('id', 'app')
document.body.appendChild(app)
dispatch('canvasReady', { detail: renderer })
App = Vue.createApp(Main).use(TinyI18nHost).provide(I18nInjectionKey, TinyI18nHost)
App.config.globalProperties.lowcodeConfig = window.parent.TinyGlobalConfig
App.mount(document.querySelector('#app'))
App.mount(document.body)
new ResizeObserver(() => {
dispatch('canvasResize')
@ -133,7 +125,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 +134,5 @@ export const createRender = (config) => {
...scripts.map((src) => addScript(src)).concat([...thirdStyles, ...styles].map((src) => addStyle(src)))
]).finally(create)
}
document.addEventListener('updateDependencies', updateDependencies)

View File

@ -10,7 +10,89 @@
*
*/
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,
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,
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

View File

@ -23,6 +23,9 @@ export default defineConfig({
'@': path.resolve(__dirname, 'src')
}
},
define: {
'process.env': {}
},
plugins: [
vue({
template: {

View File

@ -96,7 +96,7 @@ export default {
const params = []
const data = props?.data?.params || {}
curValue.replace(/\{(.+?)\}/g, (substr, key) => {
curValue.replace(/\$\{(.+?)\}/g, (substr, key) => {
key && params.push({ name: key, value: data[key] || '' })
})
paramsForm.value = params

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -103,14 +103,14 @@ import { inject, computed, watch, ref, reactive, provide } from 'vue'
import { Popover, Tooltip } from '@opentiny/vue'
import { IconWriting, IconHelpCircle, IconPlusCircle } from '@opentiny/vue-icon'
import { typeOf } from '@opentiny/vue-renderless/common/type'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import i18n from '../js/i18n'
import { MetaComponents } from '../index'
import MetaBindVariable from './MetaBindVariable.vue'
import MetaCodeEditor from './MetaCodeEditor.vue'
import MultiTypeSelector from './MultiTypeSelector.vue'
import { useHistory, useProperties, useResource, useLayout, useCanvas } from '@opentiny/tiny-engine-controller'
import { generateFunction } from '@opentiny/tiny-engine-controller/utils'
import { SCHEMA_DATA_TYPE, PAGE_STATUS, TYPES } from '@opentiny/tiny-engine-controller/js/constants'
import { SCHEMA_DATA_TYPE, PAGE_STATUS, TYPES } from '../js/constants'
const hasRule = (required, rules) => {
if (required) {
@ -244,7 +244,7 @@ export default {
})
const updateValue = (value) => {
const { property, type } = props.property
const { property } = props.property
const { setProp } = useProperties()
//
@ -283,7 +283,7 @@ export default {
}
if (props.isTopLayer) {
setProp(property, value, type)
setProp(property, value)
}
}

View File

@ -78,13 +78,14 @@
<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'
import { initCompletion } from '@opentiny/tiny-engine-controller/js/completion'
import { initLinter, lint } from '@opentiny/tiny-engine-controller/js/linter'
import { initCompletion } from '../js/completion'
import { initLinter, lint } from '../js/linter'
import { SvgButton } from '../index'
export default {
@ -124,12 +125,11 @@ export default {
bindLifeCycles: {},
editorValue: '{}',
hasError: false,
linterWorker: null,
completionProvider: null
linterWorker: null
})
watchEffect(() => {
state.bindLifeCycles = props.bindLifeCycles || useCanvas().canvasApi.value?.getSchema()?.lifeCycles || {}
state.bindLifeCycles = props.bindLifeCycles || getSchema()?.lifeCycles || {}
})
const searchLifeCyclesList = (value) => {
@ -141,7 +141,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 +164,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 }' : ''}) {} `
@ -211,7 +210,7 @@ export default {
return
}
// Lowcode API
state.completionProvider = initCompletion(editorRef.value.getMonaco(), editorRef.value.getEditor()?.getModel())
initCompletion(editorRef.value.getMonaco())
// ESLint worker
state.linterWorker = initLinter(editor, editorRef.value.getMonaco(), state)
@ -227,9 +226,6 @@ export default {
}
onBeforeUnmount(() => {
state.completionProvider?.forEach?.((provider) => {
provider.dispose()
})
// ESLint worker
state.linterWorker?.terminate?.()
})

View File

@ -1,41 +0,0 @@
<template>
<a class="help-box" :href="href" target="_blank">
<svg-button id="help-icon" :name="iconName" :tips="tips"></svg-button>
</a>
</template>
<script>
import SvgButton from './SvgButton.vue'
export default {
components: {
SvgButton
},
props: {
href: {
type: String,
default: ''
},
tips: {
type: String,
default: '帮助'
},
iconName: {
type: String,
default: 'plugin-icon-plugin-help'
}
}
}
</script>
<style lang="less" scoped>
.help-box {
width: 24px;
height: 42px;
#help-icon {
:deep(.svg-icon.icon-plugin-icon-plugin-help) {
font-size: 16px;
}
}
}
</style>

View File

@ -6,7 +6,6 @@
</slot>
<tiny-dialog-box
v-if="dialogShouldInitialize"
:visible="state.isVisible"
title="变量绑定"
width="48%"
@ -124,14 +123,15 @@ 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'
import SvgButton from './SvgButton.vue'
import { parse, traverse, generate } from '@opentiny/tiny-engine-controller/js/ast'
import { DEFAULT_LOOP_NAME } from '@opentiny/tiny-engine-controller/js/constants'
import { parse, traverse, generate } from '../js/ast'
import { DEFAULT_LOOP_NAME } from '../js/constants.js'
import MonacoEditor from './VueMonaco.vue'
import { formatString } from '@opentiny/tiny-engine-controller/js/ast'
import { formatString } from '../js/ast.js'
const { EXPRESSION_TYPE } = constants
@ -149,12 +149,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]
@ -187,10 +181,6 @@ export default {
modelValue: {
type: [String, Number, Boolean, Array, Object, Date],
default: ''
},
lazyLoad: {
type: Boolean,
default: true
}
},
setup(props, { emit }) {
@ -403,10 +393,10 @@ export default {
const confirm = () => {
let variableContent = state.isEditorEditMode ? editor.value?.getEditor().getValue() : state.variable
const { setSaved, canvasApi } = useCanvas()
//
if (oldValue !== variableContent) {
setSaved(false)
const { pageState } = useCanvas()
pageState.isSaved = false
variableContent = formatString(variableContent, 'javascript')
}
@ -415,14 +405,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)
@ -451,13 +441,11 @@ export default {
return ''
}
const dialogShouldInitialize = ref(!props.lazyLoad)
const open = () => {
dialogShouldInitialize.value = true
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 +453,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 +468,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 +494,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 +515,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]
}
}
@ -540,7 +526,6 @@ export default {
remove,
cancel,
confirm,
dialogShouldInitialize,
open,
selectItem,
state,
@ -553,7 +538,7 @@ export default {
<style lang="less" scoped>
.icon {
margin-left: 8px;
margin-left: 6px;
}
.meta-bind-variable-dialog-box {

View File

@ -11,7 +11,7 @@
<script>
import { ref, watchEffect } from 'vue'
import { ButtonGroup } from '@opentiny/vue'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import i18n from '../js/i18n'
export default {
name: 'MetaButtonGroup',

View File

@ -16,7 +16,7 @@
<script>
import { ref, watchEffect } from 'vue'
import { Cascader } from '@opentiny/vue'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import i18n from '../js/i18n'
export default {
name: 'MetaCascader',

View File

@ -10,7 +10,7 @@
<script>
import { ref, watchEffect, computed } from 'vue'
import { CheckboxGroup } from '@opentiny/vue'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import i18n from '../js/i18n'
export default {
name: 'MetaCheckboxGroup',

View File

@ -1,29 +1,28 @@
<template>
<div class="editor-wrap">
<slot :open="open">
<slot>
<div v-if="buttonShowContent" :class="['full-width', { 'empty-color': value === '' }]" @click="open">
<span class="text-content text-ellipsis-multiple">{{ value === '' ? buttonLabel : value }}</span>
<svg-icon class="edit-icon" name="edit"></svg-icon>
<span>{{ value === '' ? buttonLabel : value?.slice(0, 30) }}</span>
<svg-icon class="edit-icon" name="flow-edit"></svg-icon>
</div>
<tiny-button v-else class="edit-btn" @click="open">
{{ buttonLabel }}
</tiny-button>
</slot>
<tiny-dialog-box
v-model:visible="editorState.show"
:title="titleLabel"
width="50vw"
class="meta-code-editor-dialog-box"
append-to-body
:close-on-click-modal="false"
v-model:visible="editorState.show"
:title="titleLabel"
width="50vw"
class="meta-code-editor-dialog-box"
append-to-body
>
<div class="source-code">
<div v-if="editorTipsTitle" class="header-tips-container">
<span class="header-tips-title" :title="editorTipsTitle">{{ editorTipsTitle }}</span>
<div
v-if="editorTipsDemo"
class="header-tips-showdemo"
@click="editorState.showEditorDemo = !editorState.showEditorDemo"
v-if="editorTipsDemo"
class="header-tips-showdemo"
@click="editorState.showEditorDemo = !editorState.showEditorDemo"
>
<span>{{ editorState.showEditorDemo ? $t('common.collapseExample') : $t('common.expandExample') }}</span>
<icon-chevron-up v-if="editorState.showEditorDemo" class="collapse-icon"></icon-chevron-up>
@ -36,28 +35,22 @@
</div>
</div>
<monaco-editor
ref="editor"
class="source-code-content"
:value="value"
:options="options"
@editorDidMount="editorDidMount"
ref="editor"
class="source-code-content"
:value="value"
:options="options"
@editorDidMount="editorDidMount"
></monaco-editor>
<div v-if="showErrorMsg" class="error-msg">{{ editorState.errorMsg }}</div>
</div>
<template #footer>
<div class="btn-box">
<tiny-button
v-if="language === 'json' && showFormatBtn"
class="format-btn"
plain
type="danger"
@click="formatCode"
>
<tiny-button type="danger" v-if="language === 'json' && showFormatBtn" @click="formatCode">
{{ $t('common.format') }}
</tiny-button>
<div>
<tiny-button @click="close">{{ $t('common.cancel') }}</tiny-button>
<tiny-button type="primary" @click="save">{{ $t('common.save') }}</tiny-button>
<tiny-button @click="close">{{ $t('common.close') }}</tiny-button>
</div>
</div>
</template>
@ -70,8 +63,8 @@ import { reactive, ref, computed, watchEffect, nextTick } from 'vue'
import { Button, DialogBox } from '@opentiny/vue'
import { iconChevronDown, iconChevronUp } from '@opentiny/vue-icon'
import VueMonaco from './VueMonaco.vue'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import { formatString } from '@opentiny/tiny-engine-controller/js/ast'
import i18n from '../js/i18n'
import { formatString } from '../js/ast.js'
export default {
components: {
@ -146,7 +139,7 @@ export default {
watchEffect(() => {
const { modelValue, dataType } = props
const val = dataType ? modelValue?.value || '' : modelValue
const val = dataType ? modelValue?.value : modelValue
value.value = typeof val === 'string' ? val : JSON.stringify(val, null, 2)
})
@ -276,32 +269,19 @@ export default {
}
.btn-box {
display: flex;
justify-content: flex-end;
&:has(.format-btn) {
justify-content: space-between;
}
justify-content: space-between;
}
.full-width {
border: 1px solid #adb0b8;
border-radius: 2px;
padding: 4px 8px;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 32px;
padding: 4px 8px;
border: 1px solid var(--ti-lowcode-meta-codeEditor-border-color);
border-radius: 6px;
&:hover {
border-color: var(--ti-lowcode-meta-codeEditor-border-hover-color);
}
.text-content {
--ellipsis-line: 1;
}
&.empty-color {
color: var(--ti-lowcode-common-text-desc-color);
}
.edit-icon {
margin-left: 4px;
flex-shrink: 0;
cursor: pointer;
color: var(--ti-lowcode-common-text-main-color);
}

View File

@ -1,6 +1,6 @@
<template>
<div>
<meta-list-items class="list" :optionsList="children" @dragEnd="dragEnd">
<meta-list-items class="list" :optionsList="children">
<template #content="{ data }">
<div class="item-text">
<div class="tiny-input">
@ -67,21 +67,7 @@ export default {
useHistory().addHistory()
}
const dragEnd = (params = {}) => {
const { oldIndex, newIndex } = params?.moved || {}
if (oldIndex === undefined || newIndex === undefined) {
return
}
const list = schemaChildren
const spliceItem = list.splice(oldIndex, 1)
list.splice(newIndex, 0, ...spliceItem)
children.value = [...list]
}
return { children, addChildren, delChildren, dragEnd }
return { children, addChildren, delChildren }
}
}
</script>

View File

@ -49,12 +49,11 @@
<script>
import { reactive, ref, watchEffect } from 'vue'
import { useProperties, useResource } from '@opentiny/tiny-engine-controller'
import { IconDel, IconEdit, IconClose, IconPlus } from '@opentiny/vue-icon'
import { Form, FormItem, Input, Button, Popover, Tooltip } from '@opentiny/vue'
import { IconDel, IconEdit, IconClose } from '@opentiny/vue-icon'
import { Form, FormItem, Input, Button, Popover } from '@opentiny/vue'
import { utils } from '@opentiny/tiny-engine-utils'
export default {
inheritAttrs: false,
components: {
TinyForm: Form,
TinyFormItem: FormItem,
@ -63,9 +62,7 @@ export default {
IconEdit: IconEdit(),
IconDel: IconDel(),
TinyPopover: Popover,
IconClose: IconClose(),
IconPlus: IconPlus(),
TinyTooltip: Tooltip
IconClose: IconClose()
},
props: {
modelValue: {

View File

@ -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>
@ -19,7 +19,6 @@ import { useProperties } from '@opentiny/tiny-engine-controller'
import MetaInput from './MetaInput.vue'
export default {
inheritAttrs: false,
components: {
MetaInput
},

View File

@ -37,7 +37,7 @@ import { ref, inject, watchEffect, reactive } from 'vue'
import { Input, Tooltip, Form, FormItem } from '@opentiny/vue'
import { useProperties, useCanvas, useModal } from '@opentiny/tiny-engine-controller'
import SvgICons from '@opentiny/vue-icon'
import { verifyJsVarName } from '@opentiny/tiny-engine-controller/js/verification'
import { verifyJsVarName } from '../js/verification'
export default {
components: {

View File

@ -118,8 +118,8 @@ export default {
emit('deleteItem', params)
}
const dragEnd = (value) => {
emit('dragEnd', value)
const dragEnd = () => {
emit('changeItem')
}
const editItem = (params) => {

View File

@ -21,7 +21,7 @@
<script>
import { ref, watchEffect, computed } from 'vue'
import { RadioGroup, RadioButton } from '@opentiny/vue'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import i18n from '../js/i18n'
export default {
name: 'MetaRadioGroup',

View File

@ -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]
}

View File

@ -45,7 +45,7 @@
<script>
import { computed, reactive, watchEffect } from 'vue'
import { Select, Option, OptionGroup, Tooltip } from '@opentiny/vue'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import i18n from '../js/i18n'
export default {
components: {

View File

@ -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 {

View File

@ -1,32 +0,0 @@
<template>
<MetaArrayItem v-bind="$attrs" @update:modelValue="updateColumns"></MetaArrayItem>
</template>
<script setup>
import { nextTick } from 'vue'
import { useProperties, useResource, useCanvas } from '@opentiny/tiny-engine-controller'
import MetaArrayItem from './MetaArrayItem.vue'
const { children: schemaChildren, componentName, props } = useProperties().getSchema()
const configureMap = useResource().getConfigureMap()
const childComponentName =
configureMap[componentName]?.nestingRule?.childWhitelist?.[0] || schemaChildren?.[0]?.componentName
const updateColumns = (columns) => {
if (!columns?.length) {
return
}
const children = columns.map((item) => {
return {
componentName: childComponentName,
props: { ...item }
}
})
useProperties().getSchema().children = children
nextTick(useCanvas().canvasApi.value.updateRect)
}
updateColumns(props?.columns)
</script>

View File

@ -1,9 +1,6 @@
<template>
<div :class="['editor-container', { 'editor-container-full': fullscreen }]">
<div class="toolbar">
<div class="toolbar-start">
<slot name="toolbarStart"></slot>
</div>
<div>
<div :class="['toolbar', { _full: fullscreen }]">
<div class="buttons">
<slot name="buttons"></slot>
<tiny-tooltip v-if="showFormatBtn && options.language === 'json'" content="格式化" placement="top">
@ -11,17 +8,17 @@
</tiny-tooltip>
<span v-if="showFullScreenBtn">
<tiny-tooltip v-if="!fullscreen" content="全屏" placement="top">
<public-icon name="full-screen" @click="switchFullScreen(true)"></public-icon>
<public-icon name="full-screen" @click="fullscreen = true"></public-icon>
</tiny-tooltip>
<tiny-tooltip v-else content="退出全屏" placement="top">
<public-icon name="cancel-full-screen" @click="switchFullScreen(false)"></public-icon>
<public-icon name="cancel-full-screen" @click="fullscreen = false"></public-icon>
</tiny-tooltip>
</span>
</div>
</div>
<monaco-editor
ref="editor"
class="editor"
:class="fullscreen ? 'fullscreen' : 'iniline'"
:value="value"
:options="editorOptions"
language="javascript"
@ -113,17 +110,12 @@ export default {
editor.value.getEditor().dispose()
})
const switchFullScreen = (value) => {
fullscreen.value = value
}
return {
editorOptions,
editor,
getEditor,
getEditorValue,
fullscreen,
switchFullScreen,
getValue,
formatCode
}
@ -132,45 +124,41 @@ export default {
</script>
<style lang="less" scoped>
.editor-container {
display: flex;
flex-direction: column;
padding: 0;
}
.editor-container-full {
position: fixed;
top: var(--base-top-panel-height);
bottom: 0;
left: calc(var(--base-nav-panel-width) + var(--base-left-panel-width));
right: var(--base-left-panel-width);
z-index: 10;
padding: 10px 16px 16px 16px;
background-color: var(--ti-lowcode-common-component-bg);
height: auto !important;
}
.toolbar {
display: flex;
margin-bottom: 4px;
justify-content: space-between;
justify-content: flex-end;
align-items: center;
z-index: 3000 !important;
.buttons {
display: flex;
gap: 8px;
color: var(--ti-lowcode-component-svg-button-color);
cursor: pointer;
:hover {
&:hover {
color: var(--ti-lowcode-component-svg-button-hover-color);
}
}
&._full {
position: fixed;
top: 35px;
z-index: 1;
padding-right: 15px;
right: var(--base-left-panel-width);
}
}
.editor {
flex: 1;
overflow: hidden;
border: 1px solid var(--ti-lowcode-state-management-monaco-editor-border-color);
border-radius: 6px;
.iniline {
height: 100%;
width: 100%;
}
.fullscreen {
position: fixed;
top: 35px;
bottom: 0;
left: calc(var(--base-nav-panel-width) + 1px);
right: var(--base-left-panel-width);
z-index: 2500;
}
.public-icon {
font-size: 18px;
margin-left: 10px;
}
</style>

View File

@ -1,7 +1,12 @@
<template>
<ul
v-if="state.data.length"
:class="['block-list', 'lowcode-scrollbar', { 'is-small-list': blockStyle === 'mini' }, { isShortcutPanel }]"
:class="[
'block-list',
'lowcode-scrollbar',
{ 'is-list': blockStyle === 'list' },
{ 'is-small-list': blockStyle === 'mini' },
{ isShortcutPanel }
]"
@mouseleave="state.hover = false"
>
<li
@ -12,6 +17,7 @@
'block-item',
{ 'is-active': state.activeIndex === index },
{ 'is-disabled': showBlockDetail },
{ 'block-item-list': blockStyle === 'list' },
{ 'block-item-small-list': blockStyle === 'mini' }
]"
:title="getTitle(item)"
@ -123,14 +129,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 +147,7 @@ export default {
IconSetting: iconSetting(),
IconPlus: iconPlus(),
TinyPopover: Popover,
SvgButton,
SearchEmpty
SvgButton
},
props: {
data: {
@ -348,7 +351,7 @@ export default {
position: fixed;
z-index: 9999;
top: 50px;
left: calc(var(--base-left-panel-width) + var(--base-nav-panel-width) + 10px);
left: calc(var(--base-left-panel-width, 268px) + var(--base-nav-panel-width) + 10px);
max-width: 500px;
max-height: 136px;
padding: 12px;
@ -385,7 +388,10 @@ export default {
overflow-y: auto;
overflow-x: hidden;
color: var(--ti-lowcode-common-secondary-text-color);
&.is-list {
grid-template-columns: 100%;
grid-template-rows: repeat(auto-fill, 60px);
}
&.is-small-list {
grid-template-columns: 100%;
grid-template-rows: repeat(auto-fill, 30px);
@ -421,7 +427,36 @@ export default {
border-radius: 4px 0 4px 0;
transform: scale(0.9);
}
&.block-item-list {
flex-direction: row;
height: 60px;
.item-image {
width: 50px;
}
.item-text {
text-align: left;
margin-top: 0;
margin-left: 4px;
}
.publish-flag {
position: static;
}
.block-detail,
.block-setting {
visibility: hidden;
position: static;
margin-left: 4px;
z-index: 9;
.block-detail-icon {
color: var(--ti-lowcode-component-block-list-setting-btn-color);
display: block;
&:hover {
cursor: not-allowed;
color: var(--ti-lowcode-component-block-list-setting-btn-hover-color);
}
}
}
}
&.block-item-small-list {
flex-direction: row;
align-items: center;
@ -450,7 +485,7 @@ export default {
color: var(--ti-lowcode-component-block-list-setting-btn-color);
display: block;
&:hover {
cursor: pointer;
cursor: not-allowed;
color: var(--ti-lowcode-component-block-list-setting-btn-hover-color);
}
}
@ -557,7 +592,7 @@ export default {
.block-detail-icon {
color: var(--ti-lowcode-component-block-list-setting-btn-color);
&:hover {
cursor: pointer;
cursor: not-allowed;
color: var(--ti-lowcode-component-block-list-setting-btn-hover-color);
}
}
@ -651,6 +686,7 @@ export default {
}
}
&.is-list,
&.is-small-list {
display: block;
grid-template-columns: initial;
@ -681,6 +717,22 @@ export default {
}
}
&.is-list {
.block-item {
height: 58px;
}
.item-image {
font-size: 2.5em;
width: 48px;
height: 42px;
}
.item-text {
width: calc(100% - 56px);
}
}
&.is-small-list {
.block-item {
height: 38px;

View File

@ -125,7 +125,7 @@ export default {
<style lang="less" scoped>
.plugin-setting {
position: absolute;
left: calc(var(--base-left-panel-width) - 6px);
left: calc(var(--base-left-panel-width, 268px) - 6px);
top: 0;
width: var(--base-collection-panel-width);
height: 100%;

View File

@ -1,18 +1,10 @@
<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"
:rules="rules"
ref="formRef"
label-width="120px"
:label-align="true"
label-position="left"
@ -41,10 +33,10 @@
</template>
<script>
import { reactive, computed, ref } from 'vue'
import { reactive, computed } from 'vue'
import { Input, Form, FormItem, Button, DialogBox, Select } from '@opentiny/vue'
import { useBlock, useLayout, useCanvas, useModal } from '@opentiny/tiny-engine-controller'
import { REGEXP_BLOCK_NAME } from '@opentiny/tiny-engine-controller/js/verification'
import { REGEXP_BLOCK_NAME } from '../js/verification'
export default {
components: {
@ -72,7 +64,6 @@ export default {
const { PLUGIN_NAME, activePlugin } = useLayout()
const { isSaved } = useCanvas()
const { confirm } = useModal()
const formRef = ref(null)
const categoryList = computed(() =>
getCategoryList().map((item) => ({ ...item, value: item.id, label: item.name }))
@ -93,21 +84,15 @@ export default {
cancel()
}
const addBlock = () => {
formRef.value.validate((valid) => {
if (!valid) {
return
}
if (isSaved()) {
if (isSaved()) {
handleAddBlock()
return
}
confirm({
message: '当前画布内容尚未保存,新建区块会将画布切换至新区块中,是否要继续切换?',
exec: () => {
handleAddBlock()
return
}
confirm({
message: '当前画布内容尚未保存,新建区块会将画布切换至新区块中,是否要继续切换?',
exec: () => {
handleAddBlock()
}
})
})
}
@ -122,7 +107,6 @@ export default {
return {
formData,
categoryList,
formRef,
rules,
addBlock,
cancel,

View File

@ -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>

View File

@ -4,7 +4,7 @@
<script>
import * as monacoEditor from 'monaco-editor'
import { watch, onMounted, nextTick, onBeforeUnmount, ref } from 'vue'
import { formatString } from '@opentiny/tiny-engine-controller/js/ast'
import { formatString } from '../js/ast.js'
export default {
name: 'MonacoEditor',

View File

@ -1,19 +1,18 @@
/**
* 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 PluginSetting from './component/PluginSetting.vue'
import PluginPanel from './component/PluginPanel.vue'
import SvgButton from './component/SvgButton.vue'
import LinkButton from './component/LinkButton.vue'
import ConfigCollapse from './component/ConfigCollapse.vue'
import ConfigGroup from './component/ConfigGroup.vue'
@ -78,10 +77,8 @@ import MetaRelatedColumns from './component/MetaRelatedColumns.vue'
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'
import i18n, { i18nKeyMaps } from './js/i18n.js'
import enUs from './i18n/en-us.json'
import zhCn from './i18n/zh-cn.json'
@ -91,20 +88,6 @@ const { mergeLocaleMessage } = i18n.global
mergeLocaleMessage(i18nKeyMaps.enUS, enUs)
mergeLocaleMessage(i18nKeyMaps.zhCN, zhCn)
const globalComponents = {
SaveNewBlock,
ConfigGroup,
ConfigItem
}
export const injectGlobalComponents = {
install: (app) => {
Object.entries(globalComponents).forEach(([name, component]) => {
app.component(name, component)
})
}
}
export const MetaComponents = {
MetaArrayItem,
MetaBindI18n,
@ -145,9 +128,8 @@ export const MetaComponents = {
MetaCheckboxGroup,
MetaIpSection,
MetaRelatedEditor,
MetaRelatedColumns,
MetaTableColumns,
SearchEmpty
MetaRelatedColumns
}
export {
@ -186,7 +168,6 @@ export {
PluginPanel,
VideoGuide,
SvgButton,
LinkButton,
BlockHistoryList,
BlockHistoryTemplate,
BlockLinkField,
@ -207,7 +188,5 @@ export {
SaveNewBlock,
BindI18n,
BlockDeployDialog,
ProgressBar,
MetaTableColumns,
SearchEmpty
ProgressBar
}

Some files were not shown because too many files have changed in this diff Show More