Compare commits

...

106 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
abin e88b254683
bundle.json and tiny-vue.css is 404 in prod mode (#315) 2024-03-06 14:10:13 +08:00
lichunn 7d967777d8
fix:资源管理工具类型文字字色与设计稿不一致 (#319)
* fix:资源管理工具类型文字字色与设计稿不一致

* fix:调整资源工具类型字体
2024-03-06 11:45:39 +08:00
Gene bf2bc188a5
fix: 数据源远程字段表单验证样式修改 (#309)
* fix: 数据源远程字段表单验证样式修改
2024-02-26 14:16:05 +08:00
rhlin 683fc51d52
fix: 修复preview生产打包的时候repl引入的monaco-editor里的worker跨域报错 (#245)
* fix: 修复preview生产打包的时候repl引入的monaco-editor里的worker跨域报错

* feat(design-core/preview): 预览增加调试按钮来打开vue-repl编辑模式,并实现编辑器懒加载

* fix(design-core/preview): 修复repl-patch引入脚本路径模块化不支持importScripts问题
2024-02-26 14:15:31 +08:00
chilingling 96def94945
fix(http): 优化 http 接口报错 ui, 接口详情不展示在 title (#293) 2024-02-20 15:36:51 +08:00
chilingling 52037fdf78
fix(style): delete selected symbol on select component (#306)
* fix(style): delete selected symbol on select component

* fix(style): delete space after delete selectedSymbol
2024-02-20 15:35:35 +08:00
chilingling 491c01092e
feat(setting-advanced): addcustomEvent popover change to dialog (#297)
* feat(setting-advanced): addcustomEvent popover change to dialog

* fix(setting-advanced): add custom event align left with modal title
2024-02-20 15:35:15 +08:00
chilingling 9bccbfacb6
fix(plugin-page): setting home should not close settingPanel (#308) 2024-02-20 15:30:31 +08:00
chilingling 34dfbc624e
fix(setting-stylePanel): 只允许有一个 id,样式选择器只能添加一个 id (#295) 2024-02-20 15:20:48 +08:00
Gene 3e1cb599d7
fix: 修复属性面板中设置空字符串的问题 (#307) 2024-02-19 16:47:53 +08:00
Gene 5e43b79922
fix: 修复代码编辑器全屏时的样式问题 (#303) 2024-02-18 17:34:42 +08:00
Gene 4d247ef406
fix: 修复新建区块的表单确定时未校验的问题 (#302) 2024-02-18 17:34:05 +08:00
tianxin f26456bbb5
fix(upload-button)修复下载按钮弹出没有遮罩 (#300) 2024-02-06 17:04:14 +08:00
chilingling caeb540d19
fix(chore): sync vue version (#285)
* fix(chore): sync vue version

* fix(chore): sync vue-i18n version

* fix(chore): change vue and vue-i18n to peerdependencies

* fix(chore): update block build dependencies

* fix(chore): change vue-generator dependencies to update
2024-01-31 09:46:07 +08:00
yaoyun8 3875e16e87
fix:修复第三方组件无法渲染 (#288)
* fix:修复第三方组件无法渲染

* fix:区分环境加载prod
2024-01-30 14:34:19 +08:00
Lu17301156525 422c62dbdb
Fix mockServer block group deleted bug (#279) 2024-01-26 15:30:34 +08:00
yaoyun8 e79853cb09
fix: 修改远程字段发送请求无效 (#278)
* fix:修复修改远程字段发送请求无效

* fix:修复请求参数处理函数未生效

* fix:修复数据源表单校验提前退出

* fix:修改提示语

* fix:按review添加注释
2024-01-25 16:46:48 +08:00
chilingling b726219668
fix(common): fix lifecycle unmount completionProvider null state caught error (#281) 2024-01-25 16:44:28 +08:00
Gene 7ea6ebcbbd
fix: 修复较低的分辨率下画布初始缩放不正确的问题 (#274) 2024-01-25 16:43:52 +08:00
lhuans f843d121fa
修复画布没有根元素,与vue出码、预览不一致 (#259)
* feat: 页面输入输出配置,配置内有内容时,按钮替换为展示部分文案内容。

* feat: 方法名修改更加符合语义

* fix: 1. 给画布添加根元素(与出码、预览保持一致)。2. 选中画布相当于选中根节点,右侧面板会出现可以添加属性的节点。3. 可以支持给根节点添加类名,id、ref,并修改样式。 4. 支持给根元素添加事件,循环等

* fix: 意见修复
2024-01-25 16:43:35 +08:00
Gene a192280fc4
feat: 移除前端解锁前判断权限的逻辑,适配后台解锁接口 (#276) 2024-01-25 16:42:59 +08:00
Gene 6ca84ee6f2
fix: 修复画布选中框的显示层级不正确的问题 (#246)
* fix: 修复画布选中框的显示层级不正确的问题

* fix: 修正错误注释

* fix: 选中节点后支持横向滚动到节点

* fix: 根据垂直滚动条调整工具操作条位置

* fix: 删除冗余代码

* fix: 代码优化

* fix: 修改变量名称

* fix: 调整函数顺序,消除eslint错误
2024-01-25 16:42:43 +08:00
GaoNeng 93263b5298
fix: 修复robot plugin不存在时发生的白屏错误 (#271)
* fix: 修复robot plugin不存在时发生的白屏错误

* styled: 修改代码风格

* fix: 修改监视意见
2024-01-25 10:33:40 +08:00
yaoyun8 fb2e2f1207
fix:TinyTabItem、TinyBreadcrumbItem组件配置有误 (#272)
* fix:TinyTabItem、TinyBreadcrumbItem组件配置有误

* fix:修复tiny-vue组件导出名称有误
2024-01-25 10:10:53 +08:00
yaoyun8 c3c2e2e714
fix:组件sql语句字段校验、提示优化 (#275)
* fix:修复新增组件sql语句字段校验

* fix:优化脚本提示

* fix:函数名优化

* fix:修复更新的sql问题
2024-01-25 10:10:42 +08:00
wenmine 9e57be080c
fix(material-panel-svg): 修复物料左侧插件面板里图标旋转的问题 (#280)
* docs(readme): 增加后端的启动描述

* docs(readme): 增加后端的启动描述

* docs(readme): 增加后端的启动描述

* docs(readme): 增加后端的启动描述

* docs(readme): 更新图片地址

* docs(readme): 更新图片地址

* fix(material-panel-svg): 修复物料左侧插件面板里图标旋转的问题
2024-01-25 09:57:23 +08:00
chilingling 28ee851b2a
feat(setting-stylePanel): 样式面板编辑全局样式新UI与交互 (#238)
* feat(setting-stylePanel): 样式面板编辑全局样式新UI与交互

* feat(setting-stylePanel): 样式面板编辑全局样式新UI与交互

* feat(setting-stylePanel): 样式面板编辑全局样式新UI与交互-细节优化

* feat(setting-stylePanel): 样式面板编辑全局样式新UI与交互-细节优化

* fix(settingPanel-style): 样式面板新交互细节调整

* fix(setting-stylePanel): 修复样式选择器颜色不对的 bug

* fix(setting-stylePanel): 样式选择器间距调整

* fix(setting-stylePanel): 调整样式选择器距离顶部距离与下拉列表滚动条颜色

* feat(setting-style): add help link button

* fix(setting-style): delete empty row by code review
2024-01-24 17:35:08 +08:00
chilingling dec9334eb8
fix(toolbar-save): 修复保存时红色 icon 相对位置丢失的 bug (#250) 2024-01-24 17:27:39 +08:00
Gene 91ff6aed55
feat: js panel add jsx support (#253)
* feat: JS面板支持JSX语法

* fix: 画布解析JS表达式时支持JSX语法

* fix: 修复画布解析JSX表达式时会返回undefined

* feat: 添加Tree和Tooltip的自定义渲染函数属性

* fix: 修改变量名
2024-01-19 15:58:44 +08:00
chilingling 9c72525db6
fix(canvas-renderer): 修复 isNative component 判断失效的 bug (#241) 2024-01-19 15:12:48 +08:00
rhlin 35c7e91a4c
fix: 设计器构建后使用vue runtime prod build(本地开发保持development, preview预览保持development) (#254) 2024-01-19 14:52:27 +08:00
lizhijie429 12b7a11280
fix: 修复由于物料边框引起的左侧面板样式问题 (#247) 2024-01-19 14:21:26 +08:00
Lu17301156525 435f81708f
add config (#262)
* add config

* update config
2024-01-19 11:58:35 +08:00
yaoyun8 421dda28be
fix:预览引入builtin组件依赖,built组件样式文件注入js (#251) 2024-01-15 09:16:28 +08:00
wenmine 236445e7ff
fix(styles-spacing): 增大物料设置内外边距时点击区域 #134 (#184)
* fix(styles-spacing): 增大物料设置内外边距时点击区域 #134
2024-01-15 09:14:57 +08:00
lizhijie429 3c2e14d4de
fix: 左右两侧面板宽度固定为280px (#230) 2024-01-12 15:08:56 +08:00
yaoyun8 7b9ff6cfd5
fix:修复资源管理工具类型切换 (#239)
* fix:修复资源管理工具类型切换
2024-01-11 14:43:54 +08:00
yaoyun8 00160f92df
fix:materials sql (#231)
* fix: 修复数据源mock数据不一致

* fix:新增组件写入sql数据问题
2024-01-11 14:43:12 +08:00
rhlin 1cbecd2497
[plugin-data] Fix state use jsfunction issue (#237)
* feat(monaco-editor-completion): 隔离不同代码编辑器自动补全的注册,变量声明增加自动补全去掉js函数和state变量的自动补全提示

* feat(state): 状态管理使用JS表达式创建变量的时候提示不可使用其他变量以及JS函数
2024-01-11 14:41:45 +08:00
yaoyun8 9b96d10fb2
fix:插件面板固定后画布未自适应 (#234)
* fix: 修复数据源mock数据不一致

* fix:插件面板固定后画布未自适应

* fix:插件面板固定后画布未自适应

* fix:修复eslint报错
2024-01-10 16:13:02 +08:00
chilingling 2b5fe014f9
fix default img can't change width and height (#189) 2024-01-05 09:43:40 +08:00
yaoyun8 38762017ff
fix:修改css文件cdn域名 (#228)
* fix:修改css文件cdn地址
2024-01-05 09:39:59 +08:00
yaoyun8 fe477ad74c
fix: 修复数据源mock数据不一致 (#211)
* fix: 修复数据源mock数据不一致

* fix:修复构建物料资产包重复组件
2024-01-04 20:46:55 +08:00
chilingling 47be96756c
chore(release): publish (#219)
* chore(release): publish

 - @opentiny/tiny-engine-mock@1.0.1
 - @opentiny/tiny-engine-block-build@1.0.0
 - @opentiny/tiny-engine-builtin-component@1.0.0
 - @opentiny/tiny-engine-canvas@1.0.1
 - @opentiny/tiny-engine-common@1.0.1
 - @opentiny/tiny-engine-controller@1.0.1
 - @opentiny/tiny-engine@1.0.0-beta.2
 - @opentiny/tiny-engine-http@1.0.1
 - @opentiny/tiny-engine-i18n-host@1.0.1
 - @opentiny/tiny-engine-plugin-block@1.0.2
 - @opentiny/tiny-engine-plugin-bridge@1.0.2
 - @opentiny/tiny-engine-plugin-data@1.0.2
 - @opentiny/tiny-engine-plugin-datasource@1.0.2
 - @opentiny/tiny-engine-plugin-help@1.0.2
 - @opentiny/tiny-engine-plugin-i18n@1.0.2
 - @opentiny/tiny-engine-plugin-materials@1.0.2
 - @opentiny/tiny-engine-plugin-page@1.0.2
 - @opentiny/tiny-engine-plugin-robot@1.0.2
 - @opentiny/tiny-engine-plugin-schema@1.0.2
 - @opentiny/tiny-engine-plugin-script@1.0.2
 - @opentiny/tiny-engine-plugin-tree@1.0.2
 - @opentiny/tiny-engine-plugin-tutorial@1.0.2
 - @opentiny/tiny-engine-setting-design@1.0.2
 - @opentiny/tiny-engine-setting-events@1.0.2
 - @opentiny/tiny-engine-setting-props@1.0.2
 - @opentiny/tiny-engine-setting-styles@1.0.2
 - @opentiny/tiny-engine-svgs@1.0.2
 - @opentiny/tiny-engine-theme-dark@1.0.2
 - @opentiny/tiny-engine-theme-light@1.0.3
 - @opentiny/tiny-engine-toolbar-breadcrumb@1.0.2
 - @opentiny/tiny-engine-toolbar-clean@1.0.2
 - @opentiny/tiny-engine-toolbar-collaboration@1.0.2
 - @opentiny/tiny-engine-toolbar-fullscreen@1.0.2
 - @opentiny/tiny-engine-toolbar-generate-vue@1.0.2
 - @opentiny/tiny-engine-toolbar-lang@1.0.2
 - @opentiny/tiny-engine-toolbar-layout@1.0.2
 - @opentiny/tiny-engine-toolbar-checkinout@1.0.2
 - @opentiny/tiny-engine-toolbar-logo@1.0.2
 - @opentiny/tiny-engine-toolbar-logout@1.0.2
 - @opentiny/tiny-engine-toolbar-media@1.0.2
 - @opentiny/tiny-engine-toolbar-preview@1.0.2
 - @opentiny/tiny-engine-toolbar-redoundo@1.0.2
 - @opentiny/tiny-engine-toolbar-refresh@1.0.2
 - @opentiny/tiny-engine-toolbar-save@1.0.2
 - @opentiny/tiny-engine-toolbar-setting@1.0.2
 - @opentiny/tiny-engine-utils@1.0.2
 - @opentiny/tiny-engine-dsl-vue@1.0.2
 - @opentiny/tiny-engine-webcomponent-core@1.0.2

* chore(release): publish

 - @opentiny/tiny-engine-mock@1.0.2
 - @opentiny/tiny-engine-block-build@1.0.1
 - @opentiny/tiny-engine-builtin-component@1.0.0
 - @opentiny/tiny-engine-canvas@1.0.2
 - @opentiny/tiny-engine-common@1.0.1
 - @opentiny/tiny-engine-controller@1.0.2
 - @opentiny/tiny-engine@1.0.0-beta.3
 - @opentiny/tiny-engine-http@1.0.1
 - @opentiny/tiny-engine-i18n-host@1.0.2
 - @opentiny/tiny-engine-plugin-block@1.0.2
 - @opentiny/tiny-engine-plugin-bridge@1.0.2
 - @opentiny/tiny-engine-plugin-data@1.0.2
 - @opentiny/tiny-engine-plugin-datasource@1.0.2
 - @opentiny/tiny-engine-plugin-help@1.0.2
 - @opentiny/tiny-engine-plugin-i18n@1.0.2
 - @opentiny/tiny-engine-plugin-materials@1.0.2
 - @opentiny/tiny-engine-plugin-page@1.0.2
 - @opentiny/tiny-engine-plugin-robot@1.0.2
 - @opentiny/tiny-engine-plugin-schema@1.0.2
 - @opentiny/tiny-engine-plugin-script@1.0.2
 - @opentiny/tiny-engine-plugin-tree@1.0.2
 - @opentiny/tiny-engine-plugin-tutorial@1.0.2
 - @opentiny/tiny-engine-setting-design@1.0.2
 - @opentiny/tiny-engine-setting-events@1.0.2
 - @opentiny/tiny-engine-setting-props@1.0.2
 - @opentiny/tiny-engine-setting-styles@1.0.2
 - @opentiny/tiny-engine-svgs@1.0.3
 - @opentiny/tiny-engine-theme-dark@1.0.3
 - @opentiny/tiny-engine-theme-light@1.0.4
 - @opentiny/tiny-engine-toolbar-breadcrumb@1.0.2
 - @opentiny/tiny-engine-toolbar-clean@1.0.2
 - @opentiny/tiny-engine-toolbar-collaboration@1.0.2
 - @opentiny/tiny-engine-toolbar-fullscreen@1.0.2
 - @opentiny/tiny-engine-toolbar-generate-vue@1.0.2
 - @opentiny/tiny-engine-toolbar-lang@1.0.3
 - @opentiny/tiny-engine-toolbar-layout@1.0.3
 - @opentiny/tiny-engine-toolbar-checkinout@1.0.2
 - @opentiny/tiny-engine-toolbar-logo@1.0.2
 - @opentiny/tiny-engine-toolbar-logout@1.0.3
 - @opentiny/tiny-engine-toolbar-media@1.0.2
 - @opentiny/tiny-engine-toolbar-preview@1.0.2
 - @opentiny/tiny-engine-toolbar-redoundo@1.0.2
 - @opentiny/tiny-engine-toolbar-refresh@1.0.2
 - @opentiny/tiny-engine-toolbar-save@1.0.2
 - @opentiny/tiny-engine-toolbar-setting@1.0.2
 - @opentiny/tiny-engine-utils@1.0.3
 - @opentiny/tiny-engine-dsl-vue@1.0.2
 - @opentiny/tiny-engine-webcomponent-core@1.0.3
2024-01-04 19:39:28 +08:00
Hexqi 919f06e89d
chore: add code review action (#215) 2024-01-03 11:15:57 +08:00
390 changed files with 17836 additions and 2864 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

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

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

View File

@ -0,0 +1,16 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
module.exports = {
port: process.env.MOCK_PORT || 9090,
env: process.env.NODE_ENV || 'development' // Current mode
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-mock",
"version": "1.0.0",
"version": "1.0.3",
"publishConfig": {
"access": "public"
},
@ -25,7 +25,7 @@
"lint": "eslint --fix ."
},
"dependencies": {
"@opentiny/tiny-engine-dsl-vue": "~1.0.0",
"@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

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

View File

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

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

View File

@ -25459,7 +25459,7 @@
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "TinyTabItem",
"exportName": "TabItem",
"version": "",
"destructuring": true
},
@ -25708,7 +25708,7 @@
"devMode": "proCode",
"npm": {
"package": "@opentiny/vue",
"exportName": "TinyBreadcrumbItem",
"exportName": "BreadcrumbItem",
"version": "",
"destructuring": true
},
@ -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",
@ -65,5 +65,10 @@
"engines": {
"node": ">=14",
"pnpm": ">=7"
},
"pnpm": {
"patchedDependencies": {
"@vue/repl@2.9.0": "patches/@vue__repl@2.9.0.patch"
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-block-build",
"version": "0.0.1-alpha.0",
"version": "1.0.2",
"description": "translate block to webcomponent template",
"main": "./dist/web-components.es.js",
"scripts": {
@ -32,12 +32,12 @@
"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.3.11",
"vue-i18n": "^9.8.0"
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.5.2",

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,9 +1,12 @@
{
"name": "@opentiny/tiny-engine-builtin-component",
"version": "0.1.0",
"version": "1.0.1",
"description": "",
"main": "dist/index.js",
"module": "dist/index.js",
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
@ -13,14 +16,18 @@
"directory": "packages/builtinComponent"
},
"scripts": {
"build": "vite build"
"build": "vite build",
"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

@ -13,9 +13,10 @@ import { defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vitePluginCssInjectedByJs from 'vite-plugin-css-injected-by-js'
export default defineConfig({
plugins: [vue(), vueJsx()],
plugins: [vue(), vueJsx(), vitePluginCssInjectedByJs()],
publicDir: false,
build: {
cssCodeSplit: false,
@ -26,9 +27,6 @@ export default defineConfig({
formats: ['es']
},
rollupOptions: {
output: {
banner: 'import "./style.css"'
},
external: ['vue']
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine-canvas",
"version": "1.0.0",
"version": "1.0.3",
"publishConfig": {
"access": "public"
},
@ -27,25 +27,26 @@
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@babel/core": "7.18.13",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-builtin-component": "workspace:*",
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/tiny-engine-builtin-component": "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",
"vue": "3.2.45",
"vue-i18n": "9.2.2"
"@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

@ -514,8 +514,7 @@
"schema": {
"componentName": "Img",
"props": {
"src": "img/webNova.jpg",
"style": "width: 200px; height: 200px"
"src": "https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/designer-default-icon.jpg"
}
}
}

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

View File

@ -6,7 +6,7 @@
:windowGetClickEventTarget="target"
:resize="canvasState.type === 'absolute'"
@select-slot="selectSlot"
@setting="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
@ -123,8 +125,30 @@ export default {
const doc = iframe.value.contentDocument
const win = iframe.value.contentWindow
let isScrolling = false
// iframe
win.addEventListener('mousedown', (event) => {
// html使scrollmouseup
if (event.target === doc.documentElement) {
isScrolling = false
return
}
insertPosition.value = false
setCurrentNode(event)
target.value = event.target
})
win.addEventListener('scroll', () => {
isScrolling = true
})
win.addEventListener('mouseup', (event) => {
if (event.target !== doc.documentElement || isScrolling) {
return
}
insertPosition.value = false
setCurrentNode(event)
target.value = event.target
@ -156,8 +180,8 @@ export default {
}
}
//
const settingModle = () => {
showSettingModle.value = true
const settingModel = () => {
showSettingModel.value = true
}
const updateI18n = (message) => {
@ -194,7 +218,9 @@ export default {
onMounted(() => run(iframe))
onUnmounted(() => {
removeHostkeyEvent(iframe.value.contentDocument)
if (iframe.value?.contentDocument) {
removeHostkeyEvent(iframe.value.contentDocument)
}
window.removeEventListener('message', updateI18n, false)
})
@ -212,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,26 +17,18 @@ 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
}
})
const mouseDown = ref(false)
const resizeDom = ref(null)
const onMouseMove = (event) => {
if (mouseDown.value) {
event.preventDefault()
calculateSize(event)
}
}
const calculateSize = ({ movementX }) => {
const dimension = useLayout().getDimension()
const { maxWidth, minWidth, width } = dimension
@ -48,6 +40,30 @@ export default {
})
}
const onMouseMove = (event) => {
if (mouseDown.value) {
event.preventDefault()
calculateSize(event)
}
}
const onMouseUp = () => {
const iframe = canvasState.iframe
if (iframe) {
iframe.style['pointer-events'] = 'auto'
mouseDown.value = false
document.removeEventListener('mousemove', onMouseMove, { passive: false })
document.removeEventListener('mouseup', onMouseUp)
}
}
const bindEvents = () => {
document.addEventListener('mousemove', onMouseMove, { passive: false })
document.addEventListener('mouseup', onMouseUp)
}
const onMouseDown = () => {
const iframe = canvasState.iframe
@ -58,44 +74,35 @@ export default {
}
}
const onMouseUp = () => {
const iframe = canvasState.iframe
if (iframe) {
iframe.style['pointer-events'] = 'auto'
mouseDown.value = false
unbindEvents()
}
}
const bindEvents = () => {
document.addEventListener('mousemove', onMouseMove, { passive: false })
document.addEventListener('mouseup', onMouseUp)
}
const unbindEvents = () => {
document.removeEventListener('mousemove', onMouseMove, { passive: false })
document.removeEventListener('mouseup', onMouseUp)
}
const setScale = () => {
useLayout().setDimension({ scale: 1 })
nextTick(() => {
const canvasWrap = document.querySelector('#canvas-wrap')
if (!canvasWrap) {
return
}
const rate = canvasWrap.offsetWidth / resizeDom.value.offsetWidth
useLayout().setDimension({
scale: rate > 1 ? 1 : rate
scale: Math.min(rate, 1)
})
})
}
watch(() => useLayout().getDimension().width, setScale, { flush: 'post' })
watch(() => useLayout().getDimension().width, setScale, { flush: 'post', immediate: true })
watch(() => useLayout().getPluginState().fixedPanels, setScale, { flush: 'post' })
watch(
() => useLayout().getPluginState().render,
(value) => !value && setScale(),
(value) => {
const currentFixed = useLayout().getPluginState().fixedPanels.includes(value)
if (!value || currentFixed) {
setScale()
}
},
{ flush: 'post' }
)

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

@ -376,6 +376,13 @@ export default {
)
},
render() {
// 渲染画布增加根节点,与出码和预览保持一致
const rootChildrenSchema = {
componentName: 'div',
props: schema.props,
children: schema.children
}
return h(
'tiny-i18n-host',
{
@ -384,9 +391,7 @@ export default {
ref: 'page',
className: 'design-page'
},
schema.children?.length
? schema.children.map((child) => h(renderer, { schema: child, parent: schema }))
: [h(CanvasEmpty)]
schema.children?.length ? h(renderer, { schema: rootChildrenSchema, parent: schema }) : [h(CanvasEmpty)]
)
}
}
@ -419,5 +424,3 @@ export const api = {
setDataSourceMap,
setGlobalState
}
window.api = api

View File

@ -34,8 +34,8 @@ const { BROADCAST_CHANNEL } = constants
const { hyphenateRE } = utils
const customElements = {}
const transformJSX = (code) =>
transformSync(code, {
const transformJSX = (code) => {
const res = transformSync(code, {
plugins: [
[
babelPluginJSX,
@ -46,6 +46,13 @@ const transformJSX = (code) =>
]
]
})
return (res.code || '')
.replace(/import \{.+\} from "vue";/, '')
.replace(/h\(_?resolveComponent\((.*?)\)/g, `h(this.getComponent($1)`)
.replace(/_?resolveComponent/g, 'h')
.replace(/_?createTextVNode\((.*?)\)/g, '$1')
.trim()
}
export const blockSlotDataMap = reactive({})
@ -138,7 +145,7 @@ export const newFn = (...argv) => {
return new Fn(...argv)
}
const parseExpression = (data, scope, ctx) => {
const parseExpression = (data, scope, ctx, isJsx = false) => {
try {
if (data.value.indexOf('this.i18n') > -1) {
ctx.i18n = i18nHost.global.t
@ -146,12 +153,17 @@ const parseExpression = (data, scope, ctx) => {
ctx.t = i18nHost.global.t
}
return newFn('$scope', `with($scope || {}) { return ${data.value} }`).call(ctx, {
const expression = isJsx ? transformJSX(data.value) : data.value
return newFn('$scope', `with($scope || {}) { return ${expression} }`).call(ctx, {
...ctx,
...scope,
slotScope: scope
})
} catch (err) {
// 解析抛出异常,则再尝试解析 JSX 语法。如果解析 JSX 语法仍然出现错误isJsx 变量会确保不会再次递归执行解析
if (!isJsx) {
return parseExpression(data, scope, ctx, true)
}
return undefined
}
}
@ -347,10 +359,6 @@ export const getComponent = (name) => {
const parseJSXFunction = (data, ctx) => {
try {
const newValue = transformJSX(data.value)
.code.replace(/import \{.+\} from "vue";/, '')
.replace(/h\(_?resolveComponent\((.*?)\)/g, `h(this.getComponent($1)`)
.replace(/_?resolveComponent/g, 'h')
.replace(/_?createTextVNode\((.*?)\)/g, '$1')
const fnInfo = parseFunctionString(newValue)
if (!fnInfo) throw Error('函数解析失败请检查格式。示例function fnName() { }')
@ -590,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
@ -609,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)
)
}
@ -620,20 +646,11 @@ 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'
const isCustomElm = customElements[componentName]
const isGroup = checkGroup(componentName)

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) {
@ -244,7 +244,7 @@ export default {
})
const updateValue = (value) => {
const { property } = props.property
const { property, type } = props.property
const { setProp } = useProperties()
//
@ -283,7 +283,7 @@ export default {
}
if (props.isTopLayer) {
setProp(property, value)
setProp(property, value, type)
}
}

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

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

@ -1,9 +1,9 @@
<template>
<div class="editor-wrap">
<slot>
<slot :open="open">
<div v-if="buttonShowContent" :class="['full-width', { 'empty-color': value === '' }]" @click="open">
<span>{{ value === '' ? buttonLabel : value?.slice(0, 30) }}</span>
<svg-icon class="edit-icon" name="flow-edit"></svg-icon>
<span class="text-content text-ellipsis-multiple">{{ value === '' ? buttonLabel : value }}</span>
<svg-icon class="edit-icon" name="edit"></svg-icon>
</div>
<tiny-button v-else class="edit-btn" @click="open">
{{ buttonLabel }}
@ -15,6 +15,7 @@
width="50vw"
class="meta-code-editor-dialog-box"
append-to-body
:close-on-click-modal="false"
>
<div class="source-code">
<div v-if="editorTipsTitle" class="header-tips-container">
@ -45,7 +46,13 @@
</div>
<template #footer>
<div class="btn-box">
<tiny-button plain type="danger" v-if="language === 'json' && showFormatBtn" @click="formatCode">
<tiny-button
v-if="language === 'json' && showFormatBtn"
class="format-btn"
plain
type="danger"
@click="formatCode"
>
{{ $t('common.format') }}
</tiny-button>
<div>
@ -63,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: {
@ -139,7 +146,7 @@ export default {
watchEffect(() => {
const { modelValue, dataType } = props
const val = dataType ? modelValue?.value : modelValue
const val = dataType ? modelValue?.value || '' : modelValue
value.value = typeof val === 'string' ? val : JSON.stringify(val, null, 2)
})
@ -269,20 +276,32 @@ export default {
}
.btn-box {
display: flex;
justify-content: space-between;
justify-content: flex-end;
&:has(.format-btn) {
justify-content: space-between;
}
}
.full-width {
border: 1px solid #adb0b8;
border-radius: 6px;
padding: 4px 8px;
height: 32px;
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
height: 32px;
padding: 4px 8px;
border: 1px solid var(--ti-lowcode-meta-codeEditor-border-color);
border-radius: 6px;
&:hover {
border-color: var(--ti-lowcode-meta-codeEditor-border-hover-color);
}
.text-content {
--ellipsis-line: 1;
}
&.empty-color {
color: var(--ti-lowcode-common-text-desc-color);
}
.edit-icon {
margin-left: 4px;
flex-shrink: 0;
cursor: pointer;
color: var(--ti-lowcode-common-text-main-color);
}

View File

@ -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>
.editor-container {
display: flex;
flex-direction: column;
padding: 0;
}
.editor-container-full {
position: fixed;
top: var(--base-top-panel-height);
bottom: 0;
left: calc(var(--base-nav-panel-width) + var(--base-left-panel-width));
right: var(--base-left-panel-width);
z-index: 10;
padding: 10px 16px 16px 16px;
background-color: var(--ti-lowcode-common-component-bg);
height: auto !important;
}
.toolbar {
display: flex;
margin-bottom: 4px;
justify-content: flex-end;
justify-content: space-between;
align-items: center;
z-index: 3000 !important;
.buttons {
display: flex;
gap: 8px;
color: var(--ti-lowcode-component-svg-button-color);
cursor: pointer;
&:hover {
:hover {
color: var(--ti-lowcode-component-svg-button-hover-color);
}
}
&._full {
position: fixed;
top: 35px;
z-index: 1;
padding-right: 15px;
right: var(--base-left-panel-width);
}
}
.iniline {
height: 100%;
width: 100%;
}
.fullscreen {
position: fixed;
top: 35px;
bottom: 0;
left: calc(var(--base-nav-panel-width) + 1px);
right: var(--base-left-panel-width);
z-index: 2500;
}
.public-icon {
font-size: 18px;
margin-left: 10px;
.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: {
@ -344,7 +348,7 @@ export default {
position: fixed;
z-index: 9999;
top: 50px;
left: calc(var(--base-left-panel-width, 268px) + var(--base-nav-panel-width) + 10px);
left: calc(var(--base-left-panel-width) + var(--base-nav-panel-width) + 10px);
max-width: 500px;
max-height: 136px;
padding: 12px;

View File

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

View File

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

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.0",
"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,29 +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": "3.2.45",
"vue-draggable-next": "2.1.0",
"vue-i18n": "9.2.2"
"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": {
"@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

@ -1,14 +1,14 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import { parse, parseExpression } from '@babel/parser'
import generate from '@babel/generator'
@ -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.*?\(/
@ -24,7 +25,7 @@ export const insertName = (name, content) => content.replace(METHOD_REGEXP, `fun
export const removeName = (content) => content.replace(METHOD_REGEXP, 'function (')
export const string2Ast = (string = '') => parse(string, { sourceType: 'module', plugins: ['typescript'] })
export const string2Ast = (string = '') => parse(string, { sourceType: 'module', plugins: ['typescript', 'jsx'] })
export const ast2String = (ast) => generate(ast, { retainLines: true }).code
@ -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',
@ -157,7 +158,7 @@ const getWords = (model, position) => {
const lastWord = model.getWordUntilPosition(lastPosition).word
? model.getWordUntilPosition(lastPosition)
: getCurrentChar(model, lastPosition)
if (!/[\w\.]/.test(lastWord.word)) break
if (!/[\w.]/.test(lastWord.word)) break
words.push(lastWord)
lastPosition.column = lastWord.startColumn
}
@ -172,9 +173,14 @@ const getRange = (position, words) => ({
endColumn: words[words.length - 1].endColumn
})
export const initCompletion = (monacoInstance) => {
export const initCompletion = (monacoInstance, editorModel, conditionFn) => {
const completionItemProvider = {
provideCompletionItems(model, position, _context, _token) {
if (editorModel && model.id !== editorModel.id) {
return {
suggestions: []
}
}
const words = getWords(model, position)
const wordContent = words.map((item) => item.word).join('')
const range = getRange(position, words)
@ -185,9 +191,10 @@ export const initCompletion = (monacoInstance) => {
const snippetSuggestions = getSnippetsSuggestions(monacoInstance, range, wordContent)
// 用户变量数据提示 e.g. this.dataSourceMap.xxx.load()
const userSuggestions = getUserSuggestions(monacoInstance, range, wordContent)
return {
suggestions: [...apiSuggestions, ...snippetSuggestions, ...userSuggestions]
suggestions: [...apiSuggestions, ...snippetSuggestions, ...userSuggestions].filter((item) =>
conditionFn ? conditionFn(item) : true
)
}
},
triggerCharacters: ['.']

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.0",
"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,19 +31,29 @@
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@opentiny/tiny-engine-canvas": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@babel/generator": "~7.23.2",
"@babel/parser": "~7.23.2",
"@babel/traverse": "~7.23.2",
"@opentiny/tiny-engine-builtin-component": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@vue/shared": "^3.3.4",
"vue": "3.2.45"
"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": {
"@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

@ -20,7 +20,8 @@ const helpState = {
datasource: 11,
i18n: 12,
page: 2,
script: 8
script: 8,
stylePanel: 6
}
}

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,11 +11,12 @@
*/
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'
import useTranslate from './useTranslate'
const { COMPONENT_NAME } = constants
const propsUpdateKey = ref(0)
const otherBaseKey = {
@ -150,11 +151,18 @@ const properties = shallowReactive({
parent: null
})
const isPageOrBlock = (schema) => [COMPONENT_NAME.Block, COMPONENT_NAME.Page].includes(schema?.componentName)
const getProps = (schema, parent) => {
// 现在选中的节点和当前节点一样,不需要重新计算
if (schema && properties.schema !== schema) {
// 1 现在选中的节点和当前节点一样,不需要重新计算, 2 默认进来由于scheme和properities.schema相等因此判断如果是“页面或者区块”需要进入if判断
if (schema && (properties.schema !== schema || isPageOrBlock(schema))) {
const { props, componentName } = schema
const { schema: metaSchema, content, properties } = useResource().getMaterial(componentName)
// 若选中的是page或者 blcok没有对应schemaComponentName 给 div 设置根节点属性
const {
schema: metaSchema,
content,
properties
} = useResource().getMaterial(isPageOrBlock(schema) ? 'div' : componentName)
const schemaProps = properties || metaSchema?.properties || content?.schema?.properties || []
const propGroups = [...schemaProps]
@ -169,22 +177,29 @@ const getProps = (schema, parent) => {
properties.parent = parent
}
const setProp = (name, value) => {
const setProp = (name, value, type) => {
if (!properties.schema) {
return
}
properties.schema.props = properties.schema.props || {}
if (value === '' || value === undefined || value === null) {
if ((value === '' && type !== 'String') || value === undefined || value === null) {
delete properties.schema.props[name]
} else {
properties.schema.props[name] = value
}
// 没有父级或者不在节点上面要更新内容。就用setState
getNode(properties.schema.id, true).parent || setState(useCanvas().getPageSchema().state)
const { getNode, setState, updateRect } = useCanvas().canvasApi.value || {}
getNode(properties.schema.id, true)?.parent || setState(useCanvas().getPageSchema().state)
propsUpdateKey.value++
// 更新根节点props不用updateRect
if (!properties.schema.id) {
return
}
nextTick(updateRect)
}

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,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" xml:space="preserve" isSvg="true">
<path d="M14 7c0 3.87-3.13 7-7 7s-7-3.13-7-7 3.13-7 7-7 7 3.13 7 7z" style="fill-rule: evenodd; clip-rule: evenodd;"></path>
<path d="M9.96 9.29c.09.09.14.21.14.34 0 .13-.05.25-.14.33a.47.47 0 0 1-.67 0L7 7.67 4.71 9.96c-.18.18-.49.18-.67 0a.483.483 0 0 1 0-.67L6.33 7 4.04 4.71c-.09-.09-.14-.21-.14-.34s.05-.25.14-.34c.18-.18.49-.18.67 0L7 6.33l2.29-2.29c.19-.18.49-.18.67 0 .09.09.14.21.14.34s-.05.25-.14.34L7.67 7l2.29 2.29z" style="fill: rgb(255, 255, 255);"></path>
</svg>

After

Width:  |  Height:  |  Size: 576 B

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

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>编组 7备份</title>
<g id="ICon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="编组-7备份">
<rect id="矩形" fill="currentColor" fill-rule="nonzero" opacity="0" x="0" y="0" width="24" height="24"></rect>
<path d="M12.0003,2.00015 C14.6848308,2.00015 16.8613,4.17625357 16.8613,6.86115 L16.8613,6.86115 L16.861,9.24915 L17,9.24985 C18.5970631,9.24985 19.9036168,10.4993657 19.9949049,12.073641 L20,12.24985 L20,18.99985 C20,20.6567698 18.6565074,21.99985 17,21.99985 L17,21.99985 L7,21.99985 C5.34349256,21.99985 4,20.6567698 4,18.99985 L4,18.99985 L4,12.24985 C4,10.5936364 5.34378644,9.24985 7,9.24985 L7,9.24985 L7.138,9.24915 L7.1383,6.86115 C7.1383,4.24864046 9.19888513,2.11813926 11.7837168,2.00488536 L12.0003,2.00015 Z M7.87310737,10.7499992 L7,10.74985 C6.17221356,10.74985 5.5,11.4220636 5.5,12.24985 L5.5,12.24985 L5.5,18.99985 C5.5,19.8282396 6.17181662,20.49985 7,20.49985 L7,20.49985 L17,20.49985 C17.8281834,20.49985 18.5,19.8282396 18.5,18.99985 L18.5,18.99985 L18.5,12.24985 C18.5,11.4220636 17.8277864,10.74985 17,10.74985 L17,10.74985 L16.126,10.74915 L16.1113,10.75015 L7.8883,10.75015 C7.88322385,10.75015 7.87815949,10.7500996 7.87310737,10.7499992 Z M12.0003,3.50015 C10.1432663,3.50015 8.6383,5.00461073 8.6383,6.86115 L8.6383,6.86115 L8.638,9.24925 L15.361,9.24925 L15.3613,6.86115 C15.3613,5.06661755 13.9551107,3.60084673 12.1847022,3.5051226 L12.1847022,3.5051226 L12.0003,3.50015 Z" id="形状结合" fill="currentColor" fill-rule="nonzero"></path>
<g id="编组-4" transform="translate(9.000000, 12.200000)" fill="currentColor">
<circle id="椭圆形" cx="2.95" cy="1.77" r="1.77"></circle>
<path d="M5.9,6.1890106 C5.9,5.44324225 5.62326654,4.76210845 5.16688577,4.24269538 C4.62623603,3.62737464 3.83347166,3.2390106 2.95,3.2390106 C2.0722897,3.2390106 1.28410749,3.6223259 0.743723518,4.23068636 C0.281025729,4.75158825 0,5.43748089 0,6.1890106 C0,6.95798112 5.9,7.02717331 5.9,6.1890106 Z" id="椭圆形"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 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="https://npm.onmicrosoft.cn/@opentiny/vue-theme@3.11/index.css" rel="stylesheet" />
<style type="text/css">
.loading-warp {
display: flex;

View File

@ -29,9 +29,9 @@ export default {
canvasOptions: {
Angular: {},
Vue: {
material: ['http://localhost:8080/mock/bundle.json'],
material: ['/mock/bundle.json'],
scripts: [],
styles: ['http://localhost:8080/tiny-vue.css']
styles: ['/tiny-vue.css']
},
React: {},
HTML: {},

View File

@ -1,6 +1,6 @@
{
"name": "@opentiny/tiny-engine",
"version": "1.0.0",
"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,37 +78,36 @@
"@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.2.45",
"vue-clipboard3": "^2.0.0",
"vue-draggable-next": "2.1.0",
"vue-i18n": "9.2.2",
"xlsx": "^0.18.5"
"vue": "3.4.23",
"vue-i18n": "^9.9.0"
},
"devDependencies": {
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@babel/eslint-parser": "^7.21.3",
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@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",
"esbuild-plugin-copy": "^2.1.1",
"eslint": "^8.38.0",
"eslint-plugin-vue": "^8.0.0",
"fs-extra": "^10.1.0",
@ -120,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"
},

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