Compare commits

..

66 Commits

Author SHA1 Message Date
roymondchen
5387a18024 chore: release vue-container/react-runtime-help/reate-iterator-conatiner 2025-10-14 15:49:20 +08:00
roymondchen
2b80ba3efd chore: update lockfile v1.6.1 2025-10-14 15:37:33 +08:00
roymondchen
6c3916c580 chore: release v1.6.1 2025-10-14 15:36:00 +08:00
roymondchen
4dfdc836f6 chore: update deps 2025-10-14 14:18:50 +08:00
roymondchen
fef1e2c2b4 style(editor): 组件列表标题样式调整 2025-10-14 14:14:48 +08:00
roymondchen
51f95aef6f feat(data-source, editor, schema, react-runtime-help, vue-components): 新增条件成立时隐藏的配置功能 2025-10-13 20:31:54 +08:00
roymondchen
81aa8f151d feat(editor): 属性配置中的样式面板样式优化 2025-10-13 19:32:29 +08:00
roymondchen
cae11dce12 fix(form): text与同行元素不对齐问题 2025-10-13 19:28:18 +08:00
roymondchen
83bf36d980 style(form): radio-group选项中icon大小根据size变化 2025-10-13 19:25:30 +08:00
roymondchen
2888004c17 chore: update deps 2025-10-13 15:57:48 +08:00
roymondchen
abab44ad24 chore: update deps 2025-09-16 19:49:48 +08:00
roymondchen
849b561933 feat(data-source): 数据源数据变化事件监听响应支持立即执行 2025-09-16 19:44:16 +08:00
roymondchen
1031595a97 fix(core): 异步加载数据源时,数据源事件配置失效 2025-09-16 19:34:15 +08:00
roymondchen
f5cb19dfa4 style(editor): 页面列表长度超过可视窗口时出现滚动条 2025-09-12 15:32:03 +08:00
roymondchen
e400175ffe fix: 组件声明周期函数配置中配置数据源自有方法生效 2025-09-10 16:24:44 +08:00
roymondchen
958c78d968 chore: update lockfile v1.6.0 2025-08-27 19:27:29 +08:00
roymondchen
9f81597596 chore: release v1.6.0 2025-08-27 19:26:01 +08:00
roymondchen
001f2b6c9e build(runtime): escape % if URI malformed 2025-08-27 19:18:17 +08:00
roymondchen
a56bb562d8 build: 构建playground失败 2025-08-27 19:08:57 +08:00
roymondchen
7695f005f6 style(runtime): 配置格式化 2025-08-27 17:01:08 +08:00
roymondchen
a10f9d230d fix(cli): 在pnpm工作空间中的项目,保持package.json不受配置中的packages影响 2025-08-27 16:48:51 +08:00
roymondchen
9f350541bf feat(cli): 支持tmagic.config.local配置文件,会与tmagic.config配置合并,可用于本地开发时的临时配置 2025-08-27 16:19:43 +08:00
roymondchen
0643699fac chore: 删除ui/ui-react 2025-08-27 15:43:28 +08:00
roymondchen
9f63dff49b build(runtime): 构建playground示例失败 2025-08-27 14:31:24 +08:00
roymondchen
7d3a2df793 chore: 删除vue2 runtime,后续vue-components里的组件不再考虑vue2的支持 2025-08-27 14:24:15 +08:00
roymondchen
0967c3449e build(runtime): 重构vue3 runtime的构建配置 2025-08-26 20:40:24 +08:00
roymondchen
f267643c42 chore: update deps 2025-08-26 17:27:37 +08:00
roymondchen
f3387ed5e9 chore: update lockfile v1.6.0-beta.6 2025-08-15 12:30:41 +08:00
roymondchen
71d1aab69e chore: release v1.6.0-beta.6 2025-08-15 12:29:06 +08:00
roymondchen
4c6118f50f feat(core): 事件支持关联数据源自身方法 2025-08-15 12:25:00 +08:00
roymondchen
77da3f9762 chore: update lockfile v1.6.0-beta.5 2025-08-08 16:00:48 +08:00
roymondchen
8b3cb63f77 chore: release v1.6.0-beta.5 2025-08-08 15:59:17 +08:00
roymondchen
fc1c9feafd fix(core): 事件行为应该为串行执行 2025-08-08 15:51:54 +08:00
roymondchen
371be8aa27 chore: update lockfile v1.6.0-beta.4 2025-07-24 16:49:05 +08:00
roymondchen
7bb14e6c25 chore: release v1.6.0-beta.4 2025-07-24 16:47:31 +08:00
roymondchen
cdb07dfaea fix(editor): 避免services plugin重复添加 2025-07-24 16:43:40 +08:00
roymondchen
c2830fca6b fix(editor): runtime url更新后恢复当前选中状态 2025-07-24 15:55:59 +08:00
roymondchen
bba67b5533 chore: update lockfile v1.6.0-beta.3 2025-07-24 14:14:37 +08:00
roymondchen
eda9924327 chore: release v1.6.0-beta.3 2025-07-24 14:12:58 +08:00
qyaniwu
07b8f5fe83 fix(editor): 修复代码块编辑器的更新内容后按下ctrl+s光标会偏移的问题 2025-07-24 04:13:02 +00:00
roymondchen
d31a544a23 fix(editor): runtimeUrl更新后需要重新设置runtime的dsl 2025-07-24 11:24:36 +08:00
roymondchen
01b7b58065 chore: update lockfile v1.6.0-beta.2 2025-07-22 20:31:48 +08:00
roymondchen
870dd99e2c chore: release v1.6.0-beta.2 2025-07-22 20:30:18 +08:00
roymondchen
01b88dba25 feat(core): 异步加载dsl片段下,事件触发时node可能不存在 2025-07-22 20:23:14 +08:00
roymondchen
2605011df4 feat(utile): replaceChildNode找不多目标不报错 2025-07-22 20:16:37 +08:00
roymondchen
00cf312a91 chore(vue-runtime-help): release 2025-07-17 19:08:50 +08:00
roymondchen
e3ed80b121 chore: update lockfile v1.6.0-beta.1 2025-07-17 18:51:31 +08:00
roymondchen
0752abc312 chore: release v1.6.0-beta.1 2025-07-17 18:50:02 +08:00
roymondchen
ded24c8b4f feat(editor): 页面片容器内的组件不允许选中 2025-07-17 18:26:29 +08:00
roymondchen
7799a5da61 fix(vue-components): iterator-container 传递 page-fragment-contianer-id 参数 2025-07-17 17:24:41 +08:00
roymondchen
6cb08cf458 style(vue-runtime-help): 完善类型 2025-07-17 17:22:11 +08:00
roymondchen
305ea4619f fix(data-source): 页面未初始化好之前,数据源数据变化后需要修改page.data 2025-07-17 17:20:58 +08:00
roymondchen
291de2005d feat(core): Node中的instance初始为null,用于判断是否与组件产生关联 2025-07-17 17:19:03 +08:00
roymondchen
34bc223f02 feat(vue-components): page-fragment-container编辑器中不去除内部组件id,不然会导致无法从app中获取dsl 2025-07-17 14:09:11 +08:00
roymondchen
bf9fad18b6 fix(vue-components): page-fragment-container不标记为不是node节点 2025-07-15 16:40:53 +08:00
roymondchen
cebf3506d4 build: 升级rollup 4.45.0后playground构建失败,回滚回4.44.1 2025-07-15 16:31:12 +08:00
roymondchen
a43825caa2 feat(vue-components): 添加页面片容器id prop 2025-07-15 15:31:51 +08:00
roymondchen
1736d495fd chore: update lockfile v1.6.0-beta.0 2025-07-15 15:14:30 +08:00
roymondchen
44b592f36a chore: release v1.6.0-beta.0 2025-07-15 15:13:01 +08:00
roymondchen
6a54720068 feat(core,data-source,utils,react-runtime-help,vue-runtime-help): 新增对页面片节点的管理 2025-07-15 15:08:53 +08:00
roymondchen
aaf8046c63 fix(editor): 依赖收集后没有同步到dsl中 2025-07-11 19:02:56 +08:00
roymondchen
5242585500 chore: update copyright 2025-07-11 15:04:22 +08:00
roymondchen
4637c80125 style(editor,stage): 完善moveableOptions prop类型定义 2025-07-04 14:25:05 +08:00
roymondchen
5b7d2dd248 chore: update lockfile v1.5.24 2025-07-03 20:55:54 +08:00
roymondchen
5ab7030c3a chore: release v1.5.24 2025-07-03 20:54:08 +08:00
roymondchen
84bf9ba2f9 fix(editor): 初始化收集依赖后编译数据源不能使用源数据 2025-07-03 20:50:46 +08:00
315 changed files with 3230 additions and 3972 deletions

