mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-10-14 17:33:01 +08:00
Compare commits
No commits in common. "master" and "v1.5.22" have entirely different histories.
4
.github/workflows/pages.yml
vendored
4
.github/workflows/pages.yml
vendored
@ -18,10 +18,10 @@ jobs:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Set node version to 22
|
||||
- name: Set node version to 18
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
|
||||
- run: pnpm bootstrap
|
||||
|
147
CHANGELOG.md
147
CHANGELOG.md
@ -1,150 +1,3 @@
|
||||
## [1.6.1](https://github.com/Tencent/tmagic-editor/compare/v1.6.0...v1.6.1) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** 异步加载数据源时,数据源事件配置失效 ([1031595](https://github.com/Tencent/tmagic-editor/commit/1031595a976b33f8e5e3a71d8c00944d4777beb1))
|
||||
* **form:** text与同行元素不对齐问题 ([cae11dc](https://github.com/Tencent/tmagic-editor/commit/cae11dce1290b9ea314c07daee30f1eb6f400681))
|
||||
* 组件声明周期函数配置中配置数据源自有方法生效 ([e400175](https://github.com/Tencent/tmagic-editor/commit/e400175ffe89ab3105e50d2ffcd702d9d2a12970))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **data-source, editor, schema, react-runtime-help, vue-components:** 新增条件成立时隐藏的配置功能 ([51f95ae](https://github.com/Tencent/tmagic-editor/commit/51f95aef6f649851222e09e7234648f4985d1ad8))
|
||||
* **data-source:** 数据源数据变化事件监听响应支持立即执行 ([849b561](https://github.com/Tencent/tmagic-editor/commit/849b561933d74bcc68f75f15d67e0d252c5a8468))
|
||||
* **editor:** 属性配置中的样式面板样式优化 ([81aa8f1](https://github.com/Tencent/tmagic-editor/commit/81aa8f151d765bf08d2f88c334ff7c58b38aab56))
|
||||
|
||||
|
||||
|
||||
# [1.6.0](https://github.com/Tencent/tmagic-editor/compare/v1.6.0-beta.6...v1.6.0) (2025-08-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **cli:** 在pnpm工作空间中的项目,保持package.json不受配置中的packages影响 ([a10f9d2](https://github.com/Tencent/tmagic-editor/commit/a10f9d230d93064c28ff82f2c3aba2679f8fe495))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **cli:** 支持tmagic.config.local配置文件,会与tmagic.config配置合并,可用于本地开发时的临时配置 ([9f35054](https://github.com/Tencent/tmagic-editor/commit/9f350541bf84a634499a9af53449015f34da26b5))
|
||||
|
||||
|
||||
|
||||
# [1.6.0-beta.6](https://github.com/Tencent/tmagic-editor/compare/v1.6.0-beta.5...v1.6.0-beta.6) (2025-08-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** 事件支持关联数据源自身方法 ([4c6118f](https://github.com/Tencent/tmagic-editor/commit/4c6118f50f8e78e8db157f9b32c49f5f068e3e9a))
|
||||
|
||||
|
||||
|
||||
# [1.6.0-beta.5](https://github.com/Tencent/tmagic-editor/compare/v1.6.0-beta.4...v1.6.0-beta.5) (2025-08-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** 事件行为应该为串行执行 ([fc1c9fe](https://github.com/Tencent/tmagic-editor/commit/fc1c9feafd337ad8fa8a9b5b3501278779d32410))
|
||||
|
||||
|
||||
|
||||
# [1.6.0-beta.4](https://github.com/Tencent/tmagic-editor/compare/v1.6.0-beta.3...v1.6.0-beta.4) (2025-07-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **editor:** runtime url更新后恢复当前选中状态 ([c2830fc](https://github.com/Tencent/tmagic-editor/commit/c2830fca6b21648b51a486650caba3f9eb753c91))
|
||||
* **editor:** 避免services plugin重复添加 ([cdb07df](https://github.com/Tencent/tmagic-editor/commit/cdb07dfaea13a1813caddd59ba7e8efbdb374b3c))
|
||||
|
||||
|
||||
|
||||
# [1.6.0-beta.3](https://github.com/Tencent/tmagic-editor/compare/v1.6.0-beta.2...v1.6.0-beta.3) (2025-07-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **editor:** runtimeUrl更新后需要重新设置runtime的dsl ([d31a544](https://github.com/Tencent/tmagic-editor/commit/d31a544a2312b4d78ae74eb600760df450946db8))
|
||||
* **editor:** 修复代码块编辑器的更新内容后按下ctrl+s光标会偏移的问题 ([07b8f5f](https://github.com/Tencent/tmagic-editor/commit/07b8f5fe8303bad57490c7dd0eba00e435fbfad5))
|
||||
|
||||
|
||||
|
||||
# [1.6.0-beta.2](https://github.com/Tencent/tmagic-editor/compare/v1.6.0-beta.1...v1.6.0-beta.2) (2025-07-22)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** 异步加载dsl片段下,事件触发时node可能不存在 ([01b88db](https://github.com/Tencent/tmagic-editor/commit/01b88dba251a1f7cc29947c2a80e15839e50fcca))
|
||||
* **utile:** replaceChildNode找不多目标不报错 ([2605011](https://github.com/Tencent/tmagic-editor/commit/2605011df481d9edba5503d2ee3b3c9d4b76d0a3))
|
||||
|
||||
|
||||
|
||||
# [1.6.0-beta.1](https://github.com/Tencent/tmagic-editor/compare/v1.6.0-beta.0...v1.6.0-beta.1) (2025-07-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **data-source:** 页面未初始化好之前,数据源数据变化后需要修改page.data ([305ea46](https://github.com/Tencent/tmagic-editor/commit/305ea4619fe15efbae069ef3f289c8742e8d51ee))
|
||||
* **vue-components:** iterator-container 传递 page-fragment-contianer-id 参数 ([7799a5d](https://github.com/Tencent/tmagic-editor/commit/7799a5da618b0136f4b3c12315c69d21b27a5965))
|
||||
* **vue-components:** page-fragment-container不标记为不是node节点 ([bf9fad1](https://github.com/Tencent/tmagic-editor/commit/bf9fad18b64521a9075a60f7e6e662b01d37f66f))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Node中的instance初始为null,用于判断是否与组件产生关联 ([291de20](https://github.com/Tencent/tmagic-editor/commit/291de2005de54b8eeba818de67ab3506885f5057))
|
||||
* **editor:** 页面片容器内的组件不允许选中 ([ded24c8](https://github.com/Tencent/tmagic-editor/commit/ded24c8b4ff2c203260ec7633fc85325c98a565c))
|
||||
* **vue-components:** page-fragment-container编辑器中不去除内部组件id,不然会导致无法从app中获取dsl ([34bc223](https://github.com/Tencent/tmagic-editor/commit/34bc223f0241d9eef39450ebcef8a92b23c63f37))
|
||||
* **vue-components:** 添加页面片容器id prop ([a43825c](https://github.com/Tencent/tmagic-editor/commit/a43825caa27695fcd85cc4b5351a00080fefcec0))
|
||||
|
||||
|
||||
|
||||
# [1.6.0-beta.0](https://github.com/Tencent/tmagic-editor/compare/v1.5.24...v1.6.0-beta.0) (2025-07-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **editor:** 依赖收集后没有同步到dsl中 ([aaf8046](https://github.com/Tencent/tmagic-editor/commit/aaf8046c63949ae733719409eb12109ef17b2cf1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core,data-source,utils,react-runtime-help,vue-runtime-help:** 新增对页面片节点的管理 ([6a54720](https://github.com/Tencent/tmagic-editor/commit/6a547200681991ae8abefd0e3d72a603d21f12d4))
|
||||
|
||||
|
||||
|
||||
## [1.5.24](https://github.com/Tencent/tmagic-editor/compare/v1.5.23...v1.5.24) (2025-07-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **editor:** 初始化收集依赖后编译数据源不能使用源数据 ([84bf9ba](https://github.com/Tencent/tmagic-editor/commit/84bf9ba2f96829ba8e88b278bd49f2cb454605aa))
|
||||
|
||||
|
||||
|
||||
## [1.5.23](https://github.com/Tencent/tmagic-editor/compare/v1.5.22...v1.5.23) (2025-07-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **editor,react-runtime-help,vue-runtime-help:** 修复拖动页面顺序失效问题 ([b6a2604](https://github.com/Tencent/tmagic-editor/commit/b6a260471d9369e9b911f35d16ff0fca82a7d4fe))
|
||||
* **editor:** 树组件搜索后不展开所有节点 ([c984c1a](https://github.com/Tencent/tmagic-editor/commit/c984c1a0cf0c934d41e5affb471d2ff2efa4bb97))
|
||||
* **editor:** 防止快捷键重复注册 ([18e8df4](https://github.com/Tencent/tmagic-editor/commit/18e8df4a557cb845a8f1d2868ea4e8833a75941d))
|
||||
* **editor:** 页面列表滚动条 ([727af10](https://github.com/Tencent/tmagic-editor/commit/727af1058d23e0843482536133954243c9f208d5))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core,data-source,dep,editor,schema:** 新增组件禁用代码块/数据源的配置开关 ([2f4a7a3](https://github.com/Tencent/tmagic-editor/commit/2f4a7a33b85015bfe5368bd10bf562514f07299c))
|
||||
* **dep,schema:** 添加组件没用数据源或者代码块的配置key ([3f7d039](https://github.com/Tencent/tmagic-editor/commit/3f7d03959cdbafacc439df2bf3a61aa1e21cb8c2))
|
||||
* **editor:** service新增removePlugin方法 ([8d6da37](https://github.com/Tencent/tmagic-editor/commit/8d6da3712efdc269cbaa47b003bf667bbce00ae7))
|
||||
* **editor:** stage overlay 支持放大缩小 ([3268196](https://github.com/Tencent/tmagic-editor/commit/32681964b340fc8e68f8b6a6eb420043fd569b78))
|
||||
* **editor:** 数据源属性/方法表格可以拖动列宽 ([222a96e](https://github.com/Tencent/tmagic-editor/commit/222a96e4652003b908c5c75378e5ddf2029fdd65))
|
||||
* **editor:** 新增props-panel-unmounted事件 ([9e590c5](https://github.com/Tencent/tmagic-editor/commit/9e590c5cf73c9925f7aad562409f31c1ff273b18))
|
||||
* **playground:** 新增按比例拖动组件大小快捷键 ([b78ef20](https://github.com/Tencent/tmagic-editor/commit/b78ef206022d69a8c3f81c55a301e5a4b5147641))
|
||||
* **stage:** 新增禁用标尺配置 ([95d6263](https://github.com/Tencent/tmagic-editor/commit/95d6263f42ad13c2ac0560704695408634e55d91))
|
||||
* **stage:** 添加updateMoveableOptions api,用于更新选中框状态 ([bdd59cf](https://github.com/Tencent/tmagic-editor/commit/bdd59cff9b378461f905b04e250f8ad7f6ed21a0))
|
||||
|
||||
|
||||
|
||||
## [1.5.22](https://github.com/Tencent/tmagic-editor/compare/v1.5.21...v1.5.22) (2025-06-09)
|
||||
|
||||
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
|
||||
Copyright (C) 2025 Tencent. All rights reserved.
|
||||
Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
|
||||
TMagicEditor is licensed under the Apache License Version 2.0 except for the third-party components listed below.
|
||||
|
||||
|
@ -24,7 +24,7 @@ export default defineConfig({
|
||||
|
||||
footer: {
|
||||
message: 'Powered by 腾讯视频会员平台技术中心',
|
||||
copyright: 'Copyright (C) 2025 Tencent.'
|
||||
copyright: 'Copyright (C) 2023 THL A29 Limited, a Tencent company.'
|
||||
},
|
||||
|
||||
nav: [
|
||||
|
@ -375,7 +375,7 @@ const stageContentMenu = ref([
|
||||
```html
|
||||
<template>
|
||||
<m-editor
|
||||
runtime-url="https://tencent.github.io/tmagic-editor/playground/runtime/vue/playground/index.html"
|
||||
runtime-url="https://tencent.github.io/tmagic-editor/playground/runtime/vue3/playground/index.html"
|
||||
></m-editor>
|
||||
</template>
|
||||
```
|
||||
@ -675,7 +675,7 @@ const datasourceConfigs = {
|
||||
|
||||
- **默认值:** `{}`
|
||||
|
||||
- **类型:** ((config: [CustomizeMoveableOptionsCallbackConfig](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/stage/src/types.ts#L97-L109)) => MoveableOptions) | [MoveableOptions](https://daybrush.com/moveable/release/latest/doc/)
|
||||
- **类型:** ((config?: [CustomizeMoveableOptionsCallbackConfig](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/stage/src/types.ts#L97-L109)) => MoveableOptions) | [MoveableOptions](https://daybrush.com/moveable/release/latest/doc/)
|
||||
|
||||
- **示例:**
|
||||
|
||||
|
@ -60,5 +60,6 @@ export default defineComponent({
|
||||
|
||||
## 渲染器示例
|
||||
在tmagic-editor的示例项目中,我们提供了三个版本的 @tmagic/ui。可以参考对应前端框架的渲染器实现。
|
||||
- [vue 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/vue-components/container/src/Container.vue)
|
||||
- [react 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/react-components/container/src/Container.tsx)
|
||||
- [vue3 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/packages/ui/src/container/src/Container.vue)
|
||||
- [vue2 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/packages/ui-vue2/src/container/Container.vue)
|
||||
- [react 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/packages/ui-react/src/container/Container.tsx)
|
@ -5,13 +5,15 @@ tmagic-editor的设计是希望发布的页面支持多个前端框架,即各
|
||||
|
||||
所以tmagic-editor的设计中,针对每个前端框架,都需要有一个对应的 @tmagic/ui 来承担渲染器职责。同时,也需要一个使用和 @tmagic/ui 相同前端框架的 runtime 来 @tmagic/ui 和业务组件的,具体 runtime 概念,可以参考[页面发布](../publish)。
|
||||
|
||||
我们以项目代码中提供的 vue 版本的 [vue-components](https://tencent.github.io/tmagic-editor/vue-components) 作为示例介绍其中包含的内容。
|
||||
@tmagic/ui 在tmagic-editor设计中,承担的是业务逻辑无关的,基础组件渲染的功能。一切和业务相关的逻辑,都应该在 [runtime](../runtime.html) 中实现。这样 @tmagic/ui 就能保持其通用性。
|
||||
|
||||
我们以项目代码中提供的 vue3 版本的 @tmagic/ui 作为示例介绍其中包含的内容。
|
||||
|
||||
## 渲染器
|
||||
在 vue 中,实现渲染器的具体形式参考[页面渲染](../advanced/page)中描述的[容器渲染](../advanced/page.html#容器渲染)和[组件渲染](../advanced/page.html#容器渲染)。
|
||||
在 vue3 中,实现渲染器的具体形式参考[页面渲染](../advanced/page)中描述的[容器渲染](../advanced/page.html#容器渲染)和[组件渲染](../advanced/page.html#容器渲染)。
|
||||
|
||||
## 基础组件
|
||||
在 [vue-components](https://tencent.github.io/tmagic-editor/vue-components) 中,我们提供了几个基础组件,可以在项目源码中找到对应内容。
|
||||
在 @tmagic/ui vue3 中,我们提供了几个基础组件,可以在项目源码中找到对应内容。
|
||||
|
||||
- page tmagic-editor的页面基础
|
||||
- container tmagic-editor的容器渲染器
|
||||
@ -21,3 +23,8 @@ tmagic-editor的设计是希望发布的页面支持多个前端框架,即各
|
||||
其中 page/container/Component 是 UI 的基础,是每个框架的 UI 都应该实现的。
|
||||
|
||||
button/text 其实就是一个组件开发的示例,具体组件开发相关规范可以参考[组件开发](../component)。
|
||||
|
||||
## @tmagic/ui 示例
|
||||
- [vue3 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/packages/ui)
|
||||
- [vue2 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/packages/ui-vue2)
|
||||
- [react 渲染器](https://github.com/Tencent/tmagic-editor/blob/master/packages/ui-react)
|
@ -2,14 +2,14 @@
|
||||
tmagic-editor支持业务方进行自定义组件开发。在tmagic-editor中,组件是以 npm 包形式存在的,组件和插件只要按照规范开发,就可以在tmagic-editor的 runtime 中被加入并正确渲染组件。
|
||||
|
||||
## 组件开发
|
||||
以 vue 的组件开发为例。运行项目中的 playground 示例,会自动加载 vue 的 runtime。runtime会加载[@tmagic/ui](https://github.com/Tencent/tmagic-editor/tree/master/packages/ui)
|
||||
以 vue3 的组件开发为例。运行项目中的 playground 示例,会自动加载 vue3 的 runtime。runtime会加载[@tmagic/ui](https://github.com/Tencent/tmagic-editor/tree/master/packages/ui)
|
||||
|
||||
## 组件注册
|
||||
在 [playground](https://tencent.github.io/tmagic-editor/playground/index.html#/) 中,我们可以尝试点击添加一个组件,在模拟器区域里,就会出现这个组件。其中就涉及到组件注册。
|
||||
|
||||
这一步需要开发者基于tmagic-editor搭建了平台后,实现组件列表的注册、获取机制,tmagic-editor组件注册其实就是保存好组件 `type` 的映射关系。`type` 可以参考[组件介绍](../guide/conception.html#组件)。
|
||||
|
||||
可以参考 vue 版本的 @tmagic/ui 中,[组件渲染](../guide/advanced/page.html#组件渲染)逻辑里,type 会作为组件名进入渲染。所以在 vue 的组件开发中,我们也需要在为 vue 组件声明 name 字段时,和 type 值对应起来,才能正确渲染组件。
|
||||
可以参考 vue3 版本的 @tmagic/ui 中,[组件渲染](../guide/advanced/page.html#组件渲染)逻辑里,type 会作为组件名进入渲染。所以在 vue3 的组件开发中,我们也需要在为 vue 组件声明 name 字段时,和 type 值对应起来,才能正确渲染组件。
|
||||
|
||||
### 组件规范
|
||||
组件的基础形式,需要有四个文件
|
||||
@ -22,7 +22,7 @@ tmagic-editor支持业务方进行自定义组件开发。在tmagic-editor中,
|
||||
@tmagic/ui 中的 button/text 就是基础的组件示例。我们要求声明 index 入口,因为我们希望在后续的配套打包工具实现上,可以有一个统一规范入口。
|
||||
|
||||
### 1. 创建组件
|
||||
在项目中,如 runtime 目录中,创建一个名为 test-component 的组件目录,其中包含上面四个规范文件。
|
||||
在项目中,如 runtime vue3 目录中,创建一个名为 test-component 的组件目录,其中包含上面四个规范文件。
|
||||
```javascript
|
||||
// index.js
|
||||
// vue
|
||||
@ -69,7 +69,7 @@ export default {
|
||||
};
|
||||
```
|
||||
|
||||
版本的组件代码示例
|
||||
vue3 版本的组件代码示例
|
||||
```vue
|
||||
<!-- Test.vue -->
|
||||
<template>
|
||||
@ -119,7 +119,7 @@ export default Test;
|
||||
```
|
||||
|
||||
### 2. 使用tmagic-cli
|
||||
在 runtime vue 中,我们已经提供好一份示例。在 tmagic.config.ts 文件中。只需要在 packages 加入你创建的组件的路径(如果是个 npm 包,则将路径替换为包名即可),打包工具就会自动识别到你的组件。
|
||||
在 runtime vue3 中,我们已经提供好一份示例。在 tmagic.config.ts 文件中。只需要在 packages 加入你创建的组件的路径(如果是个 npm 包,则将路径替换为包名即可),打包工具就会自动识别到你的组件。
|
||||
|
||||
### 3. 启动 playground
|
||||
在上面的步骤完成后,在 playground/src/configs/componentGroupList 中。找到组件栏的基础组件列表,在其中加入你的开发组件
|
||||
@ -155,7 +155,7 @@ npm run playground
|
||||
<img src="https://image.video.qpic.cn/oa_fd3c9c-3_548108267_1636719045199471">
|
||||
|
||||
### 4. 启动 runtime
|
||||
在完成开发中组件在编辑器中的实现后,我们将编辑器中的 DSL 源码📄 打开,复制 DSL。并在 runtime/vue/src/page 下。创建一个 page-config.js 文件。将 DSL 作为配置导出。
|
||||
在完成开发中组件在编辑器中的实现后,我们将编辑器中的 DSL 源码📄 打开,复制 DSL。并在 runtime/vue3/src/page 下。创建一个 page-config.js 文件。将 DSL 作为配置导出。
|
||||
|
||||
```javascript
|
||||
window.magicDSL = [
|
||||
@ -168,7 +168,7 @@ window.magicDSL = [
|
||||
import './page-config.js';
|
||||
```
|
||||
|
||||
然后执行在 runtime/ 目录下执行
|
||||
然后执行在 runtime/vue3 目录下执行
|
||||
```
|
||||
npm run build:libs
|
||||
npm run dev
|
||||
|
@ -60,6 +60,11 @@ DSL 是编辑器搭建页面的最终产物(描述文件),其中包含了
|
||||
### runtime
|
||||
我们把页面统一称为 runtime,更具体的 runtime 概念可以查看[页面发布](./publish.html#runtime)。**runtime 是承载tmagic-editor项目页面的运行环境**。编辑器的工作区是 runtime 的一个具体实例,另一个就是我们发布上线后,用户访问的真实项目页面。
|
||||
|
||||
### @tmagic/ui
|
||||
@tmagic/ui 包含了tmagic-editor的基础组件库,提供了容器、文本、按钮这样的基础组件。我们提供了不同语言框架的 @tmagic/ui,如 vue2 和 vue3。
|
||||
|
||||
@tmagic/ui 和 runtime 是配套出现的,runtime 必须基于 @tmagic/ui 才可以实现渲染。因为 @tmagic/ui 需要提供 runtime 所需要的渲染器。
|
||||
|
||||
## 联动
|
||||
页面搭建过程中,会涉及到两种联动形式
|
||||
- 在编辑器中,组件的表单配置项之间需要联动。
|
||||
|
@ -125,7 +125,7 @@ app.mount('#app');
|
||||
// 初始化页面数据
|
||||
}),
|
||||
|
||||
runtimeUrl: "/runtime/vue/playground/index.html",
|
||||
runtimeUrl: "/runtime/vue3/playground/index.html",
|
||||
|
||||
propsConfigs: [
|
||||
// 组件属性列表
|
||||
@ -208,10 +208,10 @@ npm install sass -D
|
||||
|
||||
```javascript
|
||||
setup() {
|
||||
asyncLoadJs(`/runtime/vue/assets/config.js`).then(() => {
|
||||
asyncLoadJs(`/runtime/vue3/assets/config.js`).then(() => {
|
||||
propsConfigs.value = window.magicPresetConfigs;
|
||||
});
|
||||
asyncLoadJs(`/runtime/vue/assets/value.js`).then(() => {
|
||||
asyncLoadJs(`/runtime/vue3/assets/value.js`).then(() => {
|
||||
propsValues.value = window.magicPresetValues;
|
||||
});
|
||||
}
|
||||
|
@ -36,6 +36,9 @@ tmagic-editor可视化开源项目是从魔方平台演化而来的开源项目
|
||||
- **@tmagic/core** 实现对组件进行跨框架管理与一些通用复杂逻辑的实现。
|
||||
- **@tmagic/data-source** 实现数据源的管理与编译。
|
||||
- **@tmagic/stage** 实现在编辑器中对组件的位置拖动与大小拖拉。
|
||||
- **@tmagic/ui** 提供一些vue3基础组件。
|
||||
- **@tmagic/ui-vue2** 提供一些vue2基础组件。
|
||||
- **@tmagic/ui-react** 提供一些react基础组件。
|
||||
- **runtime** 实现在编辑器中对使用不同框架的组件的渲染。
|
||||
|
||||
可以查阅 tmagic 的[源代码](https://github.com/Tencent/tmagic-editor),与文档描述内容可以逐一对应上,希望文档内容可以为开发者带来比较好的开发体验。
|
||||
|
@ -13,7 +13,7 @@ runtime 的概念,是理解tmagic-editor项目页运行的重要概念,runti
|
||||
|
||||
所以更深入描述,runtime 是tmagic-editor页面的渲染环境,提供不同场景下的能力封装。如果理解了tmagic-editor的设计,阅读了tmagic-editor的源码,可以发现,runtime 只是对tmagic-editor的渲染器做了一层包装,在不同 runtime 中,tmagic-editor的渲染逻辑和组件代码都是相同的。
|
||||
|
||||
并且,由于tmagic-editor在编辑器中的模拟器是通过 iframe 渲染的,和tmagic-editor平台本身可以做到框架解耦,所以 runtime 也可以用不同框架开发。目前tmagic-editor提供了 vue 和 react 的 runtime 示例。
|
||||
并且,由于tmagic-editor在编辑器中的模拟器是通过 iframe 渲染的,和tmagic-editor平台本身可以做到框架解耦,所以 runtime 也可以用不同框架开发。目前tmagic-editor提供了 vue2/vue3 和 react 的 runtime 示例。
|
||||
|
||||
各个 runtime 的作用除了作为不同场景下的渲染环境,同时也是不同环境的打包构建载体。tmagic-editor示例代码中的打包就是基于 runtime 进行的。
|
||||
|
||||
@ -21,7 +21,8 @@ runtime 的概念,是理解tmagic-editor项目页运行的重要概念,runti
|
||||
由于 runtime 是页面渲染的承载环境,其中会加载 @tmagic/ui 以及各个业务组件,业务发布项目页也是基于 runtime,所以在 runtime 中实现业务方的自定义逻辑是最合适的。runtime 可以提供一些全局 API,供业务组件调用。我们可以把下面的模拟器中的 runtime 视为一个业务方runtime。
|
||||
|
||||
tmagic-editor提供了三个版本的 runtime 示例,可以参考:
|
||||
- [vue runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue)
|
||||
- [vue3 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue3)
|
||||
- [vue2 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue2)
|
||||
- [react runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/react)
|
||||
|
||||
### 真实页面渲染(Page)
|
||||
|
@ -14,7 +14,8 @@ runtime 的 `page` 部分,就是真实项目页面的渲染环境。发布出
|
||||
- 加载第三方全局组件/插件等
|
||||
|
||||
具体的 page 实现示例,可以参考
|
||||
- [vue runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue/page)
|
||||
- [vue3 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue3/page)
|
||||
- [vue2 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue2/page)
|
||||
- [react runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/react/page)
|
||||
|
||||
### playground
|
||||
@ -44,7 +45,8 @@ window.magic.onPageElUpdate(document.querySelector('.magic-ui-page'));
|
||||
|sortNode| 组件在容器间排序 |{ `src` , `dist`, `root` }: `SortEventData` |
|
||||
|
||||
runtime 的实现示例,可以参考tmagic-editor提供的:
|
||||
- [vue runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue)
|
||||
- [vue3 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue3)
|
||||
- [vue2 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue2)
|
||||
- [react runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/react)
|
||||
|
||||
### 页面发布
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 3.[DSL](../conception.md#dsl) 解析渲染
|
||||
|
||||
tmagic 提供了 vue/react 两个个版本的解析渲染组件,可以直接使用
|
||||
tmagic 提供了 vue3/vue2/react 三个版本的解析渲染组件,可以直接使用
|
||||
|
||||
[@tmagic/ui](https://www.npmjs.com/package/@tmagic/ui)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import js from '@eslint/js';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import stylisticTs from '@stylistic/eslint-plugin-ts';
|
||||
import parserTs from '@typescript-eslint/parser';
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||
@ -20,6 +21,7 @@ export default (tsconfigRootDir) =>
|
||||
...tseslint.config(tencentEslintBaseConfig, tencentEslintImportexport, tseslint.configs.base, {
|
||||
plugins: {
|
||||
'@stylistic': stylistic,
|
||||
'@stylistic/ts': stylisticTs,
|
||||
},
|
||||
languageOptions: {
|
||||
parser: parserTs,
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@tmagic/eslint-config",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.2",
|
||||
"main": "index.mjs",
|
||||
"type": "module",
|
||||
"repository": {
|
||||
@ -9,17 +9,18 @@
|
||||
"url": "https://github.com/Tencent/tmagic-editor.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@eslint/js": "^9.34.0",
|
||||
"@typescript-eslint/parser": "^8.41.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.41.0 ",
|
||||
"@stylistic/eslint-plugin": "^5.2.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"@eslint/js": "^9.24.0",
|
||||
"@typescript-eslint/parser": "^8.30.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.30.1",
|
||||
"@stylistic/eslint-plugin": "^4.2.0",
|
||||
"@stylistic/eslint-plugin-ts": "^4.2.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"eslint-plugin-prettier": "^5.5.4 ",
|
||||
"globals": "^16.3.0",
|
||||
"typescript-eslint": "^8.41.0"
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"eslint-plugin-prettier": "^5.2.6",
|
||||
"globals": "^16.0.0",
|
||||
"typescript-eslint": "^8.30.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=9.24.0",
|
||||
|
30
package.json
30
package.json
@ -1,9 +1,9 @@
|
||||
{
|
||||
"version": "1.6.1",
|
||||
"version": "1.5.22",
|
||||
"name": "tmagic",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.18.2",
|
||||
"packageManager": "pnpm@10.11.1",
|
||||
"scripts": {
|
||||
"bootstrap": "pnpm i && pnpm build",
|
||||
"clean:top": "rimraf */**/dist */**/types */dist coverage dwt* temp packages/cli/lib",
|
||||
@ -11,14 +11,16 @@
|
||||
"clean:all": "pnpm clean:top && pnpm clean:modules",
|
||||
"lint": "eslint --cache .",
|
||||
"lint-fix": "eslint --fix --cache .",
|
||||
"playground": "pnpm --filter \"runtime-vue\" build:libs && pnpm --filter \"runtime-vue\" --filter \"tmagic-playground\" dev",
|
||||
"playground": "pnpm --filter \"runtime-vue3\" build:libs && pnpm --filter \"runtime-vue3\" --filter \"tmagic-playground\" dev",
|
||||
"pg": "pnpm playground",
|
||||
"playground:vue2": "pnpm --filter \"runtime-vue2\" build:libs && pnpm --filter \"runtime-vue2\" --filter \"tmagic-playground\" dev:vue2",
|
||||
"pg:vue2": "pnpm playground:vue2",
|
||||
"playground:react": "pnpm --filter \"runtime-react\" build:libs && pnpm --filter \"runtime-react\" --filter \"tmagic-playground\" dev:react",
|
||||
"pg:react": "pnpm playground:react",
|
||||
"build": "pnpm build:dts && node scripts/build.mjs",
|
||||
"build:dts": "pnpm --filter \"@tmagic/cli\" build && tsc -p tsconfig.build-browser.json && vue-tsc --declaration --emitDeclarationOnly --project tsconfig.build-vue.json && rollup -c rollup.dts.config.js && rimraf temp",
|
||||
"check:type": "tsc --incremental --noEmit -p tsconfig.check.json && vue-tsc --noEmit -p tsconfig.check-vue.json",
|
||||
"build:playground": "pnpm --filter \"runtime-vue\" build && pnpm --filter \"tmagic-playground\" build",
|
||||
"build:playground": "pnpm --filter \"runtime-vue3\" build && pnpm --filter \"tmagic-playground\" build",
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"docs:serve": "vitepress serve docs",
|
||||
"docs:build": "vitepress build docs",
|
||||
@ -30,7 +32,7 @@
|
||||
"release": "node scripts/release.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
"node": ">=18"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@ -44,18 +46,18 @@
|
||||
"@commitlint/config-conventional": "^19.8.1",
|
||||
"@rollup/plugin-alias": "^5.1.1",
|
||||
"@tmagic/eslint-config": "workspace:*",
|
||||
"@types/node": "24.0.10",
|
||||
"@types/node": "18.19.61",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@vitest/coverage-v8": "^2.1.9",
|
||||
"@vue/compiler-sfc": "catalog:",
|
||||
"@vue/compiler-sfc": "^3.5.13",
|
||||
"c8": "^7.14.0",
|
||||
"commitizen": "^4.3.1",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"cosmiconfig": "^8.3.6",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"element-plus": "^2.11.4",
|
||||
"element-plus": "^2.9.11",
|
||||
"enquirer": "^2.4.1",
|
||||
"eslint": "^9.37.0",
|
||||
"eslint": "^9.28.0",
|
||||
"execa": "^4.1.0",
|
||||
"highlight.js": "^11.11.1",
|
||||
"husky": "^9.1.7",
|
||||
@ -63,20 +65,20 @@
|
||||
"lint-staged": "^16.1.0",
|
||||
"minimist": "^1.2.8",
|
||||
"picocolors": "^1.1.1",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier": "^3.5.3",
|
||||
"recast": "^0.23.11",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "4.44.1",
|
||||
"rollup": "^4.38.0",
|
||||
"rollup-plugin-dts": "^6.2.1",
|
||||
"semver": "^7.7.1",
|
||||
"serialize-javascript": "^6.0.2",
|
||||
"shx": "^0.3.4",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitepress": "^1.6.4",
|
||||
"vitest": "^3.2.4",
|
||||
"vitepress": "^1.6.3",
|
||||
"vitest": "^3.2.1",
|
||||
"vue": "catalog:",
|
||||
"vue-tsc": "^3.1.1"
|
||||
"vue-tsc": "^2.2.8"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.6.1",
|
||||
"version": "1.5.22",
|
||||
"name": "@tmagic/cli",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
@ -30,17 +30,16 @@
|
||||
"chokidar": "^3.6.0",
|
||||
"esbuild": "^0.21.5",
|
||||
"fs-extra": "^11.2.0",
|
||||
"merge-options": "^3.0.4",
|
||||
"picocolors": "^1.1.1",
|
||||
"recast": "^0.23.11",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^11.0.4",
|
||||
"@types/node": "^24.0.10"
|
||||
"@types/node": "^18.19.61"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "catalog:"
|
||||
"typescript": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -1,8 +1,6 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
// @ts-ignore
|
||||
import mergeOptions from 'merge-options';
|
||||
|
||||
import App from '../Core';
|
||||
import { UserConfig } from '../types';
|
||||
@ -24,29 +22,18 @@ export const scripts = (defaultAppConfig: UserConfig) => {
|
||||
path.resolve(defaultAppConfig.temp, 'config.cjs'),
|
||||
].find((item) => fs.pathExistsSync(item));
|
||||
|
||||
const localUserConfigPath = [
|
||||
path.resolve(defaultAppConfig.source, 'tmagic.config.local.ts'),
|
||||
path.resolve(defaultAppConfig.source, 'tmagic.config.local.js'),
|
||||
path.resolve(defaultAppConfig.source, 'tmagic.config.local.cjs'),
|
||||
path.resolve(defaultAppConfig.temp, 'config.local.ts'),
|
||||
path.resolve(defaultAppConfig.temp, 'config.local.js'),
|
||||
path.resolve(defaultAppConfig.temp, 'config.local.cjs'),
|
||||
].find((item) => fs.pathExistsSync(item));
|
||||
|
||||
let userConfig = await loadUserConfig(userConfigPath);
|
||||
|
||||
if (localUserConfigPath) {
|
||||
const localUserConfig = await loadUserConfig(localUserConfigPath);
|
||||
|
||||
if (localUserConfig.packages?.length) {
|
||||
localUserConfig.packages = [...(userConfig.packages || []), ...localUserConfig.packages];
|
||||
}
|
||||
|
||||
userConfig = mergeOptions(userConfig, localUserConfig);
|
||||
}
|
||||
const { npmConfig = {}, ...userConfig } = await loadUserConfig(userConfigPath);
|
||||
|
||||
// resolve the final app config to use
|
||||
const appConfig = mergeOptions(defaultAppConfig, userConfig);
|
||||
const appConfig = {
|
||||
...defaultAppConfig,
|
||||
...userConfig,
|
||||
npmConfig: {
|
||||
...(defaultAppConfig.npmConfig || {}),
|
||||
...npmConfig,
|
||||
},
|
||||
};
|
||||
|
||||
const app = new App(appConfig);
|
||||
|
||||
// clean temp and cache
|
||||
|
@ -1,35 +1,11 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import process from 'node:process';
|
||||
|
||||
const windowsPathRegex = /^(?:[a-zA-Z]:|[\\/]{2}[^\\/]+[\\/]+[^\\/]+)?[\\/]$/;
|
||||
|
||||
export const isRootPath = (path: string) => {
|
||||
if (typeof path !== 'string') {
|
||||
throw new TypeError('Expected a string');
|
||||
}
|
||||
|
||||
path = path.trim();
|
||||
|
||||
// While it's unclear how long a root path can be on Windows, it definitely cannot be longer than 100 characters.
|
||||
if (path === '' || path.length > 100) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return process.platform === 'win32' ? windowsPathRegex.test(path) : path === '/';
|
||||
};
|
||||
|
||||
export const backupFile = (runtimeSource: string, file: string) => {
|
||||
if (isRootPath(runtimeSource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = path.join(runtimeSource, file);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.copyFileSync(filePath, `${filePath}.bak`);
|
||||
} else {
|
||||
backupFile(path.resolve(runtimeSource, '..'), file);
|
||||
}
|
||||
};
|
||||
|
||||
@ -50,17 +26,11 @@ export const backupLock = (runtimeSource: string, npmType: string) => {
|
||||
};
|
||||
|
||||
export const restoreFile = (runtimeSource: string, file: string) => {
|
||||
if (isRootPath(runtimeSource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = path.join(runtimeSource, file);
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
fs.unlinkSync(filePath);
|
||||
fs.renameSync(`${filePath}.bak`, filePath);
|
||||
} else {
|
||||
restoreFile(path.resolve(runtimeSource, '..'), file);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -601,20 +601,10 @@ const flattenPackagesConfig = (packages: (string | Record<string, string>)[]) =>
|
||||
const packagesConfig: ([string] | [string, string])[] = [];
|
||||
packages.forEach((item) => {
|
||||
if (typeof item === 'object') {
|
||||
for (const [key, packagePath] of Object.entries(item)) {
|
||||
const index = packagesConfig.findIndex(([, k]) => {
|
||||
return k === key;
|
||||
});
|
||||
if (index > -1) {
|
||||
packagesConfig[index] = [packagePath, key];
|
||||
} else {
|
||||
Object.entries(item).forEach(([key, packagePath]) => {
|
||||
packagesConfig.push([packagePath, key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (typeof item === 'string') {
|
||||
if (packagesConfig.find(([k]) => k === item)) {
|
||||
return;
|
||||
}
|
||||
packagesConfig.push([item]);
|
||||
}
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.6.1",
|
||||
"version": "1.5.22",
|
||||
"name": "@tmagic/core",
|
||||
"type": "module",
|
||||
"main": "dist/tmagic-core.umd.cjs",
|
||||
@ -41,11 +41,11 @@
|
||||
"lodash-es": "^4.17.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/events": "^3.0.3",
|
||||
"@types/events": "^3.0.0",
|
||||
"@types/lodash-es": "^4.17.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "catalog:"
|
||||
"typescript": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -29,7 +29,7 @@ import Flexible from './Flexible';
|
||||
import FlowState from './FlowState';
|
||||
import Node from './Node';
|
||||
import Page from './Page';
|
||||
import { AppOptionsConfig, ErrorHandler, GetNodeOptions } from './type';
|
||||
import { AppOptionsConfig, ErrorHandler } from './type';
|
||||
import { transformStyle as defaultTransformStyle } from './utils';
|
||||
|
||||
class App extends EventEmitter {
|
||||
@ -45,7 +45,6 @@ class App extends EventEmitter {
|
||||
public codeDsl?: CodeBlockDSL;
|
||||
public dataSourceManager?: DataSourceManager;
|
||||
public page?: Page;
|
||||
public pageFragments: Map<Id, Page> = new Map();
|
||||
public useMock = false;
|
||||
public platform = 'mobile';
|
||||
public jsEngine: JsEngine = 'browser';
|
||||
@ -160,20 +159,11 @@ class App extends EventEmitter {
|
||||
|
||||
super.emit('dsl-change', { dsl: config, curPage: pageId });
|
||||
|
||||
this.pageFragments.forEach((page) => {
|
||||
page.destroy();
|
||||
});
|
||||
this.pageFragments.clear();
|
||||
this.setPage(pageId);
|
||||
|
||||
if (this.dataSourceManager) {
|
||||
if (this.dataSourceManager.isAllDataSourceRegistered()) {
|
||||
this.eventHelper?.bindDataSourceEvents();
|
||||
} else {
|
||||
this.dataSourceManager.once('registered-all', () => {
|
||||
this.eventHelper?.bindDataSourceEvents();
|
||||
});
|
||||
}
|
||||
const dataSourceList = Array.from(this.dataSourceManager.dataSourceMap.values());
|
||||
this.eventHelper?.bindDataSourceEvents(dataSourceList);
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,11 +192,6 @@ class App extends EventEmitter {
|
||||
for (const [, node] of this.page.nodes) {
|
||||
this.eventHelper.bindNodeEvents(node);
|
||||
}
|
||||
for (const [, page] of this.pageFragments) {
|
||||
for (const [, node] of page.nodes) {
|
||||
this.eventHelper.bindNodeEvents(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.emit('page-change', this.page);
|
||||
@ -230,8 +215,8 @@ class App extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
public getNode<T extends Node = Node>(id: Id, options?: GetNodeOptions) {
|
||||
return this.page?.getNode<T>(id, options);
|
||||
public getNode<T extends Node = Node>(id: Id, iteratorContainerId?: Id[], iteratorIndex?: number[]) {
|
||||
return this.page?.getNode<T>(id, iteratorContainerId, iteratorIndex);
|
||||
}
|
||||
|
||||
public registerComponent(type: string, Component: any) {
|
||||
@ -264,15 +249,15 @@ class App extends EventEmitter {
|
||||
* @param eventConfig 代码动作的配置
|
||||
* @returns void
|
||||
*/
|
||||
public async runCode(codeId: Id, params: Record<string, any>, args: any[], flowState?: FlowState, node?: Node) {
|
||||
public async runCode(codeId: Id, params: Record<string, any>, args: any[], flowState?: FlowState) {
|
||||
if (!codeId || isEmpty(this.codeDsl)) return;
|
||||
const content = this.codeDsl?.[codeId]?.content;
|
||||
if (typeof content === 'function') {
|
||||
try {
|
||||
await content({ app: this, params, eventParams: args, flowState, node });
|
||||
await content({ app: this, params, eventParams: args, flowState });
|
||||
} catch (e: any) {
|
||||
if (this.errorHandler) {
|
||||
this.errorHandler(e, undefined, { type: 'run-code', codeId, params, eventParams: args, flowState, node });
|
||||
this.errorHandler(e, undefined, { type: 'run-code', codeId, params, eventParams: args, flowState });
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
@ -286,7 +271,6 @@ class App extends EventEmitter {
|
||||
params: Record<string, any>,
|
||||
args: any[],
|
||||
flowState?: FlowState,
|
||||
node?: Node,
|
||||
) {
|
||||
if (!dsId || !methodName) return;
|
||||
|
||||
@ -294,32 +278,28 @@ class App extends EventEmitter {
|
||||
|
||||
if (!dataSource) return;
|
||||
|
||||
try {
|
||||
const methods = dataSource.methods || [];
|
||||
|
||||
const method = methods.find((item) => item.name === methodName);
|
||||
if (method && typeof method.content === 'function') {
|
||||
await method.content({ app: this, params, dataSource, eventParams: args, flowState, node });
|
||||
} else if (typeof dataSource[methodName] === 'function') {
|
||||
await dataSource[methodName]();
|
||||
}
|
||||
|
||||
if (!method) return;
|
||||
|
||||
if (typeof method.content === 'function') {
|
||||
try {
|
||||
await method.content({ app: this, params, dataSource, eventParams: args, flowState });
|
||||
} catch (e: any) {
|
||||
if (this.errorHandler) {
|
||||
this.errorHandler(e, dataSource, { type: 'data-source-method', params, eventParams: args, flowState, node });
|
||||
this.errorHandler(e, dataSource, { type: 'data-source-method', params, eventParams: args, flowState });
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
this.removeAllListeners();
|
||||
this.page?.destroy();
|
||||
this.page = undefined;
|
||||
this.pageFragments.forEach((page) => {
|
||||
page.destroy();
|
||||
});
|
||||
this.pageFragments.clear();
|
||||
|
||||
this.flexible?.destroy();
|
||||
this.flexible = undefined;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -31,9 +31,6 @@ import {
|
||||
type DataSourceItemConfig,
|
||||
type EventActionItem,
|
||||
type EventConfig,
|
||||
Id,
|
||||
NODE_DISABLE_CODE_BLOCK_KEY,
|
||||
NODE_DISABLE_DATA_SOURCE_KEY,
|
||||
} from '@tmagic/schema';
|
||||
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX } from '@tmagic/utils';
|
||||
|
||||
@ -42,17 +39,8 @@ import FlowState from './FlowState';
|
||||
import type { default as TMagicNode } from './Node';
|
||||
import { AfterEventHandler, BeforeEventHandler } from './type';
|
||||
|
||||
interface EventCache {
|
||||
toId: Id;
|
||||
method: string;
|
||||
fromCpt: any;
|
||||
args: any[];
|
||||
handled?: boolean;
|
||||
}
|
||||
|
||||
export default class EventHelper extends EventEmitter {
|
||||
public app: TMagicApp;
|
||||
public eventQueue: EventCache[] = [];
|
||||
|
||||
private nodeEventList = new Map<(fromCpt: TMagicNode, ...args: any[]) => void, symbol>();
|
||||
private dataSourceEventList = new Map<string, Map<string, (...args: any[]) => void>>();
|
||||
@ -113,23 +101,21 @@ export default class EventHelper extends EventEmitter {
|
||||
}
|
||||
|
||||
public removeNodeEvents() {
|
||||
for (const handler of Array.from(this.nodeEventList.keys())) {
|
||||
Array.from(this.nodeEventList.keys()).forEach((handler) => {
|
||||
const name = this.nodeEventList.get(handler);
|
||||
name && this.off(name, handler);
|
||||
}
|
||||
});
|
||||
|
||||
this.nodeEventList.clear();
|
||||
}
|
||||
|
||||
public bindDataSourceEvents() {
|
||||
const dataSourceList = Array.from(this.app.dataSourceManager?.dataSourceMap.values() || []);
|
||||
|
||||
public bindDataSourceEvents(dataSourceList: DataSource[]) {
|
||||
this.removeDataSourceEvents(dataSourceList);
|
||||
|
||||
for (const dataSource of dataSourceList) {
|
||||
dataSourceList.forEach((dataSource) => {
|
||||
const dataSourceEvent = this.dataSourceEventList.get(dataSource.id) ?? new Map<string, (args: any) => void>();
|
||||
|
||||
for (const event of dataSource.schema.events || []) {
|
||||
(dataSource.schema.events || []).forEach((event) => {
|
||||
const [prefix, ...path] = event.name?.split('.') || [];
|
||||
if (!prefix) return;
|
||||
const handler = (...args: any[]) => {
|
||||
@ -143,9 +129,9 @@ export default class EventHelper extends EventEmitter {
|
||||
// 数据源自定义事件
|
||||
dataSource.on(prefix, handler);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.dataSourceEventList.set(dataSource.id, dataSourceEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public removeDataSourceEvents(dataSourceList: DataSource[]) {
|
||||
@ -154,32 +140,24 @@ export default class EventHelper extends EventEmitter {
|
||||
}
|
||||
|
||||
// 先清掉之前注册的事件,重新注册
|
||||
for (const dataSource of dataSourceList) {
|
||||
dataSourceList.forEach((dataSource) => {
|
||||
const dataSourceEvent = this.dataSourceEventList.get(dataSource.id)!;
|
||||
|
||||
if (!dataSourceEvent) return;
|
||||
|
||||
for (const eventName of Array.from(dataSourceEvent.keys())) {
|
||||
Array.from(dataSourceEvent.keys()).forEach((eventName) => {
|
||||
const [prefix, ...path] = eventName.split('.');
|
||||
if (prefix === DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX) {
|
||||
dataSource.offDataChange(path.join('.'), dataSourceEvent.get(eventName)!);
|
||||
} else {
|
||||
dataSource.off(prefix, dataSourceEvent.get(eventName)!);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.dataSourceEventList.clear();
|
||||
}
|
||||
|
||||
public getEventQueue() {
|
||||
return this.eventQueue;
|
||||
}
|
||||
|
||||
public addEventToQueue(event: EventCache) {
|
||||
this.eventQueue.push(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件联动处理函数
|
||||
* @param eventsConfigIndex 事件配置索引,可以通过此索引从node.event中获取最新事件配置
|
||||
@ -202,9 +180,9 @@ export default class EventHelper extends EventEmitter {
|
||||
if (typeof config === 'number') {
|
||||
// 事件响应中可能会有修改数据源数据的,会更新dsl,所以这里需要重新获取
|
||||
const actionItem = ((fromCpt as TMagicNode).events[config] as EventConfig).actions[i];
|
||||
await this.actionHandler(actionItem, fromCpt as TMagicNode, args, flowState);
|
||||
this.actionHandler(actionItem, fromCpt as TMagicNode, args, flowState);
|
||||
} else {
|
||||
await this.actionHandler(actions[i], fromCpt as DataSource, args, flowState);
|
||||
this.actionHandler(actions[i], fromCpt as DataSource, args, flowState);
|
||||
}
|
||||
}
|
||||
flowState.reset();
|
||||
@ -238,17 +216,10 @@ export default class EventHelper extends EventEmitter {
|
||||
// 组件动作
|
||||
await this.compActionHandler(compActionItem, fromCpt, args);
|
||||
} else if (actionItem.actionType === ActionType.CODE) {
|
||||
if (fromCpt.data[NODE_DISABLE_CODE_BLOCK_KEY]) {
|
||||
return;
|
||||
}
|
||||
const codeActionItem = actionItem as CodeItemConfig;
|
||||
// 执行代码块
|
||||
await this.app.runCode(codeActionItem.codeId, codeActionItem.params || {}, args, flowState);
|
||||
} else if (actionItem.actionType === ActionType.DATA_SOURCE) {
|
||||
if (fromCpt.data[NODE_DISABLE_DATA_SOURCE_KEY]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataSourceActionItem = actionItem as DataSourceItemConfig;
|
||||
|
||||
const [dsId, methodName] = dataSourceActionItem.dataSourceMethod;
|
||||
@ -278,44 +249,19 @@ export default class EventHelper extends EventEmitter {
|
||||
[to, methodName] = methodName;
|
||||
}
|
||||
|
||||
const toNodes = [];
|
||||
const toNode = this.app.getNode(to);
|
||||
if (toNode) {
|
||||
toNodes.push(toNode);
|
||||
}
|
||||
if (!toNode) throw new Error(`ID为${to}的组件不存在`);
|
||||
|
||||
for (const [, page] of this.app.pageFragments) {
|
||||
const node = page.getNode(to);
|
||||
if (node) {
|
||||
toNodes.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (toNodes.length === 0) {
|
||||
this.addEventToQueue({
|
||||
toId: to,
|
||||
method: methodName,
|
||||
fromCpt,
|
||||
args,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceMethodPropmise = [];
|
||||
for (const node of toNodes) {
|
||||
if (node.instance) {
|
||||
if (typeof node.instance[methodName] === 'function') {
|
||||
instanceMethodPropmise.push(node.instance[methodName](fromCpt, ...args));
|
||||
if (toNode.instance) {
|
||||
if (typeof toNode.instance[methodName] === 'function') {
|
||||
await toNode.instance[methodName](fromCpt, ...args);
|
||||
}
|
||||
} else {
|
||||
node.addEventToQueue({
|
||||
toNode.addEventToQueue({
|
||||
method: methodName,
|
||||
fromCpt,
|
||||
args,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(instanceMethodPropmise);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -18,8 +18,9 @@
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
import { DataSource } from '@tmagic/data-source';
|
||||
import type { EventConfig, MNode } from '@tmagic/schema';
|
||||
import { HookCodeType, HookType, NODE_DISABLE_CODE_BLOCK_KEY } from '@tmagic/schema';
|
||||
import { HookCodeType, HookType } from '@tmagic/schema';
|
||||
|
||||
import type { default as TMagicApp } from './App';
|
||||
import type Page from './Page';
|
||||
@ -48,7 +49,7 @@ class Node extends EventEmitter {
|
||||
[key: string]: any;
|
||||
};
|
||||
public events: EventConfig[] = [];
|
||||
public instance?: any = null;
|
||||
public instance?: any = {};
|
||||
public page?: Page;
|
||||
public parent?: Node;
|
||||
public app: TMagicApp;
|
||||
@ -74,14 +75,7 @@ class Node extends EventEmitter {
|
||||
this.events = events || [];
|
||||
this.style = style || {};
|
||||
try {
|
||||
if (
|
||||
this.instance &&
|
||||
!Object.isFrozen(this.instance) &&
|
||||
Object.getOwnPropertyDescriptor(this.instance, 'config')?.writable !== false &&
|
||||
!this.instance.__isVue
|
||||
) {
|
||||
this.instance.config = data;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e: any) {}
|
||||
|
||||
@ -140,10 +134,23 @@ class Node extends EventEmitter {
|
||||
for (const item of hookData.hookData) {
|
||||
const { codeType = HookCodeType.CODE, codeId, params: itemParams = {} } = item;
|
||||
|
||||
if (codeType === HookCodeType.CODE && typeof codeId === 'string') {
|
||||
await this.app.runCode(codeId, params || itemParams, [], undefined, this);
|
||||
let functionContent: ((...args: any[]) => any) | string | undefined;
|
||||
const functionParams: { app: TMagicApp; node: Node; params: Record<string, any>; dataSource?: DataSource } = {
|
||||
app: this.app,
|
||||
node: this,
|
||||
params: params || itemParams,
|
||||
};
|
||||
|
||||
if (codeType === HookCodeType.CODE && typeof codeId === 'string' && this.app.codeDsl?.[codeId]) {
|
||||
functionContent = this.app.codeDsl[codeId].content;
|
||||
} else if (codeType === HookCodeType.DATA_SOURCE_METHOD && Array.isArray(codeId) && codeId.length > 1) {
|
||||
await this.app.runDataSourceMethod(codeId[0], codeId[1], params || itemParams, [], undefined, this);
|
||||
const dataSource = this.app.dataSourceManager?.get(codeId[0]);
|
||||
functionContent = dataSource?.methods.find((method) => method.name === codeId[1])?.content;
|
||||
functionParams.dataSource = dataSource;
|
||||
}
|
||||
|
||||
if (functionContent && typeof functionContent === 'function') {
|
||||
await functionContent(functionParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -160,8 +167,8 @@ class Node extends EventEmitter {
|
||||
this.once('created', (instance: any) => {
|
||||
this.once('destroy', () => {
|
||||
this.instance = null;
|
||||
if (this.data[NODE_DISABLE_CODE_BLOCK_KEY] !== true) {
|
||||
this.runHookCode('destroy');
|
||||
if (typeof this.data.destroy === 'function') {
|
||||
this.data.destroy(this);
|
||||
}
|
||||
|
||||
this.listenLifeSafe();
|
||||
@ -171,9 +178,7 @@ class Node extends EventEmitter {
|
||||
this.setInstance(instance);
|
||||
}
|
||||
|
||||
if (this.data[NODE_DISABLE_CODE_BLOCK_KEY] !== true) {
|
||||
this.runHookCode('created');
|
||||
}
|
||||
});
|
||||
|
||||
this.once('mounted', (instance: any) => {
|
||||
@ -188,28 +193,7 @@ class Node extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.app.eventHelper) {
|
||||
for (const eventConfig of this.app.eventHelper.getEventQueue()) {
|
||||
for (const [, page] of this.app.pageFragments) {
|
||||
const node = page.getNode(eventConfig.toId);
|
||||
if (node && node === this) {
|
||||
if (typeof instance[eventConfig.method] === 'function') {
|
||||
await instance[eventConfig.method](eventConfig.fromCpt, ...eventConfig.args);
|
||||
}
|
||||
|
||||
eventConfig.handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.app.eventHelper.eventQueue = this.app.eventHelper
|
||||
.getEventQueue()
|
||||
.filter((eventConfig) => !eventConfig.handled);
|
||||
}
|
||||
|
||||
if (this.data[NODE_DISABLE_CODE_BLOCK_KEY] !== true) {
|
||||
this.runHookCode('mounted');
|
||||
}
|
||||
};
|
||||
handler();
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,7 +22,6 @@ import App from './App';
|
||||
import IteratorContainer from './IteratorContainer';
|
||||
import type { default as TMagicNode } from './Node';
|
||||
import Node from './Node';
|
||||
import { GetNodeOptions } from './type';
|
||||
interface ConfigOptions {
|
||||
config: MPage | MPageFragment;
|
||||
app: App;
|
||||
@ -65,15 +64,8 @@ class Page extends Node {
|
||||
|
||||
if (config.type && this.app.pageFragmentContainerType.has(config.type) && config.pageFragmentId) {
|
||||
const pageFragment = this.app.dsl?.items?.find((page) => page.id === config.pageFragmentId);
|
||||
|
||||
if (pageFragment) {
|
||||
this.app.pageFragments.set(
|
||||
config.id,
|
||||
new Page({
|
||||
config: pageFragment,
|
||||
app: this.app,
|
||||
}),
|
||||
);
|
||||
config.items = [pageFragment];
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,16 +76,13 @@ class Page extends Node {
|
||||
|
||||
public getNode<T extends TMagicNode = TMagicNode>(
|
||||
id: Id,
|
||||
{ iteratorContainerId, iteratorIndex, pageFragmentContainerId }: GetNodeOptions = {},
|
||||
iteratorContainerId?: Id[],
|
||||
iteratorIndex?: number[],
|
||||
): T | undefined {
|
||||
if (this.nodes.has(id)) {
|
||||
return this.nodes.get(id) as T;
|
||||
}
|
||||
|
||||
if (pageFragmentContainerId) {
|
||||
return this.app.pageFragments.get(pageFragmentContainerId)?.getNode(id, { iteratorContainerId, iteratorIndex });
|
||||
}
|
||||
|
||||
if (Array.isArray(iteratorContainerId) && iteratorContainerId.length && Array.isArray(iteratorIndex)) {
|
||||
let iteratorContainer = this.nodes.get(iteratorContainerId[0]) as IteratorContainer;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -43,9 +43,3 @@ export type AfterEventHandler = (args: {
|
||||
source: TMagicNode | DataSource | undefined;
|
||||
args: any[];
|
||||
}) => void;
|
||||
|
||||
export interface GetNodeOptions {
|
||||
iteratorContainerId?: Id[];
|
||||
iteratorIndex?: number[];
|
||||
pageFragmentContainerId?: Id;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -168,21 +168,15 @@ describe('App', () => {
|
||||
1,
|
||||
);
|
||||
|
||||
expect(app.getNode('text', { iteratorContainerId: ['iterator-container_1'], iteratorIndex: [0] })?.data.text).toBe(
|
||||
'1',
|
||||
);
|
||||
expect(app.getNode('text', { iteratorContainerId: ['iterator-container_1'], iteratorIndex: [1] })?.data.text).toBe(
|
||||
'2',
|
||||
);
|
||||
expect(
|
||||
app.getNode('text_page_fragment', { iteratorContainerId: ['iterator-container_1'], iteratorIndex: [0] })?.data
|
||||
.text,
|
||||
).toBe('text_page_fragment');
|
||||
expect(app.getNode('text', ['iterator-container_1'], [0])?.data.text).toBe('1');
|
||||
expect(app.getNode('text', ['iterator-container_1'], [1])?.data.text).toBe('2');
|
||||
expect(app.getNode('text_page_fragment', ['iterator-container_1'], [0])?.data.text).toBe('text_page_fragment');
|
||||
|
||||
const ic1 = app.getNode('iterator-container_11', {
|
||||
iteratorContainerId: ['iterator-container_1'],
|
||||
iteratorIndex: [0],
|
||||
}) as unknown as TMagicIteratorContainer;
|
||||
const ic1 = app.getNode(
|
||||
'iterator-container_11',
|
||||
['iterator-container_1'],
|
||||
[0],
|
||||
) as unknown as TMagicIteratorContainer;
|
||||
|
||||
ic1?.setNodes(
|
||||
[
|
||||
@ -206,10 +200,11 @@ describe('App', () => {
|
||||
1,
|
||||
);
|
||||
|
||||
const ic2 = app.getNode('iterator-container_11', {
|
||||
iteratorContainerId: ['iterator-container_1'],
|
||||
iteratorIndex: [1],
|
||||
}) as unknown as TMagicIteratorContainer;
|
||||
const ic2 = app.getNode(
|
||||
'iterator-container_11',
|
||||
['iterator-container_1'],
|
||||
[1],
|
||||
) as unknown as TMagicIteratorContainer;
|
||||
|
||||
ic2?.setNodes(
|
||||
[
|
||||
@ -233,30 +228,10 @@ describe('App', () => {
|
||||
1,
|
||||
);
|
||||
|
||||
expect(
|
||||
app.getNode('text', {
|
||||
iteratorContainerId: ['iterator-container_1', 'iterator-container_11'],
|
||||
iteratorIndex: [0, 0],
|
||||
})?.data.text,
|
||||
).toBe('111');
|
||||
expect(
|
||||
app.getNode('text', {
|
||||
iteratorContainerId: ['iterator-container_1', 'iterator-container_11'],
|
||||
iteratorIndex: [0, 1],
|
||||
})?.data.text,
|
||||
).toBe('222');
|
||||
expect(
|
||||
app.getNode('text', {
|
||||
iteratorContainerId: ['iterator-container_1', 'iterator-container_11'],
|
||||
iteratorIndex: [1, 0],
|
||||
})?.data.text,
|
||||
).toBe('11');
|
||||
expect(
|
||||
app.getNode('text', {
|
||||
iteratorContainerId: ['iterator-container_1', 'iterator-container_11'],
|
||||
iteratorIndex: [1, 1],
|
||||
})?.data.text,
|
||||
).toBe('22');
|
||||
expect(app.getNode('text', ['iterator-container_1', 'iterator-container_11'], [0, 0])?.data.text).toBe('111');
|
||||
expect(app.getNode('text', ['iterator-container_1', 'iterator-container_11'], [0, 1])?.data.text).toBe('222');
|
||||
expect(app.getNode('text', ['iterator-container_1', 'iterator-container_11'], [1, 0])?.data.text).toBe('11');
|
||||
expect(app.getNode('text', ['iterator-container_1', 'iterator-container_11'], [1, 1])?.data.text).toBe('22');
|
||||
|
||||
ic.resetNodes();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.6.1",
|
||||
"version": "1.5.22",
|
||||
"name": "@tmagic/data-source",
|
||||
"type": "module",
|
||||
"main": "dist/tmagic-data-source.umd.cjs",
|
||||
@ -37,7 +37,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tmagic/core": "workspace:*",
|
||||
"typescript": "catalog:"
|
||||
"typescript": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
@ -45,7 +45,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/events": "^3.0.3",
|
||||
"@types/events": "^3.0.0",
|
||||
"@types/lodash-es": "^4.17.4"
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -22,13 +22,7 @@ import EventEmitter from 'events';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import type { DataSourceSchema, default as TMagicApp, DisplayCond, Id, MNode } from '@tmagic/core';
|
||||
import {
|
||||
compiledNode,
|
||||
getDefaultValueFromFields,
|
||||
NODE_CONDS_KEY,
|
||||
NODE_CONDS_RESULT_KEY,
|
||||
NODE_DISABLE_DATA_SOURCE_KEY,
|
||||
} from '@tmagic/core';
|
||||
import { compiledNode, getDefaultValueFromFields, NODE_CONDS_KEY } from '@tmagic/core';
|
||||
|
||||
import { SimpleObservedData } from './observed-data/SimpleObservedData';
|
||||
import { DataSource, HttpDataSource } from './data-sources';
|
||||
@ -231,12 +225,7 @@ class DataSourceManager extends EventEmitter {
|
||||
* @param {boolean} deep 是否编译子项(items),默认为false
|
||||
* @returns {MNode} 编译后的组件dsl
|
||||
*/
|
||||
public compiledNode(n: MNode, sourceId?: Id, deep = false) {
|
||||
if (n[NODE_DISABLE_DATA_SOURCE_KEY]) {
|
||||
return n;
|
||||
}
|
||||
|
||||
const { items, ...node } = n;
|
||||
public compiledNode({ items, ...node }: MNode, sourceId?: Id, deep = false) {
|
||||
const newNode = cloneDeep(node);
|
||||
|
||||
if (items) {
|
||||
@ -244,9 +233,8 @@ class DataSourceManager extends EventEmitter {
|
||||
Array.isArray(items) && deep ? items.map((item) => this.compiledNode(item, sourceId, deep)) : items;
|
||||
}
|
||||
|
||||
if (node.condResult === false || (typeof node.condResult === 'undefined' && node[NODE_CONDS_RESULT_KEY])) {
|
||||
return newNode;
|
||||
}
|
||||
if (node.condResult === false) return newNode;
|
||||
if (node.visible === false) return newNode;
|
||||
|
||||
// 编译函数这里作为参数,方便后续支持自定义编译
|
||||
return compiledNode(
|
||||
@ -262,24 +250,8 @@ class DataSourceManager extends EventEmitter {
|
||||
* @param {{ [NODE_CONDS_KEY]?: DisplayCond[] }} node 显示条件组配置
|
||||
* @returns {boolean} 是否显示
|
||||
*/
|
||||
public compliedConds(
|
||||
node: {
|
||||
[NODE_CONDS_KEY]?: DisplayCond[];
|
||||
[NODE_CONDS_RESULT_KEY]?: boolean;
|
||||
[NODE_DISABLE_DATA_SOURCE_KEY]?: boolean;
|
||||
},
|
||||
data = this.data,
|
||||
) {
|
||||
if (node[NODE_DISABLE_DATA_SOURCE_KEY]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const result = compliedConditions(node, data);
|
||||
|
||||
if (!node[NODE_CONDS_RESULT_KEY]) {
|
||||
return result;
|
||||
}
|
||||
return !result;
|
||||
public compliedConds(node: { [NODE_CONDS_KEY]?: DisplayCond[] }) {
|
||||
return compliedConditions(node, this.data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,11 +263,7 @@ class DataSourceManager extends EventEmitter {
|
||||
*/
|
||||
public compliedIteratorItemConds(
|
||||
itemData: any,
|
||||
node: {
|
||||
[NODE_CONDS_KEY]?: DisplayCond[];
|
||||
[NODE_CONDS_RESULT_KEY]?: boolean;
|
||||
[NODE_DISABLE_DATA_SOURCE_KEY]?: boolean;
|
||||
},
|
||||
node: { [NODE_CONDS_KEY]?: DisplayCond[] },
|
||||
dataSourceField: string[] = [],
|
||||
) {
|
||||
const [dsId, ...keys] = dataSourceField;
|
||||
@ -303,7 +271,7 @@ class DataSourceManager extends EventEmitter {
|
||||
if (!ds) return true;
|
||||
|
||||
const ctxData = createIteratorContentData(itemData, ds.id, keys, this.data);
|
||||
return this.compliedConds(node, ctxData);
|
||||
return compliedConditions(node, ctxData);
|
||||
}
|
||||
|
||||
public compliedIteratorItems(itemData: any, nodes: MNode[], dataSourceField: string[] = []): MNode[] {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -18,7 +18,7 @@
|
||||
import { union } from 'lodash-es';
|
||||
|
||||
import type { default as TMagicApp } from '@tmagic/core';
|
||||
import { getDepNodeIds, getNodes, isPage, isPageFragment, replaceChildNode } from '@tmagic/core';
|
||||
import { getDepNodeIds, getNodes, isPage } from '@tmagic/core';
|
||||
|
||||
import DataSourceManager from './DataSourceManager';
|
||||
import type { ChangeEvent, DataSourceManagerData } from './types';
|
||||
@ -52,19 +52,18 @@ export const createDataSourceManager = (app: TMagicApp, useMock?: boolean, initi
|
||||
|
||||
// ssr环境下,数据应该是提前准备好的(放到initialData中),不应该发生变化,无需监听
|
||||
// 有initialData不一定是在ssr环境下
|
||||
if (app.jsEngine === 'nodejs') {
|
||||
return dataSourceManager;
|
||||
}
|
||||
|
||||
if (app.jsEngine !== 'nodejs') {
|
||||
dataSourceManager.on('change', (sourceId: string, changeEvent: ChangeEvent) => {
|
||||
const dep = dsl.dataSourceDeps?.[sourceId] || {};
|
||||
const condDep = dsl.dataSourceCondDeps?.[sourceId] || {};
|
||||
|
||||
const nodeIds = union([...Object.keys(condDep), ...Object.keys(dep)]);
|
||||
|
||||
for (const page of dsl.items) {
|
||||
if (app.platform === 'editor' || (isPage(page) && page.id === app.page?.data.id) || isPageFragment(page)) {
|
||||
const newNodes = getNodes(nodeIds, [page]).map((node) => {
|
||||
const pages = app.page?.data && app.platform !== 'editor' ? [app.page.data] : dsl.items;
|
||||
|
||||
dataSourceManager.emit(
|
||||
'update-data',
|
||||
getNodes(nodeIds, pages).map((node) => {
|
||||
if (app.platform !== 'editor') {
|
||||
node.condResult = dataSourceManager.compliedConds(node);
|
||||
}
|
||||
@ -74,33 +73,19 @@ export const createDataSourceManager = (app: TMagicApp, useMock?: boolean, initi
|
||||
if (typeof app.page?.setData === 'function') {
|
||||
if (isPage(newNode)) {
|
||||
app.page.setData(newNode);
|
||||
} else if (page.id === app.page.data.id && !app.page.instance) {
|
||||
replaceChildNode(newNode, [app.page.data]);
|
||||
}
|
||||
|
||||
app.getNode(node.id)?.setData(newNode);
|
||||
|
||||
for (const [, pageFragment] of app.pageFragments) {
|
||||
if (pageFragment.data.id === newNode.id) {
|
||||
pageFragment.setData(newNode);
|
||||
} else if (pageFragment.data.id === page.id) {
|
||||
pageFragment.getNode(newNode.id)?.setData(newNode);
|
||||
if (!pageFragment.instance) {
|
||||
replaceChildNode(newNode, [pageFragment.data]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const n = app.page.getNode(node.id);
|
||||
n?.setData(newNode);
|
||||
}
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}),
|
||||
sourceId,
|
||||
changeEvent,
|
||||
);
|
||||
});
|
||||
|
||||
if (newNodes.length) {
|
||||
dataSourceManager.emit('update-data', newNodes, sourceId, changeEvent, page.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return dataSourceManager;
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -23,9 +23,9 @@ export class DeepObservedData extends ObservedData {
|
||||
update = (data: any, path?: string) => {
|
||||
this.state?.update(path ?? '', data);
|
||||
};
|
||||
on = (path: string, callback: (newVal: any) => void, options?: { immediate?: boolean }) => {
|
||||
on = (path: string, callback: (newVal: any) => void) => {
|
||||
// subscribe 会立即执行一次,ignoreFirstCall 会忽略第一次执行
|
||||
const unsubscribe = this.state!.subscribe(path, options?.immediate ? callback : ignoreFirstCall(callback));
|
||||
const unsubscribe = this.state!.subscribe(path, ignoreFirstCall(callback));
|
||||
|
||||
// 把取消监听的函数保存下来,供 off 时调用
|
||||
const pathSubscribers = this.subscribers.get(path) ?? new Map<Function, () => void>();
|
||||
|
@ -1,7 +1,7 @@
|
||||
export abstract class ObservedData {
|
||||
abstract update(data: any, path?: string): void;
|
||||
|
||||
abstract on(path: string, callback: (newVal: any) => void, options?: { immediate?: boolean }): void;
|
||||
abstract on(path: string, callback: (newVal: any) => void): void;
|
||||
|
||||
abstract off(path: string, callback: (newVal: any) => void): void;
|
||||
|
||||
|
@ -32,10 +32,7 @@ export class SimpleObservedData extends ObservedData {
|
||||
this.event.emit('', changeEvent);
|
||||
}
|
||||
|
||||
on(path: string, callback: (newVal: any) => void, options?: { immediate?: boolean }): void {
|
||||
if (options?.immediate) {
|
||||
callback(this.getData(path));
|
||||
}
|
||||
on(path: string, callback: (newVal: any) => void): void {
|
||||
this.event.on(path, callback);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.6.1",
|
||||
"version": "1.5.22",
|
||||
"name": "@tmagic/dep",
|
||||
"type": "module",
|
||||
"main": "dist/tmagic-dep.umd.cjs",
|
||||
@ -30,7 +30,7 @@
|
||||
"peerDependencies": {
|
||||
"@tmagic/schema": "workspace:*",
|
||||
"@tmagic/utils": "workspace:*",
|
||||
"typescript": "catalog:"
|
||||
"typescript": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { NODE_DISABLE_CODE_BLOCK_KEY, NODE_DISABLE_DATA_SOURCE_KEY } from '@tmagic/schema';
|
||||
import { isObject } from '@tmagic/utils';
|
||||
|
||||
import type Target from './Target';
|
||||
@ -190,19 +189,6 @@ export default class Watcher {
|
||||
}
|
||||
|
||||
public collectItem(node: TargetNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
||||
const dataSourceTargetTypes: string[] = [
|
||||
DepTargetType.DATA_SOURCE,
|
||||
DepTargetType.DATA_SOURCE_COND,
|
||||
DepTargetType.DATA_SOURCE_METHOD,
|
||||
];
|
||||
if (node[NODE_DISABLE_DATA_SOURCE_KEY] && dataSourceTargetTypes.includes(target.type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (node[NODE_DISABLE_CODE_BLOCK_KEY] && target.type === DepTargetType.CODE_BLOCK) {
|
||||
return;
|
||||
}
|
||||
|
||||
const collectTarget = (config: Record<string | number, any>, prop = '') => {
|
||||
const doCollect = (key: string, value: any) => {
|
||||
const keyIsItems = key === this.childrenProp;
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.6.1",
|
||||
"version": "1.5.22",
|
||||
"name": "@tmagic/design",
|
||||
"type": "module",
|
||||
"sideEffects": [
|
||||
@ -42,8 +42,8 @@
|
||||
"@popperjs/core": "^2.11.8"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": "catalog:",
|
||||
"typescript": "catalog:"
|
||||
"vue": ">=3.5.0",
|
||||
"typescript": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "1.6.1",
|
||||
"version": "1.5.22",
|
||||
"name": "@tmagic/editor",
|
||||
"type": "module",
|
||||
"sideEffects": [
|
||||
@ -65,7 +65,7 @@
|
||||
"sortablejs": "^1.15.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/events": "^3.0.3",
|
||||
"@types/events": "^3.0.0",
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"@types/serialize-javascript": "^5.0.4",
|
||||
"@types/sortablejs": "^1.15.8",
|
||||
@ -75,8 +75,8 @@
|
||||
"peerDependencies": {
|
||||
"@tmagic/core": "workspace:*",
|
||||
"monaco-editor": "^0.48.0",
|
||||
"typescript": "catalog:",
|
||||
"vue": "catalog:"
|
||||
"typescript": "*",
|
||||
"vue": ">=3.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
|
@ -97,7 +97,6 @@
|
||||
:extend-state="extendFormState"
|
||||
:disabled-show-src="disabledShowSrc"
|
||||
@mounted="propsPanelMountedHandler"
|
||||
@unmounted="propsPanelUnmountedHandler"
|
||||
@form-error="propsPanelFormErrorHandler"
|
||||
@submit-error="propsPanelSubmitErrorHandler"
|
||||
>
|
||||
@ -129,7 +128,7 @@
|
||||
<script lang="ts" setup>
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
import { provide } from 'vue';
|
||||
import { provide, watch } from 'vue';
|
||||
|
||||
import type { MApp } from '@tmagic/core';
|
||||
|
||||
@ -164,7 +163,6 @@ defineOptions({
|
||||
|
||||
const emit = defineEmits<{
|
||||
'props-panel-mounted': [instance: InstanceType<typeof FormPanel>];
|
||||
'props-panel-unmounted': [];
|
||||
'update:modelValue': [value: MApp | null];
|
||||
'props-form-error': [e: any];
|
||||
'props-submit-error': [e: any];
|
||||
@ -211,6 +209,22 @@ const stageOptions: StageOptions = {
|
||||
|
||||
stageOverlayService.set('stageOptions', stageOptions);
|
||||
|
||||
watch(
|
||||
() => props.runtimeUrl,
|
||||
(url) => {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stage = editorService.get('stage');
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
stage.reloadIframe(url);
|
||||
},
|
||||
);
|
||||
|
||||
provide('services', services);
|
||||
|
||||
provide('codeOptions', props.codeOptions);
|
||||
@ -221,9 +235,6 @@ provide<EventBus>('eventBus', new EventEmitter());
|
||||
const propsPanelMountedHandler = (e: InstanceType<typeof FormPanel>) => {
|
||||
emit('props-panel-mounted', e);
|
||||
};
|
||||
const propsPanelUnmountedHandler = () => {
|
||||
emit('props-panel-unmounted');
|
||||
};
|
||||
|
||||
const propsPanelSubmitErrorHandler = (e: any) => {
|
||||
emit('props-submit-error', e);
|
||||
|
@ -3,8 +3,9 @@ import type { FormConfig, FormState } from '@tmagic/form';
|
||||
import StageCore, {
|
||||
CONTAINER_HIGHLIGHT_CLASS_NAME,
|
||||
ContainerHighlightType,
|
||||
type CustomizeMoveableOptions,
|
||||
type CustomizeMoveableOptionsCallbackConfig,
|
||||
type GuidesOptions,
|
||||
type MoveableOptions,
|
||||
RenderType,
|
||||
type UpdateDragEl,
|
||||
} from '@tmagic/stage';
|
||||
@ -55,7 +56,7 @@ export interface EditorProps {
|
||||
datasourceConfigs?: Record<string, FormConfig>;
|
||||
datasourceEventMethodList?: Record<string, { events: EventOption[]; methods: EventOption[] }>;
|
||||
/** 画布中组件选中框的移动范围 */
|
||||
moveableOptions?: CustomizeMoveableOptions;
|
||||
moveableOptions?: MoveableOptions | ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions);
|
||||
/** 编辑器初始化时默认选中的组件ID */
|
||||
defaultSelected?: Id;
|
||||
/** 拖入画布中容器时,识别到容器后给容器根dom加上的class */
|
||||
@ -126,7 +127,7 @@ export const defaultEditorProps = {
|
||||
eventMethodList: () => ({}),
|
||||
datasourceValues: () => ({}),
|
||||
datasourceConfigs: () => ({}),
|
||||
canSelect: (el: HTMLElement) => Boolean(getIdFromEl()(el) && !el.dataset.tmagicPageFragmentContainerId),
|
||||
canSelect: (el: HTMLElement) => Boolean(getIdFromEl()(el)),
|
||||
isContainer: (el: HTMLElement) => el.classList.contains('magic-ui-container'),
|
||||
codeOptions: () => ({}),
|
||||
customContentMenu: (menus: (MenuButton | MenuComponent)[]) => menus,
|
||||
|
@ -32,6 +32,7 @@
|
||||
content="选择数据源"
|
||||
>
|
||||
<TMagicButton
|
||||
style="margin-left: 5px"
|
||||
:type="showDataSourceFieldSelect ? 'primary' : 'default'"
|
||||
:size="size"
|
||||
@click="showDataSourceFieldSelect = !showDataSourceFieldSelect"
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="m-editor-data-source-fields">
|
||||
<MagicTable :data="model[name]" :columns="fieldColumns" :border="true"></MagicTable>
|
||||
<MagicTable :data="model[name]" :columns="fieldColumns"></MagicTable>
|
||||
|
||||
<div class="m-editor-data-source-fields-footer">
|
||||
<TMagicButton size="small" :disabled="disabled" plain @click="newFromJsonHandler()">快速添加</TMagicButton>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="m-editor-data-source-methods">
|
||||
<MagicTable :data="model[name]" :columns="methodColumns" :border="true"></MagicTable>
|
||||
<MagicTable :data="model[name]" :columns="methodColumns"></MagicTable>
|
||||
|
||||
<div class="m-editor-data-source-methods-footer">
|
||||
<TMagicButton size="small" type="primary" :disabled="disabled" plain @click="createCodeHandler"
|
||||
|
@ -1,19 +1,14 @@
|
||||
<template>
|
||||
<TMagicCollapse class="m-fields-style-setter" :model-value="collapseValue">
|
||||
<div class="m-fields-style-setter">
|
||||
<TMagicCollapse :model-value="collapseValue">
|
||||
<template v-for="(item, index) in list" :key="index">
|
||||
<TMagicCollapseItem :name="`${index}`">
|
||||
<template #title><MIcon :icon="Grid"></MIcon>{{ item.title }}</template>
|
||||
<component
|
||||
v-if="item.component"
|
||||
:is="item.component"
|
||||
:values="model[name]"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
@change="change"
|
||||
></component>
|
||||
<component v-if="item.component" :is="item.component" :values="model[name]" @change="change"></component>
|
||||
</TMagicCollapseItem>
|
||||
</template>
|
||||
</TMagicCollapse>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -6,21 +6,13 @@
|
||||
:key="index"
|
||||
link
|
||||
:class="model[name] === item.value && 'btn-active'"
|
||||
:disabled="disabled"
|
||||
@click="changeHandler(item.value)"
|
||||
>
|
||||
<div :class="['position-icon', item.class, model[name] === item.value && 'active']"></div>
|
||||
</TMagicButton>
|
||||
</div>
|
||||
<div class="custom-value">
|
||||
<TMagicInput
|
||||
v-model="model[name]"
|
||||
placeholder="自定义背景位置"
|
||||
clearable
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
@change="changeHandler"
|
||||
>
|
||||
<TMagicInput v-model="model[name]" size="small" placeholder="自定义背景位置" clearable @change="changeHandler">
|
||||
</TMagicInput>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,7 +30,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-value-container">
|
||||
<MContainer :config="config" :model="model" :size="size" :disabled="disabled" @change="change"></MContainer>
|
||||
<MContainer :config="config" :model="model" @change="change"></MContainer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -67,6 +67,7 @@ const config = computed(() => ({
|
||||
{
|
||||
name: `border${direction.value}Style`,
|
||||
text: '边框样式',
|
||||
|
||||
labelWidth: '68px',
|
||||
type: 'data-source-field-select',
|
||||
fieldConfig: {
|
||||
@ -89,8 +90,6 @@ const emit = defineEmits<{
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
model: FormValue;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
}>(),
|
||||
{},
|
||||
);
|
||||
|
@ -5,10 +5,9 @@
|
||||
<span class="next-input">
|
||||
<input
|
||||
v-model="model[item.name]"
|
||||
placeholder="0"
|
||||
:title="model[item.name]"
|
||||
:disabled="disabled"
|
||||
@change="change($event, item.name)"
|
||||
placeholder="0"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@ -61,8 +60,6 @@ const emit = defineEmits<{
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
model: FormValue;
|
||||
}>(),
|
||||
{},
|
||||
|
@ -4,10 +4,9 @@
|
||||
<span class="next-input">
|
||||
<input
|
||||
v-model="model[item.name]"
|
||||
placeholder="0"
|
||||
:title="model[item.name]"
|
||||
:disabled="disabled"
|
||||
@change="change($event, item.name)"
|
||||
placeholder="0"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@ -43,8 +42,6 @@ const emit = defineEmits<{
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
model: FormValue;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
}>(),
|
||||
{},
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
|
||||
<MContainer :config="config" :model="values" @change="change"></MContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -11,11 +11,7 @@ import type { StyleSchema } from '@tmagic/schema';
|
||||
import BackgroundPosition from '../components/BackgroundPosition.vue';
|
||||
import { BackgroundNoRepeat, BackgroundRepeat, BackgroundRepeatX, BackgroundRepeatY } from '../icons/background-repeat';
|
||||
|
||||
defineProps<{
|
||||
values: Partial<StyleSchema>;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
}>();
|
||||
defineProps<{ values: Partial<StyleSchema> }>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
change: [v: StyleSchema, eventData: ContainerChangeEventData];
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
|
||||
<Border :model="values" :size="size" :disabled="disabled" @change="change"></Border>
|
||||
<MContainer :config="config" :model="values" @change="change"></MContainer>
|
||||
<Border :model="values" @change="change"></Border>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -9,11 +9,7 @@ import type { StyleSchema } from '@tmagic/schema';
|
||||
|
||||
import Border from '../components/Border.vue';
|
||||
|
||||
defineProps<{
|
||||
values: Partial<StyleSchema>;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
}>();
|
||||
defineProps<{ values: Partial<StyleSchema> }>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
change: [v: StyleSchema, eventData: ContainerChangeEventData];
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
|
||||
<MContainer :config="config" :model="values" @change="change"></MContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -10,11 +10,7 @@ import type { StyleSchema } from '@tmagic/schema';
|
||||
|
||||
import { AlignCenter, AlignLeft, AlignRight } from '../icons/text-align';
|
||||
|
||||
defineProps<{
|
||||
values: Partial<StyleSchema>;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
}>();
|
||||
defineProps<{ values: Partial<StyleSchema> }>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
change: [v: StyleSchema, eventData: ContainerChangeEventData];
|
||||
|
@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
|
||||
<Box
|
||||
v-show="!['fixed', 'absolute'].includes(values.position)"
|
||||
:model="values"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
@change="change"
|
||||
></Box>
|
||||
<MContainer :config="config" :model="values" @change="change"></MContainer>
|
||||
<Box v-show="!['fixed', 'absolute'].includes(values.position)" :model="values" @change="change"></Box>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -34,8 +28,6 @@ import {
|
||||
|
||||
defineProps<{
|
||||
values: Partial<StyleSchema>;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@ -1,16 +1,12 @@
|
||||
<template>
|
||||
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
|
||||
<MContainer :config="config" :model="values" @change="change"></MContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ContainerChangeEventData, MContainer } from '@tmagic/form';
|
||||
import type { StyleSchema } from '@tmagic/schema';
|
||||
|
||||
const props = defineProps<{
|
||||
values: Partial<StyleSchema>;
|
||||
disabled?: boolean;
|
||||
size?: 'large' | 'default' | 'small';
|
||||
}>();
|
||||
const props = defineProps<{ values: Partial<StyleSchema> }>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
change: [v: string | StyleSchema, eventData: ContainerChangeEventData];
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -32,12 +32,10 @@ export const useFilter = (
|
||||
const visible = filterIsMatch(text, node);
|
||||
if (visible && parents.length) {
|
||||
parents.forEach((parent) => {
|
||||
if (text || text.length) {
|
||||
updateStatus(nodeStatusMap.value!, parent.id, {
|
||||
visible,
|
||||
expand: true,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,6 @@ export const useStage = (stageOptions: StageOptions) => {
|
||||
updateDragEl: stageOptions.updateDragEl,
|
||||
guidesOptions: stageOptions.guidesOptions,
|
||||
disabledMultiSelect: stageOptions.disabledMultiSelect,
|
||||
disabledRule: stageOptions.disabledRule,
|
||||
});
|
||||
|
||||
watch(
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { nextTick, onBeforeUnmount, reactive, toRaw, watch } from 'vue';
|
||||
import { onBeforeUnmount, reactive, toRaw, watch } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import type TMagicCore from '@tmagic/core';
|
||||
@ -21,11 +21,9 @@ import {
|
||||
NODE_CONDS_KEY,
|
||||
NodeType,
|
||||
Target,
|
||||
updateNode,
|
||||
} from '@tmagic/core';
|
||||
import { ChangeRecord } from '@tmagic/form';
|
||||
import StageCore from '@tmagic/stage';
|
||||
import { getDepNodeIds, getNodes, isPage, isValueIncludeDataSource } from '@tmagic/utils';
|
||||
import { getNodes, isPage, isValueIncludeDataSource } from '@tmagic/utils';
|
||||
|
||||
import PropsPanel from './layouts/PropsPanel.vue';
|
||||
import { isIncludeDataSource } from './utils/editor';
|
||||
@ -235,15 +233,14 @@ export const initServiceEvents = (
|
||||
) => {
|
||||
let getTMagicAppPrimise: Promise<TMagicCore | undefined> | null = null;
|
||||
|
||||
const getTMagicApp = async (): Promise<TMagicCore | undefined> => {
|
||||
const stage = await getStage();
|
||||
const { renderer } = stage;
|
||||
const getTMagicApp = (): Promise<TMagicCore | undefined> => {
|
||||
const renderer = editorService.get('stage')?.renderer;
|
||||
if (!renderer) {
|
||||
return void 0;
|
||||
return Promise.resolve(void 0);
|
||||
}
|
||||
|
||||
if (renderer.runtime) {
|
||||
return renderer.runtime.getApp?.();
|
||||
return Promise.resolve(renderer.runtime.getApp?.());
|
||||
}
|
||||
|
||||
if (getTMagicAppPrimise) {
|
||||
@ -347,86 +344,6 @@ export const initServiceEvents = (
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.runtimeUrl,
|
||||
(url) => {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
const stage = editorService.get('stage');
|
||||
if (!stage) {
|
||||
return;
|
||||
}
|
||||
|
||||
stage.reloadIframe(url);
|
||||
|
||||
stage.renderer?.once('runtime-ready', (runtime) => {
|
||||
runtime.updateRootConfig?.(cloneDeep(toRaw(editorService.get('root')))!);
|
||||
const page = editorService.get('page');
|
||||
const node = editorService.get('node');
|
||||
page?.id && runtime?.updatePageId?.(page.id);
|
||||
setTimeout(() => {
|
||||
node && stage?.select(toRaw(node.id));
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const getStage = (): Promise<StageCore> => {
|
||||
const stage = editorService.get('stage');
|
||||
if (stage) {
|
||||
return Promise.resolve(stage);
|
||||
}
|
||||
|
||||
return new Promise<StageCore>((resolve) => {
|
||||
const unWatch = watch(
|
||||
() => editorService.get('stage'),
|
||||
(stage) => {
|
||||
if (stage) {
|
||||
resolve(stage);
|
||||
nextTick(() => {
|
||||
unWatch();
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const updateStageDsl = async (value: MApp | null) => {
|
||||
const stage = await getStage();
|
||||
|
||||
const runtime = await stage.renderer?.getRuntime();
|
||||
const app = await getTMagicApp();
|
||||
|
||||
if (!app?.dataSourceManager) {
|
||||
runtime?.updateRootConfig?.(cloneDeep(toRaw(value))!);
|
||||
}
|
||||
|
||||
const page = editorService.get('page');
|
||||
const node = editorService.get('node');
|
||||
page?.id && runtime?.updatePageId?.(page.id);
|
||||
setTimeout(() => {
|
||||
node && stage?.select(toRaw(node.id));
|
||||
});
|
||||
|
||||
if (value) {
|
||||
depService.clearIdleTasks();
|
||||
|
||||
await (typeof Worker === 'undefined' ? collectIdle(value.items, true) : depService.collectByWorker(value));
|
||||
|
||||
const dsl = cloneDeep(toRaw(value));
|
||||
if (dsl.dataSources && dsl.dataSourceDeps && app?.dataSourceManager) {
|
||||
for (const node of getNodes(getDepNodeIds(dsl.dataSourceDeps), dsl.items)) {
|
||||
updateNode(app.dataSourceManager.compiledNode(node), dsl);
|
||||
}
|
||||
}
|
||||
|
||||
runtime?.updateRootConfig?.(dsl);
|
||||
}
|
||||
};
|
||||
|
||||
const initDataSourceDepTarget = (ds: DataSourceSchema) => {
|
||||
depService.addTarget(createDataSourceTarget(ds, reactive({})));
|
||||
depService.addTarget(createDataSourceMethodTarget(ds, reactive({})));
|
||||
@ -453,14 +370,18 @@ export const initServiceEvents = (
|
||||
}
|
||||
|
||||
if (Array.isArray(value.items)) {
|
||||
updateStageDsl(value);
|
||||
depService.clearIdleTasks();
|
||||
|
||||
(typeof Worker === 'undefined' ? collectIdle(value.items, true) : depService.collectByWorker(value)).then(() => {
|
||||
updateStageNodes(value.items);
|
||||
});
|
||||
} else {
|
||||
depService.clear();
|
||||
delete value.dataSourceDeps;
|
||||
delete value.dataSourceCondDeps;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const handler = async () => {
|
||||
const nodeId = editorService.get('node')?.id || props.defaultSelected;
|
||||
let node;
|
||||
if (nodeId) {
|
||||
@ -479,7 +400,9 @@ export const initServiceEvents = (
|
||||
if (toRaw(value) !== toRaw(preValue)) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
handler();
|
||||
};
|
||||
|
||||
// 新增节点,收集依赖
|
||||
|
@ -20,7 +20,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
|
||||
import { FullScreen } from '@element-plus/icons-vue';
|
||||
import { throttle } from 'lodash-es';
|
||||
import serialize from 'serialize-javascript';
|
||||
@ -62,7 +62,7 @@ const props = withDefaults(
|
||||
const emit = defineEmits(['initd', 'save']);
|
||||
|
||||
const toString = (v: string | any, language: string): string => {
|
||||
let value: string;
|
||||
let value = '';
|
||||
if (typeof v !== 'string') {
|
||||
if (language === 'json') {
|
||||
value = JSON.stringify(v, null, 2);
|
||||
@ -113,40 +113,19 @@ const setEditorValue = (v: string | any, m: string | any) => {
|
||||
if (props.type === 'diff') {
|
||||
const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
|
||||
const modifiedModel = monaco.editor.createModel(toString(m, props.language), 'text/javascript');
|
||||
const position = vsDiffEditor?.getPosition();
|
||||
const result = vsDiffEditor?.setModel({
|
||||
|
||||
return vsDiffEditor?.setModel({
|
||||
original: originalModel,
|
||||
modified: modifiedModel,
|
||||
});
|
||||
if (position) {
|
||||
vsDiffEditor?.setPosition(position);
|
||||
vsDiffEditor?.focus();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// 保存当前光标位置
|
||||
const position = vsEditor?.getPosition();
|
||||
const result = vsEditor?.setValue(values.value);
|
||||
// 恢复光标位置
|
||||
if (position) {
|
||||
vsEditor?.setPosition(position);
|
||||
vsEditor?.focus();
|
||||
}
|
||||
return result;
|
||||
|
||||
return vsEditor?.setValue(values.value);
|
||||
};
|
||||
|
||||
const getEditorValue = () =>
|
||||
(props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue()) || '';
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const newValue = getEditorValue();
|
||||
values.value = newValue;
|
||||
emit('save', props.parse ? parseCode(newValue, props.language) : newValue);
|
||||
}
|
||||
};
|
||||
const init = async () => {
|
||||
if (!codeEditorEl.value) return;
|
||||
|
||||
@ -170,7 +149,16 @@ const init = async () => {
|
||||
setEditorValue(props.initValues, props.modifiedValues);
|
||||
|
||||
emit('initd', vsEditor);
|
||||
codeEditorEl.value.addEventListener('keydown', handleKeyDown);
|
||||
|
||||
codeEditorEl.value.addEventListener('keydown', (e) => {
|
||||
if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const newValue = getEditorValue();
|
||||
values.value = newValue;
|
||||
emit('save', props.parse ? parseCode(newValue, props.language) : newValue);
|
||||
}
|
||||
});
|
||||
|
||||
if (props.type !== 'diff' && props.autoSave) {
|
||||
vsEditor?.onDidBlurEditorWidget(() => {
|
||||
@ -226,9 +214,7 @@ onBeforeUnmount(() => {
|
||||
vsEditor = null;
|
||||
vsDiffEditor = null;
|
||||
});
|
||||
onUnmounted(() => {
|
||||
codeEditorEl.value?.removeEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
|
||||
const fullScreen = ref(false);
|
||||
const fullScreenHandler = () => {
|
||||
fullScreen.value = !fullScreen.value;
|
||||
|
@ -78,7 +78,6 @@ const resizeObserver = new ResizeObserver(() => {
|
||||
|
||||
onMounted(() => {
|
||||
pageBarEl.value && resizeObserver.observe(pageBarEl.value);
|
||||
itemsContainerEl.value && resizeObserver.observe(itemsContainerEl.value);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@ -137,7 +136,7 @@ watch(
|
||||
let beforeDragList: Id[] = [];
|
||||
const options = {
|
||||
...{
|
||||
dataIdAttr: 'data-page-id', // 获取排序后的数据
|
||||
dataIdAttr: 'page-id', // 获取排序后的数据
|
||||
onStart: async (event: SortableEvent) => {
|
||||
if (typeof props.pageBarSortOptions?.beforeStart === 'function') {
|
||||
await props.pageBarSortOptions.beforeStart(event, sortable);
|
||||
|
@ -11,8 +11,7 @@
|
||||
:width="160"
|
||||
:destroy-on-close="true"
|
||||
>
|
||||
<div class="page-bar-popover-wrapper">
|
||||
<div class="page-bar-popover-inner">
|
||||
<div>
|
||||
<slot name="page-list-popover" :list="list">
|
||||
<ToolButton
|
||||
v-for="(item, index) in list"
|
||||
@ -26,7 +25,6 @@
|
||||
></ToolButton>
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<TMagicIcon class="m-editor-page-list-menu-icon">
|
||||
<Files></Files>
|
||||
|
@ -42,7 +42,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, getCurrentInstance, inject, onMounted, onUnmounted, ref, useTemplateRef, watchEffect } from 'vue';
|
||||
import { computed, getCurrentInstance, inject, onMounted, ref, useTemplateRef, watchEffect } from 'vue';
|
||||
import { Document as DocumentIcon } from '@element-plus/icons-vue';
|
||||
|
||||
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
|
||||
@ -78,7 +78,6 @@ const emit = defineEmits<{
|
||||
'submit-error': [e: any];
|
||||
'form-error': [e: any];
|
||||
mounted: [internalInstance: any];
|
||||
unmounted: [];
|
||||
}>();
|
||||
|
||||
const services = useServices();
|
||||
@ -105,10 +104,6 @@ onMounted(() => {
|
||||
emit('mounted', internalInstance?.proxy);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
emit('unmounted');
|
||||
});
|
||||
|
||||
const submit = async (v: FormValue, eventData: ContainerChangeEventData) => {
|
||||
try {
|
||||
const values = await configFormRef.value?.submitForm();
|
||||
|
@ -13,7 +13,6 @@
|
||||
@submit-error="errorHandler"
|
||||
@form-error="errorHandler"
|
||||
@mounted="mountedHandler"
|
||||
@unmounted="unmountedHandler"
|
||||
></FormPanel>
|
||||
|
||||
<Resizer v-if="showStylePanel" @change="widthChange"></Resizer>
|
||||
@ -90,7 +89,6 @@ const emit = defineEmits<{
|
||||
'submit-error': [e: any];
|
||||
'form-error': [e: any];
|
||||
mounted: [internalInstance: InstanceType<typeof FormPanel>];
|
||||
unmounted: [];
|
||||
}>();
|
||||
|
||||
const { editorService, uiService, propsService, storageService } = useServices();
|
||||
@ -167,10 +165,6 @@ const mountedHandler = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const unmountedHandler = () => {
|
||||
emit('unmounted');
|
||||
};
|
||||
|
||||
const propsPanelEl = useTemplateRef('propsPanel');
|
||||
const propsPanelWidth = ref(
|
||||
storageService.getItem(PROPS_PANEL_WIDTH_STORAGE_KEY, { protocol: Protocol.NUMBER }) || 300,
|
||||
|
@ -43,9 +43,20 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, markRaw, nextTick, onBeforeUnmount, onMounted, useTemplateRef, watch, watchEffect } from 'vue';
|
||||
import {
|
||||
computed,
|
||||
markRaw,
|
||||
nextTick,
|
||||
onBeforeUnmount,
|
||||
onMounted,
|
||||
toRaw,
|
||||
useTemplateRef,
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
import type { MContainer } from '@tmagic/core';
|
||||
import type { MApp, MContainer } from '@tmagic/core';
|
||||
import StageCore, { getOffset, Runtime } from '@tmagic/stage';
|
||||
import { calcValueByFontsize, getIdFromEl } from '@tmagic/utils';
|
||||
|
||||
@ -119,6 +130,12 @@ watchEffect(() => {
|
||||
|
||||
stage.on('runtime-ready', (rt) => {
|
||||
runtime = rt;
|
||||
// toRaw返回的值是一个引用而非快照,需要cloneDeep
|
||||
root.value && runtime?.updateRootConfig?.(cloneDeep(toRaw(root.value)));
|
||||
page.value?.id && runtime?.updatePageId?.(page.value.id);
|
||||
setTimeout(() => {
|
||||
node.value && stage?.select(toRaw(node.value.id));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -167,6 +184,14 @@ watch(page, (page) => {
|
||||
}
|
||||
});
|
||||
|
||||
const rootChangeHandler = (root: MApp) => {
|
||||
if (runtime && root) {
|
||||
runtime.updateRootConfig?.(cloneDeep(toRaw(root)));
|
||||
}
|
||||
};
|
||||
|
||||
editorService.on('root-change', rootChangeHandler);
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const { contentRect } of entries) {
|
||||
uiService.set('stageContainerRect', {
|
||||
@ -185,10 +210,10 @@ onMounted(() => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stage?.destroy();
|
||||
stage = null;
|
||||
resizeObserver.disconnect();
|
||||
editorService.set('stage', null);
|
||||
keybindingService.unregisterEl('stage');
|
||||
editorService.off('root-change', rootChangeHandler);
|
||||
});
|
||||
|
||||
const parseDSL = getEditorConfig('parseDSL');
|
||||
|
@ -1,19 +1,9 @@
|
||||
<template>
|
||||
<div v-if="stageOverlayVisible" class="m-editor-stage-overlay">
|
||||
<TMagicIcon class="m-editor-stage-overlay-close" :size="'30'" @click="closeOverlayHandler"
|
||||
<div v-if="stageOverlayVisible" class="m-editor-stage-overlay" @click="closeOverlayHandler">
|
||||
<TMagicIcon class="m-editor-stage-overlay-close" :size="'20'" @click="closeOverlayHandler"
|
||||
><CloseBold
|
||||
/></TMagicIcon>
|
||||
|
||||
<ScrollViewer
|
||||
class="m-editor-stage"
|
||||
:width="wrapWidth"
|
||||
:height="wrapHeight"
|
||||
:wrap-width="columnWidth.center"
|
||||
:wrap-height="frameworkRect.height"
|
||||
:zoom="zoom"
|
||||
>
|
||||
<div ref="stageOverlay" class="m-editor-stage-container" :style="style"></div>
|
||||
</ScrollViewer>
|
||||
<div ref="stageOverlay" class="m-editor-stage-overlay-container" :style="style" @click.stop></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -23,11 +13,10 @@ import { CloseBold } from '@element-plus/icons-vue';
|
||||
|
||||
import { TMagicIcon } from '@tmagic/design';
|
||||
|
||||
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
||||
import { useServices } from '@editor/hooks/use-services';
|
||||
import type { StageOptions } from '@editor/type';
|
||||
|
||||
const { stageOverlayService, editorService, uiService } = useServices();
|
||||
const { stageOverlayService, editorService } = useServices();
|
||||
|
||||
const stageOptions = inject<StageOptions>('stageOptions');
|
||||
|
||||
@ -37,12 +26,10 @@ const stageOverlayVisible = computed(() => stageOverlayService.get('stageOverlay
|
||||
const wrapWidth = computed(() => stageOverlayService.get('wrapWidth'));
|
||||
const wrapHeight = computed(() => stageOverlayService.get('wrapHeight'));
|
||||
const stage = computed(() => editorService.get('stage'));
|
||||
const zoom = computed(() => uiService.get('zoom'));
|
||||
const columnWidth = computed(() => uiService.get('columnWidth'));
|
||||
const frameworkRect = computed(() => uiService.get('frameworkRect'));
|
||||
|
||||
const style = computed(() => ({
|
||||
transform: `scale(${zoom.value})`,
|
||||
width: `${wrapWidth.value}px`,
|
||||
height: `${wrapHeight.value}px`,
|
||||
}));
|
||||
|
||||
watch(stage, (stage) => {
|
||||
@ -56,12 +43,6 @@ watch(stage, (stage) => {
|
||||
}
|
||||
});
|
||||
|
||||
watch(zoom, (zoom) => {
|
||||
const stage = stageOverlayService.get('stage');
|
||||
if (!stage || !zoom) return;
|
||||
stage.setZoom(zoom);
|
||||
});
|
||||
|
||||
watch(stageOverlayEl, (stageOverlay) => {
|
||||
const subStage = stageOverlayService.createStage(stageOptions);
|
||||
stageOverlayService.set('stage', subStage);
|
||||
|
@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -199,35 +199,24 @@ export default class extends EventEmitter {
|
||||
* @deprecated 请使用usePlugin代替
|
||||
*/
|
||||
public use(options: Record<string, Function>) {
|
||||
for (const [methodName, method] of Object.entries(options)) {
|
||||
Object.entries(options).forEach(([methodName, method]: [string, Function]) => {
|
||||
if (typeof method === 'function') this.middleware[methodName].push(method);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public usePlugin(options: Record<string, Function>) {
|
||||
for (const [methodName, method] of Object.entries(options)) {
|
||||
if (typeof method === 'function' && !this.pluginOptionsList[methodName].includes(method)) {
|
||||
this.pluginOptionsList[methodName].push(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public removePlugin(options: Record<string, Function>) {
|
||||
for (const [methodName, method] of Object.entries(options)) {
|
||||
if (Array.isArray(this.pluginOptionsList[methodName])) {
|
||||
this.pluginOptionsList[methodName] = this.pluginOptionsList[methodName].filter((item) => item !== method);
|
||||
}
|
||||
}
|
||||
Object.entries(options).forEach(([methodName, method]: [string, Function]) => {
|
||||
if (typeof method === 'function') this.pluginOptionsList[methodName].push(method);
|
||||
});
|
||||
}
|
||||
|
||||
public removeAllPlugins() {
|
||||
for (const key of Object.keys(this.pluginOptionsList)) {
|
||||
Object.keys(this.pluginOptionsList).forEach((key) => {
|
||||
this.pluginOptionsList[key] = [];
|
||||
}
|
||||
|
||||
for (const key of Object.keys(this.middleware)) {
|
||||
});
|
||||
Object.keys(this.middleware).forEach((key) => {
|
||||
this.middleware[key] = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async doTask() {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -145,18 +145,6 @@ class Keybinding extends BaseService {
|
||||
for (const [type = '', eventType = 'keydown'] of when) {
|
||||
const cacheItem: KeyBindingCacheItem = { type, command, keybinding, eventType, bound: false };
|
||||
|
||||
if (
|
||||
this.bindingList.find(
|
||||
(item) =>
|
||||
item.command === command &&
|
||||
item.eventType === eventType &&
|
||||
item.type === type &&
|
||||
item.keybinding === keybinding,
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.bindingList.push(cacheItem);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -97,9 +97,9 @@ class StageOverlay extends BaseService {
|
||||
public createStage(stageOptions: StageOptions = {}) {
|
||||
return useStage({
|
||||
...stageOptions,
|
||||
zoom: 1,
|
||||
runtimeUrl: '',
|
||||
autoScrollIntoView: false,
|
||||
disabledRule: true,
|
||||
render: async (stage: StageCore) => {
|
||||
this.copyDocumentElement();
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -11,17 +11,6 @@
|
||||
border-bottom: 1px solid $border-color;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
> .el-collapse-item__header {
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.el-collapse-item__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.el-collapse-item__header,
|
||||
|
@ -13,8 +13,4 @@
|
||||
flex: 2;
|
||||
}
|
||||
}
|
||||
.tmagic-design-button {
|
||||
margin-left: 5px;
|
||||
padding: 5px 8px;
|
||||
}
|
||||
}
|
||||
|
@ -92,11 +92,6 @@
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.page-bar-popover-wrapper {
|
||||
max-height: calc(100vh - $page-bar-height - 20px);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease 0s;
|
||||
|
@ -72,6 +72,11 @@
|
||||
align-items: center;
|
||||
border-bottom: 2px solid $border-color;
|
||||
}
|
||||
|
||||
.tmagic-design-form {
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.m-editor-props-panel-src-icon {
|
||||
|
@ -35,15 +35,22 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
z-index: 20;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.m-editor-stage-overlay-container {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
margin: auto;
|
||||
box-shadow: rgba(0, 0, 0, 0.04) 0px 3px 5px;
|
||||
}
|
||||
|
||||
.m-editor-stage-overlay-close.tmagic-design-icon {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
top: 10px;
|
||||
cursor: pointer;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.m-editor-stage-float-button {
|
||||
@ -59,10 +66,8 @@
|
||||
background-color: #ffffff;
|
||||
transition: background-color 0.2s;
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
box-shadow:
|
||||
0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
||||
0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||
0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||
box-shadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
|
||||
0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.m-editor-node-list-menu {
|
||||
|
@ -8,25 +8,12 @@
|
||||
.tmagic-design-collapse-item {
|
||||
> .el-collapse-item__header {
|
||||
background-color: #f2f3f7;
|
||||
height: 26px;
|
||||
min-height: 26px;
|
||||
line-height: 26px;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
height: 36px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.el-collapse-item__wrap {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.el-collapse-item__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.el-collapse-item__content {
|
||||
padding: 0;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -26,8 +26,9 @@ import type { FormConfig, TableColumnConfig } from '@tmagic/form';
|
||||
import type StageCore from '@tmagic/stage';
|
||||
import type {
|
||||
ContainerHighlightType,
|
||||
CustomizeMoveableOptions,
|
||||
CustomizeMoveableOptionsCallbackConfig,
|
||||
GuidesOptions,
|
||||
MoveableOptions,
|
||||
RenderType,
|
||||
UpdateDragEl,
|
||||
} from '@tmagic/stage';
|
||||
@ -156,14 +157,13 @@ export interface StageOptions {
|
||||
containerHighlightType?: ContainerHighlightType;
|
||||
disabledDragStart?: boolean;
|
||||
render?: (stage: StageCore) => HTMLDivElement | void | Promise<HTMLDivElement | void>;
|
||||
moveableOptions?: CustomizeMoveableOptions;
|
||||
moveableOptions?: MoveableOptions | ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions);
|
||||
canSelect?: (el: HTMLElement) => boolean | Promise<boolean>;
|
||||
isContainer?: (el: HTMLElement) => boolean | Promise<boolean>;
|
||||
updateDragEl?: UpdateDragEl;
|
||||
renderType?: RenderType;
|
||||
guidesOptions?: Partial<GuidesOptions>;
|
||||
disabledMultiSelect?: boolean;
|
||||
disabledRule?: boolean;
|
||||
zoom?: number;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -16,12 +17,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
NODE_CONDS_KEY,
|
||||
NODE_CONDS_RESULT_KEY,
|
||||
NODE_DISABLE_CODE_BLOCK_KEY,
|
||||
NODE_DISABLE_DATA_SOURCE_KEY,
|
||||
} from '@tmagic/core';
|
||||
import { NODE_CONDS_KEY } from '@tmagic/core';
|
||||
import { tMagicMessage } from '@tmagic/design';
|
||||
import type { FormConfig, FormState, TabConfig, TabPaneConfig } from '@tmagic/form';
|
||||
|
||||
@ -127,20 +123,6 @@ export const advancedTabConfig: TabPaneConfig = {
|
||||
title: '高级',
|
||||
lazy: true,
|
||||
items: [
|
||||
{
|
||||
name: NODE_DISABLE_CODE_BLOCK_KEY,
|
||||
text: '禁用代码块',
|
||||
type: 'switch',
|
||||
defaultValue: false,
|
||||
extra: '开启后,配置的代码块将不会被执行',
|
||||
},
|
||||
{
|
||||
name: NODE_DISABLE_DATA_SOURCE_KEY,
|
||||
text: '禁用数据源',
|
||||
type: 'switch',
|
||||
defaultValue: false,
|
||||
extra: '开启后,组件内配置的数据源相关配置将不会被编译,显隐条件将失效',
|
||||
},
|
||||
{
|
||||
name: 'created',
|
||||
text: 'created',
|
||||
@ -167,20 +149,8 @@ export const advancedTabConfig: TabPaneConfig = {
|
||||
|
||||
export const displayTabConfig: TabPaneConfig = {
|
||||
title: '显示条件',
|
||||
display: (_state: FormState, { model }: any) => model.type !== 'page',
|
||||
display: (_vm: FormState, { model }: any) => model.type !== 'page',
|
||||
items: [
|
||||
{
|
||||
name: NODE_CONDS_RESULT_KEY,
|
||||
type: 'select',
|
||||
text: '条件成立时',
|
||||
defaultValue: false,
|
||||
options: [
|
||||
{ text: '显示', value: false },
|
||||
{ text: '隐藏', value: true },
|
||||
],
|
||||
extra: (_state, { model }) =>
|
||||
`条件成立时${model[NODE_CONDS_RESULT_KEY] ? '隐藏' : '显示'},不成立时${model[NODE_CONDS_RESULT_KEY] ? '显示' : '隐藏'};<br />同一条件组内的所有条件配置同时成立时表示该条件组成立,任意一个条件组成立时表示条件成立(条件组内为且的关系,条件组间为或的关系);<br />条件为空时表示成立;`,
|
||||
},
|
||||
{
|
||||
type: 'display-conds',
|
||||
name: NODE_CONDS_KEY,
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||
*
|
||||
* Copyright (C) 2025 Tencent. All rights reserved.
|
||||
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user