Compare commits

..

1 Commits

Author SHA1 Message Date
jxhhdx a5e529dab8
feat: 新增 ant-design-vue 物料 (#416)
* init project

Match-id-61778b743a9dc8d8df471dcb285fb229931648a6

* fix: fix build error (#1)

* fix: fix build error

* fix: fix server dependence

* fix: add logo and fix readme (#5)

* update readme (#6)

* fix: add logo and fix readme

* fix: update readme

* feat: add pnpm to project

* fix(bundle): fix env variable can not be bundle (#16)

* docs: add pr and bug template (#18)

* fix(bindEvents): fix bind Events can not delete (#22)

* feat: 修复当画布没有内容时控制台报错 (#26)

* fix:按钮组数据编辑后显示出错 (#24)

* fix:走马灯属性配置清空标签页后添加标签页显示出错 (#23)

* fix:走马灯属性配置清空标签页后添加标签页显示出错

* fix: 优化走马灯删除标签页问题修复方案

* fix(design-core): build error with monaco-editor (#57)

* fix(page-lock):页面解锁按钮图标修复 (#43)

* fix: 修复无页面时页面内容显示错误问题 (#31)

* fix: 修复不切换页面点击页面设置时表单校验错误问题 (#32)

* fix(block-list-icon):区块管理面板去掉列表图标 (#33)

* fix:区块管理面板去掉列表图标

* fix(block-list-icon):区块管理面板去掉列表图标

* feat(docs): add Milestones (#46)

* feat(chore): update package info (#53)

* feat(chore): update package info

* feat(docs-package): add homepage url

* fix(toolbars): disable toolbar btn when page is empty (#70)

* fix(toolbars): disable toolbar btn when page is empty

* fix(toolbar): disable preview when page is empty

* feat(doc): add doc link to readme (#54)

* fix(style): adjust style detail (#67)

* fix(canvas): change canvas menu item hiding to disabed (#75)

* fix(canvas): fix del key error when nothing is selected (#69)

* feat(chore): add setup script setup environment before serving (#80)

* fix(pluginMaterial): fix block list can't scroll when visible in canvas (#77)

* feat: 页面输入输出配置,配置内有内容时,按钮替换为展示部分文案内容。 (#66)

* feat: 页面输入输出配置,配置内有内容时,按钮替换为展示部分文案内容。

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

* fix:修复属性面板-插槽开关切换schema不正确 (#85)

* fix:时间线步骤条编辑数据,步骤条会消失 (#84)

* fix: 时间线步骤条编辑data,时间线会消失

* Revert "fix: 时间线步骤条编辑data,时间线会消失"

This reverts commit b24f8ba60d.

* fix:时间线步骤条编辑数据,时间线会消失

* fix(pagePlugin): 删除文件夹添加节流,避免多次出现无法删除弹窗 (#74)

* fix(pagePlugin): 删除文件夹添加防抖,避免多次出现无法删除弹窗

* fix(pagePlugin): use throttle by review comment

* fix:loading can't be closed when state is saved (#87)

* fix:loading can't be closed when state is saved

* fix: remove global loading

* fix(metaComponent): fix #79 metaListItems dragging not working (#86)

* fix(pagePlugin): clear some state when delte current page (#72)

* fix(pagePlugin): clear some state when delte current page

* fix(pagePlugin): del unnecessary optional chaining operator

* fix(chore): add code readablity

* fix(chore): optimize code by review comment

* fix: 数据源面板中,点击新建字段报错 (#37)

* fix:修复组件属性绑定工具类函数,出码预览未定义 (#90)

* fix(insert-footer-button):设计器页面设置-输入设置中,保存取消按钮顺序错误,格式化按钮样式错误,应与UI保持一直;大纲书导航中,眼睛图标,建议改为睁开就是可见,闭眼就是不可见;提示框样式修改 (#94)

* fix(insert-footer-button):
设计器页面设置-输入设置中,保存取消按钮顺序错误,格式化按钮样式错误,应与UI保持一致;
大纲树导航中,眼睛图标,建议改为睁开就是可见,闭眼就是不可见;
提示框样式修改

* fix(styles-import): 解决tree组件选中样式背景色为黑色的问题 (#102)

* feat(builtin): builtinComponent 组件独立包抽离 (#52)

* feat(builtin): builtinComponent 组件独立包抽离

* 添加eslint&自动化构建 (#101)

* fix:组件属性绑定工具类utils,修改预览出码实现 (#93)

* fix: 修复空页面执行右键删除和右键复制时控制台报错的问题 (#103)

* fix: 修复空页面执行右键删除和右键复制时控制台报错的问题

* fix: 修复空页面执行右建操作时控制台报错的问题

* refactor: 简化operations空判断代码

* fix: verify operations copy  id

* fix: verify operations copy  id

* fix: verify operations copy  id

* fix: copyNode function verify  params

* fix: 不可操作的菜单项disabled

* fix: 点击disabled菜单项 不关闭菜单

* fix:修复自定义属性新增功能 (#108)

* fix:修复自定义属性新增功能

* fix: 修复MetaHtmlText组件控制台警告

* fix:调整透传Attributes警告解决方案

* fix:调整透传Attributes警告解决方案

* fix:调整透传Attributes警告解决方案

* fix(canvasShortCutPanel): add click event to quick insert component in… (#81)

* fix(canvasShotCutPanel): add click event to quick insert component in shortcutpanel

* fix(canvasShortPanel): add deepclone utils

* feat(utils): add deepClone method

* feat(unit-test): add unit test for deepclone function

* fix: code check (#112)

* fix: code check (#117)

* fix: 变量修改后保存页面,再次修改变量失败 (#110)

* fix:修复国际化参数配置 (#106)

* feat(style): stylePanel add style selector, write css to global styles (#41)

* feat(style): stylePanel add style selector, write css to global styles

* feat(style): classNameSelector support edit and delete

* fix(build): fix setting-style plugin build error

* fix(chore): fix review comment

* fix: 修复DSL出码报错 (#139)

* fix(plugin-script): js plugin code format when save (#138)

* fix: 手动更新画布key导致表格渲染出错 (#129)

* fix(dataSources): 解决collection添加表格时预览报错 #118 (#151)

* feat(block2webcomponent): add block2webcomponent package (#146)

* feat(block2webcomponent): add block2webcomponent package

* fix(build): fix build config uncorrect

* fix(build): change cdn link to npmmirror

* fix:删除lowcodeWrap多余参数 (#119)

* fix(chore): replace unpkg with npmmirror (#149)

* fix(chore): replace unpkg with npmmirror

* fix(dev): dev environment use node_moduels

* fix(chore): sync version

* fix(preview): use npmmirror link

* fix(preview): devtoolapi use unpkg link

* fix(preview): import map add esm cdn

* fix(version): fix tiny-vue version

* fix(version): fix tiny-vue version

* fix(version): fix tiny-vue version

* fix(blockBuild): change packageName to block build (#160)

* fix: 画布中右键弹框,修复添加父级-弹出框功能 (#60)

* fix: 画布中右键弹框,修复添加父级-弹出框功能

* fix: schema缺少children

* fix: 按检视意见修改

* fix: 修复画布宽度设置问题 (#128)

* fix: 修复画布宽度设置问题

* fix: 修复复制静态文件时接口报错 (#175)

* feat:引入第三方组件库element-plus部分组件示例 (#105)

* feat: 引入第三方组件库element-plus示例

* fix:修复格式错误

* feat:表格列配置组件

* feat:增加表格列配置组件

* fix:build error

* fix:修复scheme2code接口报错

* fix:修改import顺序

* fix: 使用import动态导入有cdn的工具类 (#133)

* fix: 使用import动态导入有cdn的工具类

* fix(preview): upgrade vue repl version (#170)

* fix(preview): upgrade vue repl version

* fix(preview): optimized by review comment

* fix:添加style变量赋值 (#154)

* fix: 修复unpkg依赖 (#188)

* fix: 修复unpkg依赖

* fix:固定opentiny/vue的次版本号

* fix:升级devtools-api版本

* fix:CDN域名提取环境变量

* fix:升级opentiny/vue版本

* fix:修改主题样式文件版本

* fix:修复codeCheck

* feat:设计器增加帮助文档说明 (#100)

* fix: 修复多个全局变量下载代码出错 (#197)

* fix: 修复多个全局变量下载代码出错

* fix: 精简应用发布时commit消息的校验规则 (#205)

* feat: 开放区块管理相关功能 (#206)

* feat: add materials script (#195)

增加拆分物料与构建物料脚本

* doc:README添加物料同步脚本说明 (#207)

* feat(generate-vue): change generate vue code location (#201)

* feat(generate-vue): change generate vue code location

* fix(generate-vue): fix generate route did not export

* feat(docs): 增加跟本地后端直连的方案 (#208)

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

* feat(chore): change dependencies type to workspace:* (#216)

* chore: add code review action (#215)

* 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

* fix: 修复数据源mock数据不一致 (#211)

* fix: 修复数据源mock数据不一致

* fix:修复构建物料资产包重复组件

* fix:修改css文件cdn域名 (#228)

* fix:修改css文件cdn地址

* fix default img can't change width and height (#189)

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

* fix: 修复数据源mock数据不一致

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

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

* fix:修复eslint报错

* [plugin-data] Fix state use jsfunction issue (#237)

* feat(monaco-editor-completion): 隔离不同代码编辑器自动补全的注册,变量声明增加自动补全去掉js函数和state变量的自动补全提示

* feat(state): 状态管理使用JS表达式创建变量的时候提示不可使用其他变量以及JS函数

* fix:materials sql (#231)

* fix: 修复数据源mock数据不一致

* fix:新增组件写入sql数据问题

* fix:修复资源管理工具类型切换 (#239)

* fix:修复资源管理工具类型切换

* fix: 左右两侧面板宽度固定为280px (#230)

* fix(styles-spacing): 增大物料设置内外边距时点击区域 #134 (#184)

* fix(styles-spacing): 增大物料设置内外边距时点击区域 #134

* fix:预览引入builtin组件依赖,built组件样式文件注入js (#251)

* add config (#262)

* add config

* update config

* fix: 修复由于物料边框引起的左侧面板样式问题 (#247)

* fix: 设计器构建后使用vue runtime prod build(本地开发保持development, preview预览保持development) (#254)

* fix(canvas-renderer): 修复 isNative component 判断失效的 bug (#241)

* feat: js panel add jsx support  (#253)

* feat: JS面板支持JSX语法

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

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

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

* fix: 修改变量名

* fix(toolbar-save): 修复保存时红色 icon 相对位置丢失的 bug (#250)

* 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

* fix(material-panel-svg): 修复物料左侧插件面板里图标旋转的问题 (#280)

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

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

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

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

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

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

* fix(material-panel-svg): 修复物料左侧插件面板里图标旋转的问题

* fix:组件sql语句字段校验、提示优化 (#275)

* fix:修复新增组件sql语句字段校验

* fix:优化脚本提示

* fix:函数名优化

* fix:修复更新的sql问题

* fix:TinyTabItem、TinyBreadcrumbItem组件配置有误 (#272)

* fix:TinyTabItem、TinyBreadcrumbItem组件配置有误

* fix:修复tiny-vue组件导出名称有误

* fix: 修复robot plugin不存在时发生的白屏错误 (#271)

* fix: 修复robot plugin不存在时发生的白屏错误

* styled: 修改代码风格

* fix: 修改监视意见

* fix: 修复画布选中框的显示层级不正确的问题 (#246)

* fix: 修复画布选中框的显示层级不正确的问题

* fix: 修正错误注释

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

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

* fix: 删除冗余代码

* fix: 代码优化

* fix: 修改变量名称

* fix: 调整函数顺序,消除eslint错误

* feat: 移除前端解锁前判断权限的逻辑,适配后台解锁接口 (#276)

* 修复画布没有根元素,与vue出码、预览不一致 (#259)

* feat: 页面输入输出配置,配置内有内容时,按钮替换为展示部分文案内容。

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

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

* fix: 意见修复

* fix: 修复较低的分辨率下画布初始缩放不正确的问题 (#274)

* fix(common): fix lifecycle unmount completionProvider null state caught error (#281)

* fix: 修改远程字段发送请求无效 (#278)

* fix:修复修改远程字段发送请求无效

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

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

* fix:修改提示语

* fix:按review添加注释

* Fix mockServer block group deleted bug (#279)

* fix:修复第三方组件无法渲染 (#288)

* fix:修复第三方组件无法渲染

* fix:区分环境加载prod

* 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

* fix(upload-button)修复下载按钮弹出没有遮罩 (#300)

* fix: 修复新建区块的表单确定时未校验的问题 (#302)

* fix: 修复代码编辑器全屏时的样式问题 (#303)

* fix: 修复属性面板中设置空字符串的问题 (#307)

* fix(setting-stylePanel): 只允许有一个 id,样式选择器只能添加一个 id (#295)

* fix(plugin-page): setting home should not close settingPanel (#308)

* 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

* fix(style): delete selected symbol on select component (#306)

* fix(style): delete selected symbol on select component

* fix(style): delete space after delete selectedSymbol

* fix(http): 优化 http 接口报错 ui, 接口详情不展示在 title (#293)

* 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问题

* fix: 数据源远程字段表单验证样式修改 (#309)

* fix: 数据源远程字段表单验证样式修改

* fix:资源管理工具类型文字字色与设计稿不一致 (#319)

* fix:资源管理工具类型文字字色与设计稿不一致

* fix:调整资源工具类型字体

* bundle.json and tiny-vue.css is 404 in prod mode (#315)

* 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

* 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

* fix: switch page error (#331)

* fix: drag block into canvas error (#336)

* fix: canvas api error (#338)

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

* fix(build): 修复构建设计器产物 ref 值未转化的 bug (#347)

* fix(preview): fix preview vue-repl unsupport jsx bug (#343)

* docs: add project name to issue template (#335)

* feat: 状态管理代码编辑UI优化 (#318)

* feat: 状态管理代码编辑UI优化

* fix(material): elTable formatter should be function type (#355)

* fix(readme-build): 添加Readme中构建产物所在文件夹信息 (#367)

* 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

* docs: 文件名称CHANGELOG拼写错误,修改文件名称 (#389)

* fix(errorMonitor): add config for errormonitor (#381)

* fix(controller-environment): preserve import.meta.env variable when build controller lib (#380)

* fix(settings): fix text wrap of bind condition (#391)

Fixes #370

* feat:增加物料:TinyDatePicker和TinyNumeric (#399)

* 增加物料:TinyDatePicker和TinyNumeric

* 修改disabled使用MetaSwitch

* Fix:编辑器设置dark主题后预览页header文字颜色不正确 (#403)

* fix:编辑器设置dark主题后预览页header文字颜色不正确

* fix:调整主题

* fix(material): add buttonComponentsMap config (#395)

* fix(demo-data): 修复官方 demo 页面底部确认购买模块移动端设备宽度下被遮住的场景 (#398)

* feat: ant-design-vue 物料适配

* feat: 数据更新

* feat: 批量适配功能

---------

Co-authored-by: OpenTiny <opentiny@sina.com>
Co-authored-by: ajaxzheng <894103554@qq.com>
Co-authored-by: chilingling <26962197+chilingling@users.noreply.github.com>
Co-authored-by: wenmine <wwmmail@foxmail.com>
Co-authored-by: lhuans <145339349+lhuans@users.noreply.github.com>
Co-authored-by: yaoyun8 <142570291+yaoyun8@users.noreply.github.com>
Co-authored-by: Hexqi <wu_12556@126.com>
Co-authored-by: tianxin <146069396+ianxinnew@users.noreply.github.com>
Co-authored-by: gargameljyh <58694428+gargameljyh@users.noreply.github.com>
Co-authored-by: lichunn <46984087+lichunn@users.noreply.github.com>
Co-authored-by: Xppp0217 <82315158+Xppp0217@users.noreply.github.com>
Co-authored-by: lizhijie429 <632163606@qq.com>
Co-authored-by: Gene <Pacify.98@gmail.com>
Co-authored-by: rhlin <r.h.lin@live.cn>
Co-authored-by: Lu17301156525 <128358973+Lu17301156525@users.noreply.github.com>
Co-authored-by: rhlin <r.h.lin@163.com>
Co-authored-by: GaoNeng <31283122+GaoNeng-wWw@users.noreply.github.com>
Co-authored-by: abin <luocongqiu@gmail.com>
Co-authored-by: zeyong tsai <tsai.zeyong@icloud.com>
Co-authored-by: zeyong.cai <zeyong.cai@xquant.com>
Co-authored-by: Kagol <kagol@sina.com>
Co-authored-by: yuhki <128052955+yuhkikele@users.noreply.github.com>
Co-authored-by: Xie Jay <xiejay97@gmail.com>
Co-authored-by: fox ouyang <stwflyfox@163.com>
Co-authored-by: 凌览 <58327088+CatsAndMice@users.noreply.github.com>
2024-05-09 15:33:05 +08:00
290 changed files with 27536 additions and 25265 deletions

View File

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

23
.github/workflows/ai-code-review.yml vendored Normal file
View File

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

View File

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

1
.gitignore vendored
View File

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

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
# 使用淘宝镜像,提升下载速度
registry=https://registry.npmmirror.com/

View File

@ -769,7 +769,7 @@
{
"componentName": "TinyTimeLine",
"props": {
"active": 2,
"active": "2",
"data": [
{
"name": "基础配置"
@ -1525,6 +1525,7 @@
{
"componentName": "div",
"props": {
"style": "border-width: 1px;z-index:1;border-style: solid; border-color: #ffffff; padding-top: 10px; padding-left: 10px; padding-right: 10px; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px; background-color: #fff; position: fixed; inset: auto 0% 0% 0%; height: 80px; line-height: 80px; border-radius: 0px;"
},
"children": [
@ -1550,7 +1551,7 @@
{
"componentName": "TinyCol",
"props": {
"span": 8
"span": "8"
},
"id": "b9d051a5",
"children": [
@ -1563,7 +1564,7 @@
{
"componentName": "TinyCol",
"props": {
"span": 5,
"span": "5",
"style": "display: flex;"
},
"id": "02352776",
@ -1597,7 +1598,7 @@
{
"componentName": "TinyCol",
"props": {
"span": 7
"span": "7"
},
"id": "82b6c659",
"children": [
@ -1665,7 +1666,7 @@
{
"componentName": "TinyCol",
"props": {
"span": 4,
"span": "4",
"style": "display: flex; flex-direction: row-reverse; border-radius: 0px; height: 100%; justify-content: flex-start; align-items: center;"
},
"id": "10b73009",
@ -1690,7 +1691,7 @@
],
"fileName": "createVm",
"meta": {
"id": "NTJ4MjvqoVj8OVsc",
"id": 1977,
"parentId": "0",
"group": "staticPages",
"occupier": {
@ -1787,7 +1788,7 @@
},
{
"componentName": "TinyPlusFrozenPage",
"package": "@opentiny/vue",
"package": "@opentiny/vuee",
"exportName": "FrozenPage",
"destructuring": true,
"version": "3.4.1"
@ -1974,48 +1975,6 @@
"destructuring": true,
"version": "0.1.16"
},
{
"componentName": "ElInput",
"package": "element-plus",
"exportName": "ElInput",
"destructuring": true,
"version": "2.4.2"
},
{
"componentName": "ElButton",
"package": "element-plus",
"exportName": "ElButton",
"destructuring": true,
"version": "2.4.2"
},
{
"componentName": "ElForm",
"package": "element-plus",
"exportName": "ElForm",
"destructuring": true,
"version": "2.4.2"
},
{
"componentName": "ElFormItem",
"package": "element-plus",
"exportName": "ElFormItem",
"destructuring": true,
"version": "2.4.2"
},
{
"componentName": "ElTable",
"package": "element-plus",
"exportName": "ElTable",
"destructuring": true,
"version": "2.4.2"
},
{
"componentName": "ElTableColumn",
"package": "element-plus",
"exportName": "ElTableColumn",
"destructuring": true,
"version": "2.4.2"
},
{
"componentName": "PortalHome",
"main": "common/components/home",

View File

@ -32,9 +32,9 @@
"dependencies": {
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/vue": "~3.14.0",
"@opentiny/vue-icon": "~3.14.0",
"@opentiny/vue-theme": "~3.14.0",
"@opentiny/vue": "~3.11.0",
"@opentiny/vue-icon": "~3.11.0",
"@opentiny/vue-theme": "~3.11.0",
"@vue/shared": "^3.3.11",
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"

View File

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

View File

@ -20,14 +20,12 @@
"preview": "vite preview"
},
"dependencies": {
"@opentiny/vue": "~3.10.0",
"vite-plugin-css-injected-by-js": "^3.3.1"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"vite": "^4.5.0"
},
"peerDependencies": {
"vue": "^3.4.15"
}
}

View File

@ -32,6 +32,9 @@
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/shared": "^3.3.4",
"@vueuse/core": "^9.6.0"
@ -43,9 +46,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-icon": "^3.14.0",
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"
}

View File

@ -1,14 +1,14 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
export const NODE_UID = 'data-uid'
export const NODE_TAG = 'data-tag'
@ -125,10 +125,9 @@ export const getClipboardSchema = (event) => translateStringToSchame(event.clipb
*/
export const dynamicImportComponents = async ({ package: pkg, script, components }) => {
if (!script) return
const scriptUrl = script.startsWith('.') ? new URL(script, location.href).href : script
if (!window.TinyComponentLibs[pkg]) {
const modules = await import(/* @vite-ignore */ scriptUrl)
const modules = await import(/* @vite-ignore */ script)
window.TinyComponentLibs[pkg] = modules
}

View File

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

View File

@ -6,7 +6,7 @@
:windowGetClickEventTarget="target"
:resize="canvasState.type === 'absolute'"
@select-slot="selectSlot"
@setting="settingModel"
@setting="settingModle"
></canvas-action>
<canvas-divider :selectState="selectState"></canvas-divider>
<canvas-resize-border :iframe="iframe"></canvas-resize-border>
@ -50,12 +50,12 @@ import {
updateRect,
getElement,
dragStart,
getOffset,
selectNode,
initCanvas,
clearLineState,
querySelectById,
getCurrent,
canvasApi
getCurrent
} from './container'
export default {
@ -71,7 +71,7 @@ export default {
const insertPanel = ref(null)
const insertPosition = ref(false)
const loading = computed(() => useCanvas().isLoading())
let showSettingModel = ref(false)
let showSettingModle = ref(false)
let target = ref(null)
const setCurrentNode = async (event) => {
@ -100,13 +100,11 @@ export default {
//
if (event.button === 2) {
openMenu(event)
openMenu(getOffset(event.target), event)
}
}
}
useCanvas().initCanvasApi(canvasApi)
const beforeCanvasReady = () => {
if (iframe.value) {
const win = iframe.value.contentWindow
@ -121,6 +119,7 @@ export default {
iframeMonitoring()
initCanvas({ emit, renderer: detail, iframe: iframe.value, controller: props.controller })
useCanvas().renderer.value = { ...detail, ...window.canvasApi }
const doc = iframe.value.contentDocument
const win = iframe.value.contentWindow
@ -180,8 +179,8 @@ export default {
}
}
//
const settingModel = () => {
showSettingModel.value = true
const settingModle = () => {
showSettingModle.value = true
}
const updateI18n = (message) => {
@ -238,9 +237,9 @@ export default {
canvasState,
insertComponent,
insertPanel,
settingModel,
settingModle,
target,
showSettingModel,
showSettingModle,
insertPosition,
loading
}

View File

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

View File

@ -17,7 +17,7 @@ export default {
props: {
data: {
type: Array,
default: () => []
default: []
}
},
emits: ['click']
@ -44,23 +44,37 @@ export default {
li {
width: unset !important;
background: var(--ti-lowcode-breadcrumb-bg);
.label {
a {
padding: 0 3px 0 16px;
border-top: 0;
color: var(--ti-lowcode-breadcrumb-color);
transition: 0.3s;
&::after {
display: inline-block;
right: -6px;
border-left: 6px solid var(--ti-lowcode-breadcrumb-bg);
transition: 0.3s;
}
&::before {
display: inline-block;
right: -7px;
left: unset;
border-left: 6px solid var(--ti-lowcode-breadcrumb-icon-color);
transition: 0.3s;
z-index: 1;
}
&:hover {
cursor: pointer;
background: var(--ti-lowcode-breadcrumb-hover-bg);
&::after {
border-left-color: var(--ti-steps-advanced-li-hover-bg-color);
border-left-color: var(--ti-lowcode-breadcrumb-hover-bg);
}
}
}
&:last-child .label {
&:last-child a {
border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
border-radius: 0;
}
&:first-child .label {
&:first-child a {
border-right: 0px solid var(--ti-lowcode-breadcrumb-color);
border-radius: 0;
border-left: unset;

View File

@ -15,7 +15,7 @@
<span>{{ item.name }}</span>
<span v-if="item.items"><icon-right></icon-right></span>
</div>
<ul v-if="item.items && current === item" class="sub-menu menu-item" :style="subMenuStyles">
<ul v-if="item.items && current === item" class="sub-menu menu-item">
<template v-for="(subItem, subIndex) in item.items" :key="subIndex">
<li
:class="[{ 'menu-item-disabled': subItem.check && !subItem.check?.() }]"
@ -33,7 +33,7 @@
<script lang="jsx">
import { ref, reactive, nextTick } from 'vue'
import { canvasState, getConfigure, getController, getCurrent, copyNode, removeNodeById } from './container'
import { getConfigure, getController, getCurrent, copyNode, removeNodeById } from './container'
import { useLayout, useModal, useCanvas } from '@opentiny/tiny-engine-controller'
import { iconRight } from '@opentiny/vue-icon'
@ -45,35 +45,27 @@ const menuState = reactive({
const current = ref(null)
const menuDom = ref(null)
const subMenuStyles = ref(null)
export const closeMenu = () => {
menuState.show = false
current.value = null
}
export const openMenu = (event) => {
export const openMenu = (offset, event) => {
const { x, y } = offset
const { getScale } = useLayout()
menuState.position = {
left: event.clientX + 2 + 'px',
top: event.clientY + 'px'
// TODO
left: event.clientX * getScale() + x + 2 + 'px',
top: event.clientY * getScale() + y + 'px'
}
menuState.show = sessionStorage.getItem('pageInfo') ? true : false
nextTick(() => {
if (menuDom.value) {
const { right, bottom, width, height } = menuDom.value.getBoundingClientRect()
const canvasRect = canvasState.iframe.getBoundingClientRect()
if (bottom > canvasRect.bottom) {
menuState.position.top = `${parseInt(menuState.position.top) - height}px`
}
if (right > canvasRect.right) {
menuState.position.left = `${parseInt(menuState.position.left) - width - 2}px`
}
// sub-menuwidth100px100
if (right + 100 < canvasRect.right) {
subMenuStyles.value = { right: '-100px' }
} else {
subMenuStyles.value = { left: '-100px' }
const { bottom, height, top } = menuDom.value.getBoundingClientRect()
if (bottom > document.body?.clientHeight) {
menuState.position.top = top - height + 'px'
}
}
})
@ -234,7 +226,6 @@ export default {
close,
current,
menuDom,
subMenuStyles,
actionDisabled,
onShowChildrenMenu
}
@ -244,7 +235,7 @@ export default {
<style lang="less" scoped>
.context-menu {
position: absolute;
position: fixed;
z-index: 10;
}
.menu-item {
@ -285,6 +276,7 @@ export default {
&.sub-menu {
width: 100px;
position: absolute;
right: -100px;
top: -2px;
}
}

View File

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

View File

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

View File

@ -20,16 +20,15 @@ import {
NODE_LOOP
} from '../common'
import { useCanvas, useLayout, useResource, useTranslate } from '@opentiny/tiny-engine-controller'
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
import Builtin from '../builtin/builtin.json'
export const POSITION = Object.freeze({
TOP: 'top',
BOTTOM: 'bottom',
LEFT: 'left',
RIGHT: 'right',
IN: 'in'
IN: 'in',
FORBID: 'forbid'
})
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
const initialDragState = {
keydown: false,
@ -99,7 +98,6 @@ const initialLineState = {
width: 0,
left: 0,
position: '',
forbidden: false,
id: '',
config: null,
doc: null
@ -322,19 +320,19 @@ export const scrollToNode = (element) => {
if (element) {
const container = getDocument().documentElement
const { clientWidth, clientHeight } = container
const { left, right, top, bottom, width, height } = element.getBoundingClientRect()
const { x, y, width, height } = element.getBoundingClientRect()
const option = {}
if (right < 0) {
option.left = container.scrollLeft + left - SCROLL_MARGIN
} else if (left > clientWidth) {
option.left = container.scrollLeft + left - clientWidth + width + SCROLL_MARGIN
if (x < 0) {
option.left = container.scrollLeft + x - SCROLL_MARGIN
} else if (x > clientWidth) {
option.left = x + width - clientWidth + SCROLL_MARGIN
}
if (bottom < 0) {
option.top = container.scrollTop + top - SCROLL_MARGIN
} else if (top > clientHeight) {
option.top = container.scrollTop + top - clientHeight + height + SCROLL_MARGIN
if (y < 0) {
option.top = container.scrollTop + y - SCROLL_MARGIN
} else if (y > clientHeight) {
option.top = y + height - clientHeight + SCROLL_MARGIN
}
if (typeof option.left === 'number' || typeof option.top === 'number') {
@ -348,13 +346,16 @@ const setSelectRect = (element) => {
element = element || getDocument().body
const { left, height, top, width } = getRect(element)
const { x, y } = getOffset(element)
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const componentName = getCurrent().schema?.componentName || ''
const scale = useLayout().getScale()
clearHover()
Object.assign(selectState, {
width,
height,
top,
left,
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
componentName,
doc: getDocument()
})
@ -415,23 +416,6 @@ export const allowInsert = (configure = hoverState.configure || {}, data = dragS
return flag
}
const isAncestor = (ancestor, descendant) => {
const ancestorId = typeof ancestor === 'string' ? ancestor : ancestor.id
let descendantId = typeof descendant === 'string' ? descendant : descendant.id
while (descendantId) {
const { parent } = getNode(descendantId, true) || {}
if (parent.id === ancestorId) {
return true
}
descendantId = parent.id
}
return false
}
// 获取位置信息,返回状态
const lineAbs = 20
const getPosLine = (rect, configure) => {
@ -439,7 +423,6 @@ const getPosLine = (rect, configure) => {
const yAbs = Math.min(lineAbs, rect.height / 3)
const xAbs = Math.min(lineAbs, rect.width / 3)
let type
let forbidden = false
if (mousePos.y < rect.top + yAbs) {
type = POSITION.TOP
@ -450,21 +433,12 @@ const getPosLine = (rect, configure) => {
} else if (mousePos.x > rect.right - xAbs) {
type = POSITION.RIGHT
} else if (configure.isContainer) {
type = POSITION.IN
if (!allowInsert()) {
forbidden = true
}
type = allowInsert() ? POSITION.IN : POSITION.FORBID
} else {
type = POSITION.BOTTOM
}
// 如果被拖拽的节点不是新增的,并且是放置的节点的祖先节点,则禁止插入
const draggedId = dragState.data?.id
if (draggedId && isAncestor(draggedId, lineState.id)) {
forbidden = true
}
return { type, forbidden }
return { type }
}
const isBodyEl = (element) => element.nodeName === 'BODY'
@ -478,6 +452,9 @@ const setHoverRect = (element, data) => {
const configure = getConfigure(componentName)
const rect = getRect(element)
const { left, height, top, width } = rect
const { x, y } = getOffset(element)
const siteCanvasRect = document.querySelector('.site-canvas').getBoundingClientRect()
const scale = useLayout().getScale()
hoverState.configure = configure
@ -506,24 +483,21 @@ const setHoverRect = (element, data) => {
if (childEle) {
const childRect = getRect(childEle)
const { left, height, top, width } = childRect
const posLine = getPosLine(childRect, lineState.configure)
const { x, y } = getOffset(childEle)
Object.assign(lineState, {
width,
height,
top,
left,
position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
position: canvasState.type === 'absolute' || getPosLine(childRect, lineState.configure).type
})
} else {
const posLine = getPosLine(rect, configure)
Object.assign(lineState, {
width,
height,
top,
left,
position: canvasState.type === 'absolute' || posLine.type,
forbidden: posLine.forbidden
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
position: canvasState.type === 'absolute' || getPosLine(rect, configure).type
})
}
@ -532,10 +506,10 @@ const setHoverRect = (element, data) => {
// 设置元素hover状态
Object.assign(hoverState, {
width,
height,
top,
left,
width: width * scale,
height: height * scale,
top: top * scale + y - siteCanvasRect.y,
left: left * scale + x - siteCanvasRect.x,
componentName
})
return undefined
@ -696,12 +670,13 @@ export const copyNode = (id) => {
export const onMouseUp = () => {
const { draging, data } = dragState
const { position, forbidden } = lineState
const { position } = lineState
const absolute = canvasState.type === 'absolute'
const sourceId = data?.id
const lineId = lineState.id
const allowInsert = position !== POSITION.FORBID
if (draging && !forbidden) {
if (draging && allowInsert) {
const { parent, node } = getNode(lineId, true) || {} // target
const targetNode = { parent, node, data: toRaw(data) }
@ -848,49 +823,6 @@ export const canvasDispatch = (name, data, doc = getDocument()) => {
doc.dispatchEvent(new CustomEvent(name, data))
}
export const canvasApi = {
dragStart,
updateRect,
getContext,
getNodePath,
dragMove,
setLocales,
setState,
deleteState,
getRenderer,
clearSelect,
selectNode,
hoverNode,
insertNode,
removeNode,
addComponent,
setPageCss,
addScript,
addStyle,
getNode,
getCurrent,
setSchema,
setUtils,
updateUtils,
deleteUtils,
getSchema,
setI18n,
getCanvasType,
setCanvasType,
setProps,
setGlobalState,
getGlobalState,
getDocument,
canvasDispatch,
Builtin,
setDataSourceMap: (...args) => {
return canvasState.renderer.setDataSourceMap(...args)
},
getDataSourceMap: (...args) => {
return canvasState.renderer.getDataSourceMap(...args)
}
}
export const initCanvas = ({ renderer, iframe, emit, controller }) => {
const currentSchema = getSchema()

View File

@ -18,6 +18,8 @@ import { generateFunction } from '@opentiny/tiny-engine-controller/utils'
import renderer, { parseData, setConfigure, setController, globalNotify, isStateAccessor } from './render'
import { getNode as getNodeById, clearNodes, getRoot, setContext, getContext, setCondition, context } from './context'
import CanvasEmpty from './CanvasEmpty.vue'
import { getCurrent, setLocales, updateRect, addStyle, addScript, canvasDispatch } from '../container/container'
import Builtin from '../builtin/builtin.json'
const { BROADCAST_CHANNEL } = constants
@ -424,3 +426,17 @@ export const api = {
setDataSourceMap,
setGlobalState
}
const canvasApi = {
getCurrent,
setLocales,
getNodeById: getNode,
updateRect,
addStyle,
addScript,
canvasDispatch
}
window.api = api
window.canvasApi = canvasApi
window.Builtin = Builtin

View File

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

View File

@ -26,77 +26,73 @@ const dispatch = (name, data) => {
window.parent.document.dispatchEvent(new CustomEvent(name, data))
}
const initRenderContext = () => {
dispatch('beforeCanvasReady')
dispatch('beforeCanvasReady')
TinyI18nHost.lowcode = lowcode
TinyI18nHost.lowcode = lowcode
// 目前先兼容老区块发布的代码,后期区块发布整改后再删除
window.React = {}
window.React.createElement = Vue.h
// 目前先兼容老区块发布的代码,后期区块发布整改后再删除
window.React = {}
window.React.createElement = Vue.h
// 不能采用new Proxy代理Vue的方案在编译后的vue会报错警告采用一下方案扩展用于注入一些区块加载逻辑
window.Vue = {
...Vue,
resolveComponent(...args) {
// 此处先执行vue内部的解析组件的方法如果可以拿到组件对象则直接返回反之则去注册区块
const component = Vue.resolveComponent(args[0])
if (component && typeof component === 'string') {
return getComponent(capitalize(camelize(args[0])))
// 不能采用new Proxy代理Vue的方案在编译后的vue会报错警告采用一下方案扩展用于注入一些区块加载逻辑
window.Vue = {
...Vue,
resolveComponent(...args) {
// 此处先执行vue内部的解析组件的方法如果可以拿到组件对象则直接返回反之则去注册区块
const component = Vue.resolveComponent(args[0])
if (component && typeof component === 'string') {
return getComponent(capitalize(camelize(args[0])))
} else {
return component
}
},
// renderSlot方法第三个参数是作用域插槽传递的数据格式{ data: vue.unref(state).componentData }
renderSlot(...args) {
// 获取当前vue的实例
const instance = Vue.getCurrentInstance()
// 获取当前区块名称
const blockName = instance.attrs.dataTag
const [, slotName, slotData] = args
// 如果是作用域插槽,则获取作用域插槽传递过来的参数
if (slotData) {
if (blockSlotDataMap[blockName]) {
blockSlotDataMap[blockName][slotName] = slotData
} else {
return component
blockSlotDataMap[blockName] = { [slotName]: slotData }
}
},
// renderSlot方法第三个参数是作用域插槽传递的数据格式{ data: vue.unref(state).componentData }
renderSlot(...args) {
// 获取当前vue的实例
const instance = Vue.getCurrentInstance()
// 获取当前区块名称
const blockName = instance.attrs.dataTag
const [, slotName, slotData] = args
// 如果是作用域插槽,则获取作用域插槽传递过来的参数
if (slotData) {
if (blockSlotDataMap[blockName]) {
blockSlotDataMap[blockName][slotName] = slotData
} else {
blockSlotDataMap[blockName] = { [slotName]: slotData }
}
}
/**
* vue源码中的renderSlot会忽略default插槽的名称所以这里必须手动添加args第三个参数的name值
* vue源码如右所示if (name !== 'default') props.name = name; return createVNode('slot', props, fallback && fallback());
**/
if (slotName === 'default') {
args[2] = args[2] || {}
args[2].name = slotName
}
return Vue.renderSlot(...args)
}
/**
* vue源码中的renderSlot会忽略default插槽的名称所以这里必须手动添加args第三个参数的name值
* vue源码如右所示if (name !== 'default') props.name = name; return createVNode('slot', props, fallback && fallback());
**/
if (slotName === 'default') {
args[2] = args[2] || {}
args[2].name = slotName
}
return Vue.renderSlot(...args)
}
window.VueI18n = VueI18n
window.TinyVue = TinyVue
window.TinyVueIcon = TinyVueIcon
window.TinyWebcomponentCore = TinyWebcomponentCore
window.TinyI18nHost = TinyI18nHost
window.TinyLowcodeComponent = {}
window.TinyComponentLibs = {}
Object.entries(TinyVue).forEach(([_key, component]) => {
const { name } = component
if (name) {
window.TinyLowcodeComponent[name] = component
}
})
document.addEventListener('updateDependencies', updateDependencies)
}
window.VueI18n = VueI18n
window.TinyVue = TinyVue
window.TinyVueIcon = TinyVueIcon
window.TinyWebcomponentCore = TinyWebcomponentCore
window.TinyI18nHost = TinyI18nHost
window.TinyLowcodeComponent = {}
window.TinyComponentLibs = {}
Object.entries(TinyVue).forEach(([_key, component]) => {
const { name } = component
if (name) {
window.TinyLowcodeComponent[name] = component
}
})
let App = null
const renderer = {
@ -133,7 +129,6 @@ const create = () => {
}
export const createRender = (config) => {
initRenderContext()
const { dslMode, canvasOptions } = config
const { styles = [], scripts = [] } = canvasOptions[dslMode]
const { styles: thirdStyles = [], scripts: thirdScripts = [] } = window.thirdPartyDeps || {}
@ -143,3 +138,5 @@ export const createRender = (config) => {
...scripts.map((src) => addScript(src)).concat([...thirdStyles, ...styles].map((src) => addStyle(src)))
]).finally(create)
}
document.addEventListener('updateDependencies', updateDependencies)

View File

@ -10,7 +10,93 @@
*
*/
export { default as CanvasContainer } from './components/container/CanvasContainer.vue'
export { default as CanvasDragItem } from './components/container/CanvasDragItem.vue'
export { default as CanvasFooter } from './components/container/CanvasFooter.vue'
export { createRender } from './components/render/runner'
import CanvasContainer from './components/container/CanvasContainer.vue'
import CanvasAction from './components/container/CanvasAction.vue'
import CanvasDragItem from './components/container/CanvasDragItem.vue'
import CanvasFooter from './components/container/CanvasFooter.vue'
import CanvasResize from './components/container/CanvasResize.vue'
import Builtin from './components/builtin/builtin.json'
import RenderMain, { api as renderApi } from './components/render/RenderMain'
import { createRender } from './components/render/runner'
import {
dragStart,
updateRect,
getContext,
getNodePath,
dragMove,
setLocales,
setState,
deleteState,
getRenderer,
clearSelect,
selectNode,
hoverNode,
insertNode,
removeNode,
addComponent,
setPageCss,
addScript,
addStyle,
getNode,
getCurrent,
setSchema,
setUtils,
updateUtils,
deleteUtils,
getSchema,
setI18n,
getCanvasType,
setCanvasType,
setProps,
setGlobalState,
getGlobalState,
getDocument,
canvasDispatch
} from './components/container/container'
export {
CanvasContainer,
CanvasAction,
CanvasFooter,
CanvasDragItem,
CanvasResize,
RenderMain,
renderApi,
Builtin,
dragStart,
dragMove,
updateRect,
getContext,
getNodePath,
getNode,
getCurrent,
setSchema,
setUtils,
updateUtils,
deleteUtils,
getSchema,
setLocales,
setState,
deleteState,
setI18n,
getRenderer,
clearSelect,
selectNode,
insertNode,
removeNode,
hoverNode,
addComponent,
setPageCss,
addScript,
addStyle,
getCanvasType,
setCanvasType,
setProps,
setGlobalState,
getGlobalState,
getDocument,
canvasDispatch,
createRender
}
export default CanvasContainer

View File

@ -23,6 +23,9 @@ 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, useNotify, useCanvas } from '@opentiny/tiny-engine-controller'
import { constants } from '@opentiny/tiny-engine-utils'
import { useLayout } from '@opentiny/tiny-engine-controller'
import { getSchema, setSchema } from '@opentiny/tiny-engine-canvas'
import VueMonaco from './VueMonaco.vue'
export default {
@ -106,7 +106,6 @@ export default {
},
emits: ['update:visible'],
setup(props, { emit, attrs }) {
const { COMPONENT_NAME } = constants
const formState = reactive({
deployInfo: '',
version: '',
@ -187,10 +186,9 @@ export default {
const remote = await api.getBlockById(block?.id)
const originalObj = remote?.content || {}
state.originalCode = JSON.stringify(originalObj, null, 2)
const getSchema = useCanvas().canvasApi.value.getSchema
// 线
const pageSchema = getSchema?.() || {}
const pageSchema = getSchema() || {}
if (pageSchema.componentName === 'Block') {
state.code = JSON.stringify(pageSchema, null, 2)
} else {
@ -214,21 +212,10 @@ export default {
}
const save = () => {
if (!state.newCode) {
close()
return
}
try {
const pageSchema = JSON.parse(state.newCode)
const setSchema = useCanvas().canvasApi.value.setSchema
setSchema?.({ ...pageSchema, componentName: COMPONENT_NAME.Block })
close()
} catch (err) {
useNotify({
type: 'error',
message: '代码静态检查有错误,请先修改后再保存'
})
}
const pageSchema = JSON.parse(state.newCode)
setSchema(pageSchema)
close()
}
return {

View File

@ -21,6 +21,7 @@ import { capitalize } from '@vue/shared'
import { extend } from '@opentiny/vue-renderless/common/object'
import { Input as TinyInput, Form as TinyForm, FormItem as TinyFormItem, Popover as TinyPopover } from '@opentiny/vue'
import { useLayout, useModal, useCanvas, useBlock } from '@opentiny/tiny-engine-controller'
import { getSchema, getCurrent } from '@opentiny/tiny-engine-canvas'
export default {
components: {
@ -37,8 +38,7 @@ export default {
const { pageState } = useCanvas()
const { addBlockEvent, removeEventLink, getCurrentBlock, appendEventEmit } = useBlock()
const { PLUGIN_NAME, activePlugin } = useLayout()
const { schema } = useCanvas().canvasApi.value?.getSchema?.() || {}
const { schema } = getSchema()
const events = schema?.events || []
const eventsList = Object.entries(events).map(([key, value]) => {
return {
@ -88,7 +88,7 @@ export default {
const {
schema: { id, componentName }
} = useCanvas().canvasApi.value?.getCurrent?.() || {}
} = getCurrent()
const newEvent = extend(true, {}, data.metaEvent, {
linked: {
@ -117,7 +117,7 @@ export default {
const editEvent = (item, data) => {
const {
schema: { id, componentName }
} = useCanvas().canvasApi.value?.getCurrent?.() || {}
} = getCurrent()
const oldEventName = data.linkedEventName

View File

@ -22,6 +22,7 @@ import { reactive, computed } from 'vue'
import { extend } from '@opentiny/vue-renderless/common/object'
import { Input as TinyInput, Popover as TinyPopover } from '@opentiny/vue'
import { useLayout, useModal, useCanvas, useBlock, useHistory } from '@opentiny/tiny-engine-controller'
import { getSchema, getCurrent } from '@opentiny/tiny-engine-canvas'
export default {
components: {
@ -35,10 +36,10 @@ export default {
},
setup(props) {
const { confirm } = useModal()
const { pageState, canvasApi } = useCanvas()
const { pageState } = useCanvas()
const { addBlockProperty, removePropertyLink, getCurrentBlock, editBlockProperty } = useBlock()
const { PLUGIN_NAME, activePlugin } = useLayout()
const { schema } = canvasApi.value.getSchema?.() || {}
const { schema } = getSchema()
const state = reactive({
newPropertyName: ''
@ -67,7 +68,7 @@ export default {
exec() {
const {
schema: { id, componentName }
} = canvasApi.value?.getCurrent?.() || {}
} = getCurrent()
const newProperty = extend(true, {}, property, {
property: state.newPropertyName || `${property.property}${id}`,
linked: {
@ -86,7 +87,7 @@ export default {
const editProperty = (property) => {
const {
schema: { id, componentName }
} = canvasApi.value?.getCurrent?.() || {}
} = getCurrent()
//
if (props.data?.linked) {

View File

@ -78,8 +78,9 @@
<script lang="jsx">
import { reactive, ref, watchEffect, onBeforeUnmount } from 'vue'
import { Button, DialogBox, Popover, Search, Alert } from '@opentiny/vue'
import { getGlobalConfig, useModal, usePage, useNotify, useCanvas } from '@opentiny/tiny-engine-controller'
import { getGlobalConfig, useModal, usePage, useNotify } from '@opentiny/tiny-engine-controller'
import { theme } from '@opentiny/tiny-engine-controller/adapter'
import { getSchema } from '@opentiny/tiny-engine-canvas'
import MetaListItems from './MetaListItems.vue'
import { iconYes } from '@opentiny/vue-icon'
import VueMonaco from './VueMonaco.vue'
@ -129,7 +130,7 @@ export default {
})
watchEffect(() => {
state.bindLifeCycles = props.bindLifeCycles || useCanvas().canvasApi.value?.getSchema()?.lifeCycles || {}
state.bindLifeCycles = props.bindLifeCycles || getSchema()?.lifeCycles || {}
})
const searchLifeCyclesList = (value) => {
@ -141,7 +142,7 @@ export default {
}
const syncLifeCycle = () => {
const currentSchema = useCanvas().canvasApi.value?.getSchema?.()
const currentSchema = getSchema()
const pageContent = getPageContent()
const { id, fileName } = pageContent
if (id === currentSchema.id || fileName === currentSchema.fileName) {
@ -164,8 +165,7 @@ export default {
const openLifeCyclesPanel = (item) => {
state.title = item
const bindLifeCycleSource =
props.bindLifeCycles?.[item] || useCanvas().canvasApi.value?.getSchema?.()?.lifeCycles?.[item]
const bindLifeCycleSource = props.bindLifeCycles?.[item] || getSchema().lifeCycles?.[item]
state.editorValue =
bindLifeCycleSource?.value ||
`function ${item} (${item === 'setup' ? '{ props, state, watch, onMounted }' : ''}) {} `

View File

@ -124,6 +124,7 @@ import { reactive, ref, computed, nextTick, watch } from 'vue'
import { camelize, capitalize } from '@vue/shared'
import { Button, DialogBox, Search, Switch, Input, Tooltip, Alert } from '@opentiny/vue'
import { useHttp } from '@opentiny/tiny-engine-http'
import { getSchema, getGlobalState, setState, getNode, getCurrent } from '@opentiny/tiny-engine-canvas'
import { useCanvas, useResource, useLayout, useApp, useProperties, useData } from '@opentiny/tiny-engine-controller'
import { theme } from '@opentiny/tiny-engine-controller/adapter'
import { constants } from '@opentiny/tiny-engine-utils'
@ -149,12 +150,6 @@ const CONSTANTS = {
}
const getJsSlot = () => {
const { getNode, getCurrent } = useCanvas().canvasApi.value || {}
if (!getNode || !getCurrent) {
return [false, {}]
}
const jsSlot = getNode(getCurrent()?.parent?.id, true)?.parent
return [jsSlot?.type === 'JSSlot', jsSlot]
@ -403,9 +398,10 @@ export default {
const confirm = () => {
let variableContent = state.isEditorEditMode ? editor.value?.getEditor().getValue() : state.variable
const { setSaved, canvasApi } = useCanvas()
//
if (oldValue !== variableContent) {
const { setSaved } = useCanvas()
setSaved(false)
variableContent = formatString(variableContent, 'javascript')
}
@ -415,14 +411,14 @@ export default {
if (variableContent) {
if (state.bindPrefix === CONSTANTS.DATASOUCEPREFIX) {
const pageSchema = canvasApi.value.getSchema()
const pageSchema = getSchema()
const stateName = state.variable.replace(`${CONSTANTS.STATE}`, '')
const staticData = state.variableContent.map(({ _id, ...other }) => other)
pageSchema.state[stateName] = staticData
//
canvasApi.value.setState({ [stateName]: staticData })
setState({ [stateName]: staticData })
// setup
genRemoteMethodToLifeSetup(stateName, state.dataSouce, pageSchema)
@ -457,7 +453,7 @@ export default {
state.isVisible = true
state.variableName = bindKey.value
state.variable = getInitVariable()
state.variables = useCanvas().canvasApi.value.getSchema()?.state || {}
state.variables = getSchema()?.state || {}
state.bindPrefix = CONSTANTS.STATE
state.variableContent = state.variables[bindKey.value]
nextTick(() => window.dispatchEvent(new Event('resize')))
@ -465,8 +461,6 @@ export default {
const selectItem = (item) => {
state.active = item.id
const { canvasApi } = useCanvas()
if (item.id === 'function') {
state.bindPrefix = CONSTANTS.THIS
const { PLUGIN_NAME, getPluginApi } = useLayout()
@ -482,7 +476,7 @@ export default {
state.variables = bridge
} else if (item.id === 'props') {
state.bindPrefix = CONSTANTS.PROPS
const properties = canvasApi.value.getSchema()?.schema?.properties
const properties = getSchema()?.schema?.properties
const bindProperties = {}
properties?.forEach(({ content }) => {
content.forEach(({ property }) => {
@ -508,7 +502,7 @@ export default {
state.bindPrefix = CONSTANTS.STORE
state.variables = {}
const stores = canvasApi.value.getGlobalState()
const stores = getGlobalState()
stores.forEach(({ id, state: storeState = {}, getters = {} }) => {
const loadProp = (prop) => {
const propBinding = `${id}.${prop}`
@ -529,7 +523,7 @@ export default {
state.variables = params.reduce((variables, param) => ({ ...variables, [param]: param }), {})
} else {
state.bindPrefix = CONSTANTS.STATE
state.variables = canvasApi.value.getSchema()?.[item.id]
state.variables = getSchema()?.[item.id]
}
}

View File

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

View File

@ -22,6 +22,7 @@
<script>
import { onMounted, ref, watchEffect } from 'vue'
import { useMessage, useCanvas } from '@opentiny/tiny-engine-controller'
import { getSchema } from '@opentiny/tiny-engine-canvas'
import MetaCodeEditor from './MetaCodeEditor.vue'
import MetaBindVariable from './MetaBindVariable.vue'
@ -90,7 +91,7 @@ export default {
let newValue = value
if (value?.type === CONSTANT.JSEXPRESSION) {
const pageSchema = useCanvas().canvasApi.value.getSchema()
const pageSchema = getSchema()
const stateName = value?.value?.replace(CONSTANT.STATE, '')
newValue = pageSchema?.state?.[stateName]
}

View File

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

View File

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

View File

@ -1,6 +1,5 @@
<template>
<ul
v-if="state.data.length"
:class="['block-list', 'lowcode-scrollbar', { 'is-small-list': blockStyle === 'mini' }, { isShortcutPanel }]"
@mouseleave="state.hover = false"
>
@ -123,14 +122,12 @@
</div>
</div>
</ul>
<search-empty :isShow="!state.data.length" />
</template>
<script>
import { computed, watch, inject, reactive } from 'vue'
import { iconSetting, iconPlus } from '@opentiny/vue-icon'
import { Tooltip, Progress, Popover } from '@opentiny/vue'
import SearchEmpty from './SearchEmpty.vue'
import SvgButton from './SvgButton.vue'
const defaultImg =
@ -143,8 +140,7 @@ export default {
IconSetting: iconSetting(),
IconPlus: iconPlus(),
TinyPopover: Popover,
SvgButton,
SearchEmpty
SvgButton
},
props: {
data: {

View File

@ -1,13 +1,6 @@
<template>
<div>
<tiny-dialog-box
:append-to-body="true"
:close-on-click-modal="false"
:visible="visible"
title="新建区块"
width="500"
@close="cancel"
>
<tiny-dialog-box :append-to-body="true" :visible="visible" title="新建区块" width="500" @close="cancel">
<tiny-form
show-message
:model="formData"

View File

@ -1,19 +0,0 @@
<template>
<div v-if="isShow" class="empty-wrap">
<svg-icon class="empty-icon" name="empty"></svg-icon>
<p class="empty-text">暂无数据</p>
</div>
</template>
<script>
export default {
props: {
/**
* 控制搜索空数据显示
*/
isShow: {
type: Boolean,
default: false
}
}
}
</script>

View File

@ -79,7 +79,6 @@ import BlockDeployDialog from './component/BlockDeployDialog.vue'
import ProgressBar from './component/ProgressBar.vue'
import MetaButtonGroup from './component/MetaButtonGroup.vue'
import MetaTableColumns from './component/MetaTableColumns.vue'
import SearchEmpty from './component/SearchEmpty.vue'
import i18n, { i18nKeyMaps } from '@opentiny/tiny-engine-controller/js/i18n'
@ -146,8 +145,7 @@ export const MetaComponents = {
MetaIpSection,
MetaRelatedEditor,
MetaRelatedColumns,
MetaTableColumns,
SearchEmpty
MetaTableColumns
}
export {
@ -208,6 +206,5 @@ export {
BindI18n,
BlockDeployDialog,
ProgressBar,
MetaTableColumns,
SearchEmpty
MetaTableColumns
}

View File

@ -31,6 +31,9 @@
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@vue/shared": "^3.3.4",
"monaco-editor": "0.33.0",
"prettier": "2.7.1",
@ -42,9 +45,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-icon": "^3.14.0",
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15"
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -24,13 +24,7 @@ const http = useHttp()
* @param { json } params {"event_type": design_error,"url": "elit in reprehenderit enim incididunt" }
* @returns { Promise }
*/
export const requestEvent = (url, params) => {
if (!url) {
return
}
return http.post(url, params).catch(() => {})
}
export const requestEvent = (url, params) => http.post(url, params).catch(() => {})
/**
* 页面更新

View File

@ -1,29 +1,27 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import eslintWorkerUrl from './worker-files/eslint.worker?worker&url'
import { PROD, BASE_URL } from './environments'
export const initLinter = (editor, monacoInstance, state) => {
let workerUrl = new URL(eslintWorkerUrl, import.meta.url)
let workerUrl = `${BASE_URL}monaco-linter/eslint.worker.js`
// 线上环境,存在 worker 资源跨域的情况
if (workerUrl.origin !== location.origin) {
const workerBlob = new Blob([`import('${workerUrl}');`], {
type: 'application/javascript'
})
if (PROD) {
const workerBlob = new Blob([`importScripts('${workerUrl}');`], { type: 'application/javascript' })
workerUrl = window.URL.createObjectURL(workerBlob)
}
const worker = new Worker(workerUrl, { type: 'module' })
const worker = new Worker(workerUrl)
// 监听 ESLint web worker 的返回
worker.onmessage = function (event) {

View File

@ -93,10 +93,6 @@ const promiseMonitoring = () => {
*/
export const iframeMonitoring = () => {
if (!monitorUrl) {
return false
}
window.frames[0].onerror = function (errorMessage, scriptURI, lineNo, columnNo, error) {
requestEvent(monitorUrl, {
event_type: 'design_iframeError',

View File

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

View File

@ -31,16 +31,18 @@
"license": "MIT",
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@babel/generator": "~7.23.2",
"@babel/parser": "~7.23.2",
"@babel/traverse": "~7.23.2",
"@babel/generator": "7.18.13",
"@babel/parser": "7.18.13",
"@babel/traverse": "7.18.13",
"@opentiny/tiny-engine-builtin-component": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-locale": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@vue/shared": "^3.3.4",
"css-tree": "^2.3.1",
"eslint-linter-browserify": "8.57.0",
"prettier": "2.7.1"
},
"devDependencies": {
@ -50,9 +52,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-locale": "^3.14.0",
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"
}

View File

@ -278,7 +278,7 @@ const initBlock = async (block = {}, _langs = {}, isEdit) => {
// 把区块的schema传递给画布
await resetBlockCanvasState({ pageSchema: getBlockPageSchema(block) })
// 这一步操作很重要让区块管理面板和画布共同维护同一份区块schema
block.content = useCanvas().canvasApi.value?.getSchema()
block.content = useCanvas().renderer.value?.getSchema()
setCurrentBlock(block)
setBreadcrumbBlock([block[nameCn] || block.label], block.histories)
@ -361,7 +361,7 @@ const createEmptyBlock = ({ name_cn, label, path, categories }) => {
}
const setComponentLinkedValue = ({ propertyName, value }) => {
const { schema } = useCanvas().canvasApi.value?.getCurrent() || {}
const { schema } = useCanvas().renderer.value?.getCurrent() || {}
if (!propertyName || !schema) {
return

View File

@ -51,20 +51,14 @@ const defaultSchema = {
outputs: []
}
const canvasApi = ref({})
const isCanvasApiReady = ref(false)
const initCanvasApi = (newCanvasApi) => {
canvasApi.value = newCanvasApi
isCanvasApiReady.value = true
}
const renderer = ref(null)
const pageState = reactive({ ...defaultPageState, loading: true })
// 重置画布数据
const resetCanvasState = async (state = {}) => {
Object.assign(pageState, defaultPageState, state)
await canvasApi.value?.setSchema(pageState.pageSchema)
await renderer.value?.setSchema(pageState.pageSchema)
}
// 页面重置画布数据
@ -148,6 +142,7 @@ const getCurrentPage = () => pageState.currentPage
export default function () {
return {
pageState,
renderer,
isBlock,
isSaved,
isLoading,
@ -158,11 +153,10 @@ export default function () {
resetPageCanvasState,
resetBlockCanvasState,
clearCurrentState,
getDataSourceMap: renderer.value?.getDataSourceMap,
setDataSourceMap: renderer.value?.setDataSourceMap,
getCurrentSchema,
setCurrentSchema,
getCurrentPage,
initCanvasApi,
canvasApi,
isCanvasApiReady
getCurrentPage
}
}

View File

@ -61,7 +61,7 @@ const push = (schema) => {
const go = (addend, valid) => {
historyState.index = historyState.index + addend
useCanvas().canvasApi.value?.setSchema(string2Schema(list[historyState.index]))
useCanvas().renderer.value?.setSchema(string2Schema(list[historyState.index]))
// 不是锁定状态,撤销操作后,传递第二个标识位,将 list 的长度减一,置灰 undoredo 操作按钮
if (typeof valid === 'boolean') {
@ -95,7 +95,7 @@ const clear = () => {
const addHistory = (schema) => {
if (!schema) {
useCanvas().setSaved(false)
push(useCanvas().canvasApi.value?.getSchema())
push(useCanvas().renderer.value?.getSchema())
} else {
clear()
// 初始 schema 需要设置为第一条历史记录

View File

@ -191,7 +191,7 @@ const setProp = (name, value, type) => {
}
// 没有父级或者不在节点上面要更新内容。就用setState
const { getNode, setState, updateRect } = useCanvas().canvasApi.value || {}
const { getNode, setState, updateRect } = useCanvas().renderer.value || {}
getNode(properties.schema.id, true)?.parent || setState(useCanvas().getPageSchema().state)
propsUpdateKey.value++

View File

@ -137,7 +137,7 @@ const registerBlock = async (data, notFetchResouce) => {
return block
} else {
if (!blockResource.get(label)) {
const { addScript, addStyle } = useCanvas().canvasApi.value
const { addScript, addStyle } = useCanvas().renderer.value
const promises = scripts
.filter((item) => item.includes('umd.js'))
.map(addScript)
@ -372,7 +372,7 @@ const fetchResource = async ({ isInit = true } = {}) => {
const { id, type } = useEditorInfo().useInfo()
useApp().appInfoState.selectedId = id
const { Builtin } = useCanvas().canvasApi.value
const Builtin = window.Builtin
Builtin.data.materials.components[0].children.map(registerComponent)
BuiltinComponentMaterials.components[0].children.map(registerComponent)
@ -452,7 +452,7 @@ const updateCanvasDependencies = (blocks) => {
getBlockDeps(block.content.dependencies)
})
useCanvas().canvasApi.value?.canvasDispatch('updateDependencies', { detail: resState.thirdPartyDeps })
useCanvas().renderer.value?.canvasDispatch('updateDependencies', { detail: resState.thirdPartyDeps })
}
export default function () {

View File

@ -101,7 +101,7 @@ const ensureI18n = (obj, send) => {
}
})
useCanvas().canvasApi.value?.setLocales(messages, true)
useCanvas().renderer.value?.setLocales(messages, true)
} catch (e) {
// 不需要处理有报错的词条会在画布初始化的时候统一调setLocales这个方法
}
@ -171,7 +171,7 @@ const initAppI18n = async (appId) => {
host: appId,
hostType: HOST_TYPE.App
})
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
useCanvas().renderer.value?.setLocales(i18nResource.messages)
}
}
@ -181,7 +181,7 @@ const initBlockI18n = async (blockId) => {
host: blockId,
hostType: HOST_TYPE.Block
})
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
useCanvas().renderer.value?.setLocales(i18nResource.messages)
}
}
@ -192,7 +192,7 @@ const initBlockLocalI18n = async (langs = {}) => {
hostType: HOST_TYPE.Block,
local: true
})
useCanvas().canvasApi.value?.setLocales(i18nResource.messages)
useCanvas().renderer.value?.setLocales(i18nResource.messages)
}
const format = (str = '', params = {}) => str.replace(/\$\{(.+?)\}/g, (substr, key) => params[key] || '')

View File

@ -17,7 +17,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
import { glob } from 'glob'
import { fileURLToPath } from 'node:url'
const jsEntries = glob.sync('./js/**/*.js').map((file) => {
const jsEntries = glob.sync('./js/**.js').map((file) => {
return [file.slice(0, file.length - path.extname(file).length), fileURLToPath(new URL(file, import.meta.url))]
})
@ -26,12 +26,9 @@ export default defineConfig({
plugins: [vue(), vueJsx()],
publicDir: false,
resolve: {},
base: './',
define: {
'import.meta': 'import.meta',
'import.meta.env.MODE': 'import.meta.env.MODE',
'import.meta.env.PROD': 'import.meta.env.PROD',
'import.meta.env.BASE_URL': 'import.meta.env.BASE_URL'
'process.env': {},
'import.meta': 'import.meta'
},
build: {
cssCodeSplit: false,

View File

@ -2,8 +2,6 @@
NODE_ENV=production
VITE_CDN_DOMAIN=https://npm.onmicrosoft.cn
VITE_LOCAL_IMPORT_MAPS=false
VITE_LOCAL_BUNDLE_DEPS=false
# VITE_ORIGIN=
# 错误监控上报 url

View File

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

View File

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

View File

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

View File

@ -30,14 +30,13 @@
"author": "OpenTiny Team",
"license": "MIT",
"dependencies": {
"@babel/core": "~7.23.2",
"@babel/generator": "~7.23.2",
"@babel/parser": "~7.23.2",
"@babel/traverse": "~7.23.2",
"@babel/core": "7.18.13",
"@babel/generator": "7.18.13",
"@babel/parser": "7.18.13",
"@babel/traverse": "7.18.13",
"@opentiny/tiny-engine-canvas": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-dsl-vue": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-i18n-host": "workspace:*",
"@opentiny/tiny-engine-plugin-block": "workspace:*",
@ -78,22 +77,21 @@
"@opentiny/tiny-engine-toolbar-setting": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/tiny-engine-webcomponent-core": "workspace:*",
"@opentiny/vue": "~3.14.0",
"@opentiny/vue-design-smb": "~3.14.0",
"@opentiny/vue-renderless": "~3.14.0",
"@opentiny/vue-theme": "~3.14.0",
"@opentiny/vue": "~3.11.0",
"@opentiny/vue-design-smb": "~3.11.0",
"@opentiny/vue-renderless": "~3.11.0",
"@opentiny/vue-theme": "~3.11.0",
"@vue/babel-plugin-jsx": "1.1.1",
"@vue/repl": "^2.9.0",
"@vueuse/core": "^9.6.0",
"element-resize-detector": "^1.2.4",
"eslint-linter-browserify": "8.57.0",
"file-saver": "^2.0.5",
"html2canvas": "^1.4.1",
"jszip": "^3.10.1",
"monaco-editor": "0.33.0",
"prettier": "2.7.1",
"sortablejs": "^1.14.0",
"vue": "3.4.23",
"vue": "^3.4.15",
"vue-i18n": "^9.9.0"
},
"devDependencies": {
@ -120,11 +118,9 @@
"rollup-plugin-polyfill-node": "^0.12.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-visualizer": "^5.8.3",
"shelljs": "^0.8.5",
"svg-sprite-loader": "^6.0.11",
"vite": "^4.3.7",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-static-copy": "^0.16.0",
"vite-plugin-monaco-editor": "^1.0.10",
"vite-plugin-svg-icons": "^2.0.1",
"vue-eslint-parser": "^8.0.1"
},

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
// importScripts 不支持 esm, 此处使用 umd, 对外暴露的变量名linter需要填写openTiny的cdn地址
importScripts('')
// 根据公司的编码规范内置了 config/rules, 可以进一步定制
const config = {
...self.linter.config,
rules: {
...self.linter.config.rules,
// JS 面板中,仅定义 function但可能不使用该方法
'no-unused-vars': 'off',
'no-alert': 'off',
'no-console': 'off'
},
settings: {}
}
// 错误的等级ESLint 与 monaco 的存在差异,做一层映射
const severityMap = {
2: 'Error',
1: 'Warning'
}
self.addEventListener('message', (event) => {
const { code, version } = event.data
const ruleDefines = self.linter.esLinter.getRules()
const errs = self.linter.esLinter.verify(code, config)
const markers = errs.map(({ ruleId = '', line, endLine, column, endColumn, message, severity }) => ({
code: {
value: ruleId,
target: ruleDefines.get(ruleId)?.meta?.docs?.url
},
startLineNumber: line,
endLineNumber: endLine,
startColumn: column,
endColumn: endColumn,
message: message,
severity: severityMap[severity],
source: 'ESLint'
}))
// ESLint 静态检查结果,发回主线程
self.postMessage({ markers, version })
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,47 +0,0 @@
import fs from 'node:fs'
import path from 'node:path'
import shelljs from 'shelljs'
export function installPackageTemporary(packageNeedToInstall, tempDir, logger = console) {
return [
{
name: 'vite-plugin-install-package-temporary',
buildStart() {
if (packageNeedToInstall.every((pkg) => pkg.exist)) {
logger.info(`[vite-plugin-install-package-temporary]: bundle dependencies packages exist, skip install `)
return
}
let code = shelljs.mkdir('-p', tempDir).code
if (code === 0) {
//code 为 0 表示成功
fs.writeFileSync(
path.resolve(tempDir, 'package.json'),
JSON.stringify(
{
name: 'bundle-deps', // tempDir to 烤串
dependencies: Object.fromEntries(packageNeedToInstall.map((cur) => [cur.packageName, cur.version]))
},
null,
2
),
{ encoding: 'utf-8' }
)
}
code =
code ||
shelljs.cd(tempDir).code ||
shelljs.exec(`npm install --force`).code ||
shelljs.cd(path.relative(tempDir, '.')).code
if (code === 0) {
logger.info(
`[vite-plugin-install-package-temporary]: bundle dependencies package install success, total ${packageNeedToInstall.length} package(s)`
)
} else {
logger.warn(`[vite-plugin-install-package-temporary]: bundle dependencies package install failed`)
}
}
}
]
}

View File

@ -21,15 +21,7 @@
import { reactive, ref, watch, onUnmounted } from 'vue'
import { ConfigProvider as TinyConfigProvider } from '@opentiny/vue'
import designSmbConfig from '@opentiny/vue-design-smb'
import {
useResource,
useLayout,
useEditorInfo,
useModal,
useApp,
useNotify,
useCanvas
} from '@opentiny/tiny-engine-controller'
import { useResource, useLayout, useEditorInfo, useModal, useApp, useNotify } from '@opentiny/tiny-engine-controller'
import AppManage from '@opentiny/tiny-engine-plugin-page'
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
import DesignToolbars from './DesignToolbars.vue'
@ -98,18 +90,7 @@ export default {
}
useEditorInfo().getUserInfo()
watch(
useCanvas().isCanvasApiReady,
(ready) => {
if (ready) {
useResource().fetchResource()
}
},
{
immediate: true
}
)
useResource().fetchResource()
const handlePopStateEvent = () => {
useResource().handlePopStateEvent()
@ -173,7 +154,6 @@ export default {
}
}
.tiny-engine-right-wrap {
position: relative;
z-index: 4;
}
:deep(.monaco-editor .suggest-widget) {

View File

@ -1,6 +1,6 @@
<template>
<div id="canvas-wrap" ref="canvasRef">
<div ref="siteCanvas" class="site-canvas" :style="siteCanvasStyle">
<div ref="siteCanvas" class="site-canvas">
<canvas-container
:controller="controller"
:materials-panel="materialsPanel"
@ -14,8 +14,15 @@
</template>
<script>
import { ref, watch, computed, onUnmounted } from 'vue'
import { CanvasContainer, CanvasFooter } from '@opentiny/tiny-engine-canvas'
import { ref, onMounted, watch } from 'vue'
import {
CanvasContainer,
CanvasFooter,
selectNode,
getNodePath,
updateRect,
getSchema
} from '@opentiny/tiny-engine-canvas'
import {
useProperties,
useCanvas,
@ -55,19 +62,11 @@ export default {
const removeNode = (node) => {
const { pageState } = useCanvas()
footData.value = useCanvas().canvasApi.value.getNodePath(node?.id)
footData.value = getNodePath(node?.id)
pageState.currentSchema = {}
pageState.properties = null
}
const siteCanvasStyle = computed(() => {
const { scale } = useLayout().getDimension()
return {
height: `calc((100% - var(--base-bottom-panel-height, 30px) - 36px) / ${scale})`,
transform: `scale(${scale})`
}
})
watch(
[() => useCanvas().isSaved(), () => useLayout().layoutState.pageStatus, () => useCanvas().getPageSchema()],
([isSaved, pageStatus, pageSchema], [oldIsSaved, _oldPageStatus, oldPageSchema]) => {
@ -134,8 +133,6 @@ export default {
useLayout().closePlugin()
}
const { getSchema, getNodePath } = useCanvas().canvasApi.value
const schema = getSchema()
// schema
useProperties().getProps(node || schema, parent)
@ -145,31 +142,12 @@ export default {
}
const selectFooterNode = ({ node }) => {
const { selectNode } = useCanvas().canvasApi.value
selectNode(node)
}
let canvasResizeObserver = null
watch(
() => [useCanvas().isCanvasApiReady.value, canvasRef.value],
([ready]) => {
if (!ready || !canvasRef.value) {
return
}
//
document.removeEventListener('canvasResize', useCanvas().canvasApi.value.updateRect)
canvasResizeObserver?.disconnect?.()
document.addEventListener('canvasResize', useCanvas().canvasApi.value.updateRect)
canvasResizeObserver = new ResizeObserver(useCanvas().canvasApi.value.updateRect).observe(canvasRef.value)
}
)
onUnmounted(() => {
document.removeEventListener('canvasResize', useCanvas().canvasApi.value.updateRect)
canvasResizeObserver?.disconnect?.()
onMounted(() => {
document.addEventListener('canvasResize', updateRect)
new ResizeObserver(updateRect).observe(canvasRef.value)
})
return {
@ -188,7 +166,6 @@ export default {
request: useHttp(),
ast
},
siteCanvasStyle,
canvasRef
}
}
@ -205,11 +182,11 @@ export default {
position: relative;
.site-canvas {
height: calc(100% - var(--base-bottom-panel-height, 30px) - 36px);
background: var(--ti-lowcode-breadcrumb-hover-bg);
position: absolute;
overflow: hidden;
margin: 18px 0;
transform-origin: top;
}
}
</style>

View File

@ -68,8 +68,7 @@ export default {
.tiny-tabs__content {
flex: 1;
overflow-y: scroll;
padding: 0;
margin-top: 0;
padding-top: 0;
}
.tiny-tabs__item {
color: var(--ti-lowcode-setting-panel-tabs-item-title-color);

View File

@ -18,11 +18,11 @@ import { defineComponent, computed, defineAsyncComponent } from 'vue'
import { Repl, ReplStore } from '@vue/repl'
import vueJsx from '@vue/babel-plugin-jsx'
import { transformSync } from '@babel/core'
import { genSFCWithDefaultPlugin, parseRequiredBlocks } from '@opentiny/tiny-engine-dsl-vue'
import { Notify } from '@opentiny/vue'
import importMap from './importMap'
import srcFiles from './srcFiles'
import generateMetaFiles, { processAppJsCode } from './generate'
import { getSearchParams, fetchMetaData, fetchImportMap, fetchAppSchema, fetchBlockSchema } from './http'
import { getSearchParams, fetchCode, fetchMetaData } from './http'
import { PanelType, PreviewTips } from '../constant'
import { injectDebugSwitch } from './debugSwitch'
import '@vue/repl/style.css'
@ -51,6 +51,8 @@ export default {
}
}
store.setImportMap(importMap)
// store.setFilesstate.activeFile = state.files[filename]activeFile
const setFiles = async (newFiles, mainFileName) => {
await store.setFiles(newFiles, mainFileName)
@ -59,7 +61,7 @@ export default {
store['initTsConfig']() // d.ts便
}
const addUtilsImportMap = (importMap, utils = []) => {
const addUtilsImportMap = (utils = []) => {
const utilsImportMaps = {}
utils.forEach(({ type, content: { package: packageName, cdnLink } }) => {
if (type === 'npm' && cdnLink) {
@ -69,81 +71,31 @@ export default {
const newImportMap = { imports: { ...importMap.imports, ...utilsImportMaps } }
store.setImportMap(newImportMap)
}
const getBlocksSchema = async (pageSchema, blockSet = new Set()) => {
let res = []
const blockNames = parseRequiredBlocks(pageSchema)
const promiseList = blockNames
.filter((name) => {
if (blockSet.has(name)) {
return false
}
blockSet.add(name)
return true
})
.map((name) => fetchBlockSchema(name))
const schemaList = await Promise.allSettled(promiseList)
schemaList.forEach((item) => {
if (item.status === 'fulfilled' && item.value?.[0]?.content) {
res.push(item.value[0].content)
res.push(...getBlocksSchema(item.value[0].content, blockSet))
}
})
return res
}
const queryParams = getSearchParams()
const getImportMap = async () => {
if (import.meta.env.VITE_LOCAL_BUNDLE_DEPS === 'true') {
const mapJSON = await fetchImportMap()
return {
imports: {
...mapJSON.imports,
...getSearchParams().scripts
}
}
}
return importMap
}
const promiseList = [
fetchAppSchema(queryParams?.app),
fetchMetaData(queryParams),
setFiles(srcFiles, 'src/Main.vue'),
getImportMap()
]
Promise.all(promiseList).then(async ([appData, metaData, _void, importMapData]) => {
addUtilsImportMap(importMapData, metaData.utils || [])
const promiseList = [fetchCode(queryParams), fetchMetaData(queryParams), setFiles(srcFiles, 'src/Main.vue')]
Promise.all(promiseList).then(([codeList, metaData]) => {
addUtilsImportMap(metaData.utils || [])
const codeErrorMsgs = codeList
.filter(({ errors }) => errors?.length)
.map(({ errors }) => errors)
.flat()
.map(({ message }) => message)
const blocks = await getBlocksSchema(queryParams.pageInfo?.schema)
// TODO: block schema
// TODO: block
const pageCode = [
{
panelName: 'Main.vue',
panelValue:
genSFCWithDefaultPlugin(queryParams.pageInfo?.schema, appData?.componentsMap || [], {
blockRelativePath: './'
}) || '',
panelType: 'vue',
index: true
},
...(blocks || []).map((blockSchema) => {
return {
panelName: blockSchema.fileName,
panelValue:
genSFCWithDefaultPlugin(blockSchema, appData?.componentsMap || [], { blockRelativePath: './' }) || '',
panelType: 'vue',
index: true
}
if (codeErrorMsgs.length) {
const title = PreviewTips.ERROR_WHEN_COMPILE
Notify({
type: 'error',
title,
message: codeErrorMsgs.join('\n'),
//
duration: 0,
position: 'top-right'
})
]
return title
}
// [@vue/repl] `Only lang="ts" is supported for <script> blocks.`
const langReg = /lang="jsx"/
@ -191,7 +143,7 @@ export default {
newFiles['app.js'] = appJsCode
pageCode.map(fixScriptLang).forEach(assignFiles)
codeList.map(fixScriptLang).forEach(assignFiles)
const metaFiles = generateMetaFiles(metaData)
Object.assign(newFiles, metaFiles)

View File

@ -50,11 +50,3 @@ export const fetchMetaData = async ({ platform, app, type, id, history, tenant }
params: { platform, app, type, id, history }
})
: {}
export const fetchImportMap = async () => {
const baseUrl = new URL(import.meta.env.BASE_URL, location.href)
return fetch(new URL('./preview-import-map-static/preview-importmap.json', baseUrl).href).then((res) => res.json())
}
export const fetchAppSchema = async (id) => http.get(`/app-center/v1/api/apps/schema/${id}`)
export const fetchBlockSchema = async (blockName) => http.get(`/material-center/api/block?label=${blockName}`)

View File

@ -13,19 +13,37 @@
// import { hyphenate } from '@vue/shared'
import { getSearchParams } from './http'
import importMapJSON from './importMap.json'
import { VITE_CDN_DOMAIN } from '@opentiny/tiny-engine-controller/js/environments'
const importMap = {}
const opentinyVueVersion = '~3.14'
const opentinyVueVersion = '~3.11'
function replacePlaceholder(v) {
return v.replace('${VITE_CDN_DOMAIN}', VITE_CDN_DOMAIN).replace('${opentinyVueVersion}', opentinyVueVersion)
const tinyVue3Imports = {
// 推荐之后统一使用@opentiny/vue去引入依赖兼容后续录入的组件来源于tiny-vue
'@opentiny/vue': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue.mjs`,
'@opentiny/vue-icon': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-icon.mjs`,
'@opentiny/vue-common': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-common.mjs`,
'@opentiny/vue-locale': `${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-locale.mjs`,
'@opentiny/vue-renderless/': `${VITE_CDN_DOMAIN}/@opentiny/vue-renderless@${opentinyVueVersion}/`
}
importMap.imports = {
...Object.fromEntries(Object.entries(importMapJSON.imports).map(([k, v]) => [k, replacePlaceholder(v)])),
vue: `${VITE_CDN_DOMAIN}/vue@3.2.36/dist/vue.runtime.esm-browser.js`,
'vue/server-renderer': `${VITE_CDN_DOMAIN}/@vue/server-renderer@3.2.36/dist/server-renderer.esm-browser.js`,
'vue-i18n': `${VITE_CDN_DOMAIN}/vue-i18n@9.2.0-beta.36/dist/vue-i18n.esm-browser.js`,
'vue-router': `${VITE_CDN_DOMAIN}/vue-router@4.0.16/dist/vue-router.esm-browser.js`,
'@vue/devtools-api': `${VITE_CDN_DOMAIN}/@vue/devtools-api@6.5.1/lib/esm/index.js`,
'@vueuse/core': `${VITE_CDN_DOMAIN}/@vueuse/core@9.6.0/index.mjs`,
'@vueuse/shared': `${VITE_CDN_DOMAIN}/@vueuse/shared@9.6.0/index.mjs`,
axios: `${VITE_CDN_DOMAIN}/axios@1.0.0-alpha.1/dist/esm/axios.js`,
'axios-mock-adapter': `${VITE_CDN_DOMAIN}/axios-mock-adapter@1.21.1/dist/axios-mock-adapter.js`,
'@opentiny/tiny-engine-webcomponent-core': `${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-webcomponent-core@1/dist/tiny-engine-webcomponent-core.es.js`,
'@opentiny/tiny-engine-i18n-host': `${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-i18n-host@1/dist/tiny-engine-i18n-host.es.js`,
'@opentiny/tiny-engine-builtin-component': `${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-builtin-component@1/dist/index.js`,
'vue-demi': `${VITE_CDN_DOMAIN}/vue-demi@0.13.11/lib/index.mjs`,
pinia: `${VITE_CDN_DOMAIN}/pinia@2.0.22/dist/pinia.esm-browser.js`,
...tinyVue3Imports,
...getSearchParams().scripts
}

View File

@ -1,23 +0,0 @@
{
"imports": {
"vue": "${VITE_CDN_DOMAIN}/vue@3.4.23/dist/vue.runtime.esm-browser.js",
"vue/server-renderer": "${VITE_CDN_DOMAIN}/@vue/server-renderer@3.4.23/dist/server-renderer.esm-browser.js",
"vue-i18n": "${VITE_CDN_DOMAIN}/vue-i18n@9.2.0-beta.36/dist/vue-i18n.esm-browser.js",
"vue-router": "${VITE_CDN_DOMAIN}/vue-router@4.0.16/dist/vue-router.esm-browser.js",
"@vue/devtools-api": "${VITE_CDN_DOMAIN}/@vue/devtools-api@6.5.1/lib/esm/index.js",
"@vueuse/core": "${VITE_CDN_DOMAIN}/@vueuse/core@9.6.0/index.mjs",
"@vueuse/shared": "${VITE_CDN_DOMAIN}/@vueuse/shared@9.6.0/index.mjs",
"axios": "${VITE_CDN_DOMAIN}/axios@1.0.0/dist/esm/axios.js",
"axios-mock-adapter": "${VITE_CDN_DOMAIN}/axios-mock-adapter@1.21.1/dist/axios-mock-adapter.js",
"@opentiny/tiny-engine-webcomponent-core": "${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-webcomponent-core@1/dist/tiny-engine-webcomponent-core.es.js",
"@opentiny/tiny-engine-i18n-host": "${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-i18n-host@1/dist/lowcode-design-i18n-host.es.js",
"@opentiny/tiny-engine-builtin-component": "${VITE_CDN_DOMAIN}/@opentiny/tiny-engine-builtin-component@1/dist/index.js",
"vue-demi": "${VITE_CDN_DOMAIN}/vue-demi@0.13.11/lib/index.mjs",
"pinia": "${VITE_CDN_DOMAIN}/pinia@2.0.22/dist/pinia.esm-browser.js",
"@opentiny/vue": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue.mjs",
"@opentiny/vue-icon": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-icon.mjs",
"@opentiny/vue-common": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-common.mjs",
"@opentiny/vue-locale": "${VITE_CDN_DOMAIN}/@opentiny/vue@${opentinyVueVersion}/runtime/tiny-vue-locale.mjs",
"@opentiny/vue-renderless/": "${VITE_CDN_DOMAIN}/@opentiny/vue-renderless@${opentinyVueVersion}/"
}
}

View File

@ -40,69 +40,4 @@ srcFiles['locales.js'] = localesJS
srcFiles['stores.js'] = storesJS
srcFiles['storesHelper.js'] = storesHelperJS
export const genPreviewTemplate = () => {
return [
{
fileName: 'App.vue',
path: '',
fileContent: appVue
},
{
fileName: 'constant.js',
path: '',
fileContent: constantJS
},
{
fileName: 'app.js',
path: '',
fileContent: appJS.replace(/VITE_CDN_DOMAIN/g, import.meta.env.VITE_CDN_DOMAIN)
},
{
fileName: 'injectGlobal.js',
path: '',
fileContent: injectGlobalJS
},
{
fileName: 'lowcode.js',
path: '',
fileContent: lowcodeJS
},
{
fileName: 'dataSourceMap.js',
path: '',
fileContent: dataSourceMapJS
},
{
fileName: 'dataSource.js',
path: '',
fileContent: dataSourceJS
},
{
fileName: 'utils.js',
path: '',
fileContent: utilsJS
},
{
fileName: 'bridge.js',
path: '',
fileContent: bridgeJS
},
{
fileName: 'locales.js',
path: '',
fileContent: localesJS
},
{
fileName: 'stores.js',
path: '',
fileContent: storesJS
},
{
fileName: 'storesHelper.js',
path: '',
fileContent: storesHelperJS
}
]
}
export default srcFiles

View File

@ -16,5 +16,5 @@ function addCss(href) {
link.setAttribute('href', href)
document.head.appendChild(link)
}
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme@3.14/index.css')
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme-mobile@3.14/index.css')
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme@3.11/index.css')
addCss('VITE_CDN_DOMAIN/@opentiny/vue-theme-mobile@3.11/index.css')

View File

@ -12,7 +12,6 @@ import lowcodeConfig from './config/lowcode.config'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { importmapPlugin } from './scripts/externalDeps'
import visualizer from 'rollup-plugin-visualizer'
import { getBaseUrlFromCli, copyBundleDeps, copyPreviewImportMap, copyLocalImportMap } from './scripts/localCdnFile'
const origin = 'http://localhost:9090/'
@ -94,6 +93,9 @@ const config = {
]
}
},
define: {
'process.env': {}
},
build: {
commonjsOptions: {
transformMixedEsModules: true,
@ -131,8 +133,8 @@ const config = {
const importMapVersions = {
prettier: '2.7.1',
vue: '3.4.23',
tinyVue: '~3.14'
vue: '3',
tinyVue: '~3.11'
}
const devAlias = {
@ -198,16 +200,14 @@ const commonAlias = {
}
export default defineConfig(({ command, mode }) => {
const { VITE_CDN_DOMAIN, VITE_LOCAL_IMPORT_MAPS, VITE_LOCAL_BUNDLE_DEPS } = loadEnv(mode, process.cwd(), '')
const isLocalImportMap = VITE_LOCAL_IMPORT_MAPS === 'true' // true公共依赖库使用本地打包文件false公共依赖库使用公共CDN
const isCopyBundleDeps = VITE_LOCAL_BUNDLE_DEPS === 'true' // true bundle里的cdn依赖处理成本地依赖 false 不处理
const { VITE_CDN_DOMAIN } = loadEnv(mode, process.cwd(), '')
const monacoPublicPath = {
local: 'editor/monaco-workers',
alpha: 'https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/monaco-assets',
prod: 'https://tinyengine-assets.obs.cn-north-4.myhuaweicloud.com/files/monaco-assets'
}
const monacoPublicPath = 'editor/monaco-workers'
const monacoEditorPluginInstance = monacoEditorPlugin({
publicPath: monacoPublicPath,
forceBuildCDN: true,
customDistPath: (_root, outDir, _base) => path.join(outDir, monacoPublicPath)
})
let monacoEditorPluginInstance = monacoEditorPlugin({ publicPath: monacoPublicPath.local })
const htmlPlugin = (mode) => {
const upgradeHttpsMetaTags = []
const includeHtmls = ['index.html', 'preview.html', 'previewApp.html']
@ -244,7 +244,7 @@ export default defineConfig(({ command, mode }) => {
}
config.resolve.alias = [
...(isLocalImportMap ? [] : [devVueAlias]),
devVueAlias,
...Object.entries({ ...commonAlias, ...devAlias }).map(([find, replacement]) => ({
find,
replacement
@ -254,6 +254,8 @@ export default defineConfig(({ command, mode }) => {
// command === 'build'
config.resolve.alias = { ...commonAlias, ...prodAlias }
monacoEditorPluginInstance = monacoEditorPlugin({ publicPath: monacoPublicPath[mode] })
if (mode === 'prod') {
config.build.minify = true
config.build.sourcemap = false
@ -277,49 +279,14 @@ export default defineConfig(({ command, mode }) => {
'@opentiny/vue-common': `${VITE_CDN_DOMAIN}/@opentiny/vue@${importMapVersions.tinyVue}/runtime/tiny-vue-common.mjs`,
'@opentiny/vue-locale': `${VITE_CDN_DOMAIN}/@opentiny/vue@${importMapVersions.tinyVue}/runtime/tiny-vue-locale.mjs`,
'@opentiny/vue-design-smb': `${VITE_CDN_DOMAIN}/@opentiny/vue-design-smb@${importMapVersions.tinyVue}/index.js`,
'@opentiny/vue-theme/theme-tool': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme-tool.js`,
'@opentiny/vue-theme/theme': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme/index.js`
'@opentiny/vue-theme/theme-tool': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme-tool`,
'@opentiny/vue-theme/theme': `${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/theme`
}
}
const importMapStyles = [`${VITE_CDN_DOMAIN}/@opentiny/vue-theme@${importMapVersions.tinyVue}/index.css`]
config.plugins.push(
monacoEditorPluginInstance,
htmlPlugin(mode),
isLocalImportMap
? copyLocalImportMap({
importMap: importmap,
styleUrls: importMapStyles,
originCdnPrefix: VITE_CDN_DOMAIN,
base: getBaseUrlFromCli(config.base),
packageCopy: [
// 这两个包的js存在相对路径引用不能单独拷贝一个文件需要整个包拷贝
'@opentiny/vue-theme/theme-tool',
'@opentiny/vue-theme/theme'
]
})
: importmapPlugin(importmap, importMapStyles),
isCopyBundleDeps
? copyBundleDeps({
bundleFile: 'public/mock/bundle.json',
targetBundleFile: 'mock/bundle.json',
originCdnPrefix: VITE_CDN_DOMAIN, // mock 中bundle的域名当前和环境的VITE_CDN_DOMAIN一致
base: getBaseUrlFromCli(config.base)
}).plugin(command === 'serve')
: [],
isLocalImportMap
? copyPreviewImportMap({
importMapJson: './src/preview/src/preview/importMap.json',
targetImportMapJson: 'preview-import-map-static/preview-importmap.json',
originCdnPrefix: VITE_CDN_DOMAIN,
base: getBaseUrlFromCli(config.base),
packageCopyLib: [
// 以下的js存在相对路径引用不能单独拷贝一个文件需要整个包拷贝
'@vue/devtools-api'
]
})
: []
)
config.plugins.push(monacoEditorPluginInstance, htmlPlugin(mode), importmapPlugin(importmap, importMapStyles))
return config
})

View File

@ -25,8 +25,9 @@
"homepage": "https://opentiny.design/tiny-engine",
"dependencies": {
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@vueuse/core": "^9.6.0",
"axios": "~0.28.0",
"axios": "^0.27.2",
"axios-mock-adapter": "^1.21.5"
},
"devDependencies": {
@ -35,7 +36,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"vue": "^3.4.15"
}
}

View File

@ -0,0 +1,20 @@
{
"name": "material-generator",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"gen-antdv": "node ./scripts/antdvToMaterials.mjs",
"gen-vant": "node ./scripts/vantToMaterials.mjs"
},
"devDependencies": {
"ant-design-vue": "^4.1.2",
"vant": "^4.8.10",
"vite": "^5.2.0"
},
"dependencies": {
"axios": "^0.27.2",
"markdown-it": "^14.0.0",
"naive-ui": "2.38.1"
}
}

View File

@ -0,0 +1,46 @@
import GenerateMaterial from './common/GenerateMaterial-old.mjs';
function checkIsStandardTable(params, standard) {
const { col, content } = params
for (let i = 0; i < col; i++) {
const item = standard[i];
const ct = content[i]
if (Array.isArray(item)) {
if (!item.includes(ct)) {
return false
}
} else {
if (item !== ct && !!item !== !!ct) {
return false
}
}
}
return true
}
async function main() {
const gm = new GenerateMaterial({
owner: "vueComponent",
repo: "ant-design-vue",
path: "components",
host: "https://api.github.com",
version: "4.1.2",
projectName: "ant-design-vue",
framework: "Vue",
script: "https://npm.onmicrosoft.cn/ant-design-vue@4.1.2/dist/antd.esm.js",
css: "https://npm.onmicrosoft.cn/ant-design-vue@4.1.2/dist/reset.css",
exportNamePrefix: "A",
// github api token
token: null,
isAttributeTable(params) {
const standard = [['参数', '属性', '成员', 'Property'], ['说明', 'Description'], ['类型', 'Type'], ['默认值', 'Default'], ['版本', 'Version', '']]
return checkIsStandardTable(params, standard)
},
isEvent(params) {
const standard = ['事件名称', '说明', '回调参数', '版本', undefined]
return checkIsStandardTable(params, standard)
},
})
await gm.start()
}
main()

View File

@ -0,0 +1,609 @@
import axios from 'axios'
import markdownit from 'markdown-it'
import fs from 'fs'
import LocalCache from './cache.mjs';
function returnFunc(param) {
if (typeof param === "function") {
return param
}
if (Array.isArray(param)) {
return param[0]
}
if (Array.isArray(param.type)) {
return param.type[0]
}
return param.type
}
const DEFAULT_DESCRIPTION = "<empty comment>"
function generateType(param) {
let type = ""
let defaultValue = undefined
let func = returnFunc(param)
if (typeof func === "function") {
type = func.name
} else { }
if ("defalut" in param) {
defaultValue = param.defalut
}
switch (type) {
case "Boolean":
defaultValue = !!defaultValue
break;
default:
break;
}
return { type, defaultValue }
}
class GenerateMaterial {
_owner = undefined
_repo = undefined
_path = undefined
_host = undefined
_version = undefined
_projectName = undefined
_framework = undefined
_requiredComponents = []
_unnecessary = []
_temporary_omit = []
_script = undefined
_css = undefined
_exportNamePrefix = undefined
_md = markdownit()
_Module = undefined
_middlewares = [];
_lc = new LocalCache()
_components = []
_blocks = []
_snippets = []
_cached = []
_default_actions = ["copy", "remove", "insert", "updateAttr", "bindEevent", "createBlock"]
_need_cache_key = ['_components', '_blocks', '_snippets', '_framework', '_cached']
_mdParams = []
get url() {
return `${this._host}/repos/${this._owner}/${this._repo}/contents/${this._path}`
}
constructor(params) {
this._checkParams(params)
this._owner = params.owner
this._repo = params.repo
this._path = params.path
this._host = params.host
this._version = params.version
this._projectName = params.projectName
this._framework = params.framework
this._script = params.script
this._css = params.css
this._exportNamePrefix = params.exportNamePrefix
this._Module = params.Module
this._init()
}
async start() {
// const files = await this._requestESMFile(this._script)
const CurrentModule = this._Module
const moduleKeys = Object.keys(CurrentModule)
for (const key of moduleKeys) {
const module = CurrentModule[key]
if (!module.name) {
console.log(`${key} 不是vue组件`)
continue
}
this._generateMaterial(module)
}
// 中间件处理
await this._execute()
// 生成数据
const jsonData = { data: this.data }
this._generateJSONFile(jsonData, "bundle.json", ".")
}
_init() {
}
_to_save() {
const caches = this._need_cache_key
for (let i = 0; i < caches.length; i++) {
const key = caches[i];
this._lc.set(key, this[key])
}
}
get data() {
return {
framework: this._framework,
materials: {
components: this._components,
blocks: this._blocks,
snippets: this._snippets,
},
}
}
async _requestGithub(url) {
try {
const res = await axios({
url,
headers: { 'Authorization': "Bearer " + this._token }
})
return res?.data || {}
} catch (error) {
console.log(error)
}
}
async _requestESMFile(url) {
try {
const res = await axios({
url
})
return res?.data || {}
} catch (error) {
console.log(error)
}
}
get baseNpm() {
return {
package: this._repo,
version: this._version,
script: this._script,
css: this._css,
dependencies: null,
}
}
_generateProperties(props) {
const properties = {
label: {
zh_CN: "基础信息"
},
description: {
zh_CN: "基础信息"
},
collapse: {
number: 6,
text: {
zh_CN: "显示更多"
}
},
content: []
}
let slots = {}
const propsKeys = Object.keys(props)
if (propsKeys.length) {
// const res = this._chunk(attrs.content, attrs.col)
for (const key of propsKeys) {
const propsValue = props[key]
if (!propsValue) {
continue
}
if (!key.startsWith("on")) {
if (propsValue === "") {
console.log()
}
const typeObject = generateType(propsValue)
const params = {
property: key,
description: DEFAULT_DESCRIPTION,
type: String(typeObject.type).toLowerCase(),
defaultValue: typeObject.defaultValue
}
const content_item = this._createPropertieContent(params)
properties.content.push(content_item)
}
}
}
return { properties, slots }
}
_generateEvents(props) {
const events = {}
const eventList = Object.keys(props).filter((keyStr) => keyStr.startsWith("on"))
if (eventList.length) {
for (const key of eventList) {
const content_item = this._createEventContent(key)
Object.assign(events, content_item)
}
}
return events
}
_generateComponent(params) {
let { heading = {}, name, props = {} } = params
const description = DEFAULT_DESCRIPTION
const npm = {
...this.baseNpm,
exportName: name
}
const { properties, slots } = this._generateProperties(props)
const configure = {
loop: true,
condition: true,
styles: true,
isContainer: false,
isModal: false,
isPopper: false,
nestingRule: {
childWhitelist: "",
parentWhitelist: "",
descendantBlacklist: "",
ancestorWhitelist: ""
},
isNullNode: false,
isLayout: false,
rootSelector: "",
shortcuts: {
properties: properties.content?.map(({ property }) => property) || []
},
contextMenu: {
actions: this._default_actions,
disable: []
},
invalidity: [""],
clickCapture: true,
framework: this._framework
}
const events = this._generateEvents(props)
const shortcuts = {}
const contentMenu = {}
const schema = { properties: [properties], events, shortcuts, slots, contentMenu }
const component = {
id: this._components.length + 1,
version: this._version,
name: { "zh_CN": heading?.subtitle },
component: npm.exportName,
icon: name,
description,
doc_url: "",
screenshot: "",
tags: "",
keywords: "",
dev_mode: "proCode",
npm,
group: this._repo,
category: this._projectName,
configure,
schema,
}
this._components.push(component)
}
_generateSnippets(params) {
const { name } = params
let snippet = this._snippets.find(({ group }) => group === this._repo)
if (!snippet) {
snippet = { group: this._repo, children: [] }
this._snippets.push(snippet)
}
const nowComponent = {
name: {
zh_CN: name
},
icon: name,
screenshot: "",
snippetName: name,
schema: {}
}
snippet.children.push(nowComponent)
}
async _generateMaterial(params) {
this._generateComponent(params)
this._generateSnippets(params)
}
_createPropertieContent(params) {
let { property, description, type, defaultValue } = params;
const widgetContent = this._createWidget({ type, defaultValue })
return {
property,
label: {
text: {
zh_CN: property
}
},
description: {
zh_CN: description,
},
required: true,
readOnly: false,
disabled: false,
cols: 12,
labelPosition: "top",
type: widgetContent.type,
defaultValue: widgetContent.defaultValue,
widget: widgetContent.widget
}
}
_createEventContent(name) {
return {
[name]: {
label: {
zh_CN: DEFAULT_DESCRIPTION
},
description: {
zh_CN: DEFAULT_DESCRIPTION
},
type: "event"
}
}
}
_createWidget(params) {
let { type, defaultValue } = params
const result = {}
if (type.includes('|')) {
const types = type.split('|').map((str) => str.trim())
const isString = types.every((str) => str.startsWith('`') && str.endsWith('`'))
if (isString) {
type = 'string'
result.widget = {
component: 'MetaSelect',
props: {
options: types.map((item) => (
{
label: item.slice(1, item.length - 1),
value: item.slice(1, item.length - 1)
}
))
}
}
}
const isBoolean = types.some((str) => str === 'boolean')
if (isBoolean) {
result.widget = { component: 'MetaSwitch', props: {} }
result.defaultValue = String(defaultValue).includes("false") ? false : true
}
}
result.type = type
switch (type) {
case 'boolean':
result.widget = { component: 'MetaSwitch', props: {} }
result.defaultValue = String(defaultValue).includes("false") ? false : true
break;
case '':
result.widget = { component: 'MetaSwitch', props: {} }
result.defaultValue = String(defaultValue).includes("false") ? false : true
break;
case 'number':
!result.widget && (result.widget = { component: 'MetaNumber', props: {} })
result.defaultValue = defaultValue
break;
case 'array':
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
result.defaultValue = defaultValue
break;
case 'function':
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
result.defaultValue = defaultValue
break;
case 'date':
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
result.defaultValue = defaultValue
break;
case 'object':
!result.widget && (result.widget = { component: 'MetaCodeEditor', props: {} })
result.defaultValue = defaultValue
break;
case 'string':
!result.widget && (result.widget = { component: 'MetaInput', props: {} })
result.defaultValue = defaultValue
break;
case 'v-slot':
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
result.defaultValue = defaultValue
break;
case 'slot':
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
result.defaultValue = defaultValue
break;
default:
break;
}
if (!result.widget) {
console.log()
}
return result
}
_chunk(array, size = 1) {
size = Math.max(Number(size), 0)
const length = array == null ? 0 : array.length
if (!length || size < 1) {
return []
}
let index = 0
let resIndex = 0
const result = new Array(Math.ceil(length / size))
while (index < length) {
result[resIndex++] = array.slice(index, (index += size))
}
return result
}
_isAttributeTable(params) {
throw new Error("需要传入 isAttributeTable")
}
_isEvent(params) {
throw new Error("需要传入 isEvent")
}
_generateJSONFile(jsonData, fileName = 'output.json', filePath = './') {
// 将 JSON 数据转换为字符串
const jsonString = JSON.stringify(jsonData, null, 2);
// 拼接文件路径
const fullFilePath = filePath.endsWith('/') ? filePath + fileName : filePath + '/' + fileName;
// 写入文件
fs.writeFile(fullFilePath, jsonString, 'utf8', (err) => {
if (err) {
console.error('写入文件时出错:', err);
} else {
console.log(`JSON 数据已成功写入到文件 ${fullFilePath}`);
}
});
}
_atob_cn(base64String) {
const bytes = Uint8Array.from(atob(base64String), c => c.charCodeAt(0))
const decodedString = new TextDecoder('utf-8').decode(bytes)
return decodedString
}
_getBaseInfo(tokens) {
let description = undefined
let heading = undefined
// let name = undefined
// 寻找表格内容
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (!description && token.type === "paragraph_open") {
description = tokens[i + 1].content
}
if (!heading && token.type === "heading_open") {
const content = tokens[i + 1].content
const heads = content.split("\n")
heading = {}
heads.forEach((item) => {
const kv = item.split(": ")
heading[kv[0]] = kv[1]
})
}
}
return { description, heading }
}
_getMdTableInfo(tokens) {
let tableContent = null
const tableContents = []
const titles = this._parseTitle(tokens)
let col = 0 // 列数
// 寻找表格内容
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (token.type === 'table_open') {
col = 0
tableContent = {
content: [],
row: 0,
col,
parents: [],
parentMap: {}
}
let endIndex = undefined
for (let t = 0; t < titles.length; t++) {
const { index } = titles[t];
if (index > i && endIndex === undefined) {
endIndex = t
break;
}
}
const newTitles = titles.slice(0, endIndex).reverse()
let first_title_index = (newTitles[0] || {}).index
if (first_title_index) {
tableContent.parents.push(newTitles[0])
}
for (let j = 0; j < newTitles.length; j++) {
const title = newTitles[j];
if (title.index > first_title_index) {
tableContent.parents.push(title)
first_title_index = title.index
}
}
} else if (token.type === 'thead_close') {
tableContent.col = col
} else if (token.type === 'table_close') {
tableContent.row = tableContent.content.length / tableContent.col
tableContents.push(tableContent)
tableContent = null
} else if (tableContent && token.type === 'inline') {
const content = token.content
col++
tableContent.content.push(content)
}
}
return { tables: tableContents }
}
_parseTitle(tokens) {
const titles = []
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (token.type === 'heading_open' && token.markup && token.markup.includes("#")) {
const content = tokens[i + 1].content
const title = {
level: String(token.markup).length,
content,
index: i
}
titles.push(title)
}
}
return titles
}
_parseMd(mdStrBase64) {
const mdStr = this._atob_cn(mdStrBase64)
let result = undefined
try {
result = this._md.parse(mdStr, {})
} catch (error) {
console.log(mdStr)
throw new Error(error)
}
const tablesInfo = this._getMdTableInfo(result)
const info = this._getBaseInfo(result)
return { ...tablesInfo, ...info }
}
_checkParams(params) {
if (!params) {
throw new Error('Initialization requires passing in parameters');
}
const requiredProperties = [
'version',
'Module',
'framework',
'script',
'css',
'exportNamePrefix',
]
for (let prop of requiredProperties) {
if (!(prop in params)) {
throw new Error(`Property '${prop}' is missing from the params.`);
}
}
}
add(middleware) {
this._middlewares.push(middleware);
}
// 执行中间件
async _execute() {
let components = this._components;
let blocks = this._blocks;
let snippets = this._snippets;
let result = { components, blocks, snippets }
for (const fn of this._middlewares) {
result = await fn(result); // 调用中间件函数,传入参数 A并更新结果
}
this._components = result.components// 返回最终结果
this._snippets = result.snippets// 返回最终结果
this._blocks = result.blocks// 返回最终结果
}
}
export default GenerateMaterial

View File

@ -0,0 +1,607 @@
import axios from 'axios'
import markdownit from 'markdown-it'
import fs from 'fs'
import LocalCache from './cache.mjs';
function checkIsStandardTable(params, standard) {
const { col, content } = params
for (let i = 0; i < col; i++) {
const item = standard[i];
const ct = content[i]
if (Array.isArray(item)) {
if (!item.includes(ct)) {
return false
}
} else {
if (item !== ct && !!item !== !!ct) {
return false
}
}
}
return true
}
function capitalizeFirstLetter(word) {
if (typeof word !== 'string' || word.length === 0) {
throw new Error('Input must be a non-empty string');
}
return word.charAt(0).toUpperCase() + word.slice(1);
}
class GenerateMaterial {
_owner = undefined
_repo = undefined
_path = undefined
_host = undefined
_version = undefined
_projectName = undefined
_framework = undefined
_requiredComponents = []
_unnecessary = []
_temporary_omit = []
_script = undefined
_css = undefined
_exportNamePrefix = undefined
_md = markdownit()
_lc = new LocalCache()
_components = []
_blocks = []
_snippets = []
_cached = []
_default_actions = ["copy", "remove", "insert", "updateAttr", "bindEevent", "createBlock"]
_need_cache_key = ['_components', '_blocks', '_snippets', '_framework', '_cached']
_mdParams = []
get url() {
return `${this._host}/repos/${this._owner}/${this._repo}/contents/${this._path}`
}
constructor(params) {
this._checkParams(params)
this._owner = params.owner
this._repo = params.repo
this._path = params.path
this._host = params.host
this._version = params.version
this._projectName = params.projectName
this._framework = params.framework
this._script = params.script
this._css = params.css
this._exportNamePrefix = params.exportNamePrefix
if (!params.token) {
throw new Error("请在系统中内置环境变量 GITHUB_TOKEN_PAT")
}
this._token = params.token
if (params.temporary_omit) {
this._temporary_omit = params.temporary_omit
}
if (params.isAttributeTable) {
this._isAttributeTable = params.isAttributeTable
}
if (params.isEvent) {
this._isEvent = params.isEvent
}
this._init()
}
async start() {
try {
const files = await this._requestGithub(this.url)
if (files && Array.isArray(files)) {
files.forEach((item) => {
const { name = "", type = "" } = item
if (
!name.startsWith("_")
&& !name.startsWith("vc-")
&& !this._unnecessary.includes(name)
&& type === "dir"
) {
if (this._temporary_omit.length) {
if (this._temporary_omit.includes(name)) {
this._requiredComponents.push(item)
}
} else {
this._requiredComponents.push(item)
}
}
})
this._requiredComponents = this._requiredComponents.filter(({ name }) => !this._cached.includes(name))
console.log(`files lenght: ${this._requiredComponents.length}`)
if (this._requiredComponents.length) {
for (const item of this._requiredComponents) {
const { git_url } = item
const data = await this._requestGithub(git_url)
if (data && Array.isArray(data.tree)) {
const mdObj = data.tree.find(({ path }) => path === "index.zh-CN.md")
console.log(item.name, mdObj)
if (!mdObj) {
console.log(`${item.name} 不存在文档已跳过`)
continue
}
const { content } = await this._requestGithub(mdObj.url)
let mdParam = this._parseMd(content)
this._mdParams.push(mdParam)
await this._generateMaterial(mdParam)
this._cached.push(item.name)
}
}
this._generateJSONFile({ data: this.data }, "bundle.json", "./packages/design-core/public/mock")
this._generateJSONFile({ data: this._mdParams }, "mdParams.json", "./packages/design-core/public/mock")
}
}
} catch (error) {
this._to_save()
throw new Error(error)
}
}
_init() {
const caches = this._need_cache_key
for (let i = 0; i < caches.length; i++) {
const key = caches[i];
const saveValue = this._lc.get(key)
if (saveValue) {
this[key] = saveValue
}
}
}
_to_save() {
const caches = this._need_cache_key
for (let i = 0; i < caches.length; i++) {
const key = caches[i];
this._lc.set(key, this[key])
}
}
get data() {
return {
framework: this._framework,
materials: {
components: this._components,
blocks: this._blocks,
snippets: this._snippets,
},
}
}
async _requestGithub(url) {
try {
const res = await axios({
url,
headers: { 'Authorization': "Bearer " + this._token }
})
return res?.data || {}
} catch (error) {
console.log(error)
}
}
get baseNpm() {
return {
package: this._repo,
version: this._version,
script: this._script,
css: this._css,
dependencies: null,
}
}
_generateProperties(tables) {
const properties = {
label: {
zh_CN: "基础信息"
},
description: {
zh_CN: "基础信息"
},
collapse: {
number: 6,
text: {
zh_CN: "显示更多"
}
},
}
const slots = {}
const attrs = tables.find(this._isAttributeTable)
if (attrs) {
const res = this._chunk(attrs.content, attrs.col)
for (let i = 1; i < res.length; i++) {
const rows = res[i];
if (!Array.isArray(properties.content)) {
properties.content = []
}
const params = {
property: rows[0],
description: rows[1],
type: rows[2],
defaultValue: rows[3]
}
if (params.property.includes('(')) {
let newproperty = params.property.split("(").shift()
params.property = newproperty.trim()
}
const content_item = this._createPropertieContent(params)
const types = params.type.split('|').map((str) => str.trim())
const isSlot = types.some((item) => item.includes('slot'))
if (isSlot) {
slots[params.property] = {
label: { zh_CN: params.property },
description: { zh_CN: params.description },
}
}
properties.content.push(content_item)
}
}
return { properties, slots }
}
_generateEvents(tables) {
const events = {}
const event = tables.find(this._isEvent)
if (event) {
const res = this._chunk(event.content, event.col)
for (let i = 1; i < res.length; i++) {
const rows = res[i];
const content_item = this._createEventContent(rows)
Object.assign(events, content_item)
}
}
return events
}
_generateComponent(params) {
const { tables, heading = {}, description } = params
const npm = {
...this.baseNpm,
exportName: `${this._exportNamePrefix}${heading?.title}`
}
const { properties, slots } = this._generateProperties(tables)
const configure = {
loop: true,
condition: true,
styles: true,
isContainer: false,
isModal: false,
isPopper: false,
nestingRule: {
childWhitelist: "",
parentWhitelist: "",
descendantBlacklist: "",
ancestorWhitelist: ""
},
isNullNode: false,
isLayout: false,
rootSelector: "",
shortcuts: {
properties: properties.content?.map(({ property }) => property) || []
},
contextMenu: {
actions: this._default_actions,
disable: []
},
invalidity: [""],
clickCapture: true,
framework: this._framework
}
const events = this._generateEvents(tables)
const shortcuts = {}
const contentMenu = {}
const schema = { properties: [properties], events, shortcuts, slots, contentMenu }
const component = {
id: this._components.length + 1,
version: this._version,
name: { "zh_CN": heading?.subtitle },
component: npm.exportName,
icon: heading?.title,
description,
doc_url: "",
screenshot: "",
tags: "",
keywords: "",
dev_mode: "proCode",
npm,
group: heading.type,
category: this._projectName,
configure,
schema,
}
this._components.push(component)
}
_generateSnippets(params) {
const { heading = {}, description } = params
let snippet = this._snippets.find(({ group }) => group === heading.type)
if (!snippet) {
snippet = { group: heading.type, children: [] }
this._snippets.push(snippet)
}
const nowComponent = {
name: {
zh_CN: heading?.subtitle
},
icon: heading?.title,
screenshot: "",
snippetName: `${this._exportNamePrefix}${heading?.title}`,
schema: {}
}
snippet.children.push(nowComponent)
}
async _generateMaterial(params) {
this._generateComponent(params)
this._generateSnippets(params)
}
_createPropertieContent(params) {
let { property, description, type, defaultValue } = params;
const widgetContent = this._createWidget({ type, defaultValue })
return {
property,
label: {
text: {
zh_CN: property
}
},
description: {
zh_CN: description,
},
required: true,
readOnly: false,
disabled: false,
cols: 12,
labelPosition: "top",
type: widgetContent.type,
defaultValue: widgetContent.defaultValue,
widget: widgetContent.widget
}
}
_createEventContent(params) {
let [property, description] = params;
if (property.includes("(")) {
property = property.split("(").shift()
property = property.trim()
}
return {
[`on${capitalizeFirstLetter(property)}`]: {
label: {
zh_CN: description
},
description: {
zh_CN: description
}
}
}
}
_createWidget(params) {
let { type, defaultValue } = params
const result = {}
if (type.includes('|')) {
const types = type.split('|').map((str) => str.trim())
const isString = types.every((str) => str.startsWith('`') && str.endsWith('`'))
if (isString) {
type = 'string'
result.widget = {
component: 'MetaSelect',
props: {
options: types.map((item) => (
{
label: item.slice(1, item.length - 1),
value: item.slice(1, item.length - 1)
}
))
}
}
}
const isBoolean = types.some((str) => str === 'boolean')
if (isBoolean) {
result.widget = { component: 'MetaSwitch', props: {} }
result.defaultValue = String(defaultValue).includes("false") ? false : true
}
}
result.type = type
switch (type) {
case 'boolean':
result.widget = { component: 'MetaSwitch', props: {} }
result.defaultValue = String(defaultValue).includes("false") ? false : true
break;
case 'string':
!result.widget && (result.widget = { component: 'MetaInput', props: {} })
result.defaultValue = defaultValue
break;
case 'v-slot':
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
result.defaultValue = defaultValue
break;
case 'slot':
!result.widget && (result.widget = { component: 'MetaJsSlot', props: { slots: ['default'] } })
result.defaultValue = defaultValue
break;
default:
break;
}
return result
}
_chunk(array, size = 1) {
size = Math.max(Number(size), 0)
const length = array == null ? 0 : array.length
if (!length || size < 1) {
return []
}
let index = 0
let resIndex = 0
const result = new Array(Math.ceil(length / size))
while (index < length) {
result[resIndex++] = array.slice(index, (index += size))
}
return result
}
_isAttributeTable(params) {
throw new Error("需要传入 isAttributeTable")
}
_isEvent(params) {
throw new Error("需要传入 isEvent")
}
_generateJSONFile(jsonData, fileName = 'output.json', filePath = './') {
// 将 JSON 数据转换为字符串
const jsonString = JSON.stringify(jsonData, null, 2);
// 拼接文件路径
const fullFilePath = filePath.endsWith('/') ? filePath + fileName : filePath + '/' + fileName;
// 写入文件
fs.writeFile(fullFilePath, jsonString, 'utf8', (err) => {
if (err) {
console.error('写入文件时出错:', err);
} else {
console.log(`JSON 数据已成功写入到文件 ${fullFilePath}`);
}
});
}
_atob_cn(base64String) {
const bytes = Uint8Array.from(atob(base64String), c => c.charCodeAt(0))
const decodedString = new TextDecoder('utf-8').decode(bytes)
return decodedString
}
_getBaseInfo(tokens) {
let description = undefined
let heading = undefined
// let name = undefined
// 寻找表格内容
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (!description && token.type === "paragraph_open") {
description = tokens[i + 1].content
}
if (!heading && token.type === "heading_open") {
const content = tokens[i + 1].content
const heads = content.split("\n")
heading = {}
heads.forEach((item) => {
const kv = item.split(": ")
heading[kv[0]] = kv[1]
})
}
}
return { description, heading }
}
_getMdTableInfo(tokens) {
let tableContent = null
const tableContents = []
const titles = this._parseTitle(tokens)
let col = 0 // 列数
// 寻找表格内容
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (token.type === 'table_open') {
col = 0
tableContent = {
content: [],
row: 0,
col,
parents: [],
parentMap: {}
}
let endIndex = undefined
for (let t = 0; t < titles.length; t++) {
const { index } = titles[t];
if (index > i && endIndex === undefined) {
endIndex = t
break;
}
}
const newTitles = titles.slice(0, endIndex).reverse()
let first_title_index = (newTitles[0] || {}).index
if (first_title_index) {
tableContent.parents.push(newTitles[0])
}
for (let j = 0; j < newTitles.length; j++) {
const title = newTitles[j];
if (title.index > first_title_index) {
tableContent.parents.push(title)
first_title_index = title.index
}
}
} else if (token.type === 'thead_close') {
tableContent.col = col
} else if (token.type === 'table_close') {
tableContent.row = tableContent.content.length / tableContent.col
tableContents.push(tableContent)
tableContent = null
} else if (tableContent && token.type === 'inline') {
const content = token.content
col++
tableContent.content.push(content)
}
}
return { tables: tableContents }
}
_parseTitle(tokens) {
const titles = []
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]
if (token.type === 'heading_open' && token.markup && token.markup.includes("#")) {
const content = tokens[i + 1].content
const title = {
level: String(token.markup).length,
content,
index: i
}
titles.push(title)
}
}
return titles
}
_parseMd(mdStrBase64) {
const mdStr = this._atob_cn(mdStrBase64)
let result = undefined
try {
result = this._md.parse(mdStr, {})
} catch (error) {
console.log(mdStr)
throw new Error(error)
}
const tablesInfo = this._getMdTableInfo(result)
const info = this._getBaseInfo(result)
return { ...tablesInfo, ...info }
}
_checkParams(params) {
if (!params) {
throw new Error('Initialization requires passing in parameters');
}
const requiredProperties = [
'owner',
'repo',
'path',
'host',
'version',
'projectName',
'framework',
'script',
'css',
'exportNamePrefix',
]
for (let prop of requiredProperties) {
if (!(prop in params)) {
throw new Error(`Property '${prop}' is missing from the params.`);
}
}
}
}
export default GenerateMaterial

View File

@ -0,0 +1,49 @@
import fs from 'fs';
import path from 'path';
class LocalCache {
constructor(cacheDir = './cache') {
this.cacheDir = path.resolve(cacheDir);
if (!fs.existsSync(this.cacheDir)) {
fs.mkdirSync(this.cacheDir);
}
}
get(key) {
const filePath = path.join(this.cacheDir, `${key}.json`);
try {
const data = fs.readFileSync(filePath, 'utf8');
return JSON.parse(data);
} catch (error) {
// 如果文件不存在或者解析出错返回null或者抛出自定义错误
return null;
}
}
set(key, value, ttl = 0) {
const filePath = path.join(this.cacheDir, `${key}.json`);
const data = JSON.stringify(value, null, 2);
fs.writeFileSync(filePath, data);
// 如果设置了ttl则使用setTimeout在ttl后删除缓存
if (ttl > 0) {
setTimeout(() => {
try {
fs.unlinkSync(filePath);
} catch (error) {
// 忽略删除失败的情况,可能文件已经被其他操作删除了
}
}, ttl);
}
}
}
export default LocalCache
// // 使用示例:
// const cache = new LocalCache();
// // 设置缓存
// cache.set('myKey', { myData: 'some data' }, 60000); // 缓存60秒
// // 获取缓存
// const data = cache.get('myKey');
// console.log(data); // 输出:{ myData: 'some data' }

View File

@ -0,0 +1,85 @@
import NaiveUI from 'naive-ui';
import GenerateMaterial from './common/GenerateMaterial-components.mjs';
async function main() {
const NewNaiveUIModule = {}
Object.keys(NaiveUI).forEach((key) => {
if (String(key).startsWith("N")) {
NewNaiveUIModule[key] = NaiveUI[key]
}
})
const genMaterial = new GenerateMaterial({
version: "2.38.1",
Module: NewNaiveUIModule,
framework: "Vue",
script: "https://unpkg.com/naive-ui@2.38.1/dist/index.mjs",
css: "",
exportNamePrefix: "N",
repo: "naive-ui",
})
genMaterial.add((data) => {
// data.snippets = data.snippets.map((item) => {
// item.children = item.children.map((children_item) => {
// const changeProps = {}
// if (String(children_item.icon).startsWith("van-")) {
// changeProps.icon = children_item.icon.slice(4)
// }
// return {
// ...children_item,
// ...changeProps
// }
// }).filter(({ snippetName }) => {
// return String(snippetName).startsWith("van-")
// })
// return item
// })
data.components = data.components.map((item) => {
return {
...item,
component: `N${item.component}`
}
})
return data;
})
// genMaterial.add((data) => {
// data.snippets = [
// {
// "group": "vant",
// "children": [
// {
// "name": {
// "zh_CN": "van-button"
// },
// "icon": "button",
// "screenshot": "",
// "snippetName": "VanButton",
// "schema": {
// "children": [
// {
// "componentName": "Template",
// "children": [
// {
// "componentName": "Text",
// "props": {
// "text": "van-button"
// }
// }
// ],
// "props": {
// "slot": {
// "name": "default"
// }
// }
// }
// ]
// }
// }
// ]
// }
// ]
// return data;
// })
genMaterial.start()
}
main()

View File

@ -0,0 +1,73 @@
import VantUI from 'vant';
import GenerateMaterial from './common/GenerateMaterial-components.mjs';
async function main() {
const genMaterial = new GenerateMaterial({
version: "4.8.10",
Module: VantUI,
framework: "Vue",
script: "https://unpkg.com/vant@4.8.10/es/index.mjs",
css: "https://unpkg.com/vant@4.8.10/lib/index.css",
exportNamePrefix: "Van",
repo: "vant",
})
genMaterial.add((data) => {
data.snippets = data.snippets.map((item) => {
item.children = item.children.map((children_item) => {
const changeProps = {}
if (String(children_item.icon).startsWith("van-")) {
changeProps.icon = children_item.icon.slice(4)
}
return {
...children_item,
...changeProps
}
}).filter(({ snippetName }) => {
return String(snippetName).startsWith("van-")
})
return item
})
return data;
})
genMaterial.add((data) => {
data.snippets = [
{
"group": "vant",
"children": [
{
"name": {
"zh_CN": "van-button"
},
"icon": "button",
"screenshot": "",
"snippetName": "VanButton",
"schema": {
"children": [
{
"componentName": "Template",
"children": [
{
"componentName": "Text",
"props": {
"text": "van-button"
}
}
],
"props": {
"slot": {
"name": "default"
}
}
}
]
}
}
]
}
]
return data;
})
genMaterial.start()
}
main()

View File

@ -0,0 +1,25 @@
/**
* Copyright (c) 2023 - present TinyEngine Authors.
* Copyright (c) 2023 - present Huawei Cloud Computing Technologies Co., Ltd.
*
* Use of this source code is governed by an MIT-style license.
*
* THE OPEN SOURCE SOFTWARE IN THIS PRODUCT IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT WITHOUT ANY WARRANTY, WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import { defineConfig } from 'vite'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, './src/index.js'),
formats: ['es']
},
sourcemap: true
}
})

View File

@ -29,6 +29,9 @@
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"html2canvas": "^1.4.1",
"pinyin-pro": "^3.13.0"
},
@ -38,9 +41,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-icon": "^3.14.0",
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15"
}
}

View File

@ -86,6 +86,7 @@
<tiny-dropdown trigger="click" @item-click="changeType">
<span>
<span>{{ state.sortTypeLabel }}</span>
<icon-chevron-down class="ml8"></icon-chevron-down>
</span>
<template #dropdown>
<tiny-dropdown-menu
@ -117,6 +118,7 @@ import {
import { PluginPanel, PluginBlockList, SvgButton, SaveNewBlock, LinkButton } from '@opentiny/tiny-engine-common'
import { useBlock, useModal, useLayout, useCanvas, useHelp } from '@opentiny/tiny-engine-controller'
import { constants } from '@opentiny/tiny-engine-utils'
import { iconChevronDown } from '@opentiny/vue-icon'
import BlockSetting, { openPanel, closePanel } from './BlockSetting.vue'
import BlockGroupArrange from './BlockGroupArrange.vue'
import CategoryEdit from './CategoryEdit.vue'
@ -186,7 +188,8 @@ export default {
CategoryEdit,
PluginBlockList,
TinyPopover,
TinyButton
TinyButton,
IconChevronDown: iconChevronDown()
},
setup() {

View File

@ -27,7 +27,9 @@
"@opentiny/tiny-engine-canvas": "workspace:*",
"@opentiny/tiny-engine-common": "workspace:*",
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*"
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.2.3",
@ -35,8 +37,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-icon": "^3.14.0",
"vue": "^3.4.15"
}
}

View File

@ -6,11 +6,7 @@
clearable
placeholder="请输入关键字搜索"
@update:modelValue="searchBridgeData"
>
<template #prefix>
<tiny-icon-search />
</template>
</tiny-search>
></tiny-search>
</div>
<div class="list">
<div
@ -23,7 +19,10 @@
<div class="item-label">{{ item.name }}</div>
<svg-icon class="setting-icon" name="setting" @click.stop="openEdit(item, index)"></svg-icon>
</div>
<search-empty :isShow="!list.length" />
<div v-if="!list.length" class="empty-wrap">
<svg-icon class="empty-icon" name="empty"></svg-icon>
<p class="empty-text">暂无数据</p>
</div>
</div>
</div>
</template>
@ -31,8 +30,6 @@
<script>
import { watchEffect, ref, reactive } from 'vue'
import { Search } from '@opentiny/vue'
import { iconSearch } from '@opentiny/vue-icon'
import { SearchEmpty } from '@opentiny/tiny-engine-common'
import {
RESOURCE_TYPE,
ACTION_TYPE,
@ -47,9 +44,7 @@ import {
export default {
components: {
TinySearch: Search,
TinyIconSearch: iconSearch(),
SearchEmpty
TinySearch: Search
},
props: {
name: {

View File

@ -11,8 +11,9 @@
*/
import { reactive } from 'vue'
import { useApp, useResource, useNotify, useCanvas } from '@opentiny/tiny-engine-controller'
import { useApp, useResource, useNotify } from '@opentiny/tiny-engine-controller'
import { isVsCodeEnv } from '@opentiny/tiny-engine-controller/js/environments'
import { updateUtils, deleteUtils } from '@opentiny/tiny-engine-canvas'
import {
fetchResourceList,
requestDeleteReSource,
@ -174,8 +175,6 @@ const generateBridgeUtil = (...args) => {
}
export const saveResource = (data, callback, emit) => {
const { updateUtils } = useCanvas().canvasApi.value
if (getActionType() === ACTION_TYPE.Edit) {
data.id = state.resource.id
requestUpdateReSource(data).then((result) => {
@ -219,8 +218,6 @@ export const saveResource = (data, callback, emit) => {
export const deleteData = (name, callback, emit) => {
const params = `app=${useApp().appInfoState.selectedId}&id=${state.resource?.id}`
const { deleteUtils } = useCanvas().canvasApi.value
requestDeleteReSource(params).then((data) => {
if (data) {
const index = useResource().resState[state.type].findIndex((item) => item.name === data.name)

View File

@ -29,6 +29,8 @@
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0",
"monaco-editor": "0.33.0"
},
"devDependencies": {
@ -37,8 +39,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-icon": "^3.14.0",
"vue": "^3.4.15"
}
}

View File

@ -15,7 +15,7 @@
</div>
</li>
</ul>
<search-empty :isShow="!filteredKey.length" />
<div v-if="!filteredKey.length" class="data-source-list-blank"><span>查询结果为空</span></div>
</div>
</template>
@ -25,15 +25,14 @@ import { useModal } from '@opentiny/tiny-engine-controller'
import { useResource } from '@opentiny/tiny-engine-controller'
import { findExpressionInAppSchema } from '@opentiny/tiny-engine-controller/js/ast'
import { constants } from '@opentiny/tiny-engine-utils'
import { SvgButton, SearchEmpty } from '@opentiny/tiny-engine-common'
import { SvgButton } from '@opentiny/tiny-engine-common'
import { STATE, OPTION_TYPE } from './js/constants'
const { COMPONENT_NAME } = constants
export default {
components: {
SvgButton,
SearchEmpty
SvgButton
},
props: {
modelValue: {

View File

@ -7,20 +7,16 @@
<close-icon @close="closePanel"></close-icon>
</div>
<tiny-tabs v-model="activeName" @click="tabClick" tab-style="button-card">
<tiny-tab-item :name="STATE.CURRENT_STATE" :title="isBlock ? '区块状态' : '页面状态'"></tiny-tab-item>
<tiny-tab-item v-if="isBlock" :name="STATE.CURRENT_STATE" title="区块状态"></tiny-tab-item>
<tiny-tab-item v-else :name="STATE.CURRENT_STATE" title="页面状态"></tiny-tab-item>
<tiny-tab-item :name="STATE.GLOBAL_STATE" title="应用状态"></tiny-tab-item>
</tiny-tabs>
<tiny-search
:modelValue="query"
class="left-filter"
placeholder="请输入搜索条件"
clearable
@update:modelValue="search"
>
<template #prefix>
<tiny-icon-search />
</template>
</tiny-search>
></tiny-search>
<div class="add-btn">
<tiny-button @click="openPanel(OPTION_TYPE.ADD)">{{
activeName === STATE.CURRENT_STATE ? '添加变量' : '添加全局变量'
@ -79,7 +75,7 @@ import {
useLayout,
useHelp
} from '@opentiny/tiny-engine-controller'
import { iconSearch } from '@opentiny/vue-icon'
import { setState, getSchema, deleteState, setGlobalState, getGlobalState } from '@opentiny/tiny-engine-canvas'
import { CloseIcon, LinkButton } from '@opentiny/tiny-engine-common'
import DataSourceList from './DataSourceList.vue'
import CreateVariable from './CreateVariable.vue'
@ -98,8 +94,7 @@ export default {
TinyTabs: Tabs,
TinyTabItem: TabItem,
CreateStore,
LinkButton,
TinyIconSearch: iconSearch()
LinkButton
},
setup(props, { emit }) {
const variableRef = ref(null)
@ -160,8 +155,6 @@ export default {
}
const add = (name, variable) => {
const { getSchema } = useCanvas().canvasApi.value
if (getSchema()) {
if (updateKey.value !== name && flag.value === OPTION_TYPE.UPDATE) {
delete state.dataSource[updateKey.value]
@ -188,7 +181,6 @@ export default {
const confirm = () => {
const { name } = state.createData
const { setState, setGlobalState } = useCanvas().canvasApi.value
if (!name || errorMessage.value) {
notifySaveError('变量名未填写或名称不符合规范,请按照提示修改后重试。')
@ -258,8 +250,6 @@ export default {
}
const remove = (key) => {
const { deleteState, getSchema } = useCanvas().canvasApi.value
delete state.dataSource[key]
//
deleteState(key)
@ -288,7 +278,6 @@ export default {
}
const setGlobalStateToDataSource = () => {
const { getGlobalState } = useCanvas().canvasApi.value
const globalState = getGlobalState()
if (!globalState) {
@ -303,7 +292,6 @@ export default {
const removeStore = (key) => {
const storeListt = [...useResource().resState.globalState] || []
const index = storeListt.findIndex((store) => store.id === key)
const { setGlobalState } = useCanvas().canvasApi.value
if (index !== -1) {
const { id } = useEditorInfo().useInfo()
@ -326,8 +314,6 @@ export default {
}
const initDataSource = (tabsName = activeName.value) => {
const { getSchema } = useCanvas().canvasApi.value
if (tabsName === STATE.GLOBAL_STATE) {
setGlobalStateToDataSource()
} else {
@ -489,8 +475,11 @@ export default {
}
:deep(.tiny-tabs__content) {
margin: 0;
height: calc(100% - 48px);
padding: 0;
& > div {
height: 100%;
}
}
:deep(.help-box) {
position: absolute;

View File

@ -29,8 +29,11 @@
"@opentiny/tiny-engine-controller": "workspace:*",
"@opentiny/tiny-engine-http": "workspace:*",
"@opentiny/tiny-engine-utils": "workspace:*",
"@opentiny/vue": "~3.10.0",
"@opentiny/vue-icon": "~3.10.0",
"@opentiny/vue-renderless": "~3.10.0",
"@vue/shared": "^3.3.4",
"axios": "~0.28.0",
"axios": "^0.27.2",
"vue-clipboard3": "^2.0.0",
"xlsx": "^0.18.5"
},
@ -40,9 +43,6 @@
"vite": "^4.3.7"
},
"peerDependencies": {
"@opentiny/vue": "^3.14.0",
"@opentiny/vue-icon": "^3.14.0",
"@opentiny/vue-renderless": "^3.14.0",
"vue": "^3.4.15"
}
}

View File

@ -13,11 +13,16 @@
</span>
<div class="section">
<div class="field-list">
<!-- 字段列表 -->
<data-source-field-list
ref="fieldList"
v-model="state.fields"
@update:modelValue="$emit('update:modelValue', state.fields)"
></data-source-field-list>
<div v-if="state.showNewFieldForm" class="new-field-form">
<data-source-field-type @cancel="closeFieldType" @select="selectFieldType"></data-source-field-type>
<data-source-field-form
ref="fieldForm"
:modelValue="modelValue"
:field="state.field"
:editable="true"
:is-open="false"
@ -25,12 +30,6 @@
@save="saveFieldForm"
></data-source-field-form>
</div>
<!-- 字段列表 -->
<data-source-field-list
ref="fieldList"
v-model="state.fields"
@update:modelValue="$emit('update:modelValue', state.fields)"
></data-source-field-list>
</div>
</div>
</div>
@ -114,7 +113,7 @@ export default {
if (name && title) {
field.type = state.field.type
state.fields.unshift(field)
state.fields.push(field)
emit('update:modelValue', state.fields)
}

View File

@ -66,10 +66,6 @@ export default {
isOpen: {
type: Boolean,
default: true
},
modelValue: {
type: Array,
default: () => []
}
},
emits: ['save', 'cancel'],
@ -102,9 +98,7 @@ export default {
const handleCancel = () => {
emit('cancel')
}
const uniqueName = () => {
return props.modelValue.some((item) => item.name === state.field.name)
}
const saveField = () => {
form.value.validate((valid) => {
if (valid) {
@ -122,10 +116,6 @@ export default {
return
}
if (uniqueName() && rule.field === 'name') {
callback(new Error('该字段已存在,请重新输入'))
return
}
callback()
}

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