Compare commits

...

61 Commits

Author SHA1 Message Date
chilingling e651dffa2b
fix: slot params missing double quote (#605)
* fix: slot params missing double quote

* fix: exclude nodemodule test case
2024-07-05 17:36:09 +08:00
chilingling 91d3ae108c
fix(material): add componentsMap to app Schema after material build (#527) 2024-07-05 14:03:32 +08:00
chilingling 277be2f11e
fix(vue-generator): fix globalstate codegen error (#547) 2024-07-05 11:06:11 +08:00
chilingling 2c478ce391
fix(metaComp): fix bug where metaHtmlText could set value to incorret schema children (#473) 2024-06-27 20:41:07 +08:00
chilingling c341f4c2c5
fix(style): fix render error caused by inline style breaks (#526) 2024-06-27 20:40:51 +08:00
chilingling 858e178af6
fix(mockServer): mockServer page preview can't render element-plus element (#503) 2024-05-27 19:19:29 +08:00
chilingling 4bfba8ca40
fix(chore): exclude vue-generator expected folder codecheck (#496) 2024-05-21 15:08:03 +08:00
chilingling 07b0245fa0
fix(setup): add controller to prebuild before start local develop (#493) 2024-05-17 17:32:00 +08:00
Kagol a0a4e293d8
chore: Upgrade dependencies to fix security vulnerability (#479) 2024-05-17 11:44:44 +08:00
Gene ffd0280877
fix: 事件绑定JSX函数时,函数解析报错 (#441)
* fix: 绑定事件时保留函数名,避免JSX转换出错
2024-05-17 11:42:10 +08:00
Gene 47c71d1129
fix: 行列容器组件纵向分割大于2的时候报错 (#474)
* fix: 行列容器组件纵向分割大于2的时候报错
2024-05-17 11:41:10 +08:00
Gene 0d36df07a7
fix: 修复画布行列容器组件中的分割工具位置错误的问题 (#458)
* fix: 修复画布行列容器组件中的分割工具位置错误的问题
2024-05-17 11:40:47 +08:00
chilingling 6d9241ed6a
fix(chore): remove ai code review workflow (#490) 2024-05-17 10:40:22 +08:00
rhlin 899d616f7e
build: 公共依赖cdn解耦并增加开关控制 (#360)
* build: external public cdn localize 公共cdn依赖包支持本地化

* build: cdn外部依赖解耦,修复baseUrl获取

* build: cdn依赖解耦 修正baseUrl获取方式

* build: cdn依赖三方解耦,优化目录型拷贝

* build: cdn依赖解耦,解决目录复制文件的相对路径问题

* build(design-core): 修复monaco的worker使用cdn地址前缀的情况下打包失效

* build: 本地开发也支持脱离cdn三方依赖

* build: 去除代码注释

* build: 三方cdn依赖解耦  物料bundle文件依赖支持替换cdn依赖

* build(design-core) 去除冗余用不到的文件和配置项硬编码

* build: 公共依赖cdn解耦测试完成 开关默认关闭

* refactor: 简化三方cdn依赖解耦的正则,改为path方法

* refactor: 三方依赖解耦脚本代码优化,减少雷同的正则匹配

* refactor: 三方cdn依赖解耦脚本 简化合并正则

* build: 三方cdn解耦 物料本地打包逻辑优化修正

* build: 三方cdn依赖解耦,优化代码简化正则,修复计算版本号问题,修复函数名大小写问题

* build: 优化importMap版本站位符号

* build: 三方cdn物料解耦 变量改为环境变量

* build: 三方cdn依赖解耦 补充处理复制文件夹时候的去重

* fix: 三方cdn解耦, 解决本地启动问题

* feat(preview): preview也支持cdn依赖解耦

* refactor(desing-core/scripts): 重构复制cdn文件本地复制模块

* build:  重构预览的importMap复制逻辑,修复两个importMap文件不存在

* build: 进一步优化monacoEditor地址,直接打包不再跨网络获取

* refactor(design-core/scripts): 调整复制应用importMap函数的参数顺序,与其他结构类似

* refactor(design-core/script): 调整文件位置和文件引用顺序

* feat(design-core/preview): preview importMap.js 使用 importMap.json 数据归一

* fix(design-core/preview): 修复importJson的引用

* fix: mock端口号订正,解决element-plus拖入画布后无法渲染

* build(design-core/script): 修复临时安装包插件安装完包后返回目录不正确

* fix: 修正monaco-editor的worker资源的打包地址

* build(design-core/script): 修复临时安装包插件安装完包后返回目录不正确

* fix(design-core/preview): 修正cdn解耦preview获取动态的importMap.json的base路径问题

* build: 三方cdn解耦,优化拷贝脚本,修复文件夹当文件拷贝当文件夹是包路径时候目标路径版本号丢失

* build: 三方cdn解耦,解决包未安装的情况下,glob匹配不到文件导致打包不拷贝内容

* fix: 根据检视意见使用fs-extra readJsonSync替代utils工具函数

* fix: 根据检视意见修改函数名大小写和端口号读取

* feat: 三方物料解耦 preview变量替换增加注释

* feat: 根据检视意见修改函数名字

* refactor: 根据检视意见,将脚本函数的参数整改为对象,并把目录值迁移到参数默认值

* docs(design-core/scripts): 三方cdn解耦 修正描述错误

* fix: 三方cdn解耦 修正preview脚本TinyVue版本号
2024-05-13 14:47:02 +08:00
chilingling 3a66996fae
feat(vue-dsl): add app generate code (#178)
* feat(vue-dsl): add app generate code

* feat(vue-dsl): add app generate code

* feat(vue-dsl): add generate global store code

* feat(vue-dsl): delete parse config and parse schema hook

* feat(tempalte): refra generateTemplate logic

* feat(vue-generator): add generate script frame code

* feat(vue-generator): add hook flow for sfc file generate

* feat(vue-generator): support generate sfc script

* feat(vue-generator): support jsx generate

* fix(vue-generator): fix double quotes issue

* feat(vue-generator): handle app generate code

* feat(toolbar-generate-vue): toolbar-generate-vue use new codegen function

* feat(vue-generator): add requiredBlock parser

* feat(docs): add readme doc

* feat(docs): add more docs

* fix(vue-generator): adapt for more scenario

* feat(vue-generator): support tiny-grid editor use tiny component

* fix(vue-generator): complete unit test

* fix(vue-generator): add unit test

* feat(vue-generator): add sfc generator unit test

* feat(vue-generator[docs]): add contributing docs

* feat(vue-generator): add test coverage script and app generate test case

* fix(generate-vue): optimize desc and file list

* fix(vue-generate): [template] fix viteConfig process.env is processed

* fix(vue-generator): escape process.env string

* feat(vue-generator): support builtin components

* fix(vue-generator): add builtin componentsMap

* fix(vue-generator): fix bind utils props

* fix(vue-generator): support utils expression

* fix(vue-generator): support children expression add utils

* fix(vue-generator): support nested folder page

* fix(vue-generator): support get datasource with name

* fix(vue-generator): only write necessary dependencies into package.json

* feat(vue-generator): simplified genTemplate hooks

* fix(vue-generator): update vue-generator  docs

* feat(vue-generator): detect jsx on custom  method

* feat(vue-generator): add d.ts types declaration
2024-05-13 11:42:32 +08:00
chilingling aa8d361bd2
feat(chore): update OpenTinyVue version (#457)
* feat(chore): update OpenTinyVue version

* feat(chore): update opentinyvue version

* fix(breadcrumb): fix breadcrumb style after update opentiny vue version

* feat(chore): change opentinyvue to peer dependencies

* feat(builtinComponent): add vue to peerDependencies
2024-05-11 17:43:33 +08:00
chilingling 30cfeb19df
feat(canvasApi): use canvasApi to access canvas methods (#425)
* feat(canvasApi): use canvasApi to access canvas methods

* fix(canvas): optimize import order

* fix(canvasApi): change by review comment tips
2024-05-11 17:36:43 +08:00
chilingling c271054870
fix(canvas): add placeholder for empty container #365 (#436)
* fix(canvas): add placeholder for empty container

* feat(canvas-render): optimize by review comment
2024-05-11 15:56:56 +08:00
jxhhdx e9961aaba8
fix(canvas-CanvasContainer): 修改Model单词拼写错误 (#468) 2024-05-10 15:23:34 +08:00
tianxin c36cc66f59
修复新增页面搜索条件存在并没有触发 (#448) 2024-05-06 19:55:04 +08:00
tianxin e98994f8d9
Fix/serarch empty:为对部分功能设置搜索为空样式处理 (#446) 2024-05-06 19:54:14 +08:00
betterdancing 0afdf2fcae
fix(reset-button): 修复样式配置中重置按钮的样式和设计稿不一致的问题 (#435) 2024-05-06 19:52:15 +08:00
betterdancing 7bb3adfcba
fix(dataSource): 数据源新增字段时,新增的表单设置在列表头部,且数组插入数据时插入到头部 (#454) 2024-05-06 19:50:50 +08:00
betterdancing 3107621129
fix(block-diff): 修复区块发布是查看差异保存时schema出错的bug (#453) 2024-05-06 19:46:36 +08:00
tianxin 50b62373a7
新建区块弹出框点击遮罩层会关闭 (#447) 2024-04-25 19:23:04 +08:00
Lu17301156525 0818d1656a
修复918.json mock数据不一致问题 (#452) 2024-04-25 19:22:36 +08:00
chilingling 56907693f6
fix(chore): fix vue version of 3.4.24 caught tiny-select component error (#445) 2024-04-24 16:44:33 +08:00
tianxin 4fd55d4261
(style-position)修复样式定位reset后未恢复默认值 (#433) 2024-04-23 09:00:23 +08:00
betterdancing d613b4e8f6
fix(data-source): on configuring static data panel, the batch delete button should horizontally aligned. (#413)
* 【设计器】静态数据配置面板,批量删除未对齐 BUG202403140918
2024-04-23 08:59:31 +08:00
Gene ade2d93331
fix: 修改滚动到节点的逻辑,适配超宽超高组件 (#428) 2024-04-23 08:56:48 +08:00
tianxin 7d965fceee
fix(plugin-dataSource): 修复数据源面板新增时未校验名称的重复性 (#434) 2024-04-23 08:55:31 +08:00
wenmine 4864fb66c5
fix(dsl-page):修复本地运行中对于props属性引用utils出码报错的问题 (#346)
* fix(dsl-page):修复对于props属性引用utils出码报错的问题

* fix(dsl-page):修复对于props属性引用utils出码报错的问题-review
2024-04-22 11:36:05 +08:00
chilingling eafe3df2c6
fix(chore): update pr check node version (#431) 2024-04-18 19:47:21 +08:00
rhlin 005f36af3c
fix(canvas): wrapper runner render contextinitializationwithfunction#161(#426) 2024-04-18 19:25:17 +08:00
Gene a69082f518
fix: 修复画布设置成自由布局后无法拖动drag-resize改变宽高的问题 (#393) 2024-04-18 19:23:26 +08:00
Gene e54edca915
fix: 修复画布组件嵌套拖拽的问题 (#364)
* fix: 修复画布组件嵌套拖拽的问题
2024-04-18 19:22:59 +08:00
rhlin 681a6fa612
Feat provide eslint prettier modify config (#385)
* fix(monaco-eslint): 修复eslint-worker支持问题

* fix(linter): 解决cdn后eslint worker相对路径问题

* fix: 移除多余的包

* feat(controller): eslint、prettier新增配置文件,worker文件打包从主应用改为随着分应用打包

* fix(controller): 修复eslint worker地址

* feat(controller): eslint-linter-browserify 降版本

* build: 去掉define process.env设置为空对象(旧版tinyVue库需要),当前引起eslint-linter-browserify脚本(已经处理过process的包)构建报错,且主应用有nodePollyfill可以处理process场景重复了

* fix(controller): 去掉多余注释和测试代码注释

* fix: 解决代码检视问题, 去掉注释,补充包引用去掉external,去掉多余的新增依赖

* feat: 单页预览格式化统一引用prettierrc文件配置

* fix: 解决子包的js没有打包为入口的问题

* fix(controller): 修复因目录结构改变需要worker打包为相对地址
2024-04-16 14:22:10 +08:00
chilingling 259ef69ba3
fix(controller): build with env variable (#420) 2024-04-16 11:21:54 +08:00
chilingling 8e89cc47e7
fix(chore): fix some vue warining on console (#421) 2024-04-16 11:21:27 +08:00
chilingling da8f58b0d9
fix(err-monitor): stop monitoring if monitorurl not exist (#423)
* fix(err-monitor): stop monitoring if monitorurl not exist

* fix(error-monitor): cancel request if url not exist
2024-04-16 11:21:06 +08:00
凌览 f7980d8c83
fix(demo-data): 修复官方 demo 页面底部确认购买模块移动端设备宽度下被遮住的场景 (#398) 2024-04-12 14:25:30 +08:00
chilingling e94b9674aa
fix(material): add buttonComponentsMap config (#395) 2024-04-12 14:17:00 +08:00
lichunn c55397928a
Fix:编辑器设置dark主题后预览页header文字颜色不正确 (#403)
* fix:编辑器设置dark主题后预览页header文字颜色不正确

* fix:调整主题
2024-04-11 17:26:56 +08:00
fox ouyang 852217f711
feat:增加物料:TinyDatePicker和TinyNumeric (#399)
* 增加物料:TinyDatePicker和TinyNumeric

* 修改disabled使用MetaSwitch
2024-04-11 14:35:58 +08:00
Xie Jay e9d6d7e3f9
fix(settings): fix text wrap of bind condition (#391)
Fixes #370
2024-04-10 18:02:05 +08:00
chilingling 0e6d30183a
fix(controller-environment): preserve import.meta.env variable when build controller lib (#380) 2024-04-10 16:34:16 +08:00
chilingling 68838ed5f9
fix(errorMonitor): add config for errormonitor (#381) 2024-04-09 20:13:53 +08:00
yuhki a010bfa4d6
docs: 文件名称CHANGELOG拼写错误,修改文件名称 (#389) 2024-04-09 09:50:18 +08:00
chilingling 6c92ffefec
fix(scrollbar): fix robot plugin scrollbar hide almost all scrollbar bug (#340)
* fix(scrollbar): fix robot plugin scrollbar hide almost all scrollbar bug

* fix(scrollbar): change by review comment

* fix(theme-scrollbar): change by review comment

* fix(theme-base): apply default scrollbar style to body * children
2024-04-07 14:16:48 +08:00
wenmine e961e0638f
fix(readme-build): 添加Readme中构建产物所在文件夹信息 (#367) 2024-04-07 10:14:12 +08:00
chilingling 8c09632e9c
fix(material): elTable formatter should be function type (#355) 2024-03-30 16:31:23 +08:00
Gene e9a67b9de0
feat: 状态管理代码编辑UI优化 (#318)
* feat: 状态管理代码编辑UI优化
2024-03-27 16:15:51 +08:00
Kagol 6a59dacfd6
docs: add project name to issue template (#335) 2024-03-27 09:26:59 +08:00
chilingling d0241a9972
fix(preview): fix preview vue-repl unsupport jsx bug (#343) 2024-03-22 16:25:51 +08:00
chilingling 13db22af27
fix(build): 修复构建设计器产物 ref 值未转化的 bug (#347) 2024-03-21 20:01:24 +08:00
zeyong tsai 030497fbad
fix(datasource): data type icon invisible #312 (#332)
* fix(datasource): data type icon invisible

* fix(common): #325 reduce unnecessary network requests.

* style(datasource): remove unnecessary comment

---------

Co-authored-by: zeyong.cai <zeyong.cai@xquant.com>
2024-03-19 18:50:30 +08:00
Hexqi 68637db8db
fix: canvas api error (#338) 2024-03-15 17:51:04 +08:00
Hexqi 38c89a856c
fix: drag block into canvas error (#336) 2024-03-14 19:13:34 +08:00
Hexqi 99d0e47c29
fix: switch page error (#331) 2024-03-13 15:17:51 +08:00
Hexqi 731a8536aa
refactor: 解决子包循环依赖问题 (#313)
* refactor: move common/js to controller/js

* refactor: replace common/js

* refactor: replace ../js

* refactor: update package.json

* refactor: resolve canvas rely on common

* refactor: remove controller deps in http

* refactor: add canvas renderer state

* refactor: remove useless file ext

* refactor: modify controller deps path

* refactor: fix eslint

* refactor: resolve http env
2024-03-08 15:10:42 +08:00
chilingling 913f40c4d2
chore(release): publish (#320)
- @opentiny/tiny-engine-mock: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-block-build: 1.0.1 => 1.0.2
- @opentiny/tiny-engine-builtin-component: 1.0.0 => 1.0.1
- @opentiny/tiny-engine-canvas: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-common: 1.0.1 => 1.0.2
- @opentiny/tiny-engine-controller: 1.0.2 => 1.0.3
- @opentiny/tiny-engine: 1.0.0-beta.3 => 1.0.0-beta.4
- @opentiny/tiny-engine-http: 1.0.1 => 1.0.2
- @opentiny/tiny-engine-i18n-host: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-block: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-bridge: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-data: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-datasource: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-help: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-i18n: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-materials: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-page: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-robot: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-schema: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-script: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-tree: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-plugin-tutorial: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-setting-design: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-setting-events: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-setting-props: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-setting-styles: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-svgs: 1.0.3 => 1.0.4
- @opentiny/tiny-engine-theme-dark: 1.0.3 => 1.0.4
- @opentiny/tiny-engine-theme-light: 1.0.4 => 1.0.5
- @opentiny/tiny-engine-toolbar-breadcrumb: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-clean: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-collaboration: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-fullscreen: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-generate-vue: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-lang: 1.0.3 => 1.0.4
- @opentiny/tiny-engine-toolbar-layout: 1.0.3 => 1.0.4
- @opentiny/tiny-engine-toolbar-checkinout: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-logo: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-logout: 1.0.3 => 1.0.4
- @opentiny/tiny-engine-toolbar-media: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-preview: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-redoundo: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-refresh: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-save: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-toolbar-setting: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-utils: 1.0.3 => 1.0.4
- @opentiny/tiny-engine-dsl-vue: 1.0.2 => 1.0.3
- @opentiny/tiny-engine-webcomponent-core: 1.0.3 => 1.0.4
2024-03-07 15:13:10 +08:00
353 changed files with 15319 additions and 1767 deletions

View File

@ -8,4 +8,4 @@ tool_params:
secsolar:
source_dir: ./
cmetrics:
exclude: vite.config.js|package.json|index.js|mockServer/assets
exclude: vite.config.js|package.json|index.js|mockServer/assets|packages/vue-generator/test

View File

@ -64,6 +64,14 @@ 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), we will invite you to the WeChat discussion group later.
about: Add this WeChat(opentiny-official), we will invite you to the WeChat discussion group later.

View File

@ -21,3 +21,11 @@ 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,23 +0,0 @@
name: AI Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: anc95/ChatGPT-CodeReview@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
LANGUAGE: Chinese
OPENAI_API_ENDPOINT: https://api.openai.com/v1
MODEL: gpt-3.5-turbo
MAX_TOKENS: 4096
MAX_PATCH_LENGTH: 10000

View File

@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 16
node-version: 18
- name: Install pnpm
run: npm i -g pnpm
@ -24,7 +24,7 @@ jobs:
- name: Get changed files
id: get_changed_files
uses: tj-actions/changed-files@v40
uses: tj-actions/changed-files@v41
with:
files: |
**.js

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ package-lock.json
yarn.lock
pnpm-lock.yaml
lerna-debug.log
packages/design-core/bundle-deps
# local env files
.env.local

View File

@ -76,6 +76,10 @@ pnpm build:plugin
pnpm build:alpha or build:prod
```
The folder where the product is located after building
```
tiny-engine/packages/design-core/dist/
```
## Milestones

View File

@ -76,6 +76,10 @@ pnpm run build:plugin
pnpm run build:alpha 或 build:prod
```
构建后产物所在文件夹
```
tiny-engine/packages/design-core/dist/
```
## 里程碑

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-mock",
"version": "1.0.2",
"version": "1.0.3",
"publishConfig": {
"access": "public"
},
@ -25,7 +25,7 @@
"lint": "eslint --fix ."
},
"dependencies": {
"@opentiny/tiny-engine-dsl-vue": "1.0.2",
"@opentiny/tiny-engine-dsl-vue": "^1.0.3",
"@seald-io/nedb": "^4.0.2",
"fs-extra": "^11.1.1",
"glob": "^10.3.4",

View File

@ -769,7 +769,7 @@
{
"componentName": "TinyTimeLine",
"props": {
"active": "2",
"active": 2,
"data": [
{
"name": "基础配置"
@ -1525,7 +1525,7 @@
{
"componentName": "div",
"props": {
"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;"
"style": "border-width: 1px;z-index:1;border-style: solid; border-color: #ffffff; padding-top: 10px; padding-left: 10px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px; background-color: #fff; position: fixed; inset: auto 0% 0% 0%; height: 80px; line-height: 80px; border-radius: 0px;"
},
"children": [
{
@ -1550,7 +1550,7 @@
{
"componentName": "TinyCol",
"props": {
"span": "8"
"span": 8
},
"id": "b9d051a5",
"children": [
@ -1563,7 +1563,7 @@
{
"componentName": "TinyCol",
"props": {
"span": "5",
"span": 5,
"style": "display: flex;"
},
"id": "02352776",
@ -1597,7 +1597,7 @@
{
"componentName": "TinyCol",
"props": {
"span": "7"
"span": 7
},
"id": "82b6c659",
"children": [
@ -1665,7 +1665,7 @@
{
"componentName": "TinyCol",
"props": {
"span": "4",
"span": 4,
"style": "display: flex; flex-direction: row-reverse; border-radius: 0px; height: 100%; justify-content: flex-start; align-items: center;"
},
"id": "10b73009",
@ -1690,7 +1690,7 @@
],
"fileName": "createVm",
"meta": {
"id": 1977,
"id": "NTJ4MjvqoVj8OVsc",
"parentId": "0",
"group": "staticPages",
"occupier": {
@ -1787,7 +1787,7 @@
},
{
"componentName": "TinyPlusFrozenPage",
"package": "@opentiny/vuee",
"package": "@opentiny/vue",
"exportName": "FrozenPage",
"destructuring": true,
"version": "3.4.1"
@ -1925,6 +1925,13 @@
"destructuring": true,
"version": "0.1.16"
},
{
"componentName": "TinyButton",
"package": "@opentiny/vue",
"exportName": "Button",
"destructuring": true,
"version": "0.1.16"
},
{
"componentName": "TinyButtonGroup",
"package": "@opentiny/vue",
@ -1967,6 +1974,48 @@
"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",

View File

@ -28769,6 +28769,867 @@
"disable": ["copy", "remove"]
}
}
},
{
"name": {
"zh_CN": "日期选择"
},
"component": "TinyDatePicker",
"icon": "datepick",
"description": "用于输入或选择日期",
"docUrl": "",
"screenshot": "",
"tags": "",
"keywords": "",
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "DatePicker",
"version": "",
"destructuring": true
},
"group": "component",
"priority": 1,
"schema": {
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "modelValue",
"label": {
"text": {
"zh_CN": "绑定值"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "双向绑定值"
},
"labelPosition": "left"
},
{
"property": "type",
"label": {
"text": {
"zh_CN": "类型"
}
},
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "日期",
"value": "date"
},
{
"label": "日期时间",
"value": "datetime"
},
{
"label": "周",
"value": "week"
},
{
"label": "月份",
"value": "month"
},
{
"label": "年份",
"value": "year"
}
]
}
},
"description": {
"zh_CN": "设置日期框的type属性"
}
},
{
"property": "placeholder",
"label": {
"text": {
"zh_CN": "占位文本"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "输入框占位文本"
},
"labelPosition": "left"
},
{
"property": "clearable",
"label": {
"text": {
"zh_CN": "清除按钮"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "是否显示清除按钮"
},
"labelPosition": "left"
},
{
"property": "disabled",
"label": {
"text": {
"zh_CN": "禁用"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "禁用"
}
},
{
"property": "readonly",
"label": {
"text": {
"zh_CN": "是否只读"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": ""
}
},
{
"property": "size",
"label": {
"text": {
"zh_CN": "尺寸"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "medium",
"value": "medium"
},
{
"label": "small",
"value": "small"
},
{
"label": "mini",
"value": "mini"
}
]
}
},
"description": {
"zh_CN": "日期框尺寸。该属性的可选值为 medium / small / mini"
}
}
]
},
{
"name": "1",
"label": {
"zh_CN": "其他"
},
"content": [
{
"property": "maxlength",
"label": {
"text": {
"zh_CN": "最大长度"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {}
},
"description": {
"zh_CN": "设置 input 框的maxLength"
},
"labelPosition": "left"
},
{
"property": "autofocus",
"label": {
"text": {
"zh_CN": "聚焦"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "自动获取焦点"
},
"labelPosition": "left"
}
],
"description": {
"zh_CN": ""
}
}
],
"events": {
"onChange": {
"label": {
"zh_CN": "值改变时触发"
},
"description": {
"zh_CN": "在 Input 值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框改变后的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onInput": {
"label": {
"zh_CN": "输入值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框输入的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onUpdate:modelValue": {
"label": {
"zh_CN": "双向绑定的值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "双向绑定的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onBlur": {
"label": {
"zh_CN": "失去焦点时触发"
},
"description": {
"zh_CN": "在 Input 失去焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onFocus": {
"label": {
"zh_CN": "获取焦点时触发"
},
"description": {
"zh_CN": "在 Input 获取焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onClear": {
"label": {
"zh_CN": "点击清空按钮时触发"
},
"description": {
"zh_CN": "点击清空按钮时触发"
},
"type": "event",
"functionInfo": {
"params": [],
"returns": {}
},
"defaultValue": ""
}
}
},
"configure": {
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isModal": false,
"nestingRule": {
"childWhitelist": "",
"parentWhitelist": "",
"descendantBlacklist": "",
"ancestorWhitelist": ""
},
"isNullNode": false,
"isLayout": false,
"rootSelector": "",
"shortcuts": {
"properties": ["value", "disabled"]
},
"contextMenu": {
"actions": ["create symbol"],
"disable": ["copy", "remove"]
}
}
},
{
"name": {
"zh_CN": "数字输入框"
},
"component": "TinyNumeric",
"icon": "numeric",
"description": "通过鼠标或键盘输入字符",
"docUrl": "",
"screenshot": "",
"tags": "",
"keywords": "",
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "Numeric",
"version": "",
"destructuring": true
},
"group": "component",
"priority": 1,
"schema": {
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "modelValue",
"label": {
"text": {
"zh_CN": "绑定值"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "双向绑定值"
},
"labelPosition": "left"
},
{
"property": "placeholder",
"label": {
"text": {
"zh_CN": "占位文本"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "输入框占位文本"
},
"labelPosition": "left"
},
{
"property": "allow-empty",
"label": {
"text": {
"zh_CN": "内容可清空"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "是否内容可清空"
},
"labelPosition": "left"
},
{
"property": "disabled",
"label": {
"text": {
"zh_CN": "禁用"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "禁用"
}
},
{
"property": "size",
"label": {
"text": {
"zh_CN": "尺寸"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "medium",
"value": "medium"
},
{
"label": "small",
"value": "small"
},
{
"label": "mini",
"value": "mini"
}
]
}
},
"description": {
"zh_CN": "输入框尺寸。该属性的可选值为 medium / small / mini"
}
},
{
"property": "controls",
"label": {
"text": {
"zh_CN": "是否使用加减按钮"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": ""
}
},
{
"property": "controls-position",
"label": {
"text": {
"zh_CN": "加减按钮位置"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "左右两侧",
"value": ""
},
{
"label": "只在右侧",
"value": "right"
}
]
}
},
"description": {
"zh_CN": "加减按钮位置"
},
"labelPosition": "left"
},
{
"property": "precision",
"label": {
"text": {
"zh_CN": "数值精度"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "数值精度"
},
"labelPosition": "left"
},
{
"property": "step",
"label": {
"text": {
"zh_CN": "步长"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "步长"
},
"labelPosition": "left"
},
{
"property": "max",
"label": {
"text": {
"zh_CN": "可输入的最大数值"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "可输入的最大数值"
},
"labelPosition": "left"
},
{
"property": "min",
"label": {
"text": {
"zh_CN": "可输入的最小数值"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "可输入的最大数值"
},
"labelPosition": "left"
}
]
}
],
"events": {
"onChange": {
"label": {
"zh_CN": "值改变时触发"
},
"description": {
"zh_CN": "在 Input 值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框改变后的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onInput": {
"label": {
"zh_CN": "输入值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框输入的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onUpdate:modelValue": {
"label": {
"zh_CN": "双向绑定的值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "双向绑定的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onBlur": {
"label": {
"zh_CN": "失去焦点时触发"
},
"description": {
"zh_CN": "在 Input 失去焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onFocus": {
"label": {
"zh_CN": "获取焦点时触发"
},
"description": {
"zh_CN": "在 Input 获取焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onClear": {
"label": {
"zh_CN": "点击清空按钮时触发"
},
"description": {
"zh_CN": "点击清空按钮时触发"
},
"type": "event",
"functionInfo": {
"params": [],
"returns": {}
},
"defaultValue": ""
}
}
},
"configure": {
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isModal": false,
"nestingRule": {
"childWhitelist": "",
"parentWhitelist": "",
"descendantBlacklist": "",
"ancestorWhitelist": ""
},
"isNullNode": false,
"isLayout": false,
"rootSelector": "",
"shortcuts": {
"properties": ["value", "disabled"]
},
"contextMenu": {
"actions": ["create symbol"],
"disable": ["copy", "remove"]
}
}
}
]
},

View File

@ -29,7 +29,7 @@
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@types/node": "^18.0.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^1.3.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"assert": "^2.0.0",
"buffer": "^6.0.3",
"chokidar": "^3.5.3",

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-block-build",
"version": "1.0.1",
"version": "1.0.2",
"description": "translate block to webcomponent template",
"main": "./dist/web-components.es.js",
"scripts": {
@ -32,9 +32,9 @@
"dependencies": {
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/vue": "~3.11.0",
"@opentiny/vue-icon": "~3.11.0",
"@opentiny/vue-theme": "~3.11.0",
"@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"

View File

@ -43,7 +43,7 @@ export default defineConfig(({ command, mode }) => {
}
const vuePluginConfig = {}
const styleLinks = ['https://npm.onmicrosoft.cn/@opentiny/vue-theme@3.11/index.css']
const styleLinks = ['https://npm.onmicrosoft.cn/@opentiny/vue-theme@3.14/index.css']
config.publicDir = false

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-builtin-component",
"version": "1.0.0",
"version": "1.0.1",
"description": "",
"main": "dist/index.js",
"module": "dist/index.js",
@ -20,12 +20,14 @@
"preview": "vite preview"
},
"dependencies": {
"@opentiny/vue": "~3.10.0",
"vite-plugin-css-injected-by-js": "^3.3.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"vite": "^4.5.0"
},
"peerDependencies": {
"vue": "^3.4.15"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-canvas",
"version": "1.0.2",
"version": "1.0.3",
"publishConfig": {
"access": "public"
},
@ -28,25 +28,24 @@
"dependencies": {
"@babel/core": "7.18.13",
"@opentiny/tiny-engine-builtin-component": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/shared": "^3.3.4",
"@vueuse/core": "^9.6.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"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

@ -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,9 +125,10 @@ 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 */ script)
const modules = await import(/* @vite-ignore */ scriptUrl)
window.TinyComponentLibs[pkg] = modules
}

View File

@ -27,14 +27,46 @@
</div>
<!-- 绝对定位画布时调节元素大小 -->
<template v-else>
<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>
<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>
</template>
<div v-if="showAction" ref="optionRef" class="corner-mark-right" :style="fixStyle">
<template v-if="!isModal">
@ -68,7 +100,7 @@
<div v-show="hoverState.configure?.isContainer" class="corner-mark-bottom-right">拖放元素到容器内</div>
</div>
<div v-show="lineState.height && lineState.width" class="canvas-rect line">
<div :class="['hover-line', lineState.position]">
<div :class="['hover-line', lineState.position, { forbidden: lineState.forbidden }]">
<div v-if="lineState.position === 'in' && hoverState.configure" class="choose-slots"></div>
</div>
</div>
@ -85,6 +117,7 @@ import {
IconEyeclose
} from '@opentiny/vue-icon'
import {
canvasState,
getCurrent,
removeNodeById,
selectNode,
@ -347,15 +380,14 @@ export default {
}
}
const getStyleValues = (selectState, siteCanvasWidth, siteCanvasHeight) => {
const getStyleValues = (selectState, canvasSize, labelWidth, optionWidth) => {
const { left, top, width, height, doc } = selectState
const labelRect = labelRef.value.getBoundingClientRect()
const optionRect = optionRef.value.getBoundingClientRect()
const { width: canvasWidth, height: canvasHeight } = canvasSize
//
const fullRectWidth = labelRect.width + optionRect.width + OPTION_SPACE
const fullRectWidth = labelWidth + optionWidth + OPTION_SPACE
// label top
const isLabelAtBottom = top <= LABEL_HEIGHT
const isLabelAtBottom = top < LABEL_HEIGHT
const labelAlign = new Align({
alignLeft: true,
horizontalValue: 0,
@ -364,7 +396,7 @@ export default {
})
//
const isOptionAtBottom = siteCanvasHeight - top - height > OPTION_BAR_HEIGHT
const isOptionAtBottom = canvasHeight - top - height >= OPTION_BAR_HEIGHT
const optionAlign = new Align({
alignLeft: false,
horizontalValue: 0,
@ -378,13 +410,13 @@ export default {
//
// labelabellabel
const isLabelAlignRight = labelRect.width > width && left + labelRect.width + scrollBarWidth > siteCanvasWidth
const isLabelAlignRight = labelWidth > width && left + labelWidth + scrollBarWidth > canvasWidth
if (isLabelAlignRight) {
labelAlign.align(positions.RIGHT)
}
// optionoptionoption
const isOptionAlignLeft = optionRect.width > width && left + width - optionRect.width < 0
const isOptionAlignLeft = optionWidth > width && left + width - optionWidth < 0
if (isOptionAlignLeft) {
optionAlign.align(positions.LEFT)
}
@ -392,14 +424,14 @@ export default {
if (isLabelAtBottom === isOptionAtBottom) {
//
if (left + fullRectWidth < siteCanvasWidth) {
if (left + fullRectWidth < canvasWidth) {
//
labelAlign.align(positions.LEFT)
optionAlign.align(positions.LEFT, labelRect.width + OPTION_SPACE)
optionAlign.align(positions.LEFT, labelWidth + OPTION_SPACE)
} else {
//
optionAlign.align(positions.RIGHT)
labelAlign.align(positions.RIGHT, optionRect.width + OPTION_SPACE)
labelAlign.align(positions.RIGHT, optionWidth + OPTION_SPACE)
}
}
} else {
@ -407,10 +439,10 @@ export default {
labelAlign.align(positions.LEFT, Math.min(-left, width - fullRectWidth))
}
if (left + width + scrollBarWidth > siteCanvasWidth) {
if (left + width + scrollBarWidth > canvasWidth) {
optionAlign.align(
positions.RIGHT,
Math.min(left + width + scrollBarWidth - siteCanvasWidth, width - fullRectWidth)
Math.min(left + width + scrollBarWidth - canvasWidth, width - fullRectWidth)
)
}
}
@ -422,8 +454,7 @@ export default {
}
watchPostEffect(async () => {
let { left } = props.selectState
const { top, width, height, doc } = props.selectState
const { left, top, width, height, doc } = props.selectState
// nextTickrefawait
await nextTick()
@ -438,15 +469,17 @@ export default {
return
}
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const scale = useLayout().getScale()
const canvasRect = canvasState.iframe.getBoundingClientRect()
const { width: labelWidth } = labelRef.value.getBoundingClientRect()
const { width: optionWidth } = optionRef.value.getBoundingClientRect()
left -= ((1 - scale) / 2) * siteCanvasRect.width
// canvasiframeiframeclientRectscale
const { labelStyleValue, optionStyleValue } = getStyleValues(
{ left, top, width, height, doc },
siteCanvasRect.width * scale,
siteCanvasRect.height
{ width: canvasRect.width / scale, height: canvasRect.height / scale },
labelWidth / scale,
optionWidth / scale
)
labelStyle.value = labelStyleValue
@ -540,11 +573,12 @@ export default {
height: 100%;
background: var(--ti-lowcode-canvas-hover-line-in-bg-color);
}
&.forbid {
width: 100%;
height: 100%;
&.forbidden:not(.in) {
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 {

View File

@ -6,7 +6,7 @@
:windowGetClickEventTarget="target"
:resize="canvasState.type === 'absolute'"
@select-slot="selectSlot"
@setting="settingModle"
@setting="settingModel"
></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-common/js/monitor'
import { iframeMonitoring } from '@opentiny/tiny-engine-controller/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
getCurrent,
canvasApi
} from './container'
export default {
@ -71,7 +71,7 @@ export default {
const insertPanel = ref(null)
const insertPosition = ref(false)
const loading = computed(() => useCanvas().isLoading())
let showSettingModle = ref(false)
let showSettingModel = ref(false)
let target = ref(null)
const setCurrentNode = async (event) => {
@ -100,11 +100,13 @@ export default {
//
if (event.button === 2) {
openMenu(getOffset(event.target), event)
openMenu(event)
}
}
}
useCanvas().initCanvasApi(canvasApi)
const beforeCanvasReady = () => {
if (iframe.value) {
const win = iframe.value.contentWindow
@ -178,8 +180,8 @@ export default {
}
}
//
const settingModle = () => {
showSettingModle.value = true
const settingModel = () => {
showSettingModel.value = true
}
const updateI18n = (message) => {
@ -236,9 +238,9 @@ export default {
canvasState,
insertComponent,
insertPanel,
settingModle,
settingModel,
target,
showSettingModle,
showSettingModel,
insertPosition,
loading
}

View File

@ -43,7 +43,8 @@ const ROW_SNIPPET = {
grow: true,
shrink: true,
widthType: 'auto'
}
},
children: []
}
]
}
@ -103,16 +104,7 @@ export default {
if (schema.componentName === 'CanvasCol') {
//
if (!schema.children?.length) {
schema.children = [
{
...extend(true, {}, ROW_SNIPPET),
children: [{ ...extend(true, {}, COL_SNIPPET) }]
},
{
...extend(true, {}, ROW_SNIPPET),
children: [{ ...extend(true, {}, COL_SNIPPET) }]
}
]
schema.children = [extend(true, {}, ROW_SNIPPET), extend(true, {}, ROW_SNIPPET)]
} else if (schema.children[0].componentName !== 'CanvasRow') {
// row children row col
schema.children = [
@ -129,7 +121,7 @@ export default {
]
} else {
//
schema.children.push(...extend(true, {}, ROW_SNIPPET))
schema.children.push(extend(true, {}, ROW_SNIPPET))
}
}
@ -202,7 +194,7 @@ export default {
<style lang="less" scoped>
.divider-wrapper {
position: fixed;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
@ -239,7 +231,7 @@ export default {
}
.divider-line {
position: fixed;
position: absolute;
border: 1px dashed var(--ti-lowcode-common-primary-color);
z-index: 2;
}

View File

@ -17,7 +17,7 @@ export default {
props: {
data: {
type: Array,
default: []
default: () => []
}
},
emits: ['click']
@ -44,37 +44,23 @@ export default {
li {
width: unset !important;
background: var(--ti-lowcode-breadcrumb-bg);
a {
.label {
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 {
background: var(--ti-lowcode-breadcrumb-hover-bg);
cursor: pointer;
&::after {
border-left-color: var(--ti-lowcode-breadcrumb-hover-bg);
border-left-color: var(--ti-steps-advanced-li-hover-bg-color);
}
}
}
&:last-child a {
&:last-child .label {
border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
border-radius: 0;
}
&:first-child a {
&:first-child .label {
border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
border-radius: 0;
border-left: unset;

View File

@ -15,7 +15,7 @@
<span>{{ item.name }}</span>
<span v-if="item.items"><icon-right></icon-right></span>
</div>
<ul v-if="item.items && current === item" class="sub-menu menu-item">
<ul v-if="item.items && current === item" class="sub-menu menu-item" :style="subMenuStyles">
<template v-for="(subItem, subIndex) in item.items" :key="subIndex">
<li
:class="[{ 'menu-item-disabled': subItem.check && !subItem.check?.() }]"
@ -27,15 +27,14 @@
</ul>
</li>
</ul>
<save-new-block :boxVisibility="boxVisibility" fromCanvas @close="close"></save-new-block>
<SaveNewBlock :boxVisibility="boxVisibility" fromCanvas @close="close"></SaveNewBlock>
</div>
</template>
<script lang="jsx">
import { ref, reactive, nextTick } from 'vue'
import { getConfigure, getController, getCurrent, copyNode, removeNodeById } from './container'
import { canvasState, 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({
@ -46,27 +45,35 @@ 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 = (offset, event) => {
const { x, y } = offset
const { getScale } = useLayout()
export const openMenu = (event) => {
menuState.position = {
// TODO
left: event.clientX * getScale() + x + 2 + 'px',
top: event.clientY * getScale() + y + 'px'
left: event.clientX + 2 + 'px',
top: event.clientY + 'px'
}
menuState.show = sessionStorage.getItem('pageInfo') ? true : false
nextTick(() => {
if (menuDom.value) {
const { bottom, height, top } = menuDom.value.getBoundingClientRect()
if (bottom > document.body?.clientHeight) {
menuState.position.top = top - height + 'px'
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' }
}
}
})
@ -74,7 +81,6 @@ export const openMenu = (offset, event) => {
export default {
components: {
SaveNewBlock,
IconRight: iconRight()
},
setup(props, { emit }) {
@ -228,6 +234,7 @@ export default {
close,
current,
menuDom,
subMenuStyles,
actionDisabled,
onShowChildrenMenu
}
@ -237,7 +244,7 @@ export default {
<style lang="less" scoped>
.context-menu {
position: fixed;
position: absolute;
z-index: 10;
}
.menu-item {
@ -278,7 +285,6 @@ export default {
&.sub-menu {
width: 100px;
position: absolute;
right: -100px;
top: -2px;
}
}

View File

@ -17,13 +17,12 @@ import { canvasState } from './container'
export default {
setup() {
const sizeStyle = computed(() => {
const { width, maxWidth, minWidth, scale } = useLayout().getDimension()
const { width, height, maxWidth, minWidth } = useLayout().getDimension()
return {
width,
height,
maxWidth,
minWidth,
height: `${100 / scale}%`,
transform: `scale(${scale}) translateY(-${(100 / scale - 100) / 2}%)`
minWidth
}
})

View File

@ -8,6 +8,7 @@
<script>
import { reactive, watch } from 'vue'
import { useLayout } from '@opentiny/tiny-engine-controller'
import { getCurrent, updateRect, selectState, querySelectById } from './container'
export default {
@ -38,12 +39,17 @@ export default {
}
const handleResize = (event, type) => {
let { clientX, clientY } = event
if (!props.iframe) {
return
}
if (type === 'iframe' && props.iframe) {
const iframeRect = props.iframe.getBoundingClientRect()
clientX += iframeRect.left
clientY += iframeRect.top
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
}
const { parent, schema } = getCurrent()
@ -65,14 +71,9 @@ export default {
const parentWidth = parseInt(window.getComputedStyle(parentDomNode).width, 10)
//
if (newWidth >= parentWidth) {
newWidth = parentWidth
}
newWidth = Math.min(newWidth, parentWidth)
// 32
if (newWidth <= 32) {
newWidth = 32
}
newWidth = Math.max(newWidth, 32)
schema.props.flexBasis = `${newWidth}px`
schema.props.widthType = 'fixed'
@ -186,7 +187,7 @@ export default {
<style lang="less" scoped>
.resize-border {
position: fixed;
position: absolute;
display: flex;
justify-content: center;
align-items: center;

View File

@ -20,15 +20,16 @@ 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',
FORBID: 'forbid'
IN: 'in'
})
import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments'
const initialDragState = {
keydown: false,
@ -98,6 +99,7 @@ const initialLineState = {
width: 0,
left: 0,
position: '',
forbidden: false,
id: '',
config: null,
doc: null
@ -320,19 +322,19 @@ export const scrollToNode = (element) => {
if (element) {
const container = getDocument().documentElement
const { clientWidth, clientHeight } = container
const { x, y, width, height } = element.getBoundingClientRect()
const { left, right, top, bottom, width, height } = element.getBoundingClientRect()
const option = {}
if (x < 0) {
option.left = container.scrollLeft + x - SCROLL_MARGIN
} else if (x > clientWidth) {
option.left = x + width - clientWidth + SCROLL_MARGIN
if (right < 0) {
option.left = container.scrollLeft + left - SCROLL_MARGIN
} else if (left > clientWidth) {
option.left = container.scrollLeft + left - clientWidth + width + SCROLL_MARGIN
}
if (y < 0) {
option.top = container.scrollTop + y - SCROLL_MARGIN
} else if (y > clientHeight) {
option.top = y + height - clientHeight + 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') {
@ -346,16 +348,13 @@ const setSelectRect = (element) => {
element = element || getDocument().body
const { left, height, top, width } = getRect(element)
const { x, y } = getOffset(element)
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const componentName = getCurrent().schema?.componentName || ''
const scale = useLayout().getScale()
clearHover()
Object.assign(selectState, {
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
width,
height,
top,
left,
componentName,
doc: getDocument()
})
@ -416,6 +415,23 @@ export const allowInsert = (configure = hoverState.configure || {}, data = dragS
return flag
}
const isAncestor = (ancestor, descendant) => {
const ancestorId = typeof ancestor === 'string' ? ancestor : ancestor.id
let descendantId = typeof descendant === 'string' ? descendant : descendant.id
while (descendantId) {
const { parent } = getNode(descendantId, true) || {}
if (parent.id === ancestorId) {
return true
}
descendantId = parent.id
}
return false
}
// 获取位置信息,返回状态
const lineAbs = 20
const getPosLine = (rect, configure) => {
@ -423,6 +439,7 @@ const getPosLine = (rect, configure) => {
const yAbs = Math.min(lineAbs, rect.height / 3)
const xAbs = Math.min(lineAbs, rect.width / 3)
let type
let forbidden = false
if (mousePos.y < rect.top + yAbs) {
type = POSITION.TOP
@ -433,12 +450,21 @@ const getPosLine = (rect, configure) => {
} else if (mousePos.x > rect.right - xAbs) {
type = POSITION.RIGHT
} else if (configure.isContainer) {
type = allowInsert() ? POSITION.IN : POSITION.FORBID
type = POSITION.IN
if (!allowInsert()) {
forbidden = true
}
} else {
type = POSITION.BOTTOM
}
return { type }
// 如果被拖拽的节点不是新增的,并且是放置的节点的祖先节点,则禁止插入
const draggedId = dragState.data?.id
if (draggedId && isAncestor(draggedId, lineState.id)) {
forbidden = true
}
return { type, forbidden }
}
const isBodyEl = (element) => element.nodeName === 'BODY'
@ -452,9 +478,6 @@ const setHoverRect = (element, data) => {
const configure = getConfigure(componentName)
const rect = getRect(element)
const { left, height, top, width } = rect
const { x, y } = getOffset(element)
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const scale = useLayout().getScale()
hoverState.configure = configure
@ -483,21 +506,24 @@ const setHoverRect = (element, data) => {
if (childEle) {
const childRect = getRect(childEle)
const { left, height, top, width } = childRect
const { x, y } = getOffset(childEle)
const posLine = getPosLine(childRect, lineState.configure)
Object.assign(lineState, {
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
position: canvasState.type === 'absolute' || getPosLine(childRect, lineState.configure).type
width,
height,
top,
left,
position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden
})
} else {
const posLine = getPosLine(rect, configure)
Object.assign(lineState, {
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
position: canvasState.type === 'absolute' || getPosLine(rect, configure).type
width,
height,
top,
left,
position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden
})
}
@ -506,10 +532,10 @@ const setHoverRect = (element, data) => {
// 设置元素hover状态
Object.assign(hoverState, {
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
width,
height,
top,
left,
componentName
})
return undefined
@ -670,13 +696,12 @@ export const copyNode = (id) => {
export const onMouseUp = () => {
const { draging, data } = dragState
const { position } = lineState
const { position, forbidden } = lineState
const absolute = canvasState.type === 'absolute'
const sourceId = data?.id
const lineId = lineState.id
const allowInsert = position !== POSITION.FORBID
if (draging && allowInsert) {
if (draging && !forbidden) {
const { parent, node } = getNode(lineId, true) || {} // target
const targetNode = { parent, node, data: toRaw(data) }
@ -823,6 +848,49 @@ 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>
<config-group :group="propertiesList" :index="0">
<ConfigGroup :group="propertiesList" :index="0">
<template #item="{ data, propIndex }">
<config-item
<ConfigItem
:key="propIndex"
:property="data"
:data-prop-index="propIndex"
@ -22,22 +22,19 @@
<template v-if="!data.noBinding" #suffix>
<slot name="suffix" :data="data"></slot>
</template>
</config-item>
</ConfigItem>
</template>
</config-group>
</ConfigGroup>
</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

@ -424,5 +424,3 @@ export const api = {
setDataSourceMap,
setGlobalState
}
window.api = api

View File

@ -598,6 +598,20 @@ const getLoopScope = ({ scope, index, item, loopArgs }) => {
}
}
const injectPlaceHolder = (componentName, children) => {
const isEmptyArr = Array.isArray(children) && !children.length
if (configure[componentName]?.isContainer && (!children || isEmptyArr)) {
return [
{
componentName: 'CanvasPlaceholder'
}
]
}
return children
}
const renderGroup = (children, scope, parent) => {
return children.map?.((schema) => {
const { componentName, children, loop, loopArgs, condition, id } = schema
@ -617,10 +631,14 @@ const renderGroup = (children, scope, parent) => {
return null
}
const renderChildren = injectPlaceHolder(componentName, children)
return h(
getComponent(componentName),
getBindProps(schema, mergeScope),
Array.isArray(children) ? renderSlot(children, mergeScope, schema) : parseData(children, mergeScope)
Array.isArray(renderChildren)
? renderSlot(renderChildren, mergeScope, schema)
: parseData(renderChildren, mergeScope)
)
}
@ -628,19 +646,9 @@ const renderGroup = (children, scope, parent) => {
})
}
const ContainerComponent = ['CanvasCol', 'CanvasRow', 'CanvasRowColContainer']
const getChildren = (schema, mergeScope) => {
const { componentName, children } = schema
let renderChildren = children
if (ContainerComponent.includes(componentName) && !renderChildren?.length) {
renderChildren = [
{
componentName: 'CanvasPlaceholder'
}
]
}
const renderChildren = injectPlaceHolder(componentName, children)
const component = getComponent(componentName)
const isNative = typeof component === 'string'

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-common/js/i18n'
import TinyI18nHost, { I18nInjectionKey } from '@opentiny/tiny-engine-controller/js/i18n'
import * as TinyWebcomponentCore from '@opentiny/tiny-engine-webcomponent-core'
import TinyVue from '@opentiny/vue'
import * as TinyVueIcon from '@opentiny/vue-icon'
@ -26,73 +26,77 @@ const dispatch = (name, data) => {
window.parent.document.dispatchEvent(new CustomEvent(name, data))
}
dispatch('beforeCanvasReady')
const initRenderContext = () => {
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])))
} 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
// 不能采用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 {
blockSlotDataMap[blockName] = { [slotName]: slotData }
return component
}
}
},
// renderSlot方法第三个参数是作用域插槽传递的数据格式{ data: vue.unref(state).componentData }
renderSlot(...args) {
// 获取当前vue的实例
const instance = Vue.getCurrentInstance()
/**
* 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
}
// 获取当前区块名称
const blockName = instance.attrs.dataTag
return Vue.renderSlot(...args)
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)
}
}
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 = {
@ -129,6 +133,7 @@ const create = () => {
}
export const createRender = (config) => {
initRenderContext()
const { dslMode, canvasOptions } = config
const { styles = [], scripts = [] } = canvasOptions[dslMode]
const { styles: thirdStyles = [], scripts: thirdScripts = [] } = window.thirdPartyDeps || {}
@ -138,5 +143,3 @@ 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,93 +10,7 @@
*
*/
import CanvasContainer from './components/container/CanvasContainer.vue'
import CanvasAction from './components/container/CanvasAction.vue'
import CanvasDragItem from './components/container/CanvasDragItem.vue'
import CanvasFooter from './components/container/CanvasFooter.vue'
import CanvasResize from './components/container/CanvasResize.vue'
import Builtin from './components/builtin/builtin.json'
import RenderMain, { api as renderApi } from './components/render/RenderMain'
import { createRender } from './components/render/runner'
import {
dragStart,
updateRect,
getContext,
getNodePath,
dragMove,
setLocales,
setState,
deleteState,
getRenderer,
clearSelect,
selectNode,
hoverNode,
insertNode,
removeNode,
addComponent,
setPageCss,
addScript,
addStyle,
getNode,
getCurrent,
setSchema,
setUtils,
updateUtils,
deleteUtils,
getSchema,
setI18n,
getCanvasType,
setCanvasType,
setProps,
setGlobalState,
getGlobalState,
getDocument,
canvasDispatch
} from './components/container/container'
export {
CanvasContainer,
CanvasAction,
CanvasFooter,
CanvasDragItem,
CanvasResize,
RenderMain,
renderApi,
Builtin,
dragStart,
dragMove,
updateRect,
getContext,
getNodePath,
getNode,
getCurrent,
setSchema,
setUtils,
updateUtils,
deleteUtils,
getSchema,
setLocales,
setState,
deleteState,
setI18n,
getRenderer,
clearSelect,
selectNode,
insertNode,
removeNode,
hoverNode,
addComponent,
setPageCss,
addScript,
addStyle,
getCanvasType,
setCanvasType,
setProps,
setGlobalState,
getGlobalState,
getDocument,
canvasDispatch,
createRender
}
export default CanvasContainer
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'

View File

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

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 } from '@opentiny/tiny-engine-controller'
import { getSchema, setSchema } from '@opentiny/tiny-engine-canvas'
import { useLayout, useNotify, useCanvas } from '@opentiny/tiny-engine-controller'
import { constants } from '@opentiny/tiny-engine-utils'
import VueMonaco from './VueMonaco.vue'
export default {
@ -106,6 +106,7 @@ export default {
},
emits: ['update:visible'],
setup(props, { emit, attrs }) {
const { COMPONENT_NAME } = constants
const formState = reactive({
deployInfo: '',
version: '',
@ -186,9 +187,10 @@ 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 {
@ -212,10 +214,21 @@ export default {
}
const save = () => {
const pageSchema = JSON.parse(state.newCode)
setSchema(pageSchema)
close()
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: '代码静态检查有错误,请先修改后再保存'
})
}
}
return {

View File

@ -21,7 +21,6 @@ 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: {
@ -38,7 +37,8 @@ export default {
const { pageState } = useCanvas()
const { addBlockEvent, removeEventLink, getCurrentBlock, appendEventEmit } = useBlock()
const { PLUGIN_NAME, activePlugin } = useLayout()
const { schema } = getSchema()
const { schema } = useCanvas().canvasApi.value?.getSchema?.() || {}
const events = schema?.events || []
const eventsList = Object.entries(events).map(([key, value]) => {
return {
@ -88,7 +88,7 @@ export default {
const {
schema: { id, componentName }
} = getCurrent()
} = useCanvas().canvasApi.value?.getCurrent?.() || {}
const newEvent = extend(true, {}, data.metaEvent, {
linked: {
@ -117,7 +117,7 @@ export default {
const editEvent = (item, data) => {
const {
schema: { id, componentName }
} = getCurrent()
} = useCanvas().canvasApi.value?.getCurrent?.() || {}
const oldEventName = data.linkedEventName

View File

@ -22,7 +22,6 @@ 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: {
@ -36,10 +35,10 @@ export default {
},
setup(props) {
const { confirm } = useModal()
const { pageState } = useCanvas()
const { pageState, canvasApi } = useCanvas()
const { addBlockProperty, removePropertyLink, getCurrentBlock, editBlockProperty } = useBlock()
const { PLUGIN_NAME, activePlugin } = useLayout()
const { schema } = getSchema()
const { schema } = canvasApi.value.getSchema?.() || {}
const state = reactive({
newPropertyName: ''
@ -68,7 +67,7 @@ export default {
exec() {
const {
schema: { id, componentName }
} = getCurrent()
} = canvasApi.value?.getCurrent?.() || {}
const newProperty = extend(true, {}, property, {
property: state.newPropertyName || `${property.property}${id}`,
linked: {
@ -87,7 +86,7 @@ export default {
const editProperty = (property) => {
const {
schema: { id, componentName }
} = getCurrent()
} = canvasApi.value?.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 '../js/i18n'
import i18n from '@opentiny/tiny-engine-controller/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 '../js/constants'
import { SCHEMA_DATA_TYPE, PAGE_STATUS, TYPES } from '@opentiny/tiny-engine-controller/js/constants'
const hasRule = (required, rules) => {
if (required) {

View File

@ -78,14 +78,13 @@
<script lang="jsx">
import { reactive, ref, watchEffect, onBeforeUnmount } from 'vue'
import { Button, DialogBox, Popover, Search, Alert } from '@opentiny/vue'
import { getGlobalConfig, useModal, usePage, useNotify } from '@opentiny/tiny-engine-controller'
import { getGlobalConfig, useModal, usePage, useNotify, useCanvas } 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 '../js/completion'
import { initLinter, lint } from '../js/linter'
import { initCompletion } from '@opentiny/tiny-engine-controller/js/completion'
import { initLinter, lint } from '@opentiny/tiny-engine-controller/js/linter'
import { SvgButton } from '../index'
export default {
@ -130,7 +129,7 @@ export default {
})
watchEffect(() => {
state.bindLifeCycles = props.bindLifeCycles || getSchema()?.lifeCycles || {}
state.bindLifeCycles = props.bindLifeCycles || useCanvas().canvasApi.value?.getSchema()?.lifeCycles || {}
})
const searchLifeCyclesList = (value) => {
@ -142,7 +141,7 @@ export default {
}
const syncLifeCycle = () => {
const currentSchema = getSchema()
const currentSchema = useCanvas().canvasApi.value?.getSchema?.()
const pageContent = getPageContent()
const { id, fileName } = pageContent
if (id === currentSchema.id || fileName === currentSchema.fileName) {
@ -165,7 +164,8 @@ export default {
const openLifeCyclesPanel = (item) => {
state.title = item
const bindLifeCycleSource = props.bindLifeCycles?.[item] || getSchema().lifeCycles?.[item]
const bindLifeCycleSource =
props.bindLifeCycles?.[item] || useCanvas().canvasApi.value?.getSchema?.()?.lifeCycles?.[item]
state.editorValue =
bindLifeCycleSource?.value ||
`function ${item} (${item === 'setup' ? '{ props, state, watch, onMounted }' : ''}) {} `

View File

@ -6,6 +6,7 @@
</slot>
<tiny-dialog-box
v-if="dialogShouldInitialize"
:visible="state.isVisible"
title="变量绑定"
width="48%"
@ -123,15 +124,14 @@ 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 '../js/ast'
import { DEFAULT_LOOP_NAME } from '../js/constants.js'
import { parse, traverse, generate } from '@opentiny/tiny-engine-controller/js/ast'
import { DEFAULT_LOOP_NAME } from '@opentiny/tiny-engine-controller/js/constants'
import MonacoEditor from './VueMonaco.vue'
import { formatString } from '../js/ast.js'
import { formatString } from '@opentiny/tiny-engine-controller/js/ast'
const { EXPRESSION_TYPE } = constants
@ -149,6 +149,12 @@ 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]
@ -181,6 +187,10 @@ export default {
modelValue: {
type: [String, Number, Boolean, Array, Object, Date],
default: ''
},
lazyLoad: {
type: Boolean,
default: true
}
},
setup(props, { emit }) {
@ -393,10 +403,9 @@ export default {
const confirm = () => {
let variableContent = state.isEditorEditMode ? editor.value?.getEditor().getValue() : state.variable
const { setSaved, canvasApi } = useCanvas()
//
if (oldValue !== variableContent) {
const { setSaved } = useCanvas()
setSaved(false)
variableContent = formatString(variableContent, 'javascript')
}
@ -406,14 +415,14 @@ export default {
if (variableContent) {
if (state.bindPrefix === CONSTANTS.DATASOUCEPREFIX) {
const pageSchema = getSchema()
const pageSchema = canvasApi.value.getSchema()
const stateName = state.variable.replace(`${CONSTANTS.STATE}`, '')
const staticData = state.variableContent.map(({ _id, ...other }) => other)
pageSchema.state[stateName] = staticData
//
setState({ [stateName]: staticData })
canvasApi.value.setState({ [stateName]: staticData })
// setup
genRemoteMethodToLifeSetup(stateName, state.dataSouce, pageSchema)
@ -442,11 +451,13 @@ 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 = getSchema()?.state || {}
state.variables = useCanvas().canvasApi.value.getSchema()?.state || {}
state.bindPrefix = CONSTANTS.STATE
state.variableContent = state.variables[bindKey.value]
nextTick(() => window.dispatchEvent(new Event('resize')))
@ -454,6 +465,8 @@ 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()
@ -469,7 +482,7 @@ export default {
state.variables = bridge
} else if (item.id === 'props') {
state.bindPrefix = CONSTANTS.PROPS
const properties = getSchema()?.schema?.properties
const properties = canvasApi.value.getSchema()?.schema?.properties
const bindProperties = {}
properties?.forEach(({ content }) => {
content.forEach(({ property }) => {
@ -495,7 +508,7 @@ export default {
state.bindPrefix = CONSTANTS.STORE
state.variables = {}
const stores = getGlobalState()
const stores = canvasApi.value.getGlobalState()
stores.forEach(({ id, state: storeState = {}, getters = {} }) => {
const loadProp = (prop) => {
const propBinding = `${id}.${prop}`
@ -516,7 +529,7 @@ export default {
state.variables = params.reduce((variables, param) => ({ ...variables, [param]: param }), {})
} else {
state.bindPrefix = CONSTANTS.STATE
state.variables = getSchema()?.[item.id]
state.variables = canvasApi.value.getSchema()?.[item.id]
}
}
@ -527,6 +540,7 @@ export default {
remove,
cancel,
confirm,
dialogShouldInitialize,
open,
selectItem,
state,

View File

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

View File

@ -70,8 +70,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 '../js/i18n'
import { formatString } from '../js/ast.js'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
import { formatString } from '@opentiny/tiny-engine-controller/js/ast'
export default {
components: {

View File

@ -10,7 +10,7 @@
</div>
</div>
<div class="head-content">
<meta-input v-model="state.text" type="textarea" @change="change"></meta-input>
<meta-input v-model="state.text" type="textarea" @update:modelValue="change"></meta-input>
</div>
</template>
<script>

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 '../js/verification'
import { verifyJsVarName } from '@opentiny/tiny-engine-controller/js/verification'
export default {
components: {

View File

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

View File

@ -22,7 +22,6 @@
<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'
@ -91,7 +90,7 @@ export default {
let newValue = value
if (value?.type === CONSTANT.JSEXPRESSION) {
const pageSchema = getSchema()
const pageSchema = useCanvas().canvasApi.value.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 '../js/i18n'
import i18n from '@opentiny/tiny-engine-controller/js/i18n'
export default {
components: {

View File

@ -25,8 +25,7 @@
<script>
import { ref, watchEffect, nextTick } from 'vue'
import { Popover } from '@opentiny/vue'
import { useProperties, useResource, useModal } from '@opentiny/tiny-engine-controller'
import { updateRect } from '@opentiny/tiny-engine-canvas'
import { useProperties, useResource, useModal, useCanvas } from '@opentiny/tiny-engine-controller'
import { iconHelpCircle } from '@opentiny/vue-icon'
export default {
@ -121,7 +120,7 @@ export default {
})
}
updateRect()
useCanvas().canvasApi.value.updateRect()
}
return {

View File

@ -4,8 +4,7 @@
<script setup>
import { nextTick } from 'vue'
import { updateRect } from '@opentiny/tiny-engine-canvas'
import { useProperties, useResource } from '@opentiny/tiny-engine-controller'
import { useProperties, useResource, useCanvas } from '@opentiny/tiny-engine-controller'
import MetaArrayItem from './MetaArrayItem.vue'
const { children: schemaChildren, componentName, props } = useProperties().getSchema()
@ -26,7 +25,7 @@ const updateColumns = (columns) => {
})
useProperties().getSchema().children = children
nextTick(updateRect)
nextTick(useCanvas().canvasApi.value.updateRect)
}
updateColumns(props?.columns)

View File

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

View File

@ -1,5 +1,6 @@
<template>
<ul
v-if="state.data.length"
:class="['block-list', 'lowcode-scrollbar', { 'is-small-list': blockStyle === 'mini' }, { isShortcutPanel }]"
@mouseleave="state.hover = false"
>
@ -122,12 +123,14 @@
</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 =
@ -140,7 +143,8 @@ export default {
IconSetting: iconSetting(),
IconPlus: iconPlus(),
TinyPopover: Popover,
SvgButton
SvgButton,
SearchEmpty
},
props: {
data: {

View File

@ -1,6 +1,13 @@
<template>
<div>
<tiny-dialog-box :append-to-body="true" :visible="visible" title="新建区块" width="500" @close="cancel">
<tiny-dialog-box
:append-to-body="true"
:close-on-click-modal="false"
:visible="visible"
title="新建区块"
width="500"
@close="cancel"
>
<tiny-form
show-message
:model="formData"
@ -37,7 +44,7 @@
import { reactive, computed, ref } 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 '../js/verification'
import { REGEXP_BLOCK_NAME } from '@opentiny/tiny-engine-controller/js/verification'
export default {
components: {

View File

@ -0,0 +1,19 @@
<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 '../js/ast.js'
import { formatString } from '@opentiny/tiny-engine-controller/js/ast'
export default {
name: 'MonacoEditor',

View File

@ -79,8 +79,9 @@ 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 './js/i18n.js'
import i18n, { i18nKeyMaps } from '@opentiny/tiny-engine-controller/js/i18n'
import enUs from './i18n/en-us.json'
import zhCn from './i18n/zh-cn.json'
@ -90,6 +91,20 @@ 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,
@ -131,7 +146,8 @@ export const MetaComponents = {
MetaIpSection,
MetaRelatedEditor,
MetaRelatedColumns,
MetaTableColumns
MetaTableColumns,
SearchEmpty
}
export {
@ -192,5 +208,6 @@ export {
BindI18n,
BlockDeployDialog,
ProgressBar,
MetaTableColumns
MetaTableColumns,
SearchEmpty
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-common",
"version": "1.0.1",
"version": "1.0.2",
"publishConfig": {
"access": "public"
},
@ -10,12 +10,10 @@
"main": "dist/index.js",
"module": "dist/index.js",
"exports": {
".": "./dist/index.js",
"./js/*": "./js/*.js"
".": "./dist/index.js"
},
"files": [
"dist",
"js"
"dist"
],
"repository": {
"type": "git",
@ -29,31 +27,24 @@
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@babel/generator": "7.18.13",
"@babel/parser": "7.18.13",
"@babel/traverse": "7.18.13",
"@opentiny/tiny-engine-canvas": "workspace:*",
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0",
"@opentiny/vue-locale": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@vue/shared": "^3.3.4",
"css-tree": "^2.3.1",
"monaco-editor": "0.33.0",
"prettier": "2.7.1",
"vue-draggable-next": "2.1.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"vite": "^4.3.7"
},
"peerDependencies": {
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-icon": "^3.14.0",
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15"
}
}

View File

@ -20,9 +20,6 @@ export default defineConfig({
plugins: [vue(), vueJsx()],
publicDir: false,
resolve: {},
define: {
'process.env': {}
},
build: {
cssCodeSplit: false,
lib: {

View File

@ -17,6 +17,7 @@ import prettier from 'prettier'
import parserHtml from 'prettier/parser-html'
import parseCss from 'prettier/parser-postcss'
import parserBabel from 'prettier/parser-babel'
import prettierConfig from './config-files/prettierrc'
const METHOD_REGEXP = /function.*?\(/
@ -33,10 +34,7 @@ const formatScript = (string) => {
const options = {
parser: 'babel',
plugins: [parserBabel],
printWidth: 120,
singleQuote: true,
semi: false,
trailingComma: 'none'
...prettierConfig
}
try {
// 低码中的编辑器大多只会输入js值并不是一个完整的javascript表达式无法格式化因此需要特殊处理预格式化该种情形
@ -53,21 +51,21 @@ const formatJson = (string) =>
parser: 'json',
plugins: [parserBabel],
trailingComma: 'es5',
tabWidth: 2,
semi: false,
singleQuote: true
...prettierConfig
})
const formatHtml = (string) =>
prettier.format(string, {
parser: 'html',
plugins: [parserBabel, parserHtml]
plugins: [parserBabel, parserHtml],
...prettierConfig
})
const formatCss = (string) =>
prettier.format(string, {
parser: 'css',
plugins: [parseCss]
plugins: [parseCss],
...prettierConfig
})
const formatterMap = {

View File

@ -11,7 +11,8 @@
*/
import { PAGE_STATUS } from './constants'
import { useEditorInfo, useResource } from '@opentiny/tiny-engine-controller'
import useEditorInfo from '../src/useEditorInfo'
import useResource from '../src/useResource'
export const getCanvasStatus = (data) => {
// 写死ID 待删除

View File

@ -10,7 +10,8 @@
*
*/
import { useCanvas, useResource } from '@opentiny/tiny-engine-controller'
import useCanvas from '../src/useCanvas'
import useResource from '../src/useResource'
const keyWords = [
'state',

View File

@ -0,0 +1,8 @@
import eslintRecommended from '@eslint/js/src/configs/eslint-recommended.js'
export default {
...eslintRecommended.rules,
'no-console': 'error',
'no-debugger': 'error',
'space-before-function-paren': 'off',
'no-use-before-define': 'error'
}

View File

@ -0,0 +1,8 @@
export default {
semi: false,
singleQuote: true,
printWidth: 120,
trailingComma: 'none',
endOfLine: 'auto',
tabWidth: 2
}

View File

@ -11,7 +11,7 @@
*/
import * as cssTree from 'css-tree'
import { hyphenate } from '@opentiny/tiny-engine-controller/utils'
import { hyphenate } from '../utils'
/**
* 传入 css 字符串得到以选择器为 Keycss 规则为 value object

View File

@ -11,7 +11,9 @@
*/
import { useHttp } from '@opentiny/tiny-engine-http'
import { usePage, useCanvas, useNotify } from '@opentiny/tiny-engine-controller'
import usePage from '../src/usePage'
import useCanvas from '../src/useCanvas'
import useNotify from '../src/useNotify'
import { isVsCodeEnv } from './environments'
import { generateRouter, generatePage } from './vscodeGenerateFile'
@ -22,8 +24,13 @@ const http = useHttp()
* @param { json } params {"event_type": design_error,"url": "elit in reprehenderit enim incididunt" }
* @returns { Promise }
*/
export const requestEvent = (params) =>
http.post('/platform-center/api/platform/monitoring/event', params).catch(() => {})
export const requestEvent = (url, params) => {
if (!url) {
return
}
return http.post(url, params).catch(() => {})
}
/**
* 页面更新

View File

@ -1,27 +1,29 @@
/**
* 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 { PROD, BASE_URL } from './environments'
import eslintWorkerUrl from './worker-files/eslint.worker?worker&url'
export const initLinter = (editor, monacoInstance, state) => {
let workerUrl = `${BASE_URL}monaco-linter/eslint.worker.js`
let workerUrl = new URL(eslintWorkerUrl, import.meta.url)
// 线上环境,存在 worker 资源跨域的情况
if (PROD) {
const workerBlob = new Blob([`importScripts('${workerUrl}');`], { type: 'application/javascript' })
if (workerUrl.origin !== location.origin) {
const workerBlob = new Blob([`import('${workerUrl}');`], {
type: 'application/javascript'
})
workerUrl = window.URL.createObjectURL(workerBlob)
}
const worker = new Worker(workerUrl)
const worker = new Worker(workerUrl, { type: 'module' })
// 监听 ESLint web worker 的返回
worker.onmessage = function (event) {

View File

@ -1,17 +1,19 @@
/**
* 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 { requestEvent } from './http.js'
let monitorUrl = ''
/**
* 全局js异常埋点上报
* @param errorMessage 异常信息
@ -35,7 +37,7 @@ const getUrlUnit = () => {
const globalMonitoring = () => {
window.onerror = function (errorMessage, scriptURI, lineNo, columnNo, error) {
requestEvent({
requestEvent(monitorUrl, {
event_type: 'design_JSError',
url: window.location.href,
unit: getUrlUnit(),
@ -67,7 +69,7 @@ const promiseMonitoring = () => {
}
}
requestEvent({
requestEvent(monitorUrl, {
event_type: 'design_promiseError',
url: window.location.href,
unit: getUrlUnit(),
@ -91,8 +93,12 @@ const promiseMonitoring = () => {
*/
export const iframeMonitoring = () => {
if (!monitorUrl) {
return false
}
window.frames[0].onerror = function (errorMessage, scriptURI, lineNo, columnNo, error) {
requestEvent({
requestEvent(monitorUrl, {
event_type: 'design_iframeError',
url: window.location.href,
unit: getUrlUnit(),
@ -106,7 +112,8 @@ export const iframeMonitoring = () => {
}
}
export const initMonitor = () => {
export const initMonitor = (url) => {
monitorUrl = url
globalMonitoring()
promiseMonitoring()
}

View File

@ -12,7 +12,7 @@
import { constants } from '@opentiny/tiny-engine-utils'
import { isDevelopEnv } from './environments'
import { useResource } from '@opentiny/tiny-engine-controller'
import useResource from '../src/useResource'
// prefer old unicode hacks for backward compatibility
const { COMPONENT_NAME } = constants

View File

@ -0,0 +1,71 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import { Linter } from 'eslint-linter-browserify'
import eslintRule from '../config-files/eslint-rule'
const defaultConfig = {
env: {
browser: true,
es6: true
},
parserOptions: {
ecmaFeatures: {
jsx: true
},
ecmaVersion: 'latest',
sourceType: 'module'
}
}
const config = {
...defaultConfig,
rules: {
...eslintRule,
// JS 面板中,仅定义 function但可能不使用该方法
'no-unused-vars': 'off',
'no-alert': 'off',
'no-console': 'off'
},
settings: {}
}
// 错误的等级ESLint 与 monaco 的存在差异,做一层映射
const severityMap = {
2: 'Error',
1: 'Warning'
}
const linter = new Linter()
self.addEventListener('message', (event) => {
const { code, version } = event.data
const ruleDefines = linter.getRules()
const errs = linter.verify(code, config)
const markers = errs.map(({ ruleId = '', line, endLine, column, endColumn, message, severity }) => ({
code: {
value: ruleId || '',
target: ruleDefines.get(ruleId)?.meta?.docs?.url || ''
},
startLineNumber: line,
endLineNumber: endLine,
startColumn: column,
endColumn: endColumn,
message: message,
severity: severityMap[severity],
source: 'ESLint'
}))
// ESLint 静态检查结果,发回主线程
self.postMessage({ markers, version })
})

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-controller",
"version": "1.0.2",
"version": "1.0.3",
"publishConfig": {
"access": "public"
},
@ -10,10 +10,12 @@
"main": "dist/index.js",
"module": "dist/index.js",
"files": [
"dist"
"dist",
"js"
],
"exports": {
".": "./dist/index.js",
"./js/": "./dist/js/",
"./utils": "./dist/utils.js",
"./adapter": "./dist/adapter.js"
},
@ -29,21 +31,29 @@
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@babel/generator": "~7.23.2",
"@babel/parser": "~7.23.2",
"@babel/traverse": "~7.23.2",
"@opentiny/tiny-engine-builtin-component": "workspace:*",
"@opentiny/tiny-engine-canvas": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@vue/shared": "^3.3.4"
"@vue/shared": "^3.3.4",
"css-tree": "^2.3.1",
"eslint-linter-browserify": "8.57.0",
"prettier": "2.7.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^1.3.10",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"glob": "^10.3.4",
"vite": "^4.3.7"
},
"peerDependencies": {
"vue": "^3.4.15"
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-locale": "^3.14.0",
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"
}
}

View File

@ -15,17 +15,16 @@ import { hyphenate } from '@vue/shared'
import { extend, copyArray } from '@opentiny/vue-renderless/common/object'
import { format } from '@opentiny/vue-renderless/common/date'
import { remove } from '@opentiny/vue-renderless/common/array'
import { getCanvasStatus } from '@opentiny/tiny-engine-common/js/index'
import { ast2String, parseExpression } from '@opentiny/tiny-engine-common/js/ast'
import { getCssObjectFromStyleStr } from '@opentiny/tiny-engine-common/js/css'
import { constants } from '@opentiny/tiny-engine-utils'
import { getCanvasStatus } from '../js/canvas'
import { ast2String, parseExpression } from '../js/ast'
import { getCssObjectFromStyleStr } from '../js/css'
import useCanvas from './useCanvas'
import useTranslate from './useTranslate'
import useEditorInfo from './useEditorInfo'
import useBreadcrumb from './useBreadcrumb'
import useLayout from './useLayout'
import { getGlobalConfig } from './globalConfig'
import { getSchema, getCurrent } from '@opentiny/tiny-engine-canvas'
const { SORT_TYPE, SCHEMA_DATA_TYPE, BLOCK_OPENNESS } = constants
@ -279,7 +278,7 @@ const initBlock = async (block = {}, _langs = {}, isEdit) => {
// 把区块的schema传递给画布
await resetBlockCanvasState({ pageSchema: getBlockPageSchema(block) })
// 这一步操作很重要让区块管理面板和画布共同维护同一份区块schema
block.content = getSchema()
block.content = useCanvas().canvasApi.value?.getSchema()
setCurrentBlock(block)
setBreadcrumbBlock([block[nameCn] || block.label], block.histories)
@ -362,7 +361,7 @@ const createEmptyBlock = ({ name_cn, label, path, categories }) => {
}
const setComponentLinkedValue = ({ propertyName, value }) => {
const { schema } = getCurrent()
const { schema } = useCanvas().canvasApi.value?.getCurrent() || {}
if (!propertyName || !schema) {
return

View File

@ -11,8 +11,7 @@
*/
/* eslint-disable no-new-func */
import { reactive } from 'vue'
import { setSchema, renderApi } from '@opentiny/tiny-engine-canvas'
import { reactive, ref } from 'vue'
import { constants } from '@opentiny/tiny-engine-utils'
import useHistory from './useHistory'
@ -52,12 +51,20 @@ const defaultSchema = {
outputs: []
}
const canvasApi = ref({})
const isCanvasApiReady = ref(false)
const initCanvasApi = (newCanvasApi) => {
canvasApi.value = newCanvasApi
isCanvasApiReady.value = true
}
const pageState = reactive({ ...defaultPageState, loading: true })
// 重置画布数据
const resetCanvasState = async (state = {}) => {
Object.assign(pageState, defaultPageState, state)
await setSchema(pageState.pageSchema)
await canvasApi.value?.setSchema(pageState.pageSchema)
}
// 页面重置画布数据
@ -151,10 +158,11 @@ export default function () {
resetPageCanvasState,
resetBlockCanvasState,
clearCurrentState,
getDataSourceMap: renderApi.getDataSourceMap,
setDataSourceMap: renderApi.setDataSourceMap,
getCurrentSchema,
setCurrentSchema,
getCurrentPage
getCurrentPage,
initCanvasApi,
canvasApi,
isCanvasApiReady
}
}

View File

@ -12,7 +12,6 @@
import { reactive, isProxy, toRaw, watch } from 'vue'
import useCanvas from './useCanvas'
import { setSchema, getSchema } from '@opentiny/tiny-engine-canvas'
const schema2String = (schema) => {
if (isProxy(schema)) {
@ -62,7 +61,7 @@ const push = (schema) => {
const go = (addend, valid) => {
historyState.index = historyState.index + addend
setSchema(string2Schema(list[historyState.index]))
useCanvas().canvasApi.value?.setSchema(string2Schema(list[historyState.index]))
// 不是锁定状态,撤销操作后,传递第二个标识位,将 list 的长度减一,置灰 undoredo 操作按钮
if (typeof valid === 'boolean') {
@ -84,17 +83,6 @@ const forward = () => {
}
}
const addHistory = (schema) => {
if (!schema) {
useCanvas().setSaved(false)
push(getSchema())
} else {
clear()
// 初始 schema 需要设置为第一条历史记录
push(schema)
}
}
const clear = () => {
list.splice(0)
Object.assign(historyState, {
@ -104,6 +92,17 @@ const clear = () => {
})
}
const addHistory = (schema) => {
if (!schema) {
useCanvas().setSaved(false)
push(useCanvas().canvasApi.value?.getSchema())
} else {
clear()
// 初始 schema 需要设置为第一条历史记录
push(schema)
}
}
// 监控下标,判断是否允许前进后退标志
watch(
() => historyState.index,

View File

@ -11,7 +11,6 @@
*/
import { toRaw, nextTick, shallowReactive, ref } from 'vue'
import { getNode, setState, updateRect } from '@opentiny/tiny-engine-canvas'
import { constants } from '@opentiny/tiny-engine-utils'
import useCanvas from './useCanvas'
import useResource from './useResource'
@ -192,6 +191,7 @@ const setProp = (name, value, type) => {
}
// 没有父级或者不在节点上面要更新内容。就用setState
const { getNode, setState, updateRect } = useCanvas().canvasApi.value || {}
getNode(properties.schema.id, true)?.parent || setState(useCanvas().getPageSchema().state)
propsUpdateKey.value++

View File

@ -14,9 +14,8 @@ import { reactive } from 'vue'
import { getGlobalConfig } from './globalConfig'
import { useHttp } from '@opentiny/tiny-engine-http'
import { utils, constants } from '@opentiny/tiny-engine-utils'
import { Builtin, addScript, addStyle, canvasDispatch } from '@opentiny/tiny-engine-canvas'
import { getCanvasStatus } from '@opentiny/tiny-engine-common/js/index'
import { meta as BuiltinComponentMaterials } from '@opentiny/tiny-engine-builtin-component'
import { getCanvasStatus } from '../js/canvas'
import useApp from './useApp'
import useCanvas from './useCanvas'
import useTranslate from './useTranslate'
@ -138,6 +137,7 @@ const registerBlock = async (data, notFetchResouce) => {
return block
} else {
if (!blockResource.get(label)) {
const { addScript, addStyle } = useCanvas().canvasApi.value
const promises = scripts
.filter((item) => item.includes('umd.js'))
.map(addScript)
@ -372,6 +372,7 @@ const fetchResource = async ({ isInit = true } = {}) => {
const { id, type } = useEditorInfo().useInfo()
useApp().appInfoState.selectedId = id
const { Builtin } = useCanvas().canvasApi.value
Builtin.data.materials.components[0].children.map(registerComponent)
BuiltinComponentMaterials.components[0].children.map(registerComponent)
@ -451,7 +452,7 @@ const updateCanvasDependencies = (blocks) => {
getBlockDeps(block.content.dependencies)
})
canvasDispatch('updateDependencies', { detail: resState.thirdPartyDeps })
useCanvas().canvasApi.value?.canvasDispatch('updateDependencies', { detail: resState.thirdPartyDeps })
}
export default function () {

View File

@ -11,7 +11,7 @@
*/
import { Modal } from '@opentiny/vue'
import { VITE_ORIGIN } from '@opentiny/tiny-engine-common/js/environments'
import { VITE_ORIGIN } from '../js/environments'
import useCanvas from './useCanvas'
import { getGlobalConfig } from './globalConfig'

View File

@ -13,12 +13,12 @@
import { reactive, ref } from 'vue'
import { useHttp } from '@opentiny/tiny-engine-http'
import { utils } from '@opentiny/tiny-engine-utils'
import { setLocales } from '@opentiny/tiny-engine-canvas'
import { isVsCodeEnv } from '@opentiny/tiny-engine-common/js/environments'
import { isVsCodeEnv } from '../js/environments'
import { constants } from '@opentiny/tiny-engine-utils'
import { generateI18n } from '@opentiny/tiny-engine-common/js/vscodeGenerateFile'
import { generateI18n } from '../js/vscodeGenerateFile'
import useResource from './useResource'
import { PROP_DATA_TYPE } from '../utils'
import useCanvas from './useCanvas'
const { HOST_TYPE } = constants
const state = reactive({
@ -101,7 +101,7 @@ const ensureI18n = (obj, send) => {
}
})
setLocales(messages, true)
useCanvas().canvasApi.value?.setLocales(messages, true)
} catch (e) {
// 不需要处理有报错的词条会在画布初始化的时候统一调setLocales这个方法
}
@ -171,7 +171,7 @@ const initAppI18n = async (appId) => {
host: appId,
hostType: HOST_TYPE.App
})
setLocales(i18nResource.messages)
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
}
}
@ -181,7 +181,7 @@ const initBlockI18n = async (blockId) => {
host: blockId,
hostType: HOST_TYPE.Block
})
setLocales(i18nResource.messages)
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
}
}
@ -192,7 +192,7 @@ const initBlockLocalI18n = async (langs = {}) => {
hostType: HOST_TYPE.Block,
local: true
})
setLocales(i18nResource.messages)
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
}
const format = (str = '', params = {}) => str.replace(/\$\{(.+?)\}/g, (substr, key) => params[key] || '')

View File

@ -14,26 +14,40 @@ import { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { glob } from 'glob'
import { fileURLToPath } from 'node:url'
const jsEntries = glob.sync('./js/**/*.js').map((file) => {
return [file.slice(0, file.length - path.extname(file).length), fileURLToPath(new URL(file, import.meta.url))]
})
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx()],
publicDir: false,
resolve: {},
base: './',
define: {
'import.meta': 'import.meta',
'import.meta.env.MODE': 'import.meta.env.MODE',
'import.meta.env.PROD': 'import.meta.env.PROD',
'import.meta.env.BASE_URL': 'import.meta.env.BASE_URL'
},
build: {
cssCodeSplit: false,
lib: {
entry: {
index: path.resolve(__dirname, './src/index.js'),
adapter: path.resolve(__dirname, './adapter.js'),
utils: path.resolve(__dirname, './utils.js')
utils: path.resolve(__dirname, './utils.js'),
...Object.fromEntries(jsEntries)
},
name: 'controller',
fileName: (formats, entryName) => `${entryName}.js`,
formats: ['es']
},
rollupOptions: {
external: ['vue', /@opentiny\/tiny-engine.*/, /@opentiny\/vue.*/]
external: ['vue', 'vue-i18n', /@opentiny\/tiny-engine.*/, /@opentiny\/vue.*/, /^prettier.*/, /^@babel.*/]
}
}
})

View File

@ -2,4 +2,11 @@
NODE_ENV=production
VITE_CDN_DOMAIN=https://npm.onmicrosoft.cn
VITE_LOCAL_IMPORT_MAPS=false
VITE_LOCAL_BUNDLE_DEPS=false
# VITE_ORIGIN=
# 错误监控上报 url
VITE_ERROR_MONITOR_URL=/platform-center/api/platform/monitoring/event
# 是否开启错误监控
VITE_ERROR_MONITOR=false

View File

@ -2,5 +2,7 @@
NODE_ENV=development
VITE_CDN_DOMAIN=https://npm.onmicrosoft.cn
VITE_LOCAL_IMPORT_MAPS=false
VITE_LOCAL_BUNDLE_DEPS=false
# request data via alpha service
# VITE_ORIGIN=
# VITE_ORIGIN=

View File

@ -2,4 +2,6 @@
NODE_ENV=production
VITE_CDN_DOMAIN=https://npm.onmicrosoft.cn
#VITE_ORIGIN=
VITE_LOCAL_IMPORT_MAPS=false
VITE_LOCAL_BUNDLE_DEPS=false
#VITE_ORIGIN=

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>国际化</title>
<g id="国际化" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组" transform="translate(2.000000, 2.000000)" stroke="#191919">
<circle id="椭圆形" cx="6" cy="6" r="6"></circle>
<path d="M1.61538462,6.69230769 C3.61538462,5.76923077 5.61538462,5.30769231 7.61538462,5.30769231 C9.61538462,5.30769231 11.6153846,5.76923077 13.6153846,6.69230769" id="直线" transform="translate(7.615385, 6.000000) rotate(90.000000) translate(-7.615385, -6.000000) "></path>
<path d="M-1.61538462,6.69230769 C0.384615385,5.76923077 2.38461538,5.30769231 4.38461538,5.30769231 C6.38461538,5.30769231 8.38461538,5.76923077 10.3846154,6.69230769" id="直线备份" transform="translate(4.384615, 6.000000) scale(-1, 1) rotate(90.000000) translate(-4.384615, -6.000000) "></path>
<path d="M0.461538462,4.61538462 C2.30769231,4.30769231 4.15384615,4.15384615 6,4.15384615 C7.84615385,4.15384615 9.69230769,4.30769231 11.5384615,4.61538462" id="直线-13" stroke-linecap="round" transform="translate(6.000000, 4.384615) scale(1, -1) translate(-6.000000, -4.384615) "></path>
<path d="M0.461538462,7.84615385 C2.30769231,7.53846154 4.15384615,7.38461538 6,7.38461538 C7.84615385,7.38461538 9.69230769,7.53846154 11.5384615,7.84615385" id="直线-13备份" stroke-linecap="round"></path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -4,7 +4,6 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="%VITE_CDN_DOMAIN%/@opentiny/vue-theme@3.11/index.css" rel="stylesheet" />
<style type="text/css">
.loading-warp {
display: flex;

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine",
"version": "1.0.0-beta.3",
"version": "1.0.0-beta.4",
"description": "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.",
"homepage": "https://opentiny.design/tiny-engine",
"keywords": [
@ -30,13 +30,14 @@
"author": "OpenTiny Team",
"license": "MIT",
"dependencies": {
"@babel/core": "7.18.13",
"@babel/generator": "7.18.13",
"@babel/parser": "7.18.13",
"@babel/traverse": "7.18.13",
"@babel/core": "~7.23.2",
"@babel/generator": "~7.23.2",
"@babel/parser": "~7.23.2",
"@babel/traverse": "~7.23.2",
"@opentiny/tiny-engine-canvas": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-dsl-vue": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-plugin-block": "workspace:*",
@ -77,21 +78,22 @@
"@opentiny/tiny-engine-toolbar-setting": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/vue": "~3.11.0",
"@opentiny/vue-design-smb": "~3.11.0",
"@opentiny/vue-renderless": "~3.11.0",
"@opentiny/vue-theme": "~3.11.0",
"@opentiny/vue": "~3.14.0",
"@opentiny/vue-design-smb": "~3.14.0",
"@opentiny/vue-renderless": "~3.14.0",
"@opentiny/vue-theme": "~3.14.0",
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/repl": "^2.9.0",
"@vueuse/core": "^9.6.0",
"element-resize-detector": "^1.2.4",
"eslint-linter-browserify": "8.57.0",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",
"jszip": "^3.10.1",
"monaco-editor": "0.33.0",
"prettier": "2.7.1",
"sortablejs": "^1.14.0",
"vue": "^3.4.15",
"vue": "3.4.23",
"vue-i18n": "^9.9.0"
},
"devDependencies": {
@ -101,7 +103,7 @@
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@types/node": "^18.0.0",
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^1.3.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"assert": "^2.0.0",
"buffer": "^6.0.3",
"cross-env": "^7.0.3",
@ -118,9 +120,11 @@
"rollup-plugin-polyfill-node": "^0.12.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-visualizer": "^5.8.3",
"shelljs": "^0.8.5",
"svg-sprite-loader": "^6.0.11",
"vite": "^4.3.7",
"vite-plugin-monaco-editor": "^1.0.10",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-static-copy": "^0.16.0",
"vite-plugin-svg-icons": "^2.0.1",
"vue-eslint-parser": "^8.0.1"
},

View File

@ -311,7 +311,7 @@
"loop": true,
"condition": true,
"styles": true,
"isContainer": false,
"isContainer": true,
"isModal": false,
"isPopper": false,
"nestingRule": {
@ -632,7 +632,7 @@
"loop": true,
"condition": true,
"styles": true,
"isContainer": false,
"isContainer": true,
"isModal": false,
"isPopper": false,
"nestingRule": {
@ -1089,7 +1089,7 @@
"loop": true,
"condition": true,
"styles": true,
"isContainer": false,
"isContainer": true,
"isModal": false,
"isPopper": false,
"nestingRule": {
@ -1805,7 +1805,9 @@
},
"widget": {
"component": "MetaCodeEditor",
"props": {}
"props": {
"dataType": "JSFunction"
}
}
},
{
@ -4137,6 +4139,9 @@
"contentMenu": {
"actions": []
}
},
"configure": {
"isContainer": true
}
},
{
@ -4495,6 +4500,9 @@
"contentMenu": {
"actions": []
}
},
"configure": {
"isContainer": true
}
},
{
@ -5977,7 +5985,7 @@
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isContainer": false,
"isModal": false,
"nestingRule": {
"childWhitelist": "",
@ -6392,7 +6400,7 @@
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isContainer": false,
"isModal": false,
"nestingRule": {
"childWhitelist": "",
@ -8181,7 +8189,7 @@
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isContainer": false,
"isModal": false,
"nestingRule": {
"childWhitelist": "",
@ -8948,7 +8956,9 @@
"cols": 12,
"widget": {
"component": "MetaCodeEditor",
"props": {}
"props": {
"language": "json"
}
},
"description": {
"zh_CN": ""
@ -8994,7 +9004,7 @@
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isContainer": false,
"clickCapture": false,
"isModal": false,
"nestingRule": {
@ -12134,6 +12144,867 @@
"disable": ["copy", "remove"]
}
}
},
{
"name": {
"zh_CN": "日期选择"
},
"component": "TinyDatePicker",
"icon": "datepick",
"description": "用于输入或选择日期",
"docUrl": "",
"screenshot": "",
"tags": "",
"keywords": "",
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "DatePicker",
"version": "",
"destructuring": true
},
"group": "component",
"priority": 1,
"schema": {
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "modelValue",
"label": {
"text": {
"zh_CN": "绑定值"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "双向绑定值"
},
"labelPosition": "left"
},
{
"property": "type",
"label": {
"text": {
"zh_CN": "类型"
}
},
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "日期",
"value": "date"
},
{
"label": "日期时间",
"value": "datetime"
},
{
"label": "周",
"value": "week"
},
{
"label": "月份",
"value": "month"
},
{
"label": "年份",
"value": "year"
}
]
}
},
"description": {
"zh_CN": "设置日期框的type属性"
}
},
{
"property": "placeholder",
"label": {
"text": {
"zh_CN": "占位文本"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "输入框占位文本"
},
"labelPosition": "left"
},
{
"property": "clearable",
"label": {
"text": {
"zh_CN": "清除按钮"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "是否显示清除按钮"
},
"labelPosition": "left"
},
{
"property": "disabled",
"label": {
"text": {
"zh_CN": "禁用"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "禁用"
}
},
{
"property": "readonly",
"label": {
"text": {
"zh_CN": "是否只读"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": ""
}
},
{
"property": "size",
"label": {
"text": {
"zh_CN": "尺寸"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "medium",
"value": "medium"
},
{
"label": "small",
"value": "small"
},
{
"label": "mini",
"value": "mini"
}
]
}
},
"description": {
"zh_CN": "日期框尺寸。该属性的可选值为 medium / small / mini"
}
}
]
},
{
"name": "1",
"label": {
"zh_CN": "其他"
},
"content": [
{
"property": "maxlength",
"label": {
"text": {
"zh_CN": "最大长度"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {}
},
"description": {
"zh_CN": "设置 input 框的maxLength"
},
"labelPosition": "left"
},
{
"property": "autofocus",
"label": {
"text": {
"zh_CN": "聚焦"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "自动获取焦点"
},
"labelPosition": "left"
}
],
"description": {
"zh_CN": ""
}
}
],
"events": {
"onChange": {
"label": {
"zh_CN": "值改变时触发"
},
"description": {
"zh_CN": "在 Input 值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框改变后的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onInput": {
"label": {
"zh_CN": "输入值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框输入的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onUpdate:modelValue": {
"label": {
"zh_CN": "双向绑定的值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "双向绑定的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onBlur": {
"label": {
"zh_CN": "失去焦点时触发"
},
"description": {
"zh_CN": "在 Input 失去焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onFocus": {
"label": {
"zh_CN": "获取焦点时触发"
},
"description": {
"zh_CN": "在 Input 获取焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onClear": {
"label": {
"zh_CN": "点击清空按钮时触发"
},
"description": {
"zh_CN": "点击清空按钮时触发"
},
"type": "event",
"functionInfo": {
"params": [],
"returns": {}
},
"defaultValue": ""
}
}
},
"configure": {
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isModal": false,
"nestingRule": {
"childWhitelist": "",
"parentWhitelist": "",
"descendantBlacklist": "",
"ancestorWhitelist": ""
},
"isNullNode": false,
"isLayout": false,
"rootSelector": "",
"shortcuts": {
"properties": ["value", "disabled"]
},
"contextMenu": {
"actions": ["create symbol"],
"disable": ["copy", "remove"]
}
}
},
{
"name": {
"zh_CN": "数字输入框"
},
"component": "TinyNumeric",
"icon": "numeric",
"description": "通过鼠标或键盘输入字符",
"docUrl": "",
"screenshot": "",
"tags": "",
"keywords": "",
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "Numeric",
"version": "",
"destructuring": true
},
"group": "component",
"priority": 1,
"schema": {
"properties": [
{
"label": {
"zh_CN": "基础信息"
},
"description": {
"zh_CN": "基础信息"
},
"collapse": {
"number": 6,
"text": {
"zh_CN": "显示更多"
}
},
"content": [
{
"property": "modelValue",
"label": {
"text": {
"zh_CN": "绑定值"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "双向绑定值"
},
"labelPosition": "left"
},
{
"property": "placeholder",
"label": {
"text": {
"zh_CN": "占位文本"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaBindI18n",
"props": {}
},
"description": {
"zh_CN": "输入框占位文本"
},
"labelPosition": "left"
},
{
"property": "allow-empty",
"label": {
"text": {
"zh_CN": "内容可清空"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "是否内容可清空"
},
"labelPosition": "left"
},
{
"property": "disabled",
"label": {
"text": {
"zh_CN": "禁用"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": "禁用"
}
},
{
"property": "size",
"label": {
"text": {
"zh_CN": "尺寸"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "medium",
"value": "medium"
},
{
"label": "small",
"value": "small"
},
{
"label": "mini",
"value": "mini"
}
]
}
},
"description": {
"zh_CN": "输入框尺寸。该属性的可选值为 medium / small / mini"
}
},
{
"property": "controls",
"label": {
"text": {
"zh_CN": "是否使用加减按钮"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSwitch",
"props": {}
},
"description": {
"zh_CN": ""
}
},
{
"property": "controls-position",
"label": {
"text": {
"zh_CN": "加减按钮位置"
}
},
"required": true,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaSelect",
"props": {
"options": [
{
"label": "左右两侧",
"value": ""
},
{
"label": "只在右侧",
"value": "right"
}
]
}
},
"description": {
"zh_CN": "加减按钮位置"
},
"labelPosition": "left"
},
{
"property": "precision",
"label": {
"text": {
"zh_CN": "数值精度"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "数值精度"
},
"labelPosition": "left"
},
{
"property": "step",
"label": {
"text": {
"zh_CN": "步长"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "步长"
},
"labelPosition": "left"
},
{
"property": "max",
"label": {
"text": {
"zh_CN": "可输入的最大数值"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "可输入的最大数值"
},
"labelPosition": "left"
},
{
"property": "min",
"label": {
"text": {
"zh_CN": "可输入的最小数值"
}
},
"required": false,
"readOnly": false,
"disabled": false,
"cols": 12,
"widget": {
"component": "MetaNumber",
"props": {
"allowEmpty": true
}
},
"description": {
"zh_CN": "可输入的最大数值"
},
"labelPosition": "left"
}
]
}
],
"events": {
"onChange": {
"label": {
"zh_CN": "值改变时触发"
},
"description": {
"zh_CN": "在 Input 值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框改变后的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onInput": {
"label": {
"zh_CN": "输入值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "输入框输入的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onUpdate:modelValue": {
"label": {
"zh_CN": "双向绑定的值改变时触发"
},
"description": {
"zh_CN": "在 Input 输入值改变时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "value",
"type": "string",
"defaultValue": "",
"description": {
"zh_CN": "双向绑定的值"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onBlur": {
"label": {
"zh_CN": "失去焦点时触发"
},
"description": {
"zh_CN": "在 Input 失去焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onFocus": {
"label": {
"zh_CN": "获取焦点时触发"
},
"description": {
"zh_CN": "在 Input 获取焦点时触发"
},
"type": "event",
"functionInfo": {
"params": [
{
"name": "event",
"type": "Object",
"defaultValue": "",
"description": {
"zh_CN": "原生 event"
}
}
],
"returns": {}
},
"defaultValue": ""
},
"onClear": {
"label": {
"zh_CN": "点击清空按钮时触发"
},
"description": {
"zh_CN": "点击清空按钮时触发"
},
"type": "event",
"functionInfo": {
"params": [],
"returns": {}
},
"defaultValue": ""
}
}
},
"configure": {
"loop": true,
"condition": true,
"styles": true,
"isContainer": true,
"isModal": false,
"nestingRule": {
"childWhitelist": "",
"parentWhitelist": "",
"descendantBlacklist": "",
"ancestorWhitelist": ""
},
"isNullNode": false,
"isLayout": false,
"rootSelector": "",
"shortcuts": {
"properties": ["value", "disabled"]
},
"contextMenu": {
"actions": ["create symbol"],
"disable": ["copy", "remove"]
}
}
}
],
"blocks": [],
@ -13152,6 +14023,38 @@
]
}
}
},
{
"name": {
"zh_CN": "日期选择"
},
"icon": "datepick",
"screenshot": "",
"snippetName": "TinyDatePicker",
"schema": {
"componentName": "TinyDatePicker",
"props": {
"placeholder": "请输入",
"modelValue": ""
}
}
},
{
"name": {
"zh_CN": "数字输入框"
},
"icon": "numeric",
"screenshot": "",
"snippetName": "TinyNumeric",
"schema": {
"componentName": "TinyNumeric",
"props": {
"allow-empty": true,
"placeholder": "请输入",
"controlsPosition": "right",
"step": 1
}
}
}
]
},

View File

@ -1,57 +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.
*
*/
// importScripts 不支持 esm, 此处使用 umd, 对外暴露的变量名linter需要填写openTiny的cdn地址
importScripts('')
// 根据公司的编码规范内置了 config/rules, 可以进一步定制
const config = {
...self.linter.config,
rules: {
...self.linter.config.rules,
// JS 面板中,仅定义 function但可能不使用该方法
'no-unused-vars': 'off',
'no-alert': 'off',
'no-console': 'off'
},
settings: {}
}
// 错误的等级ESLint 与 monaco 的存在差异,做一层映射
const severityMap = {
2: 'Error',
1: 'Warning'
}
self.addEventListener('message', (event) => {
const { code, version } = event.data
const ruleDefines = self.linter.esLinter.getRules()
const errs = self.linter.esLinter.verify(code, config)
const markers = errs.map(({ ruleId = '', line, endLine, column, endColumn, message, severity }) => ({
code: {
value: ruleId,
target: ruleDefines.get(ruleId)?.meta?.docs?.url
},
startLineNumber: line,
endLineNumber: endLine,
startColumn: column,
endColumn: endColumn,
message: message,
severity: severityMap[severity],
source: 'ESLint'
}))
// ESLint 静态检查结果,发回主线程
self.postMessage({ markers, version })
})

View File

@ -0,0 +1,82 @@
import path from 'node:path'
import { readJsonSync } from 'fs-extra'
import { installPackageTemporary } from '../vite-plugins/installPackageTemporary'
import { configServerAddProxy } from '../vite-plugins/configureServerAddProxy'
import { viteStaticCopy } from 'vite-plugin-static-copy'
import {
getCdnPathNpmInfoForSingleFile,
getPackageNeedToInstallAndFilesUsingSameVersion,
dedupeCopyFiles,
copyfileToDynamicSrcMapper
} from './locateCdnNpmInfo'
export function extraBundleCdnLink(filename, originCdnPrefix) {
const result = []
const bundle = readJsonSync(filename)
bundle.data?.materials?.components?.forEach((component) => {
if (component.npm) {
const possibleUrl = [component.npm.script, component.npm.css]
possibleUrl.forEach((url) => {
if (url?.startsWith(originCdnPrefix) && !result.includes(url)) {
result.push(url)
}
})
}
})
return result
}
export function replaceBundleCdnLink(bundle, fileMap) {
bundle.data?.materials?.components?.forEach((component) => {
if (component.npm) {
const possibleUrl = ['script', 'css']
possibleUrl.forEach((key) => {
const matchRule = fileMap.find((rule) => component.npm[key] === rule.originUrl)
if (matchRule) {
component.npm[key] = matchRule.newUrl
}
})
}
})
}
export function copyBundleDeps({
bundleFile,
targetBundleFile,
originCdnPrefix,
base,
dir = 'material-static',
bundleTempDir = 'bundle-deps/material-static'
}) {
const cdnFiles = extraBundleCdnLink(bundleFile, originCdnPrefix).map((url) =>
getCdnPathNpmInfoForSingleFile(url, originCdnPrefix, base, dir, false, bundleTempDir)
)
const { packages: packageNeedToInstall, files } = getPackageNeedToInstallAndFilesUsingSameVersion(cdnFiles)
const plugin = (isDev) => {
return [
...(isDev ? configServerAddProxy(targetBundleFile, targetBundleFile.replace(/\.([^.]+?$)/, '-local.$1')) : []),
...installPackageTemporary(packageNeedToInstall, bundleTempDir),
...viteStaticCopy({
targets: [
...dedupeCopyFiles(files).map(copyfileToDynamicSrcMapper),
{
src: bundleFile,
dest: path.dirname(targetBundleFile),
transform: (content) => {
const json = JSON.parse(content)
replaceBundleCdnLink(json, files)
return JSON.stringify(json, null, 2)
},
rename: (filename, fileExtension) =>
isDev ? `${filename}-local.${fileExtension}` : path.basename(targetBundleFile),
overwrite: true // 覆盖public的
}
]
})
]
}
return {
plugin
}
}

View File

@ -0,0 +1,67 @@
import { viteStaticCopy } from 'vite-plugin-static-copy'
import {
getPackageNeedToInstallAndFilesUsingSameVersion,
copyfileToDynamicSrcMapper,
dedupeCopyFiles,
getCdnPathNpmInfoForPackage,
getCdnPathNpmInfoForSingleFile
} from './locateCdnNpmInfo'
import { importmapPlugin } from '../externalDeps'
import { installPackageTemporary } from '../vite-plugins/installPackageTemporary'
export const copyLocalImportMap = ({
importMap,
styleUrls,
originCdnPrefix,
base,
dir = 'import-map-static',
bundleTempDir = 'bundle-deps/design-core-import-map',
packageCopy = [] // 值为importMap的imports的左值 (非右值的地址上的包名)
}) => {
const importMapFiles = Object.entries(importMap.imports)
.filter(([_libKey, libPath]) => libPath.startsWith(originCdnPrefix))
.map(([libKey, libPath]) => {
if (packageCopy.includes(libKey)) {
return getCdnPathNpmInfoForPackage(libPath, originCdnPrefix, base, dir, true, bundleTempDir)
}
return getCdnPathNpmInfoForSingleFile(libPath, originCdnPrefix, base, dir, false, bundleTempDir)
})
const styleFiles = styleUrls
.filter((styleUrl) => styleUrl.startsWith(originCdnPrefix))
.map((url) => getCdnPathNpmInfoForSingleFile(url, originCdnPrefix, base, dir, false), bundleTempDir)
const { packages: packageNeedToInstall, files } = getPackageNeedToInstallAndFilesUsingSameVersion(
importMapFiles.concat(styleFiles)
)
return [
...installPackageTemporary(packageNeedToInstall, bundleTempDir),
...viteStaticCopy({
targets: dedupeCopyFiles(files).map(copyfileToDynamicSrcMapper)
}),
{
config(config, { command }) {
// 处理devAlias带CDN域名 另外需要使得本地vue和importMap的vue是同一个实例
if (command === 'serve') {
config.resolve.alias = [
...config.resolve.alias,
{
find: /^vue$/,
replacement: `http://localhost:${config.server.port || 8080}/${
files.find(({ originUrl }) => importMap.imports.vue === originUrl).newUrl
}` // 实际端口号需要更具本地启动修改
}
]
}
}
},
importmapPlugin(
{
imports: Object.fromEntries(
Object.entries(importMap.imports).map(([k, v]) => [k, files.find((f) => f.originUrl === v)?.newUrl ?? v])
)
},
styleUrls.map((url) => styleFiles.find((f) => f.originUrl === url).newUrl ?? url)
)
]
}

View File

@ -0,0 +1,82 @@
import path from 'node:path'
import { readJsonSync } from 'fs-extra'
import {
getPackageNeedToInstallAndFilesUsingSameVersion,
copyfileToDynamicSrcMapper,
dedupeCopyFiles,
getCdnPathNpmInfoForPackage,
getCdnPathNpmInfoForSingleFile
} from './locateCdnNpmInfo'
import { viteStaticCopy } from 'vite-plugin-static-copy'
import { installPackageTemporary } from '../vite-plugins/installPackageTemporary'
export function extraPreviewImport(filename, originCdnPrefix) {
const result = []
const importMap = readJsonSync(filename)
Object.entries(importMap.imports)?.forEach(([_key, location]) => {
const url = location.replace('${VITE_CDN_DOMAIN}', originCdnPrefix).replace('${opentinyVueVersion}', '~3.14')
if (url?.startsWith(originCdnPrefix) && !result.includes(url)) {
result.push(url)
}
})
return result
}
export function replacePreviewImport(importMap, fileMap, originCdnPrefix) {
return {
imports: Object.fromEntries(
Object.entries(importMap.imports)?.map(([key, location]) => {
// 这里的替换占位符规则等同于packages/design-core/src/preview/src/preview/importMap.js, 两边修改需要同步
const url = location.replace('${VITE_CDN_DOMAIN}', originCdnPrefix).replace('${opentinyVueVersion}', '~3.14')
const matchRule = fileMap.find((rule) => url === rule.originUrl)
if (matchRule) {
return [key, matchRule.newUrl]
}
return [key, location]
})
)
}
}
export function extraPreviewImportFile(filename, targetFileName, originCdnPrefix) {
return (fileMap) => [
{
src: filename,
dest: path.dirname(targetFileName),
transform: (content) => {
return JSON.stringify(replacePreviewImport(JSON.parse(content), fileMap, originCdnPrefix), null, 2)
},
rename: path.basename(targetFileName)
}
]
}
export function copyPreviewImportMap({
importMapJson,
targetImportMapJson,
originCdnPrefix,
base,
dir = 'preview-import-map-static',
bundleTempDir = 'bundle-deps/preview-import-map',
packageCopyLib = [] // 值为cdn地址上的包名
}) {
const cdnFiles = extraPreviewImport(importMapJson, originCdnPrefix).map((url) => {
const { packageName } = url.match(
new RegExp(`^${originCdnPrefix}/?(?<packageName>.+?)@(?<versionDemand>[^/]+)(?<filePathInPackage>.*?)$`)
).groups
if (packageCopyLib.includes(packageName)) {
return getCdnPathNpmInfoForPackage(url, originCdnPrefix, base, dir, true, bundleTempDir)
}
return getCdnPathNpmInfoForSingleFile(url, originCdnPrefix, base, dir, false, bundleTempDir)
})
const { packages: packageNeedToInstall, files } = getPackageNeedToInstallAndFilesUsingSameVersion(cdnFiles)
return [
...installPackageTemporary(packageNeedToInstall, bundleTempDir),
...viteStaticCopy({
targets: [
...dedupeCopyFiles(files).map(copyfileToDynamicSrcMapper),
...extraPreviewImportFile(importMapJson, targetImportMapJson, originCdnPrefix)(files)
]
})
]
}

View File

@ -0,0 +1,6 @@
export * from './copyBundleDeps'
export * from './copyImportMap'
export * from './copyPreviewImportMap'
export * from './utils'
export * from './locateCdnNpmInfo'
export * from './replaceImportPath.mjs'

View File

@ -0,0 +1,204 @@
import path from 'node:path'
import fs from 'node:fs'
import fg from 'fast-glob'
import { normalizePath } from 'vite'
import { readJsonSync } from 'fs-extra'
import { babelReplaceImportPathWithCertainFileName } from './replaceImportPath.mjs'
function transform(content, filename) {
if (filename.endsWith('.js')) {
const result = babelReplaceImportPathWithCertainFileName(content, filename, console)
return result.code || content
}
return content
}
function onlyFiles(globString) {
// viteStaticCopy 自带的glob匹配无法过滤目录 手动过滤目录作为数组传入
return fg.sync(globString + '/**/*', { onlyFiles: true }).map((p) => normalizePath(p))
}
function replaceTailSlash(pathStr) {
// 替换尾部的 / 可以把目录当文件复制
return pathStr.replace(/\/$/, '')
}
export function copyfileToDynamicSrcMapper({ src, dest, transform, rename, folder, ...rest }) {
// viteStaticCopy 自带的glob匹配无法过滤目录 手动过滤目录作为数组传入但是不存在的包需要推迟glob的时机到安装文件后
return {
...rest,
get src() {
// 注意对象不能被解构否则getter无法动态计算
return src || onlyFiles(folder)
},
dest,
transform,
rename
}
}
// 生成复制单个文件所需要的信息
export function getCdnPathNpmInfoForSingleFile(
url, // cdn托管的npm文件地址数组
originCdnPrefix, // cdn的前缀
base, // build构建的baseBASE_URL参数
dir, // 复制到目标的文件目录
transformIContent = false, // 是否需要转换内容, 如果传入url实际为目录则不会转会
tempDir = 'bundle-deps' // 新安装包的安装目录
) {
const baseSlash = base.endsWith('/') ? '' : '/'
const { packageName, versionDemand, filePathInPackage } = url.match(
new RegExp(`^${originCdnPrefix}/?(?<packageName>.+?)@(?<versionDemand>[^/]+)(?<filePathInPackage>.*?)$`)
).groups
let version = versionDemand
let isFolder = filePathInPackage.endsWith('/')
let src = replaceTailSlash(`node_modules/${packageName}${filePathInPackage}`)
const sourceExist = fs.existsSync(path.resolve(src))
let sourceExistExternal = false
if (sourceExist) {
const stat = fs.statSync(path.resolve(src))
if (stat.isDirectory()) {
isFolder = true
}
const content = readJsonSync(`node_modules/${packageName}/package.json`)
version = content.version // 忽略请求的包版本,使用本地包版本号
} else {
src = tempDir + '/' + src
sourceExistExternal = fs.existsSync(path.resolve(src)) // 安装过的不重新安装, 当且仅当所有包都安装过
if (sourceExistExternal) {
const packageJson = readJsonSync(`${tempDir}/node_modules/${packageName}/package.json`)
version = packageJson.version // 如果重新安装这个版本号还需要刷新
}
}
const updateVersion = (version) => {
const destPackageDir = `${dir}/${packageName}@${version}`
const destFullPath = `${destPackageDir}${filePathInPackage}`
const destFullPathWithoutTailSlash = replaceTailSlash(destFullPath)
const dest = path.dirname(destFullPathWithoutTailSlash)
const rename = dest.startsWith(destPackageDir) ? null : path.basename(destFullPathWithoutTailSlash) // 版本号被截断,需要补充回去
return {
version,
newUrl: `${base}${baseSlash}${destFullPath}`,
dest,
rename
}
}
return {
originUrl: url,
// newUrl, // overwrite by updateVersion(version)
src,
// dest, // overwrite by updateVersion(version)
packageName,
// version, // overwrite by updateVersion(version)
versionDemand,
filePathInPackage,
sourceExist,
sourceExistExternal,
...updateVersion(version),
updateVersion,
transform: transformIContent && !isFolder ? transform : null
}
}
export function getCdnPathNpmInfoForPackage(
url, // cdn托管的npm文件地址数组
originCdnPrefix, // cdn的前缀
base, // build构建的baseBASE_URL参数
dir, // 复制到目标的文件目录
transformIContent = false, // 是否需要转换内容
tempDir = 'bundle-deps' // 新安装包的安装目录
) {
const baseSlash = base.endsWith('/') ? '' : '/'
const { packageName, versionDemand, filePathInPackage } = url.match(
new RegExp(`^${originCdnPrefix}/?(?<packageName>.+?)@(?<versionDemand>[^/]+)(?<filePathInPackage>.*?)$`)
).groups
let version = versionDemand
let src = `node_modules/${packageName}`
const sourceExist = fs.existsSync(path.resolve(src))
let sourceExistExternal = false
if (sourceExist) {
const content = readJsonSync(`${src}/package.json`)
version = content.version // 忽略请求的包版本,使用本地包版本号
} else {
src = tempDir + '/' + src
sourceExistExternal = fs.existsSync(path.resolve(src)) // 安装过的不重新安装, 当且仅当所有包都安装过
if (sourceExistExternal) {
const packageJson = readJsonSync(`${src}/package.json`)
version = packageJson.version // 如果重新安装这个版本号还需要刷新
}
}
const updateVersion = (version) => {
const packageDir = `${dir}/${packageName}@${version}`
const packageDirBasename = path.basename(packageDir)
return {
version,
newUrl: `${base}${baseSlash}${dir}/${packageName}@${version}${filePathInPackage}`,
dest: path.dirname(packageDir),
rename: (_filename, _fileExtension, fullPath) => `${packageDirBasename}${fullPath.replace(src, '')}`
}
}
return {
folder: src,
originUrl: url,
// newUrl, // overwrite by updateVersion(version)
src: sourceExist || sourceExistExternal ? onlyFiles(src) : null,
// dest, // overwrite by updateVersion(version)
packageName,
// version, // overwrite by updateVersion(version)
versionDemand,
filePathInPackage,
sourceExist,
sourceExistExternal,
...updateVersion(version),
updateVersion,
transform: transformIContent ? transform : null
}
}
export function dedupeCopyFiles(files) {
return files.reduce((acc, cur) => {
//去重,分别处理字符串和数组
if (
(cur.folder && !acc.some((item) => !!item.folder && item.folder === cur.folder && item.dest === cur.dest)) || // 文件夹拷贝
(typeof cur.src === 'string' && !acc.some((item) => item.src === cur.src && item.dest === cur.dest)) // 文件拷贝
) {
acc.push(cur)
}
return acc
}, [])
}
export function getPackageNeedToInstallAndFilesUsingSameVersion(files) {
const packageNeedToInstall = files
.filter((item) => !item.sourceExist)
.map(({ packageName, version, sourceExistExternal: exist }) => ({ packageName, version, exist }))
.reduce((acc, cur) => {
// 同个包避免多个版本只保留一个版本
if (!acc.some(({ packageName }) => cur.packageName === packageName)) {
acc.push(cur)
}
return acc
}, [])
let newFiles = null
if (packageNeedToInstall.length) {
// 确保同个包多个版本只能从一个版本引用文件
newFiles = files.map((file) => {
const samePackageDifferentVersion = packageNeedToInstall.find(
({ packageName, version }) => packageName === file.packageName && version !== file.version
)
if (samePackageDifferentVersion) {
return {
...file,
...file.updateVersion(samePackageDifferentVersion.version)
}
}
return file
})
}
return {
packages: packageNeedToInstall,
files: newFiles ?? files
}
}

View File

@ -0,0 +1,89 @@
import fs from 'node:fs'
import path from 'node:path'
import { parse } from '@babel/core'
import traversePkg from '@babel/traverse'
import generatePkg from '@babel/generator'
const traverse = traversePkg.default
const generate = generatePkg.default
export function relativePathPattern(relativePath) {
return './' + (path.sep === '/' ? relativePath : relativePath.replace(/\\/g, '/'))
}
export function resolvePath(importPath, currentFilePath) {
if (['js', 'mjs'].some(suffix =>importPath.endsWith(suffix))) { // 文件名已经带有.js.mjs后缀
return importPath
}
const parentPath = path.resolve(currentFilePath, '../')
const filePrefix = path.resolve(parentPath, importPath)
if (fs.existsSync(filePrefix)) {
const stat = fs.statSync(filePrefix)
if (stat.isDirectory()) {
let mainFileName = 'index.js'
const packageFile = path.resolve(filePrefix, 'package.json')
if (fs.existsSync(packageFile)) {
const packageFileContent = fs.readFileSync(packageFile, { encoding: 'utf-8' })
const packageJson = JSON.parse(packageFileContent)
mainFileName = packageJson.module || packageJson.main || mainFileName
}
const mainFile = path.resolve(filePrefix, mainFileName)
if (fs.existsSync(mainFile)) {
return relativePathPattern(path.relative(parentPath, mainFile))
}
return null
}
return importPath
}
const possibleSuffix = ['.js', '.mjs']
const suffix = possibleSuffix.find(suf => fs.existsSync(filePrefix + suf))
if (suffix) {
return relativePathPattern(path.relative(path.resolve(currentFilePath, '../'), filePrefix + suffix))
}
return null
}
// babel 替换 js的相对地址引用为确定文件后缀
export function babelReplaceImportPathWithCertainFileName(content, currentFilePath, logger = console) {
let fileChangedMark = false
let result = {
code: null,
success: [],
error: []
}
const ast = parse(content, { sourceType: 'module' })
traverse(ast, {
ImportOrExportDeclaration: (astPath) => {
const node = astPath.node
if (!node.source) {
return
}
const importPath = node.source.value
if(importPath.startsWith('.')) {
const certainPath = resolvePath(importPath, currentFilePath)
if(!certainPath) {
logger.warn(`File not found: ${importPath} used in ${currentFilePath}`)
result.error.push(importPath)
}
if(certainPath !== importPath) {
node.source.value = certainPath
fileChangedMark = true
result.success.push({before: importPath, after: certainPath})
}
}
}
})
if (fileChangedMark) {
const { code } = generate(ast, {
jsescOption: {
quotes: 'single'
}
})
result.code = code
}
return result
}

View File

@ -0,0 +1,5 @@
export function getBaseUrlFromCli(fallback = '') {
// 理论上要从resolvedConfig阶段的钩子里面拿到base由于插件嵌套插件子插件的配置项需要在resolveConfig前传入这里无法等resolvedConfig故手动获取命令行base
const index = process.argv?.indexOf('--base')
return index > -1 ? process.argv[index + 1] || fallback : fallback
}

View File

@ -0,0 +1,15 @@
export function configServerAddProxy(path, target) {
return [
{
name: 'vite-plugin-config-server-add-proxy',
configureServer(server) {
server.middlewares.use((req, _res, next) => {
if (req.url.includes(path)) {
req.url = req.url.replace(path, target)
}
next()
})
}
}
]
}

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