View File

@ -1,3 +1,125 @@
## [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)

View File

@ -1,6 +1,6 @@
Tencent is pleased to support the open source community by making TMagicEditor available.
Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
Copyright (C) 2025 Tencent. All rights reserved.
TMagicEditor is licensed under the Apache License Version 2.0 except for the third-party components listed below.

View File

@ -24,7 +24,7 @@ export default defineConfig({
footer: {
message: 'Powered by 腾讯视频会员平台技术中心',
copyright: 'Copyright (C) 2023 THL A29 Limited, a Tencent company.'
copyright: 'Copyright (C) 2025 Tencent.'
},
nav: [

View File

@ -375,7 +375,7 @@ const stageContentMenu = ref([
```html
<template>
<m-editor
runtime-url="https://tencent.github.io/tmagic-editor/playground/runtime/vue3/playground/index.html"
runtime-url="https://tencent.github.io/tmagic-editor/playground/runtime/vue/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/)
- **示例:**

View File

@ -60,6 +60,5 @@ export default defineComponent({
## 渲染器示例
在tmagic-editor的示例项目中我们提供了三个版本的 @tmagic/ui。可以参考对应前端框架的渲染器实现。
- [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)
- [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)

View File

@ -5,15 +5,13 @@ tmagic-editor的设计是希望发布的页面支持多个前端框架即各
所以tmagic-editor的设计中针对每个前端框架都需要有一个对应的 @tmagic/ui 来承担渲染器职责。同时,也需要一个使用和 @tmagic/ui 相同前端框架的 runtime 来 @tmagic/ui 和业务组件的,具体 runtime 概念,可以参考[页面发布](../publish)。
@tmagic/ui 在tmagic-editor设计中承担的是业务逻辑无关的基础组件渲染的功能。一切和业务相关的逻辑都应该在 [runtime](../runtime.html) 中实现。这样 @tmagic/ui 就能保持其通用性。
我们以项目代码中提供的 vue3 版本的 @tmagic/ui 作为示例介绍其中包含的内容。
我们以项目代码中提供的 vue 版本的 [vue-components](https://tencent.github.io/tmagic-editor/vue-components) 作为示例介绍其中包含的内容。
## 渲染器
在 vue3 中,实现渲染器的具体形式参考[页面渲染](../advanced/page)中描述的[容器渲染](../advanced/page.html#容器渲染)和[组件渲染](../advanced/page.html#容器渲染)。
在 vue 中,实现渲染器的具体形式参考[页面渲染](../advanced/page)中描述的[容器渲染](../advanced/page.html#容器渲染)和[组件渲染](../advanced/page.html#容器渲染)。
## 基础组件
@tmagic/ui vue3 中,我们提供了几个基础组件,可以在项目源码中找到对应内容。
[vue-components](https://tencent.github.io/tmagic-editor/vue-components) 中,我们提供了几个基础组件,可以在项目源码中找到对应内容。
- page tmagic-editor的页面基础
- container tmagic-editor的容器渲染器
@ -23,8 +21,3 @@ 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)

View File

@ -2,14 +2,14 @@
tmagic-editor支持业务方进行自定义组件开发。在tmagic-editor中组件是以 npm 包形式存在的组件和插件只要按照规范开发就可以在tmagic-editor的 runtime 中被加入并正确渲染组件。
## 组件开发
以 vue3 的组件开发为例。运行项目中的 playground 示例,会自动加载 vue3 的 runtime。runtime会加载[@tmagic/ui](https://github.com/Tencent/tmagic-editor/tree/master/packages/ui)
以 vue 的组件开发为例。运行项目中的 playground 示例,会自动加载 vue 的 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#组件)。
可以参考 vue3 版本的 @tmagic/ui 中,[组件渲染](../guide/advanced/page.html#组件渲染)逻辑里type 会作为组件名进入渲染。所以在 vue3 的组件开发中,我们也需要在为 vue 组件声明 name 字段时,和 type 值对应起来,才能正确渲染组件。
可以参考 vue 版本的 @tmagic/ui 中,[组件渲染](../guide/advanced/page.html#组件渲染)逻辑里type 会作为组件名进入渲染。所以在 vue 的组件开发中,我们也需要在为 vue 组件声明 name 字段时,和 type 值对应起来,才能正确渲染组件。
### 组件规范
组件的基础形式,需要有四个文件
@ -22,7 +22,7 @@ tmagic-editor支持业务方进行自定义组件开发。在tmagic-editor中
@tmagic/ui 中的 button/text 就是基础的组件示例。我们要求声明 index 入口,因为我们希望在后续的配套打包工具实现上,可以有一个统一规范入口。
### 1. 创建组件
在项目中,如 runtime vue3 目录中,创建一个名为 test-component 的组件目录,其中包含上面四个规范文件。
在项目中,如 runtime 目录中,创建一个名为 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 vue3 中,我们已经提供好一份示例。在 tmagic.config.ts 文件中。只需要在 packages 加入你创建的组件的路径(如果是个 npm 包,则将路径替换为包名即可),打包工具就会自动识别到你的组件。
在 runtime vue 中,我们已经提供好一份示例。在 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/vue3/src/page 下。创建一个 page-config.js 文件。将 DSL 作为配置导出。
在完成开发中组件在编辑器中的实现后,我们将编辑器中的 DSL 源码📄 打开,复制 DSL。并在 runtime/vue/src/page 下。创建一个 page-config.js 文件。将 DSL 作为配置导出。
```javascript
window.magicDSL = [
@ -168,7 +168,7 @@ window.magicDSL = [
import './page-config.js';
```
然后执行在 runtime/vue3 目录下执行
然后执行在 runtime/ 目录下执行
```
npm run build:libs
npm run dev

View File

@ -60,11 +60,6 @@ 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 所需要的渲染器。
## 联动
页面搭建过程中,会涉及到两种联动形式
- 在编辑器中,组件的表单配置项之间需要联动。

View File

@ -125,7 +125,7 @@ app.mount('#app');
// 初始化页面数据
}),
runtimeUrl: "/runtime/vue3/playground/index.html",
runtimeUrl: "/runtime/vue/playground/index.html",
propsConfigs: [
// 组件属性列表
@ -208,10 +208,10 @@ npm install sass -D
```javascript
setup() {
asyncLoadJs(`/runtime/vue3/assets/config.js`).then(() => {
asyncLoadJs(`/runtime/vue/assets/config.js`).then(() => {
propsConfigs.value = window.magicPresetConfigs;
});
asyncLoadJs(`/runtime/vue3/assets/value.js`).then(() => {
asyncLoadJs(`/runtime/vue/assets/value.js`).then(() => {
propsValues.value = window.magicPresetValues;
});
}

View File

@ -36,9 +36,6 @@ 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),与文档描述内容可以逐一对应上,希望文档内容可以为开发者带来比较好的开发体验。

View File

@ -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提供了 vue2/vue3 和 react 的 runtime 示例。
并且由于tmagic-editor在编辑器中的模拟器是通过 iframe 渲染的和tmagic-editor平台本身可以做到框架解耦所以 runtime 也可以用不同框架开发。目前tmagic-editor提供了 vue 和 react 的 runtime 示例。
各个 runtime 的作用除了作为不同场景下的渲染环境同时也是不同环境的打包构建载体。tmagic-editor示例代码中的打包就是基于 runtime 进行的。
@ -21,8 +21,7 @@ runtime 的概念是理解tmagic-editor项目页运行的重要概念runti
由于 runtime 是页面渲染的承载环境,其中会加载 @tmagic/ui 以及各个业务组件,业务发布项目页也是基于 runtime所以在 runtime 中实现业务方的自定义逻辑是最合适的。runtime 可以提供一些全局 API供业务组件调用。我们可以把下面的模拟器中的 runtime 视为一个业务方runtime。
tmagic-editor提供了三个版本的 runtime 示例,可以参考:
- [vue3 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue3)
- [vue2 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue2)
- [vue runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue)
- [react runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/react)
### 真实页面渲染Page

View File

@ -14,8 +14,7 @@ runtime 的 `page` 部分,就是真实项目页面的渲染环境。发布出
- 加载第三方全局组件/插件等
具体的 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)
- [vue runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue/page)
- [react runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/react/page)
### playground
@ -45,8 +44,7 @@ window.magic.onPageElUpdate(document.querySelector('.magic-ui-page'));
|sortNode| 组件在容器间排序 |{ `src` , `dist`, `root` }: `SortEventData` |
runtime 的实现示例可以参考tmagic-editor提供的
- [vue3 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue3)
- [vue2 runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue2)
- [vue runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/vue)
- [react runtime](https://github.com/Tencent/tmagic-editor/blob/master/runtime/react)
### 页面发布

View File

@ -1,6 +1,6 @@
# 3.[DSL](../conception.md#dsl) 解析渲染
tmagic 提供了 vue3/vue2/react 三个版本的解析渲染组件,可以直接使用
tmagic 提供了 vue/react 两个个版本的解析渲染组件,可以直接使用
[@tmagic/ui](https://www.npmjs.com/package/@tmagic/ui)

View File

@ -1,6 +1,5 @@
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';
@ -21,7 +20,6 @@ export default (tsconfigRootDir) =>
...tseslint.config(tencentEslintBaseConfig, tencentEslintImportexport, tseslint.configs.base, {
plugins: {
'@stylistic': stylistic,
'@stylistic/ts': stylisticTs,
},
languageOptions: {
parser: parserTs,

View File

@ -1,6 +1,6 @@
{
"name": "@tmagic/eslint-config",
"version": "0.0.2",
"version": "0.0.3",
"main": "index.mjs",
"type": "module",
"repository": {
@ -9,18 +9,17 @@
"url": "https://github.com/Tencent/tmagic-editor.git"
},
"dependencies": {
"@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/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-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-vue": "^10.0.0",
"eslint-plugin-prettier": "^5.2.6",
"globals": "^16.0.0",
"typescript-eslint": "^8.30.1"
"eslint-plugin-vue": "^10.4.0",
"eslint-plugin-prettier": "^5.5.4 ",
"globals": "^16.3.0",
"typescript-eslint": "^8.41.0"
},
"peerDependencies": {
"eslint": ">=9.24.0",

View File

@ -1,9 +1,9 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "tmagic",
"private": true,
"type": "module",
"packageManager": "pnpm@10.11.1",
"packageManager": "pnpm@10.18.2",
"scripts": {
"bootstrap": "pnpm i && pnpm build",
"clean:top": "rimraf */**/dist */**/types */dist coverage dwt* temp packages/cli/lib",
@ -11,16 +11,14 @@
"clean:all": "pnpm clean:top && pnpm clean:modules",
"lint": "eslint --cache .",
"lint-fix": "eslint --fix --cache .",
"playground": "pnpm --filter \"runtime-vue3\" build:libs && pnpm --filter \"runtime-vue3\" --filter \"tmagic-playground\" dev",
"playground": "pnpm --filter \"runtime-vue\" build:libs && pnpm --filter \"runtime-vue\" --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-vue3\" build && pnpm --filter \"tmagic-playground\" build",
"build:playground": "pnpm --filter \"runtime-vue\" build && pnpm --filter \"tmagic-playground\" build",
"docs:dev": "vitepress dev docs",
"docs:serve": "vitepress serve docs",
"docs:build": "vitepress build docs",
@ -32,7 +30,7 @@
"release": "node scripts/release.mjs"
},
"engines": {
"node": ">=20"
"node": "^20.19.0 || >=22.12.0"
},
"workspaces": [
"packages/*"
@ -49,15 +47,15 @@
"@types/node": "24.0.10",
"@vitejs/plugin-vue": "^5.2.3",
"@vitest/coverage-v8": "^2.1.9",
"@vue/compiler-sfc": "^3.5.13",
"@vue/compiler-sfc": "catalog:",
"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.9.11",
"element-plus": "^2.11.4",
"enquirer": "^2.4.1",
"eslint": "^9.28.0",
"eslint": "^9.37.0",
"execa": "^4.1.0",
"highlight.js": "^11.11.1",
"husky": "^9.1.7",
@ -65,20 +63,20 @@
"lint-staged": "^16.1.0",
"minimist": "^1.2.8",
"picocolors": "^1.1.1",
"prettier": "^3.5.3",
"prettier": "^3.6.2",
"recast": "^0.23.11",
"rimraf": "^3.0.2",
"rollup": "^4.44.1",
"rollup": "4.44.1",
"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.3",
"vitest": "^3.2.1",
"vitepress": "^1.6.4",
"vitest": "^3.2.4",
"vue": "catalog:",
"vue-tsc": "^3.0.0"
"vue-tsc": "^3.1.1"
},
"config": {
"commitizen": {

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/cli",
"main": "lib/index.js",
"types": "lib/index.d.ts",
@ -30,6 +30,7 @@
"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"

View File

@ -1,6 +1,8 @@
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';
@ -22,18 +24,29 @@ export const scripts = (defaultAppConfig: UserConfig) => {
path.resolve(defaultAppConfig.temp, 'config.cjs'),
].find((item) => fs.pathExistsSync(item));
const { npmConfig = {}, ...userConfig } = await loadUserConfig(userConfigPath);
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);
}
// resolve the final app config to use
const appConfig = {
...defaultAppConfig,
...userConfig,
npmConfig: {
...(defaultAppConfig.npmConfig || {}),
...npmConfig,
},
};
const appConfig = mergeOptions(defaultAppConfig, userConfig);
const app = new App(appConfig);
// clean temp and cache

View File

@ -1,11 +1,35 @@
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);
}
};
@ -26,11 +50,17 @@ 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);
}
};

View File

@ -601,10 +601,20 @@ const flattenPackagesConfig = (packages: (string | Record<string, string>)[]) =>
const packagesConfig: ([string] | [string, string])[] = [];
packages.forEach((item) => {
if (typeof item === 'object') {
Object.entries(item).forEach(([key, packagePath]) => {
packagesConfig.push([packagePath, key]);
for (const [key, packagePath] of Object.entries(item)) {
const index = packagesConfig.findIndex(([, k]) => {
return k === key;
});
if (index > -1) {
packagesConfig[index] = [packagePath, key];
} else {
packagesConfig.push([packagePath, key]);
}
}
} else if (typeof item === 'string') {
if (packagesConfig.find(([k]) => k === item)) {
return;
}
packagesConfig.push([item]);
}
});

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/core",
"type": "module",
"main": "dist/tmagic-core.umd.cjs",

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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 } from './type';
import { AppOptionsConfig, ErrorHandler, GetNodeOptions } from './type';
import { transformStyle as defaultTransformStyle } from './utils';
class App extends EventEmitter {
@ -45,6 +45,7 @@ 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';
@ -159,11 +160,20 @@ 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) {
const dataSourceList = Array.from(this.dataSourceManager.dataSourceMap.values());
this.eventHelper?.bindDataSourceEvents(dataSourceList);
if (this.dataSourceManager.isAllDataSourceRegistered()) {
this.eventHelper?.bindDataSourceEvents();
} else {
this.dataSourceManager.once('registered-all', () => {
this.eventHelper?.bindDataSourceEvents();
});
}
}
}
@ -192,6 +202,11 @@ 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);
@ -215,8 +230,8 @@ class App extends EventEmitter {
}
}
public getNode<T extends Node = Node>(id: Id, iteratorContainerId?: Id[], iteratorIndex?: number[]) {
return this.page?.getNode<T>(id, iteratorContainerId, iteratorIndex);
public getNode<T extends Node = Node>(id: Id, options?: GetNodeOptions) {
return this.page?.getNode<T>(id, options);
}
public registerComponent(type: string, Component: any) {
@ -249,15 +264,15 @@ class App extends EventEmitter {
* @param eventConfig
* @returns void
*/
public async runCode(codeId: Id, params: Record<string, any>, args: any[], flowState?: FlowState) {
public async runCode(codeId: Id, params: Record<string, any>, args: any[], flowState?: FlowState, node?: Node) {
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 });
await content({ app: this, params, eventParams: args, flowState, node });
} catch (e: any) {
if (this.errorHandler) {
this.errorHandler(e, undefined, { type: 'run-code', codeId, params, eventParams: args, flowState });
this.errorHandler(e, undefined, { type: 'run-code', codeId, params, eventParams: args, flowState, node });
} else {
throw e;
}
@ -271,6 +286,7 @@ class App extends EventEmitter {
params: Record<string, any>,
args: any[],
flowState?: FlowState,
node?: Node,
) {
if (!dsId || !methodName) return;
@ -278,28 +294,32 @@ class App extends EventEmitter {
if (!dataSource) return;
try {
const methods = dataSource.methods || [];
const method = methods.find((item) => item.name === methodName);
if (!method) return;
if (typeof method.content === 'function') {
try {
await method.content({ app: this, params, dataSource, eventParams: args, flowState });
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]();
}
} catch (e: any) {
if (this.errorHandler) {
this.errorHandler(e, dataSource, { type: 'data-source-method', params, eventParams: args, flowState });
this.errorHandler(e, dataSource, { type: 'data-source-method', params, eventParams: args, flowState, node });
} 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;

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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,6 +31,7 @@ import {
type DataSourceItemConfig,
type EventActionItem,
type EventConfig,
Id,
NODE_DISABLE_CODE_BLOCK_KEY,
NODE_DISABLE_DATA_SOURCE_KEY,
} from '@tmagic/schema';
@ -41,8 +42,17 @@ 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>>();
@ -103,21 +113,23 @@ export default class EventHelper extends EventEmitter {
}
public removeNodeEvents() {
Array.from(this.nodeEventList.keys()).forEach((handler) => {
for (const handler of Array.from(this.nodeEventList.keys())) {
const name = this.nodeEventList.get(handler);
name && this.off(name, handler);
});
}
this.nodeEventList.clear();
}
public bindDataSourceEvents(dataSourceList: DataSource[]) {
public bindDataSourceEvents() {
const dataSourceList = Array.from(this.app.dataSourceManager?.dataSourceMap.values() || []);
this.removeDataSourceEvents(dataSourceList);
dataSourceList.forEach((dataSource) => {
for (const dataSource of dataSourceList) {
const dataSourceEvent = this.dataSourceEventList.get(dataSource.id) ?? new Map<string, (args: any) => void>();
(dataSource.schema.events || []).forEach((event) => {
for (const event of dataSource.schema.events || []) {
const [prefix, ...path] = event.name?.split('.') || [];
if (!prefix) return;
const handler = (...args: any[]) => {
@ -131,9 +143,9 @@ export default class EventHelper extends EventEmitter {
// 数据源自定义事件
dataSource.on(prefix, handler);
}
});
}
this.dataSourceEventList.set(dataSource.id, dataSourceEvent);
});
}
}
public removeDataSourceEvents(dataSourceList: DataSource[]) {
@ -142,24 +154,32 @@ export default class EventHelper extends EventEmitter {
}
// 先清掉之前注册的事件,重新注册
dataSourceList.forEach((dataSource) => {
for (const dataSource of dataSourceList) {
const dataSourceEvent = this.dataSourceEventList.get(dataSource.id)!;
if (!dataSourceEvent) return;
Array.from(dataSourceEvent.keys()).forEach((eventName) => {
for (const eventName of Array.from(dataSourceEvent.keys())) {
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中获取最新事件配置
@ -182,9 +202,9 @@ export default class EventHelper extends EventEmitter {
if (typeof config === 'number') {
// 事件响应中可能会有修改数据源数据的会更新dsl所以这里需要重新获取
const actionItem = ((fromCpt as TMagicNode).events[config] as EventConfig).actions[i];
this.actionHandler(actionItem, fromCpt as TMagicNode, args, flowState);
await this.actionHandler(actionItem, fromCpt as TMagicNode, args, flowState);
} else {
this.actionHandler(actions[i], fromCpt as DataSource, args, flowState);
await this.actionHandler(actions[i], fromCpt as DataSource, args, flowState);
}
}
flowState.reset();
@ -258,19 +278,44 @@ export default class EventHelper extends EventEmitter {
[to, methodName] = methodName;
}
const toNodes = [];
const toNode = this.app.getNode(to);
if (!toNode) throw new Error(`ID为${to}的组件不存在`);
if (toNode) {
toNodes.push(toNode);
}
if (toNode.instance) {
if (typeof toNode.instance[methodName] === 'function') {
await toNode.instance[methodName](fromCpt, ...args);
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));
}
} else {
toNode.addEventToQueue({
node.addEventToQueue({
method: methodName,
fromCpt,
args,
});
}
}
await Promise.all(instanceMethodPropmise);
}
}

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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,6 @@
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';
@ -49,7 +48,7 @@ class Node extends EventEmitter {
[key: string]: any;
};
public events: EventConfig[] = [];
public instance?: any = {};
public instance?: any = null;
public page?: Page;
public parent?: Node;
public app: TMagicApp;
@ -75,7 +74,14 @@ 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) {}
@ -134,23 +140,10 @@ class Node extends EventEmitter {
for (const item of hookData.hookData) {
const { codeType = HookCodeType.CODE, codeId, params: itemParams = {} } = item;
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;
if (codeType === HookCodeType.CODE && typeof codeId === 'string') {
await this.app.runCode(codeId, params || itemParams, [], undefined, this);
} else if (codeType === HookCodeType.DATA_SOURCE_METHOD && Array.isArray(codeId) && codeId.length > 1) {
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);
await this.app.runDataSourceMethod(codeId[0], codeId[1], params || itemParams, [], undefined, this);
}
}
}
@ -195,6 +188,25 @@ 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');
}

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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,6 +22,7 @@ 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;
@ -64,8 +65,15 @@ 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) {
config.items = [pageFragment];
this.app.pageFragments.set(
config.id,
new Page({
config: pageFragment,
app: this.app,
}),
);
}
}
@ -76,13 +84,16 @@ class Page extends Node {
public getNode<T extends TMagicNode = TMagicNode>(
id: Id,
iteratorContainerId?: Id[],
iteratorIndex?: number[],
{ iteratorContainerId, iteratorIndex, pageFragmentContainerId }: GetNodeOptions = {},
): 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;

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -43,3 +43,9 @@ export type AfterEventHandler = (args: {
source: TMagicNode | DataSource | undefined;
args: any[];
}) => void;
export interface GetNodeOptions {
iteratorContainerId?: Id[];
iteratorIndex?: number[];
pageFragmentContainerId?: Id;
}

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -168,15 +168,21 @@ describe('App', () => {
1,
);
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');
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');
const ic1 = app.getNode(
'iterator-container_11',
['iterator-container_1'],
[0],
) as unknown as TMagicIteratorContainer;
const ic1 = app.getNode('iterator-container_11', {
iteratorContainerId: ['iterator-container_1'],
iteratorIndex: [0],
}) as unknown as TMagicIteratorContainer;
ic1?.setNodes(
[
@ -200,11 +206,10 @@ describe('App', () => {
1,
);
const ic2 = app.getNode(
'iterator-container_11',
['iterator-container_1'],
[1],
) as unknown as TMagicIteratorContainer;
const ic2 = app.getNode('iterator-container_11', {
iteratorContainerId: ['iterator-container_1'],
iteratorIndex: [1],
}) as unknown as TMagicIteratorContainer;
ic2?.setNodes(
[
@ -228,10 +233,30 @@ describe('App', () => {
1,
);
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');
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');
ic.resetNodes();

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/data-source",
"type": "module",
"main": "dist/tmagic-data-source.umd.cjs",

View File

@ -2,7 +2,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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,13 @@ 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_DISABLE_DATA_SOURCE_KEY } from '@tmagic/core';
import {
compiledNode,
getDefaultValueFromFields,
NODE_CONDS_KEY,
NODE_CONDS_RESULT_KEY,
NODE_DISABLE_DATA_SOURCE_KEY,
} from '@tmagic/core';
import { SimpleObservedData } from './observed-data/SimpleObservedData';
import { DataSource, HttpDataSource } from './data-sources';
@ -238,8 +244,9 @@ class DataSourceManager extends EventEmitter {
Array.isArray(items) && deep ? items.map((item) => this.compiledNode(item, sourceId, deep)) : items;
}
if (node.condResult === false) return newNode;
if (node.visible === false) return newNode;
if (node.condResult === false || (typeof node.condResult === 'undefined' && node[NODE_CONDS_RESULT_KEY])) {
return newNode;
}
// 编译函数这里作为参数,方便后续支持自定义编译
return compiledNode(
@ -255,11 +262,24 @@ class DataSourceManager extends EventEmitter {
* @param {{ [NODE_CONDS_KEY]?: DisplayCond[] }} node
* @returns {boolean}
*/
public compliedConds(node: { [NODE_CONDS_KEY]?: DisplayCond[]; [NODE_DISABLE_DATA_SOURCE_KEY]?: 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;
}
return compliedConditions(node, this.data);
const result = compliedConditions(node, data);
if (!node[NODE_CONDS_RESULT_KEY]) {
return result;
}
return !result;
}
/**
@ -271,7 +291,11 @@ class DataSourceManager extends EventEmitter {
*/
public compliedIteratorItemConds(
itemData: any,
node: { [NODE_CONDS_KEY]?: DisplayCond[] },
node: {
[NODE_CONDS_KEY]?: DisplayCond[];
[NODE_CONDS_RESULT_KEY]?: boolean;
[NODE_DISABLE_DATA_SOURCE_KEY]?: boolean;
},
dataSourceField: string[] = [],
) {
const [dsId, ...keys] = dataSourceField;
@ -279,7 +303,7 @@ class DataSourceManager extends EventEmitter {
if (!ds) return true;
const ctxData = createIteratorContentData(itemData, ds.id, keys, this.data);
return compliedConditions(node, ctxData);
return this.compliedConds(node, ctxData);
}
public compliedIteratorItems(itemData: any, nodes: MNode[], dataSourceField: string[] = []): MNode[] {

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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 } from '@tmagic/core';
import { getDepNodeIds, getNodes, isPage, isPageFragment, replaceChildNode } from '@tmagic/core';
import DataSourceManager from './DataSourceManager';
import type { ChangeEvent, DataSourceManagerData } from './types';
@ -52,18 +52,19 @@ export const createDataSourceManager = (app: TMagicApp, useMock?: boolean, initi
// ssr环境下数据应该是提前准备好的放到initialData中不应该发生变化无需监听
// 有initialData不一定是在ssr环境下
if (app.jsEngine !== 'nodejs') {
if (app.jsEngine === 'nodejs') {
return dataSourceManager;
}
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)]);
const pages = app.page?.data && app.platform !== 'editor' ? [app.page.data] : dsl.items;
dataSourceManager.emit(
'update-data',
getNodes(nodeIds, pages).map((node) => {
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) => {
if (app.platform !== 'editor') {
node.condResult = dataSourceManager.compliedConds(node);
}
@ -73,19 +74,33 @@ export const createDataSourceManager = (app: TMagicApp, useMock?: boolean, initi
if (typeof app.page?.setData === 'function') {
if (isPage(newNode)) {
app.page.setData(newNode);
} else {
const n = app.page.getNode(node.id);
n?.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]);
}
}
}
}
return newNode;
}),
sourceId,
changeEvent,
);
});
if (newNodes.length) {
dataSourceManager.emit('update-data', newNodes, sourceId, changeEvent, page.id);
}
}
}
});
return dataSourceManager;
};

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -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) => {
on = (path: string, callback: (newVal: any) => void, options?: { immediate?: boolean }) => {
// subscribe 会立即执行一次ignoreFirstCall 会忽略第一次执行
const unsubscribe = this.state!.subscribe(path, ignoreFirstCall(callback));
const unsubscribe = this.state!.subscribe(path, options?.immediate ? callback : ignoreFirstCall(callback));
// 把取消监听的函数保存下来,供 off 时调用
const pathSubscribers = this.subscribers.get(path) ?? new Map<Function, () => void>();

View File

@ -1,7 +1,7 @@
export abstract class ObservedData {
abstract update(data: any, path?: string): void;
abstract on(path: string, callback: (newVal: any) => void): void;
abstract on(path: string, callback: (newVal: any) => void, options?: { immediate?: boolean }): void;
abstract off(path: string, callback: (newVal: any) => void): void;

View File

@ -32,7 +32,10 @@ export class SimpleObservedData extends ObservedData {
this.event.emit('', changeEvent);
}
on(path: string, callback: (newVal: any) => void): void {
on(path: string, callback: (newVal: any) => void, options?: { immediate?: boolean }): void {
if (options?.immediate) {
callback(this.getData(path));
}
this.event.on(path, callback);
}

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/dep",
"type": "module",
"main": "dist/tmagic-dep.umd.cjs",

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/design",
"type": "module",
"sideEffects": [

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/editor",
"type": "module",
"sideEffects": [

View File

@ -129,7 +129,7 @@
<script lang="ts" setup>
import { EventEmitter } from 'events';
import { provide, watch } from 'vue';
import { provide } from 'vue';
import type { MApp } from '@tmagic/core';
@ -211,22 +211,6 @@ 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);

View File

@ -3,9 +3,8 @@ import type { FormConfig, FormState } from '@tmagic/form';
import StageCore, {
CONTAINER_HIGHLIGHT_CLASS_NAME,
ContainerHighlightType,
type CustomizeMoveableOptionsCallbackConfig,
type CustomizeMoveableOptions,
type GuidesOptions,
type MoveableOptions,
RenderType,
type UpdateDragEl,
} from '@tmagic/stage';
@ -56,7 +55,7 @@ export interface EditorProps {
datasourceConfigs?: Record<string, FormConfig>;
datasourceEventMethodList?: Record<string, { events: EventOption[]; methods: EventOption[] }>;
/** 画布中组件选中框的移动范围 */
moveableOptions?: MoveableOptions | ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions);
moveableOptions?: CustomizeMoveableOptions;
/** 编辑器初始化时默认选中的组件ID */
defaultSelected?: Id;
/** 拖入画布中容器时识别到容器后给容器根dom加上的class */
@ -127,7 +126,7 @@ export const defaultEditorProps = {
eventMethodList: () => ({}),
datasourceValues: () => ({}),
datasourceConfigs: () => ({}),
canSelect: (el: HTMLElement) => Boolean(getIdFromEl()(el)),
canSelect: (el: HTMLElement) => Boolean(getIdFromEl()(el) && !el.dataset.tmagicPageFragmentContainerId),
isContainer: (el: HTMLElement) => el.classList.contains('magic-ui-container'),
codeOptions: () => ({}),
customContentMenu: (menus: (MenuButton | MenuComponent)[]) => menus,

View File

@ -32,7 +32,6 @@
content="选择数据源"
>
<TMagicButton
style="margin-left: 5px"
:type="showDataSourceFieldSelect ? 'primary' : 'default'"
:size="size"
@click="showDataSourceFieldSelect = !showDataSourceFieldSelect"

View File

@ -1,14 +1,19 @@
<template>
<div class="m-fields-style-setter">
<TMagicCollapse :model-value="collapseValue">
<TMagicCollapse class="m-fields-style-setter" :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]" @change="change"></component>
<component
v-if="item.component"
:is="item.component"
:values="model[name]"
:size="size"
:disabled="disabled"
@change="change"
></component>
</TMagicCollapseItem>
</template>
</TMagicCollapse>
</div>
</template>
<script setup lang="ts">

View File

@ -6,13 +6,21 @@
: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]" size="small" placeholder="自定义背景位置" clearable @change="changeHandler">
<TMagicInput
v-model="model[name]"
placeholder="自定义背景位置"
clearable
:size="size"
:disabled="disabled"
@change="changeHandler"
>
</TMagicInput>
</div>
</div>

View File

@ -30,7 +30,7 @@
</div>
</div>
<div class="border-value-container">
<MContainer :config="config" :model="model" @change="change"></MContainer>
<MContainer :config="config" :model="model" :size="size" :disabled="disabled" @change="change"></MContainer>
</div>
</div>
</template>
@ -67,7 +67,6 @@ const config = computed(() => ({
{
name: `border${direction.value}Style`,
text: '边框样式',
labelWidth: '68px',
type: 'data-source-field-select',
fieldConfig: {
@ -90,6 +89,8 @@ const emit = defineEmits<{
withDefaults(
defineProps<{
model: FormValue;
disabled?: boolean;
size?: 'large' | 'default' | 'small';
}>(),
{},
);

View File

@ -5,9 +5,10 @@
<span class="next-input">
<input
v-model="model[item.name]"
:title="model[item.name]"
@change="change($event, item.name)"
placeholder="0"
:title="model[item.name]"
:disabled="disabled"
@change="change($event, item.name)"
/>
</span>
</div>
@ -60,6 +61,8 @@ const emit = defineEmits<{
withDefaults(
defineProps<{
disabled?: boolean;
size?: 'large' | 'default' | 'small';
model: FormValue;
}>(),
{},

View File

@ -4,9 +4,10 @@
<span class="next-input">
<input
v-model="model[item.name]"
:title="model[item.name]"
@change="change($event, item.name)"
placeholder="0"
:title="model[item.name]"
:disabled="disabled"
@change="change($event, item.name)"
/>
</span>
</div>
@ -42,6 +43,8 @@ const emit = defineEmits<{
withDefaults(
defineProps<{
model: FormValue;
disabled?: boolean;
size?: 'large' | 'default' | 'small';
}>(),
{},
);

View File

@ -1,5 +1,5 @@
<template>
<MContainer :config="config" :model="values" @change="change"></MContainer>
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
</template>
<script lang="ts" setup>
@ -11,7 +11,11 @@ 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> }>();
defineProps<{
values: Partial<StyleSchema>;
disabled?: boolean;
size?: 'large' | 'default' | 'small';
}>();
const emit = defineEmits<{
change: [v: StyleSchema, eventData: ContainerChangeEventData];

View File

@ -1,6 +1,6 @@
<template>
<MContainer :config="config" :model="values" @change="change"></MContainer>
<Border :model="values" @change="change"></Border>
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
<Border :model="values" :size="size" :disabled="disabled" @change="change"></Border>
</template>
<script lang="ts" setup>
@ -9,7 +9,11 @@ import type { StyleSchema } from '@tmagic/schema';
import Border from '../components/Border.vue';
defineProps<{ values: Partial<StyleSchema> }>();
defineProps<{
values: Partial<StyleSchema>;
disabled?: boolean;
size?: 'large' | 'default' | 'small';
}>();
const emit = defineEmits<{
change: [v: StyleSchema, eventData: ContainerChangeEventData];

View File

@ -1,5 +1,5 @@
<template>
<MContainer :config="config" :model="values" @change="change"></MContainer>
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @change="change"></MContainer>
</template>
<script lang="ts" setup>
@ -10,7 +10,11 @@ import type { StyleSchema } from '@tmagic/schema';
import { AlignCenter, AlignLeft, AlignRight } from '../icons/text-align';
defineProps<{ values: Partial<StyleSchema> }>();
defineProps<{
values: Partial<StyleSchema>;
disabled?: boolean;
size?: 'large' | 'default' | 'small';
}>();
const emit = defineEmits<{
change: [v: StyleSchema, eventData: ContainerChangeEventData];

View File

@ -1,6 +1,12 @@
<template>
<MContainer :config="config" :model="values" @change="change"></MContainer>
<Box v-show="!['fixed', 'absolute'].includes(values.position)" :model="values" @change="change"></Box>
<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>
</template>
<script lang="ts" setup>
@ -28,6 +34,8 @@ import {
defineProps<{
values: Partial<StyleSchema>;
disabled?: boolean;
size?: 'large' | 'default' | 'small';
}>();
const emit = defineEmits<{

View File

@ -1,12 +1,16 @@
<template>
<MContainer :config="config" :model="values" @change="change"></MContainer>
<MContainer :config="config" :model="values" :size="size" :disabled="disabled" @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> }>();
const props = defineProps<{
values: Partial<StyleSchema>;
disabled?: boolean;
size?: 'large' | 'default' | 'small';
}>();
const emit = defineEmits<{
change: [v: string | StyleSchema, eventData: ContainerChangeEventData];

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -347,6 +347,32 @@ 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) {
@ -369,15 +395,13 @@ export const initServiceEvents = (
};
const updateStageDsl = async (value: MApp | null) => {
const dsl = cloneDeep(toRaw(value));
const stage = await getStage();
const runtime = await stage.renderer?.getRuntime();
const app = await getTMagicApp();
if (!app?.dataSourceManager) {
runtime?.updateRootConfig?.(dsl!);
runtime?.updateRootConfig?.(cloneDeep(toRaw(value))!);
}
const page = editorService.get('page');
@ -389,15 +413,17 @@ export const initServiceEvents = (
if (value) {
depService.clearIdleTasks();
await (typeof Worker === 'undefined' ? collectIdle(value.items, true) : depService.collectByWorker(value));
if (value.dataSources && value.dataSourceDeps && app?.dataSourceManager) {
for (const node of getNodes(getDepNodeIds(value.dataSourceDeps), value.items)) {
updateNode(app.dataSourceManager.compiledNode(node), 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?.(cloneDeep(value));
runtime?.updateRootConfig?.(dsl);
}
};
@ -434,7 +460,7 @@ export const initServiceEvents = (
delete value.dataSourceCondDeps;
}
const handler = async () => {
(async () => {
const nodeId = editorService.get('node')?.id || props.defaultSelected;
let node;
if (nodeId) {
@ -453,9 +479,7 @@ export const initServiceEvents = (
if (toRaw(value) !== toRaw(preValue)) {
emit('update:modelValue', value);
}
};
handler();
})();
};
// 新增节点,收集依赖

View File

@ -20,7 +20,7 @@
</template>
<script lang="ts" setup>
import { nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
import { nextTick, onBeforeUnmount, onMounted, onUnmounted, 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 = '';
let value: string;
if (typeof v !== 'string') {
if (language === 'json') {
value = JSON.stringify(v, null, 2);
@ -113,19 +113,40 @@ 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');
return vsDiffEditor?.setModel({
const position = vsDiffEditor?.getPosition();
const result = vsDiffEditor?.setModel({
original: originalModel,
modified: modifiedModel,
});
if (position) {
vsDiffEditor?.setPosition(position);
vsDiffEditor?.focus();
}
return vsEditor?.setValue(values.value);
return result;
}
//
const position = vsEditor?.getPosition();
const result = vsEditor?.setValue(values.value);
//
if (position) {
vsEditor?.setPosition(position);
vsEditor?.focus();
}
return result;
};
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;
@ -149,16 +170,7 @@ const init = async () => {
setEditorValue(props.initValues, props.modifiedValues);
emit('initd', vsEditor);
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);
}
});
codeEditorEl.value.addEventListener('keydown', handleKeyDown);
if (props.type !== 'diff' && props.autoSave) {
vsEditor?.onDidBlurEditorWidget(() => {
@ -214,7 +226,9 @@ onBeforeUnmount(() => {
vsEditor = null;
vsDiffEditor = null;
});
onUnmounted(() => {
codeEditorEl.value?.removeEventListener('keydown', handleKeyDown);
});
const fullScreen = ref(false);
const fullScreenHandler = () => {
fullScreen.value = !fullScreen.value;

View File

@ -11,7 +11,8 @@
:width="160"
:destroy-on-close="true"
>
<div>
<div class="page-bar-popover-wrapper">
<div class="page-bar-popover-inner">
<slot name="page-list-popover" :list="list">
<ToolButton
v-for="(item, index) in list"
@ -25,6 +26,7 @@
></ToolButton>
</slot>
</div>
</div>
<template #reference>
<TMagicIcon class="m-editor-page-list-menu-icon">
<Files></Files>

View File

@ -2,7 +2,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.
@ -206,7 +206,9 @@ export default class extends EventEmitter {
public usePlugin(options: Record<string, Function>) {
for (const [methodName, method] of Object.entries(options)) {
if (typeof method === 'function') this.pluginOptionsList[methodName].push(method);
if (typeof method === 'function' && !this.pluginOptionsList[methodName].includes(method)) {
this.pluginOptionsList[methodName].push(method);
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -11,6 +11,17 @@
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,

View File

@ -13,4 +13,8 @@
flex: 2;
}
}
.tmagic-design-button {
margin-left: 5px;
padding: 5px 8px;
}
}

View File

@ -92,6 +92,11 @@
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;

View File

@ -72,11 +72,6 @@
align-items: center;
border-bottom: 2px solid $border-color;
}
.tmagic-design-form {
padding-right: 10px;
padding-left: 10px;
}
}
.m-editor-props-panel-src-icon {

View File

@ -8,12 +8,25 @@
.tmagic-design-collapse-item {
> .el-collapse-item__header {
background-color: #f2f3f7;
height: 36px;
padding: 10px;
height: 26px;
min-height: 26px;
line-height: 26px;
padding: 0 20px;
box-sizing: border-box;
}
.el-collapse-item__wrap {
padding: 0 10px;
padding: 10px 20px;
}
.el-collapse-item__title {
display: flex;
align-items: center;
gap: 3px;
}
.el-collapse-item__content {
padding: 0;
}
}
}

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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,9 +26,8 @@ import type { FormConfig, TableColumnConfig } from '@tmagic/form';
import type StageCore from '@tmagic/stage';
import type {
ContainerHighlightType,
CustomizeMoveableOptionsCallbackConfig,
CustomizeMoveableOptions,
GuidesOptions,
MoveableOptions,
RenderType,
UpdateDragEl,
} from '@tmagic/stage';
@ -157,7 +156,7 @@ export interface StageOptions {
containerHighlightType?: ContainerHighlightType;
disabledDragStart?: boolean;
render?: (stage: StageCore) => HTMLDivElement | void | Promise<HTMLDivElement | void>;
moveableOptions?: MoveableOptions | ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions);
moveableOptions?: CustomizeMoveableOptions;
canSelect?: (el: HTMLElement) => boolean | Promise<boolean>;
isContainer?: (el: HTMLElement) => boolean | Promise<boolean>;
updateDragEl?: UpdateDragEl;

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.
@ -17,7 +16,12 @@
* limitations under the License.
*/
import { NODE_CONDS_KEY, NODE_DISABLE_CODE_BLOCK_KEY, NODE_DISABLE_DATA_SOURCE_KEY } from '@tmagic/core';
import {
NODE_CONDS_KEY,
NODE_CONDS_RESULT_KEY,
NODE_DISABLE_CODE_BLOCK_KEY,
NODE_DISABLE_DATA_SOURCE_KEY,
} from '@tmagic/core';
import { tMagicMessage } from '@tmagic/design';
import type { FormConfig, FormState, TabConfig, TabPaneConfig } from '@tmagic/form';
@ -163,8 +167,20 @@ export const advancedTabConfig: TabPaneConfig = {
export const displayTabConfig: TabPaneConfig = {
title: '显示条件',
display: (_vm: FormState, { model }: any) => model.type !== 'page',
display: (_state: 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,

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/element-plus-adapter",
"type": "module",
"main": "dist/tmagic-element-plus-adapter.umd.cjs",

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/form-schema",
"type": "module",
"main": "dist/tmagic-form-schema.umd.cjs",

View File

@ -1,5 +1,5 @@
{
"version": "1.5.23",
"version": "1.6.1",
"name": "@tmagic/form",
"type": "module",
"sideEffects": [

View File

@ -9,12 +9,12 @@
>
<TMagicTooltip v-if="option.tooltip" placement="top-start" :content="option.tooltip">
<div>
<TMagicIcon v-if="option.icon" :size="'16'"><component :is="option.icon"></component></TMagicIcon>
<TMagicIcon v-if="option.icon" :size="iconSize"><component :is="option.icon"></component></TMagicIcon>
<span>{{ option.text }}</span>
</div>
</TMagicTooltip>
<div v-else>
<TMagicIcon v-if="option.icon" :size="'16'"><component :is="option.icon"></component></TMagicIcon>
<TMagicIcon v-if="option.icon" :size="iconSize"><component :is="option.icon"></component></TMagicIcon>
<span>{{ option.text }}</span>
</div>
</component>
@ -49,4 +49,14 @@ const clickHandler = (item: any) => {
};
useAddField(props.prop);
const iconSize = computed(() => {
if (props.size === 'small') {
return '12';
}
if (props.size === 'large') {
return '16';
}
return '14';
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<div style="width: 100%">
<div class="m-fields-text">
<TMagicInput
v-model="model[name]"
ref="input"

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,3 +1,9 @@
.m-fields-text {
display: flex;
align-items: center;
width: 100%;
}
.m-form-validate__warning {
color: var(--el-color-warning);
font-size: 12px;

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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.

View File

@ -1,7 +1,7 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
* Copyright (C) 2025 Tencent. 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