Compare commits

..

88 Commits

Author SHA1 Message Date
roymondchen
f7811fdb24 docs: 完善文档 2025-12-11 14:20:39 +08:00
roymondchen
7a0dae9f5a build(tmagic-form): package.json中文件入口配置错误 2025-12-09 20:49:23 +08:00
roymondchen
304aaac8dc chore: update lockfile v1.7.2 2025-12-09 19:15:19 +08:00
roymondchen
0f596b5c42 chore: release v1.7.2 2025-12-09 19:13:51 +08:00
roymondchen
e3db0c93e5 chore: update lockfile v1.7.1 2025-12-09 17:41:35 +08:00
roymondchen
3250c53097 chore: release v1.7.1 2025-12-09 17:23:21 +08:00
roymondchen
756612eda5 feat(vue-components): 不再兼容vue2 2025-12-09 17:20:07 +08:00
roymondchen
2a7ab4e916 feat(vue-runtime-help): 去掉vue2的兼容 2025-12-09 16:20:56 +08:00
roymondchen
d039cee913 fix(tmagic-form): runtime刷新导致root丢失 2025-12-09 16:18:35 +08:00
roymondchen
83664cd440 fix(core): getNode 添加stict参数来表示必须知道页面片容器id才会返回页面内的节点 2025-12-09 15:51:15 +08:00
roymondchen
b3ce1a3b93 fix(form): tabs组件子项配置了name后,配置生成的数据出错 2025-12-09 14:22:57 +08:00
roymondchen
738e8611a4 style(editor): 组件属性配置中表格操作栏会覆盖tab头
fix #664
2025-12-05 19:45:08 +08:00
roymondchen
9815fb7c51 chore: update lockfile v1.7.0 2025-12-04 17:37:17 +08:00
roymondchen
411a28946e chore: release v1.7.0 2025-12-04 17:35:47 +08:00
roymondchen
70d730ca82 refactor(editor): 新增数据源按钮封装成组件 2025-12-04 17:31:38 +08:00
roymondchen
a2a9556ab8 style(editor): 调整已选组件节点右侧按钮样式 2025-12-04 16:36:34 +08:00
roymondchen
7e71c070f1 fix(form): datetime如果设置为时间戳,然后初始值是一个字符串的数字显示不正确 2025-12-04 15:20:30 +08:00
roymondchen
8d55d0cd8d feat(editor): 代码编辑器支持配置自动高度 2025-12-03 17:50:48 +08:00
roymondchen
8d7c8fa725 fix(design): dialog 默认参数 2025-12-03 17:15:26 +08:00
roymondchen
ad746319e9 style(tdesign-vue-next-adapter): 去掉无用prop 2025-12-03 17:09:47 +08:00
roymondchen
0dd7f54ebc feat(form): table支持配置操作按钮的icon 2025-12-02 15:25:34 +08:00
roymondchen
69ac90fe22 feat(form): table支持配置拖动操作中的按钮排序而不是拖动整行 2025-12-01 21:09:11 +08:00
roymondchen
2667981e4c fix(form): table拖拽排序后重新渲染组件 2025-11-26 16:40:26 +08:00
roymondchen
b536eba81c fix(form): 生成表单values时将initValus深拷贝,避免修改到传入的对象 2025-11-26 16:38:37 +08:00
roymondchen
054ed561b6 chore: update lockfile v1.7.0-beta.5 2025-11-24 20:31:39 +08:00
roymondchen
c1dc58c97c chore: release v1.7.0-beta.5 2025-11-24 20:30:11 +08:00
roymondchen
bb7ec0aa1b fix(element-plus-adapter): 构建时将vue源码构建入目标代码中,导致页面存在多个版本vue 2025-11-24 20:27:53 +08:00
roymondchen
849b4dc319 fix(tdesign-vue-next-adapter): 没有extra时form item不渲染help节点 2025-11-24 19:15:50 +08:00
roymondchen
8aef360ed7 chore: update lockfile v1.7.0-beta.4 2025-11-24 16:21:50 +08:00
roymondchen
8d65127cdd chore: release v1.7.0-beta.4 2025-11-24 16:20:19 +08:00
roymondchen
93640257e9 fix(design,editro,element-plus-adapter,tdesign-vue-next-adapter): elememt-plus表单渲染失败 2025-11-24 16:14:21 +08:00
roymondchen
09dfaad2cc feat(design): 在html中添加adapter类型class 2025-11-24 13:56:11 +08:00
roymondchen
5dbe1fb655 feat(core): getNode未指定页面片容器id时取获得到的第一个 2025-11-24 13:54:09 +08:00
roymondchen
82df2a8f5c chore: update deps 2025-11-21 16:50:46 +08:00
roymondchen
4dc5f0a298 style: 优化部分结构 2025-11-21 15:41:41 +08:00
roymondchen
3b913c1af4 feat(form): group list新增添加按钮配置 2025-11-20 18:05:53 +08:00
roymondchen
310054b7d6 feat(form): form dialog新增show close/show cancel 配置 2025-11-20 17:57:44 +08:00
roymondchen
fa0e10f687 feat(form): table操作列支持配置固定在左边还是右边 2025-11-20 15:44:14 +08:00
roymondchen
bf6598c871 feat(form): form dialog新增props 2025-11-20 15:03:17 +08:00
roymondchen
6a7f80c48d feat(form): table新增新增按钮配置 2025-11-20 11:27:12 +08:00
roymondchen
e2708b868b feat(form): 表格拖动支持tdesign 2025-11-19 14:39:49 +08:00
roymondchen
97affb2bff feat(form): tip图标放到label中去 2025-11-19 14:37:24 +08:00
roymondchen
e418130a66 feat(form, design, form-schema): table支持自定义title,table 表单组件支持配置title tip 2025-11-19 13:23:03 +08:00
roymondchen
55a2869818 fix(form): 文本输入change出发太频繁 2025-11-18 19:13:56 +08:00
roymondchen
0ecc116652 feat(design, tdesign-vue-next-adapter): formItem新增labelAlign prop 2025-11-18 19:09:14 +08:00
roymondchen
310aa47c1d feat(form): panel点击标题可以展开或者收缩内容 2025-11-18 19:03:29 +08:00
roymondchen
27555d6b2f fix(design): select visible-change事件名写错 2025-11-13 20:27:49 +08:00
roymondchen
da9884645f fix(playgournd): 存在多个vue版本问题 2025-11-12 19:52:32 +08:00
roymondchen
1a08b16a39 fix(tdesign-vue-next-adapter): select支持allowCreate 2025-11-12 19:52:32 +08:00
roymondchen
a96ca8092c feat(design,tdesign-vue-next-adapter): input添加click事件 2025-11-12 19:52:32 +08:00
roymondchen
489d321677 chore: update lockfile v1.7.0-beta.3 2025-11-12 19:52:32 +08:00
roymondchen
3d0081e48e chore: release v1.7.0-beta.3 2025-11-12 19:52:32 +08:00
roymondchen
acda22d5cb feat(design,tdesign-vue,next-adapter): textarea支持autosize 2025-11-12 19:52:32 +08:00
roymondchen
7b19a01849 chore(playground): adapter改成异步加载 2025-11-12 19:52:32 +08:00
roymondchen
d0179028fb feat(element-plus-adapter, from, tdesign-vue-adapter): button兼容type=default和type为空的情况 2025-11-12 19:52:32 +08:00
roymondchen
564a7f4271 fix(editor): 表单组件保持单向数据流 2025-11-12 19:52:32 +08:00
roymondchen
6f0498a9e7 feat(form): text组件配置的append.hander函数添加setModel/setFormValue方法 2025-11-12 19:52:32 +08:00
roymondchen
4f52fcb122 style(form): group-list样式改成card的样式 2025-11-12 19:52:31 +08:00
roymondchen
1297939db2 style(table): table全屏后居中显示 2025-11-12 19:52:31 +08:00
roymondchen
b3cfc0b5d3 chore: update lockfile v1.7.0-beta.2 2025-11-12 19:52:31 +08:00
roymondchen
b96036b23c chore: release v1.7.0-beta.2 2025-11-12 19:52:31 +08:00
roymondchen
3c3ba6525b style(form): table新增按钮放到右边 2025-11-12 19:52:31 +08:00
roymondchen
0f3dfcf511 fix(form): daterange 配置names后配置失效 2025-11-12 19:52:31 +08:00
roymondchen
12e6dd18b4 feat(design, element-plus-adapter, tdesign-vue-next-adapter): 添加adapterType, 完善tdesign useZIndex 2025-11-12 19:52:31 +08:00
roymondchen
cbec52936d fix(form): dialog submit event获取到的changeRecords为空 2025-11-12 19:52:31 +08:00
roymondchen
979b834fac feat(design, tdesign-vue-next-adapter, table, element-plus-adapter): 完善tdesign适配 2025-11-12 19:52:31 +08:00
roymondchen
dabeba85a0 build(playground): 去掉auto-import 2025-11-12 19:52:31 +08:00
roymondchen
954bd37d52 chore: update lockfile v1.7.0-beta.1 2025-11-12 19:52:31 +08:00
roymondchen
59aa9f14b3 chore: release v1.7.0-beta.1 2025-11-12 19:52:31 +08:00
roymondchen
38192a6d48 feat(design, form, form-schema, tdesign-vue-next-adapter): textarea支持rows配置 2025-11-12 19:52:31 +08:00
roymondchen
ec479b9296 feat(form): 表单校验后的错误信息将name转换成text 2025-11-12 19:52:31 +08:00
roymondchen
7f0aef7bee build(editor): css变量使用错误导致lightingcss构建失败 2025-11-12 19:52:31 +08:00
roymondchen
36508aba88 chore(playgournd): 添加@tmagic/design依赖 2025-11-12 19:52:31 +08:00
roymondchen
f30a8ddc41 chore: update lockfile v1.7.0-beta.0 2025-11-12 19:52:31 +08:00
roymondchen
a28705e3df chore: release v1.7.0-beta.0 2025-11-12 19:52:31 +08:00
roymondchen
ca0f8fc988 feat(design, form, tdesign-vue-next-adapter): 完善tdesign适配 2025-11-12 19:52:31 +08:00
roymondchen
68c69ac405 fix(form): tabel复制行不生效 2025-11-12 19:52:30 +08:00
roymondchen
3a9c94a6a6 feat(form): 新增style,fieldStyle配置;tooltip支持配置placement;配置中的函数新增getFormValue方法 2025-11-12 19:52:30 +08:00
roymondchen
5fe57cd389 feat(form): text新增prepend, append不默认使用button 2025-11-12 19:52:30 +08:00
roymondchen
51e9732894 feat(form): fieldset中checkbox新增name,trueValue,falseValue配置 2025-11-12 19:52:30 +08:00
roymondchen
630301bce2 feat(form): 新增flex-layout组件 2025-11-12 19:52:30 +08:00
roymondchen
507e51a2dc feat(design, element-plus-adapter, tdesign-vue-next-adapter): 新增popconfirm组件 2025-11-12 19:52:30 +08:00
roymondchen
11d25603a8 feat(playground): 支持UI组件库切换 2025-11-12 19:52:30 +08:00
roymondchen
8f0d99a4a6 style(design, editor, tdesign-vue-next-adapter): 编辑器顶部导航按钮tdesign下样式优化 2025-11-12 19:52:30 +08:00
roymondchen
08b476e04f feat(design,editor,element-plus-adapter,form,table,tdesign-vue-next-adapter): 重构table组件,适配tdesign 2025-11-12 19:52:30 +08:00
roymondchen
1cb2d57ade refactor(editor): 调整表单配置 2025-11-12 19:52:30 +08:00
roymondchen
d59428d2d6 refactor(form): 保持单向数据流,表单内部的组件不去修改表单的值,统一通过chang事件通知表单修改 2025-11-12 19:52:30 +08:00
roymondchen
e36da82d29 fix(vue-runtime-hlep): 页面片销毁后需要取消app的事件监听 2025-11-12 19:49:55 +08:00
153 changed files with 6605 additions and 5290 deletions

View File

@ -1,3 +1,146 @@
## [1.7.2](https://github.com/Tencent/tmagic-editor/compare/v1.7.1...v1.7.2) (2025-12-09)
## [1.7.1](https://github.com/Tencent/tmagic-editor/compare/v1.7.0...v1.7.1) (2025-12-09)
### Bug Fixes
* **core:** getNode 添加stict参数来表示必须知道页面片容器id才会返回页面内的节点 ([83664cd](https://github.com/Tencent/tmagic-editor/commit/83664cd44019c3b5f05d2ad60bbb8fcf751f6b35))
* **form:** tabs组件子项配置了name后,配置生成的数据出错 ([b3ce1a3](https://github.com/Tencent/tmagic-editor/commit/b3ce1a3b930e9cbfc74b72bfb7dd9268fe341626))
* **tmagic-form:** runtime刷新导致root丢失 ([d039cee](https://github.com/Tencent/tmagic-editor/commit/d039cee9136e7e892161dafe25ff34ee95bce958))
### Features
* **vue-components:** 不再兼容vue2 ([756612e](https://github.com/Tencent/tmagic-editor/commit/756612eda51fa8079420203eaca585175b039e8b))
* **vue-runtime-help:** 去掉vue2的兼容 ([2a7ab4e](https://github.com/Tencent/tmagic-editor/commit/2a7ab4e916444a68c7cc05af4fa57ddd59994393))
# [1.7.0](https://github.com/Tencent/tmagic-editor/compare/v1.7.0-beta.5...v1.7.0) (2025-12-04)
### Bug Fixes
* **design:** dialog 默认参数 ([8d7c8fa](https://github.com/Tencent/tmagic-editor/commit/8d7c8fa725dba507c3a4874f879989ad9c050603))
* **form:** datetime如果设置为时间戳然后初始值是一个字符串的数字显示不正确 ([7e71c07](https://github.com/Tencent/tmagic-editor/commit/7e71c070f1ee9857fd5610000188ef1a222fade1))
* **form:** table拖拽排序后重新渲染组件 ([2667981](https://github.com/Tencent/tmagic-editor/commit/2667981e4c0c451ded88e06f412dd4bcb5ec41dc))
* **form:** 生成表单values时将initValus深拷贝,避免修改到传入的对象 ([b536eba](https://github.com/Tencent/tmagic-editor/commit/b536eba81c33f8e532179b69072ea7c5d4033235))
### Features
* **editor:** 代码编辑器支持配置自动高度 ([8d55d0c](https://github.com/Tencent/tmagic-editor/commit/8d55d0cd8d1059dcbca86fc5b72ec82f22c35d9c))
* **form:** table支持配置拖动操作中的按钮排序而不是拖动整行 ([69ac90f](https://github.com/Tencent/tmagic-editor/commit/69ac90fe22778fedb9e35b95595428395fd65cc5))
* **form:** table支持配置操作按钮的icon ([0dd7f54](https://github.com/Tencent/tmagic-editor/commit/0dd7f54ebc3518404653d6e43ee428e05566dd28))
# [1.7.0-beta.5](https://github.com/Tencent/tmagic-editor/compare/v1.7.0-beta.4...v1.7.0-beta.5) (2025-11-24)
### Bug Fixes
* **element-plus-adapter:** 构建时将vue源码构建入目标代码中,导致页面存在多个版本vue ([bb7ec0a](https://github.com/Tencent/tmagic-editor/commit/bb7ec0aa1b55f83f68d7021fb98090d8e555ed65))
* **tdesign-vue-next-adapter:** 没有extra时form item不渲染help节点 ([849b4dc](https://github.com/Tencent/tmagic-editor/commit/849b4dc319b03a54dc1cfcccc1f505e61e1ccd5b))
# [1.7.0-beta.4](https://github.com/Tencent/tmagic-editor/compare/v1.6.1...v1.7.0-beta.4) (2025-11-24)
### Bug Fixes
* **design,editro,element-plus-adapter,tdesign-vue-next-adapter:** elememt-plus表单渲染失败 ([9364025](https://github.com/Tencent/tmagic-editor/commit/93640257e90e6aa4708184a2513b80149887b0b9))
* **design:** select visible-change事件名写错 ([27555d6](https://github.com/Tencent/tmagic-editor/commit/27555d6b2f3c4c2022f77504bb4d8faf7c6f3e55))
* **editor:** 表单组件保持单向数据流 ([564a7f4](https://github.com/Tencent/tmagic-editor/commit/564a7f4271b9adb345d679f85929506e952c9cb3))
* **form:** daterange 配置names后配置失效 ([0f3dfcf](https://github.com/Tencent/tmagic-editor/commit/0f3dfcf5118e1247f351357a9eb05bfbdc7cc43b))
* **form:** dialog submit event获取到的changeRecords为空 ([cbec529](https://github.com/Tencent/tmagic-editor/commit/cbec52936db52d5c57c454468a11bea1dbbcd058))
* **form:** tabel复制行不生效 ([68c69ac](https://github.com/Tencent/tmagic-editor/commit/68c69ac4058bbe7adf00a73b21df2ec7adb713cc))
* **form:** 文本输入change出发太频繁 ([55a2869](https://github.com/Tencent/tmagic-editor/commit/55a28698183998e6dec73d8d41d5e5f029d17e7a))
* **playgournd:** 存在多个vue版本问题 ([da98846](https://github.com/Tencent/tmagic-editor/commit/da9884645fe51d3de65e461c2e434d3e221e56b5))
* **tdesign-vue-next-adapter:** select支持allowCreate ([1a08b16](https://github.com/Tencent/tmagic-editor/commit/1a08b16a399dae00e4b0d62d9d930514c84f669d))
* **vue-runtime-hlep:** 页面片销毁后需要取消app的事件监听 ([e36da82](https://github.com/Tencent/tmagic-editor/commit/e36da82d2928af6617c280939b02b6bc9f534b70))
### Features
* **core:** getNode未指定页面片容器id时取获得到的第一个 ([5dbe1fb](https://github.com/Tencent/tmagic-editor/commit/5dbe1fb6555188bb17aa5b3dd1d7205b8c4d9824))
* **design, element-plus-adapter, tdesign-vue-next-adapter:** 新增popconfirm组件 ([507e51a](https://github.com/Tencent/tmagic-editor/commit/507e51a2dc91defb67087a539d786d01ef95b9ad))
* **design, element-plus-adapter, tdesign-vue-next-adapter:** 添加adapterType, 完善tdesign useZIndex ([12e6dd1](https://github.com/Tencent/tmagic-editor/commit/12e6dd18b47c4e6a11f273cbbb138c5d0e33f0ca))
* **design, form, form-schema, tdesign-vue-next-adapter:** textarea支持rows配置 ([38192a6](https://github.com/Tencent/tmagic-editor/commit/38192a6d4854904d7c1303556ad6ef4b76b7cd21))
* **design, form, tdesign-vue-next-adapter:** 完善tdesign适配 ([ca0f8fc](https://github.com/Tencent/tmagic-editor/commit/ca0f8fc9887d9fcff31052239b396d02f258d1b4))
* **design, tdesign-vue-next-adapter, table, element-plus-adapter:** 完善tdesign适配 ([979b834](https://github.com/Tencent/tmagic-editor/commit/979b834facbabf50d711b76ca5180f07b319b432))
* **design, tdesign-vue-next-adapter:** formItem新增labelAlign prop ([0ecc116](https://github.com/Tencent/tmagic-editor/commit/0ecc11665235c136a2d9e7b361fe2ecab33ff5e3))
* **design,editor,element-plus-adapter,form,table,tdesign-vue-next-adapter:** 重构table组件,适配tdesign ([08b476e](https://github.com/Tencent/tmagic-editor/commit/08b476e04f36154ef58f1f3388bc2516742e37d8))
* **design,tdesign-vue-next-adapter:** input添加click事件 ([a96ca80](https://github.com/Tencent/tmagic-editor/commit/a96ca8092c51d1700943e58e197a5d1584297d76))
* **design,tdesign-vue,next-adapter:** textarea支持autosize ([acda22d](https://github.com/Tencent/tmagic-editor/commit/acda22d5cb4905ae5d1f4bd70efb38ee5820baeb))
* **design:** 在html中添加adapter类型class ([09dfaad](https://github.com/Tencent/tmagic-editor/commit/09dfaad2ccf9cd50491a8352e33416c7a17027c4))
* **element-plus-adapter, from, tdesign-vue-adapter:** button兼容type=default和type为空的情况 ([d017902](https://github.com/Tencent/tmagic-editor/commit/d0179028fbab5b485cb0b970d8d558da4f22d869))
* **form, design, form-schema:** table支持自定义title,table 表单组件支持配置title tip ([e418130](https://github.com/Tencent/tmagic-editor/commit/e418130a66c54b7fed533977b1558e7206d729fb))
* **form:** fieldset中checkbox新增name,trueValue,falseValue配置 ([51e9732](https://github.com/Tencent/tmagic-editor/commit/51e973289473af7fae14f56e3d2fed5db838c797))
* **form:** form dialog新增props ([bf6598c](https://github.com/Tencent/tmagic-editor/commit/bf6598c8718d5f2e3d276029bf924a7de7cb458b))
* **form:** form dialog新增show close/show cancel 配置 ([310054b](https://github.com/Tencent/tmagic-editor/commit/310054b7d63e688c349142c9b7767fc5f8a2f766))
* **form:** group list新增添加按钮配置 ([3b913c1](https://github.com/Tencent/tmagic-editor/commit/3b913c1af48aa6a47e632725b008061d7faca204))
* **form:** panel点击标题可以展开或者收缩内容 ([310aa47](https://github.com/Tencent/tmagic-editor/commit/310aa47c1df9a88304f264325c697b85e09287df))
* **form:** table操作列支持配置固定在左边还是右边 ([fa0e10f](https://github.com/Tencent/tmagic-editor/commit/fa0e10f6872ade5599957051e2e6923fa2d4ca66))
* **form:** table新增新增按钮配置 ([6a7f80c](https://github.com/Tencent/tmagic-editor/commit/6a7f80c48de0e85587c27f67fe0f132f7dfee000))
* **form:** text新增prepend, append不默认使用button ([5fe57cd](https://github.com/Tencent/tmagic-editor/commit/5fe57cd389e6c955049c2ee81f62c37c7a86930f))
* **form:** text组件配置的append.hander函数添加setModel/setFormValue方法 ([6f0498a](https://github.com/Tencent/tmagic-editor/commit/6f0498a9e7ee633310b4a08374409b3f1b7607b9))
* **form:** tip图标放到label中去 ([97affb2](https://github.com/Tencent/tmagic-editor/commit/97affb2bff856b271136b50dccd45d95e17abd6a))
* **form:** 新增flex-layout组件 ([630301b](https://github.com/Tencent/tmagic-editor/commit/630301bce213e3ff28a7fb50e9cc9b8bb0c2b756))
* **form:** 新增style,fieldStyle配置;tooltip支持配置placement;配置中的函数新增getFormValue方法 ([3a9c94a](https://github.com/Tencent/tmagic-editor/commit/3a9c94a6a6529a63e022955b2f7eee23bf125d81))
* **form:** 表单校验后的错误信息将name转换成text ([ec479b9](https://github.com/Tencent/tmagic-editor/commit/ec479b9296d5dbea0df121a4702a123518808e58))
* **form:** 表格拖动支持tdesign ([e2708b8](https://github.com/Tencent/tmagic-editor/commit/e2708b868b2fbb126f1d8d846e25335b6005cd86))
* **playground:** 支持UI组件库切换 ([11d2560](https://github.com/Tencent/tmagic-editor/commit/11d25603a8b5f8bc3d9abbbeedf9c298ca0e2c72))
* **table:** action支持配置disabled ([8809351](https://github.com/Tencent/tmagic-editor/commit/88093515373e1b431c83c0da9c28a2688421d151))
# [1.7.0-beta.3](https://github.com/Tencent/tmagic-editor/compare/v1.7.0-beta.2...v1.7.0-beta.3) (2025-10-31)
### Bug Fixes
* **editor:** 表单组件保持单向数据流 ([09663c8](https://github.com/Tencent/tmagic-editor/commit/09663c8320829dc52a4d4ffb65c73fe147a3f5fc))
### Features
* **design,tdesign-vue,next-adapter:** textarea支持autosize ([be75ac9](https://github.com/Tencent/tmagic-editor/commit/be75ac994f0c03a9bdae05bc433513278d32c905))
* **element-plus-adapter, from, tdesign-vue-adapter:** button兼容type=default和type为空的情况 ([2a29971](https://github.com/Tencent/tmagic-editor/commit/2a2997131873a4836303ac2411371dd78726f6f9))
* **form:** text组件配置的append.hander函数添加setModel/setFormValue方法 ([57a634a](https://github.com/Tencent/tmagic-editor/commit/57a634a687c53132f521a8ed35a649edec488738))
# [1.7.0-beta.2](https://github.com/Tencent/tmagic-editor/compare/v1.7.0-beta.1...v1.7.0-beta.2) (2025-10-28)
### Bug Fixes
* **form:** daterange 配置names后配置失效 ([d22e520](https://github.com/Tencent/tmagic-editor/commit/d22e520fbf6fc95c2c9600f02107d987b3a46fa7))
* **form:** dialog submit event获取到的changeRecords为空 ([02e1c7f](https://github.com/Tencent/tmagic-editor/commit/02e1c7f479e5370ba6ea2153b9547bbb0c090431))
### Features
* **design, element-plus-adapter, tdesign-vue-next-adapter:** 添加adapterType, 完善tdesign useZIndex ([b312b5a](https://github.com/Tencent/tmagic-editor/commit/b312b5a5bc827a8199d5a7b36418f0ad933be081))
* **design, tdesign-vue-next-adapter, table, element-plus-adapter:** 完善tdesign适配 ([fa20837](https://github.com/Tencent/tmagic-editor/commit/fa208372ab5c204b6cb34ff977cd133156513fda))
# [1.7.0-beta.1](https://github.com/Tencent/tmagic-editor/compare/v1.7.0-beta.0...v1.7.0-beta.1) (2025-10-24)
### Features
* **design, form, form-schema, tdesign-vue-next-adapter:** textarea支持rows配置 ([c717472](https://github.com/Tencent/tmagic-editor/commit/c7174726b3f017afb5666f9ccc343a0c98d8d1fa))
* **form:** 表单校验后的错误信息将name转换成text ([b247490](https://github.com/Tencent/tmagic-editor/commit/b2474909cff049b0800da5be3293ad35309c1b1e))
# [1.7.0-beta.0](https://github.com/Tencent/tmagic-editor/compare/v1.6.1...v1.7.0-beta.0) (2025-10-23)

View File

@ -4,34 +4,101 @@
- **参数:**
-
- {[ComponentGroup](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/editor/src/type.ts#L355)[]} componentGroupList 组件列表配置
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
设置左侧面板的组件列表配置
:::tip
该方法通常由编辑器内部调用,开发者可以通过 [m-editor 的 componentGroupList prop](./props.md#componentgrouplist) 来配置组件列表
:::
- **示例:**
```js
import { componentListService } from '@tmagic/editor';
componentListService.setList([
{
title: '基础组件',
items: [
{
icon: 'text-icon',
text: '文本',
type: 'text',
},
{
icon: 'button-icon',
text: '按钮',
type: 'button',
},
],
},
]);
```
## getList
- **参数:**
-
- **参数:**
- **返回:**
- `{Promise<void>}`
- {[ComponentGroup](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/editor/src/type.ts#L355)[]} 组件列表配置
- **详情:**
获取当前的组件列表配置
- **示例:**
```js
import { componentListService } from '@tmagic/editor';
const list = componentListService.getList();
console.log(list);
```
## resetState
- **参数:**
- **返回:**
- `{void}`
- **详情:**
重置组件列表状态,清空所有配置
- **示例:**
```js
import { componentListService } from '@tmagic/editor';
componentListService.resetState();
```
## destroy
- **参数:**
-
- **参数:**
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
销毁 componentListService清空状态并移除所有事件监听和插件
- **示例:**
```js
import { componentListService } from '@tmagic/editor';
componentListService.destroy();
```

View File

@ -2,30 +2,540 @@
## get
- **参数:**
- `{StateKey}` name 状态键名
- **返回:**
- `{any}` 对应的状态值
- **详情:**
获取数据源服务的内部状态
可用的状态键:
- `datasourceTypeList`: 数据源类型列表
- `dataSources`: 当前数据源列表
- `editable`: 是否可编辑
- `configs`: 数据源表单配置
- `values`: 数据源默认值
- `events`: 数据源事件列表
- `methods`: 数据源方法列表
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const dataSources = dataSourceService.get('dataSources');
console.log(dataSources);
```
## set
- **参数:**
- `{StateKey}` name 状态键名
- `{any}` value 状态值
- **返回:**
- `{void}`
- **详情:**
设置数据源服务的内部状态
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.set('editable', false);
```
## getFormConfig
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型,默认为 'base'
- **返回:**
- {[FormConfig](https://github.com/Tencent/tmagic-editor/blob/c143a5f7670ae61d80c1a2cfcc780cfb5259849d/packages/form/src/schema.ts#L706)} 表单配置
- **详情:**
获取指定类型数据源的表单配置
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const config = dataSourceService.getFormConfig('http');
console.log(config);
```
## setFormConfig
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型
- {[FormConfig](https://github.com/Tencent/tmagic-editor/blob/c143a5f7670ae61d80c1a2cfcc780cfb5259849d/packages/form/src/schema.ts#L706)} config 表单配置
- **返回:**
- `{void}`
- **详情:**
设置指定类型数据源的表单配置
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.setFormConfig('http', [
{
name: 'url',
text: '请求地址',
type: 'text',
},
{
name: 'method',
text: '请求方法',
type: 'select',
options: [
{ text: 'GET', value: 'GET' },
{ text: 'POST', value: 'POST' },
],
},
]);
```
## getFormValue
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型,默认为 'base'
- **返回:**
- {Partial<[DataSourceSchema](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/schema/src/index.ts#L221)>} 数据源默认值
- **详情:**
获取指定类型数据源的默认值
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const defaultValue = dataSourceService.getFormValue('http');
console.log(defaultValue);
```
## setFormValue
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型
- {Partial<[DataSourceSchema](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/schema/src/index.ts#L221)>} value 数据源默认值
- **返回:**
- `{void}`
- **详情:**
设置指定类型数据源的默认值
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.setFormValue('http', {
type: 'http',
method: 'GET',
url: '',
});
```
## getFormEvent
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型,默认为 'base'
- **返回:**
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} 事件列表
- **详情:**
获取指定类型数据源的事件列表
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const events = dataSourceService.getFormEvent('http');
console.log(events);
```
## setFormEvent
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} value 事件列表
- **返回:**
- `{void}`
- **详情:**
设置指定类型数据源的事件列表
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.setFormEvent('http', [
{ label: '请求成功', value: 'success' },
{ label: '请求失败', value: 'error' },
]);
```
## getFormMethod
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型,默认为 'base'
- **返回:**
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} 方法列表
- **详情:**
获取指定类型数据源的方法列表
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const methods = dataSourceService.getFormMethod('http');
console.log(methods);
```
## setFormMethod
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` type 数据源类型
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} value 方法列表
- **返回:**
- `{void}`
- **详情:**
设置指定类型数据源的方法列表
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.setFormMethod('http', [
{ label: '发起请求', value: 'request' },
{ label: '重试', value: 'retry' },
]);
```
## add
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- {[DataSourceSchema](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/schema/src/index.ts#L221)} config 数据源配置
- **返回:**
- {[DataSourceSchema](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/schema/src/index.ts#L221)} 添加后的数据源配置
- **详情:**
添加一个数据源如果配置中没有id或id已存在会自动生成新的id
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const newDs = dataSourceService.add({
type: 'http',
title: '用户信息',
url: '/api/user',
method: 'GET',
});
console.log(newDs.id); // 自动生成的id
```
## update
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- {[DataSourceSchema](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/schema/src/index.ts#L221)} config 数据源配置
- `{Object}` options 可选配置
- {[ChangeRecord](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/form/src/schema.ts#L27-L39)[]} changeRecords 变更记录
- **返回:**
- {[DataSourceSchema](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/schema/src/index.ts#L221)} 更新后的数据源配置
- **详情:**
更新数据源
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const updatedDs = dataSourceService.update({
id: 'ds_123',
type: 'http',
title: '用户详情',
url: '/api/user/detail',
});
console.log(updatedDs);
```
## remove
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{string}` id 数据源id
- **返回:**
- `{void}`
- **详情:**
删除指定id的数据源
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.remove('ds_123');
```
## createId
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- **返回:**
- `{string}` 生成的唯一id
- **详情:**
生成一个唯一的数据源id格式为 `ds_` + guid
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const id = dataSourceService.createId();
console.log(id); // 'ds_xxx-xxx-xxx'
```
## getDataSourceById
- **参数:**
- `{string}` id 数据源id
- **返回:**
- {[DataSourceSchema](https://github.com/Tencent/tmagic-editor/blob/5880dfbe15fcead63e9dc7c91900f8c4e7a574d8/packages/schema/src/index.ts#L221) | undefined} 数据源配置
- **详情:**
根据id获取数据源配置
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
const ds = dataSourceService.getDataSourceById('ds_123');
console.log(ds);
```
## copyWithRelated
- **参数:**
- {[MNode](https://github.com/Tencent/tmagic-editor/blob/c143a5f7670ae61d80c1a2cfcc780cfb5259849d/packages/schema/src/index.ts#L99) | [MNode](https://github.com/Tencent/tmagic-editor/blob/c143a5f7670ae61d80c1a2cfcc780cfb5259849d/packages/schema/src/index.ts#L99)[]} config 组件节点配置
- `{TargetOptions}` collectorOptions 可选的收集器配置
- **返回:**
- `{void}`
- **详情:**
复制组件时会带上组件关联的数据源,将关联的数据源存储到 localStorage
- **示例:**
```js
import { dataSourceService, editorService } from '@tmagic/editor';
const node = editorService.get('node');
dataSourceService.copyWithRelated(node);
```
## paste
- **参数:**
- **返回:**
- `{void}`
- **详情:**
粘贴数据源,从 localStorage 中读取复制的数据源并添加到当前页面
如果数据源id已存在则不会覆盖
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.paste();
```
## resetState
## destroy
- **参数:**
- **返回:**
- `{void}`
- **详情:**
重置数据源服务状态,清空所有数据源
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.resetState();
```
## destroy
- **参数:**
- **返回:**
- `{void}`
- **详情:**
销毁 dataSourceService移除所有事件监听并重置状态
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.destroy();
```
## usePlugin
- **详情:**
相对于[use](#use), usePlugin支持更加灵活更加细致的扩展 上述方法中标记有`扩展支持: 是`的方法都支持使用usePlugin扩展
每个支持扩展的方法都支持定制before、after两个hook来干预原有方法的行为before可以用于修改传入参数after可以用于修改返回的值
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.usePlugin({
beforeAdd(config) {
console.log('添加前:', config);
return [config];
},
afterAdd(result, config) {
console.log('添加后:', result);
return result;
},
});
```
## removeAllPlugins
- **详情:**
删掉当前设置的所有扩展
- **示例:**
```js
import { dataSourceService } from '@tmagic/editor';
dataSourceService.removeAllPlugins();
```

View File

@ -1,97 +1,244 @@
# eventService方法
# eventsService方法
## init
- **参数:**
-
- {Record<string, { events: [EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]; methods: [EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[] }>} eventMethodList 事件方法列表配置
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
初始化事件服务,设置所有组件的事件和方法列表
:::tip
该方法通常由编辑器内部调用,开发者可以通过 [m-editor 的 eventMethodList prop](./props.md#eventmethodlist) 来配置
:::
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
eventsService.init({
page: {
events: [
{ label: '页面加载', value: 'load' },
{ label: '页面卸载', value: 'unload' },
],
methods: [
{ label: '刷新', value: 'refresh' },
{ label: '返回', value: 'back' },
],
},
button: {
events: [
{ label: '点击', value: 'click' },
],
methods: [],
},
});
```
## setEvents
- **参数:**
-
- {Record<string, [EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]>} events 事件配置对象
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
批量设置多个组件类型的事件列表
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
eventsService.setEvents({
page: [
{ label: '页面加载', value: 'load' },
{ label: '页面显示', value: 'show' },
],
text: [
{ label: '点击', value: 'click' },
],
});
```
## setEvent
- **参数:**
-
- `{string}` type 组件类型
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} events 事件列表
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
设置指定组件类型的事件列表
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
eventsService.setEvent('button', [
{ label: '点击', value: 'click' },
{ label: '长按', value: 'longpress' },
]);
```
## getEvent
- **参数:**
-
- `{string}` type 组件类型
- **返回:**
- `{Promise<void>}`
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} 事件列表
- **详情:**
获取指定组件类型的事件列表
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
const events = eventsService.getEvent('button');
console.log(events); // [{ label: '点击', value: 'click' }, ...]
```
## setMethods
- **参数:**
-
- {Record<string, [EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]>} methods 方法配置对象
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
批量设置多个组件类型的方法列表
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
eventsService.setMethods({
page: [
{ label: '刷新', value: 'refresh' },
{ label: '滚动到顶部', value: 'scrollToTop' },
],
video: [
{ label: '播放', value: 'play' },
{ label: '暂停', value: 'pause' },
],
});
```
## setMethod
- **参数:**
-
- `{string}` type 组件类型
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} methods 方法列表
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
设置指定组件类型的方法列表
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
eventsService.setMethod('video', [
{ label: '播放', value: 'play' },
{ label: '暂停', value: 'pause' },
{ label: '停止', value: 'stop' },
]);
```
## getMethod
- **参数:**
-
- `{string}` type 组件类型
- **返回:**
- `{Promise<void>}`
- {[EventOption](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/core/src/events.ts#L26-L29)[]} 方法列表
- **详情:**
获取指定组件类型的方法列表
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
const methods = eventsService.getMethod('video');
console.log(methods); // [{ label: '播放', value: 'play' }, ...]
```
## resetState
- **参数:**
- **返回:**
- `{void}`
- **详情:**
重置事件服务状态,清空所有事件和方法配置
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
eventsService.resetState();
```
## destroy
- **参数:**
-
- **参数:**
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
销毁 eventsService重置状态并移除所有事件监听和插件
- **示例:**
```js
import { eventsService } from '@tmagic/editor';
eventsService.destroy();
```

View File

@ -967,83 +967,316 @@ const updateDragEl = (el, target) => {
- **详情:**
标尺配置
画布标尺和参考线的配置选项
- **默认值:** `undefined`
- **类型:** `Partial<GuidesOptions>`
- **示例:**
```html
<template>
<m-editor :guides-options="guidesOptions"></m-editor>
</template>
<script setup>
const guidesOptions = {
// 标尺刻度单位
unit: 1,
// 标尺背景色
backgroundColor: '#f0f0f0',
// 标尺文字颜色
textColor: '#333',
// 参考线颜色
lineColor: '#ff0000',
};
</script>
```
## disabledPageFragment
- **详情:**
禁用页面片
禁用页面片功能
页面片是可以在多个页面中复用的组件集合
- **默认值:** `false`
- **类型:** `boolean`
- **示例:**
```html
<template>
<m-editor :disabled-page-fragment="true"></m-editor>
</template>
```
## disabledStageOverlay
- **详情:**
禁用双击在浮层中单独编辑选中组件
禁用双击在浮层中单独编辑选中组件的功能
启用时,双击组件可以在浮层中单独编辑,避免其他组件干扰
- **默认值:** `false`
- **类型:** `boolean`
- **示例:**
```html
<template>
<m-editor :disabled-stage-overlay="true"></m-editor>
</template>
```
## disabledShowSrc
- **详情:**
禁用属性配置面板右下角显示源码的按钮
禁用属性配置面板右下角"显示源码"的按钮
该按钮可以查看和编辑组件的 JSON 配置
- **默认值:** `false`
- **类型:** `boolean`
- **示例:**
```html
<template>
<m-editor :disabled-show-src="true"></m-editor>
</template>
```
## disabledDataSource
- **详情:**
禁用数据源
禁用数据源功能
禁用后,左侧面板将不显示数据源选项卡
- **默认值:** `false`
- **类型:** `boolean`
- **示例:**
```html
<template>
<m-editor :disabled-data-source="true"></m-editor>
</template>
```
## disabledCodeBlock
- **详情:**
禁用代码块
禁用代码块功能
禁用后,左侧面板将不显示代码块选项卡
- **默认值:** `false`
- **类型:** `boolean`
- **示例:**
```html
<template>
<m-editor :disabled-code-block="true"></m-editor>
</template>
```
## treeIndent
- **详情:**
已选组件、代码编辑、数据源缩进配置
组件树、代码块列表、数据源列表的缩进配置单位px
- **默认值:** `undefined`
- **类型:** `number`
- **示例:**
```html
<template>
<m-editor :tree-indent="20"></m-editor>
</template>
```
## treeNextLevelIndentIncrement
- **详情:**
已选组件、代码编辑、数据源子节点缩进增量配置
组件树、代码块列表、数据源列表子节点缩进增量配置单位px
每一级子节点会在父节点缩进基础上增加该值
- **默认值:** `undefined`
- **类型:** `number`
- **示例:**
```html
<template>
<!-- 第一级缩进20px第二级缩进35px第三级缩进50px -->
<m-editor :tree-indent="20" :tree-next-level-indent-increment="15"></m-editor>
</template>
```
## customContentMenu
- **详情:**
用于自定义组件树与画布的右键菜单
用于自定义组件树与画布的右键菜单
- **类型:** `function`
该函数会在显示右键菜单前被调用,接收默认菜单项作为参数,返回最终显示的菜单项
- **默认值:** `(menus) => menus`
- **类型:** `(menus: (MenuButton | MenuComponent)[], data: { node?: MNode; page?: MPage; parent?: MContainer; stage?: StageCore }) => (MenuButton | MenuComponent)[]`
- **示例:**
```html
<template>
<m-editor :custom-content-menu="customContentMenu"></m-editor>
</template>
<script setup>
const customContentMenu = (menus, { node }) => {
// 为特定类型的组件添加自定义菜单
if (node?.type === 'container') {
menus.push({
type: 'button',
text: '清空容器',
handler: () => {
// 清空容器的逻辑
},
});
}
// 可以过滤掉某些菜单项
return menus.filter(menu => menu.text !== '删除');
};
</script>
```
## extendFormState
- **详情:**
扩展表单状态
用于在属性表单中注入自定义的状态数据,这些数据可以在表单配置的各个字段为函数时的第一个参数中获取
- **默认值:** `undefined`
- **类型:** `(state: FormState) => Record<string, any> | Promise<Record<string, any>>`
- **示例:**
```html
<template>
<m-editor :extend-form-state="extendFormState"></m-editor>
</template>
<script setup>
const extendFormState = async (state) => {
// 返回自定义的状态数据
return {
// 可以是同步数据
currentUser: {
name: 'Admin',
role: 'admin',
},
// 也可以是异步获取的数据
projectConfig: await fetchProjectConfig(),
};
};
</script>
```
:::tip
扩展的状态可以在表单配置中通过 `state` 访问,例如:
```js
{
name: 'title',
text: '标题',
// 根据扩展的状态动态设置
disabled: (state) => state.currentUser.role !== 'admin',
}
```
:::
## pageBarSortOptions
- **详情:**
页面顺序拖拽配置参数
页面标签栏的拖拽排序配置参数
用于配置页面标签的拖拽排序行为
- **默认值:** `undefined`
- **类型:** [PageBarSortOptions](https://github.com/Tencent/tmagic-editor/blob/master/packages/editor/src/type.ts)
- **示例:**
```html
<template>
<m-editor :page-bar-sort-options="sortOptions"></m-editor>
</template>
<script setup>
const sortOptions = {
// 是否启用拖拽排序
animation: 150,
// 拖拽手柄的class
handle: '.page-bar-item',
// 其他 sortablejs 配置
};
</script>
```
## pageFilterFunction
- **详情:**
页面搜索函数
页面搜索/过滤函数
用于自定义页面的搜索逻辑,在页面列表中输入关键词时会调用该函数进行过滤
- **默认值:** `undefined`
- **类型:** `(page: MPage | MPageFragment, keyword: string) => boolean`
- **示例:**
```html
<template>
<m-editor :page-filter-function="pageFilterFunction"></m-editor>
</template>
<script setup>
const pageFilterFunction = (page, keyword) => {
// 自定义搜索逻辑
// 不仅搜索页面名称,还搜索页面的其他属性
return (
page.name?.includes(keyword) ||
page.title?.includes(keyword) ||
page.id?.includes(keyword)
);
};
</script>
```

View File

@ -1,29 +1,111 @@
# Editor组件 slots
## header
- **详情:** 编辑器最顶部区域
- **默认:**
- **示例:**
```html
<template>
<m-editor>
<template #header>
<div class="custom-header">自定义头部内容</div>
</template>
</m-editor>
</template>
```
## nav
- **详情:** 编辑器顶部菜单栏
- **默认:** [NavMenu.vue](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/editor/src/layouts/NavMenu.vue)
- **插槽 Props**
- `editorService`: editorService 实例
:::warning
属性配置[menu](./props.md#menu)由默认组件接收如设置该slot[menu](./props.md#menu)配置将失效
:::
- **示例:**
```html
<template>
<m-editor>
<template #nav="{ editorService }">
<div class="custom-nav">
<button @click="save">保存</button>
</div>
</template>
</m-editor>
</template>
```
## content-before
- **详情:** 编辑器主要内容区域之前
- **默认:**
## src-code
- **详情:** 源码查看区域
- **默认:** 默认的代码编辑器
- **插槽 Props**
- `editorService`: editorService 实例
## sidebar
- **详情:** 左边栏
- **默认:** [Sidebar.vue](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/editor/src/layouts/sidebar/Sidebar.vue)
- **插槽 Props**
- `editorService`: editorService 实例
:::warning
属性配置[sidebar](./props.md#sidebar)由默认组件接收如设置该slot[sidebar](./props.md#sidebar)配置将失效
:::
- **示例:**
```html
<template>
<m-editor>
<template #sidebar="{ editorService }">
<div class="custom-sidebar">
<!-- 自定义侧边栏内容 -->
</div>
</template>
</m-editor>
</template>
```
## component-list
- **详情:** 左边栏中的组件列表
- **默认:** 默认的组件列表
- **插槽 Props**
- `componentGroupList`: 组件分组列表
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
## component-list-panel-header
- **详情:** 左边栏中的组件列表内上方位置
- **默认:**
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
@ -34,24 +116,84 @@
- **默认:** 图片加文案
- **插槽 Props**
- `component`: 组件配置对象
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
- **示例:**
```html
<template>
<m-editor>
<template #component-list-item="{ component }">
<div class="custom-item">
<span>{{ component.text }}</span>
</div>
</template>
</m-editor>
</template>
```
## layer-panel-header
- **详情:** 左边栏中的已选组件(组件树)内顶部位置
- **默认:**
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
## layer-node-content
- **详情:** 左边栏中的已选组件(组件树)节点
- **详情:** 左边栏中的已选组件(组件树)节点完整内容
- **默认:** 组件名称加id和工具按钮
- **插槽 Props**
- `data`: 节点数据
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
## layer-node-label
- **详情:** 左边栏中的已选组件(组件树)节点标签部分
- **默认:** 组件名称加id
- **插槽 Props**
- `data`: 节点数据
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
- **示例:**
```html
<template>
<m-editor>
<template #layer-node-label="{ data }">
<span>{{ data.type }} - {{ data.name }}</span>
</template>
</m-editor>
</template>
```
## layer-node-tool
- **详情:** 左边栏中的已选组件(组件树)节点右侧工具区域
- **默认:**
- **插槽 Props**
- `data`: 节点数据
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
@ -60,6 +202,8 @@
- **详情:** 左边栏中的代码块列表内顶部位置
- **默认:**
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
@ -68,13 +212,44 @@
- **详情:** 左边栏中的代码块列表中代码块右侧位置
- **默认:**
- **插槽 Props**
- `id`: 代码块id
- `data`: 代码块数据
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
## code-block-edit-panel-header
## code-block-panel-search
- **详情:** 代码块弹窗编辑器中弹窗顶部区域
- **详情:** 左边栏中的代码块列表搜索框位置
- **默认:**
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
## data-source-panel-tool
- **详情:** 左边栏中的数据源列表中数据源右侧位置
- **默认:**
- **插槽 Props**
- `data`: 数据源数据
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
:::
## data-source-panel-search
- **详情:** 左边栏中的数据源列表搜索框位置
- **默认:**
:::warning
如设置了[sidebar](#sidebar)插槽,此插槽将失效
@ -86,6 +261,9 @@
- **默认:** [Workspace.vue](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/editor/src/layouts/workspace/Workspace.vue)
- **插槽 Props**
- `editorService`: editorService 实例
## stage
- **详情:** 画布
@ -94,15 +272,63 @@
## workspace-content
- **详情:** 编辑器中间区域内
- **详情:** 编辑器中间区域内,画布上方位置
- **默认:**
- **插槽 Props**
- `editorService`: editorService 实例
## page-bar
- **详情:** 编辑器中间区域底部页面标签栏
- **默认:** 默认的页面标签栏
## page-bar-add-button
- **详情:** 页面标签栏中的"添加页面"按钮
- **默认:** 默认的添加按钮
## page-bar-title
- **详情:** 编辑器中间区域底部页面标题
- **默认:** 页面名称
- **插槽 Props**
- `page`: 页面配置对象
- **示例:**
```html
<template>
<m-editor>
<template #page-bar-title="{ page }">
<span>{{ page.name }} - {{ page.id }}</span>
</template>
</m-editor>
</template>
```
## page-bar-popover
- **详情:** 编辑器中间区域底部页面标题悬浮框
- **详情:** 编辑器中间区域底部页面标题悬浮框内容
- **默认:** 页面详细信息
- **插槽 Props**
- `page`: 页面配置对象
## page-list-popover
- **详情:** 页面列表弹出框内容
- **默认:** 页面列表
- **插槽 Props**
- `list`: 页面列表
## props-panel
@ -114,8 +340,40 @@
- **详情:** 编辑器右侧属性配置内顶部区域
- **默认:**
## content-after
- **详情:** 编辑器主要内容区域之后
- **默认:**
## footer
- **详情:** 编辑器底部区域
- **默认:**
## empty
- **详情:** 当前没有页面时,编辑器中间区域
- **默认:** [AddPageBox.vue](https://github.com/Tencent/tmagic-editor/blob/239b5d3efeae916a8cf3e3566d88063ecccc0553/packages/editor/src/layouts/AddPageBox.vue)
- **插槽 Props**
- `editorService`: editorService 实例
- **示例:**
```html
<template>
<m-editor>
<template #empty="{ editorService }">
<div class="custom-empty">
<p>暂无页面</p>
<button @click="createFirstPage">创建第一个页面</button>
</div>
</template>
</m-editor>
</template>
```

View File

@ -4,114 +4,250 @@
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
-
- **参数:**
- **返回:**
- `{Promise<void>}`
- `{Storage}` Storage 对象
- **详情:**
获取数据存储对象,默认返回 localStorage
可以通过插件机制替换为其他存储对象(如 sessionStorage
- **示例:**
```js
import { storageService } from '@tmagic/editor';
const storage = storageService.getStorage();
console.log(storage); // localStorage
// 通过插件替换为 sessionStorage
storageService.usePlugin({
afterGetStorage() {
return window.sessionStorage;
},
});
```
## getNamespace
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
-
- **参数:**
- **返回:**
- `{Promise<void>}`
- `{string}` 命名空间字符串
- **详情:**
获取存储项的命名空间,默认为 'tmagic'
命名空间用于区分不同应用的存储数据
- **示例:**
```js
import { storageService } from '@tmagic/editor';
const namespace = storageService.getNamespace();
console.log(namespace); // 'tmagic'
```
## clear
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
-
- **参数:**
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
清空当前存储对象中的所有数据
- **示例:**
```js
import { storageService } from '@tmagic/editor';
storageService.clear();
```
## getItem
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
-
- `{string}` key 存储项的键名
- `{Options}` options 可选配置
- `namespace?: string` 自定义命名空间
- `protocol?: Protocol` 数据协议类型
- **返回:**
- `{Promise<void>}`
- `{any}` 存储的值,如果不存在返回 null
- **详情:**
获取存储项,支持多种数据类型的自动解析
支持的协议类型:
- `Protocol.OBJECT`: JavaScript 对象
- `Protocol.JSON`: JSON 格式
- `Protocol.NUMBER`: 数字类型
- `Protocol.BOOLEAN`: 布尔类型
- `Protocol.STRING`: 字符串类型
- **示例:**
```js
import { storageService } from '@tmagic/editor';
// 获取字符串
const str = storageService.getItem('myKey');
// 使用自定义命名空间
const value = storageService.getItem('key', { namespace: 'custom' });
// 指定协议类型
const num = storageService.getItem('count', { protocol: Protocol.NUMBER });
```
## key
- **参数:**
-
- `{number}` index 索引位置
- **返回:**
- `{Promise<void>}`
- `{string | null}` 指定位置的键名,不存在返回 null
- **详情:**
获取存储对象中指定索引位置的键名
- **示例:**
```js
import { storageService } from '@tmagic/editor';
const firstKey = storageService.key(0);
console.log(firstKey);
```
## removeItem
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
-
- `{string}` key 存储项的键名
- `{Options}` options 可选配置
- `namespace?: string` 自定义命名空间
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
移除指定的存储项
- **示例:**
```js
import { storageService } from '@tmagic/editor';
// 移除默认命名空间下的存储项
storageService.removeItem('myKey');
// 移除自定义命名空间下的存储项
storageService.removeItem('key', { namespace: 'custom' });
```
## setItem
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
-
- `{string}` key 存储项的键名
- `{any}` value 要存储的值
- `{Options}` options 可选配置
- `namespace?: string` 自定义命名空间
- `protocol?: Protocol` 数据协议类型
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
设置存储项,自动序列化复杂数据类型
- **示例:**
```js
import { storageService, Protocol } from '@tmagic/editor';
// 存储字符串
storageService.setItem('name', 'tmagic');
// 存储对象
storageService.setItem('config', { a: 1, b: 2 }, { protocol: Protocol.OBJECT });
// 存储数字
storageService.setItem('count', 100, { protocol: Protocol.NUMBER });
// 使用自定义命名空间
storageService.setItem('key', 'value', { namespace: 'custom' });
```
## destroy
- **参数:**
-
- **参数:**
- **返回:**
- `{Promise<void>}`
- `{void}`
- **详情:**
销毁 storageService移除所有事件监听和插件
- **示例:**
```js
import { storageService } from '@tmagic/editor';
storageService.destroy();
```
## use
使用中间件的方式扩展方法,上述方法中标记有`扩展支持: 是`的方法都支持使用use扩展
- **示例:**
```js
import { storageService } from '@tmagic/editor';
storageService.use({
getItem(key, options, next) {
console.log('获取存储项:', key);
return next();
},
});
```
## usePlugin
- **详情:**
@ -120,9 +256,35 @@
每个支持扩展的方法都支持定制before、after两个hook来干预原有方法的行为before可以用于修改传入参数after可以用于修改返回的值
- **示例:**
```js
import { storageService } from '@tmagic/editor';
storageService.usePlugin({
beforeSetItem(key, value, options) {
console.log('设置前:', key, value);
return [key, value, options];
},
afterGetItem(result, key, options) {
console.log('获取后:', result);
return result;
},
});
```
## removeAllPlugins
- **详情:**
删掉当前设置的所有扩展
- **示例:**
```js
import { storageService } from '@tmagic/editor';
storageService.removeAllPlugins();
```

View File

@ -1,12 +1,89 @@
# uiService方法
## set
- **参数:**
- `{keyof UiState}` name 状态键名
- `{any}` value 状态值
- **返回:**
- `{void}`
- **详情:**
设置UI服务的状态
可用的状态键:
- `uiSelectMode`: UI选择模式
- `showSrc`: 是否显示源码
- `showStylePanel`: 是否显示样式面板
- `zoom`: 缩放比例
- `stageContainerRect`: 画布容器尺寸
- `stageRect`: 画布尺寸
- `columnWidth`: 列宽度配置
- `showGuides`: 是否显示参考线
- `showRule`: 是否显示标尺
- `propsPanelSize`: 属性面板尺寸
- `showAddPageButton`: 是否显示添加页面按钮
- `showPageListButton`: 是否显示页面列表按钮
- `hideSlideBar`: 是否隐藏侧边栏
- `sideBarItems`: 侧边栏项目
- `navMenuRect`: 导航菜单尺寸
- `frameworkRect`: 框架尺寸
- **示例:**
```js
import { uiService } from '@tmagic/editor';
// 设置缩放比例
uiService.set('zoom', 1.5);
// 设置画布尺寸
uiService.set('stageRect', { width: 375, height: 667 });
// 显示/隐藏参考线
uiService.set('showGuides', true);
// 显示/隐藏标尺
uiService.set('showRule', true);
```
## get
- **参数:**
- `{keyof UiState}` name 状态键名
- **返回:**
- `{any}` 对应的状态值
- **详情:**
获取UI服务的状态值
- **示例:**
```js
import { uiService } from '@tmagic/editor';
const zoom = uiService.get('zoom');
console.log('当前缩放:', zoom);
const stageRect = uiService.get('stageRect');
console.log('画布尺寸:', stageRect);
```
## zoom
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- `{number}` zoom 缩放倍数
- `{number}` zoom 缩放增量(可以为负数)
- **返回:**
@ -14,24 +91,111 @@
- **详情:**
设置缩放倍数最小为0.1
调整缩放倍数最小为0.1
传入的值会被累加到当前缩放倍数上
- **示例:**
```js
import { uiService } from '@tmagic/editor';
// 放大0.1倍
await uiService.zoom(0.1);
// 缩小0.1倍
await uiService.zoom(-0.1);
// 当前缩放如果是1.0执行zoom(0.5)后变为1.5
await uiService.zoom(0.5);
```
## calcZoom
- **[扩展支持](../../guide/editor-expand#行为扩展)** 是
- **参数:**
- **返回:**
- `{Promise<number>}`
- `{Promise<number>}` 计算出的缩放倍数
- **详情:**
计算出缩放以适应的倍数
计算"缩放以适应"的倍数
根据画布容器的尺寸和画布尺寸自动计算出合适的缩放比例,使画布完全显示在容器内
- **示例:**
```js
import { uiService } from '@tmagic/editor';
const fitZoom = await uiService.calcZoom();
console.log('适应缩放:', fitZoom);
// 应用缩放以适应
uiService.set('zoom', fitZoom);
```
## resetState
- **参数:**
- **返回:**
- `{void}`
- **详情:**
重置UI服务状态到初始值
- **示例:**
```js
import { uiService } from '@tmagic/editor';
uiService.resetState();
```
## destroy
- **参数:**
- **返回:**
- `{void}`
- **详情:**
销毁 uiService重置状态并移除所有事件监听和插件
- **示例:**
```js
import { uiService } from '@tmagic/editor';
uiService.destroy();
```
## use
使用中间件的方式扩展方法,上述方法中标记有`扩展支持: 是`的方法都支持使用use扩展
- **示例:**
```js
import { uiService } from '@tmagic/editor';
uiService.use({
async zoom(value, next) {
console.log('缩放前:', uiService.get('zoom'));
await next();
console.log('缩放后:', uiService.get('zoom'));
},
});
```
## usePlugin
- **详情:**
@ -40,9 +204,36 @@
每个支持扩展的方法都支持定制before、after两个hook来干预原有方法的行为before可以用于修改传入参数after可以用于修改返回的值
- **示例:**
```js
import { uiService } from '@tmagic/editor';
uiService.usePlugin({
beforeZoom(value) {
console.log('缩放增量:', value);
return [value];
},
afterCalcZoom(result) {
console.log('计算的缩放:', result);
// 可以修改返回值
return result;
},
});
```
## removeAllPlugins
- **详情:**
删掉当前设置的所有扩展
- **示例:**
```js
import { uiService } from '@tmagic/editor';
uiService.removeAllPlugins();
```

View File

@ -2,14 +2,109 @@
## sort-change
- **参数:**
- `{ column, prop, order }` - 排序信息对象
- `column: Object` - 排序的列配置
- `prop: string` - 排序的列属性名
- `order: 'ascending' | 'descending' | null` - 排序方式
- **说明:** 当表格的排序条件发生变化时触发
- **示例:**
```js
const handleSortChange = ({ column, prop, order }) => {
console.log('排序变化:', prop, order);
};
```
## after-action
- **参数:**
- `action: string` - 操作类型
- `data: any` - 操作相关数据
- **说明:** 表格操作完成后触发
- **示例:**
```js
const handleAfterAction = (action, data) => {
console.log('操作完成:', action, data);
};
```
## select
- **参数:**
- `selection: Array<any>` - 当前选中的行数据数组
- `row: any` - 刚刚被选中的行数据
- **说明:** 当用户手动勾选某一行时触发
- **示例:**
```js
const handleSelect = (selection, row) => {
console.log('选中行:', row);
console.log('当前选中:', selection);
};
```
## select-all
- **参数:**
- `selection: Array<any>` - 当前选中的行数据数组
- **说明:** 当用户手动勾选全选 Checkbox 时触发
- **示例:**
```js
const handleSelectAll = (selection) => {
console.log('全选/取消全选:', selection.length);
};
```
## selection-change
- **参数:**
- `selection: Array<any>` - 当前选中的行数据数组
- **说明:** 当选择项发生变化时触发
- **示例:**
```js
const handleSelectionChange = (selection) => {
console.log('选中项变化:', selection);
};
```
## expand-change
- **参数:**
- `row: any` - 被展开/收起的行数据
- `expandedRows: Array<any>` - 当前所有展开的行数据数组
- **说明:** 当用户展开或收起某一行时触发(用于可展开表格)
- **示例:**
```js
const handleExpandChange = (row, expandedRows) => {
console.log('展开状态变化:', row);
console.log('当前展开行:', expandedRows);
};
```
## cell-click
- **参数:**
- `row: any` - 行数据
- `column: Object` - 列配置
- `cell: HTMLElement` - 单元格 DOM 元素
- `event: Event` - 原生事件对象
- **说明:** 当某个单元格被点击时触发
- **示例:**
```js
const handleCellClick = (row, column, cell, event) => {
console.log('单元格点击:', row, column.property);
};
```

View File

@ -2,6 +2,40 @@
## toggleRowSelection
- **参数:**
- `row: any` - 要切换选中状态的行数据
- `selected?: boolean` - 是否选中,不传则切换当前状态
- **说明:** 切换某一行的选中状态
- **示例:**
```js
tableRef.value.toggleRowSelection(row, true); // 选中
tableRef.value.toggleRowSelection(row, false); // 取消选中
tableRef.value.toggleRowSelection(row); // 切换状态
```
## toggleRowExpansion
- **参数:**
- `row: any` - 要展开/收起的行数据
- `expanded?: boolean` - 是否展开,不传则切换当前状态
- **说明:** 切换某一行的展开状态(用于可展开表格)
- **示例:**
```js
tableRef.value.toggleRowExpansion(row, true); // 展开
tableRef.value.toggleRowExpansion(row, false); // 收起
```
## clearSelection
- **参数:**
- **说明:** 清空所有选中的行
- **示例:**
```js
tableRef.value.clearSelection();
```

View File

@ -2,123 +2,122 @@
## data
- **详情:**
- **详情:** 表格数据,数组格式
- **默认值:** `[]`
- **默认值:**
- **类型:**
- **类型:** `Array<any>`
- **示例:**
```js
[
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 25 }
]
```
## columns
- **详情:**
- **详情:** 表格列配置
- **默认值:** `[]`
- **默认值:**
- **类型:**
- **类型:** `Array<ColumnConfig>`
- **示例:**
```js
[
{ prop: 'name', label: '姓名', width: 120 },
{ prop: 'age', label: '年龄', width: 80 }
]
```
## spanMethod
- **详情:** 合并行或列的计算方法
- **默认值:** `undefined`
- **类型:** `Function`
- **参数:**
- `{ row, column, rowIndex, columnIndex }`
- **返回值:** `[rowspan, colspan]``{ rowspan, colspan }`
- **默认值:**
- **类型:**
- **示例:**
```js
({ rowIndex, columnIndex }) => {
if (rowIndex % 2 === 0) {
if (columnIndex === 0) {
return [1, 2];
} else if (columnIndex === 1) {
return [0, 0];
}
}
}
```
## loading
- **详情:**
- **详情:** 是否显示加载状态
- **默认值:** `false`
- **默认值:**
- **类型:**
- **类型:** `boolean`
## showHeader
- **详情:** 是否显示表头
- **默认值:** `true`
- **默认值:**
- **类型:**
- **类型:** `boolean`
## bodyHeight
- **详情:** Table的最大高度。合法的值为数字或者单位为 px 的高度
- **详情:** Table 的最大高度。合法的值为数字或者单位为 px 的高度
- **默认值:**
- **默认值:** `undefined`
- **类型:** `string | number`
- **类型:**
- **示例:**
```js
bodyHeight: 400
bodyHeight: '400px'
```
## emptyText
- **详情:** 空数据时显示的文本内容
- **默认值:**
- **类型:**
- **默认值:** `'暂无数据'`
- **类型:** `string`
## defaultExpandAll
- **详情:** 是否默认展开所有行当Table包含展开行存在或者为树形表格时有效
- **详情:** 是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效
- **默认值:**
- **类型:**
- **默认值:** `false`
- **类型:** `boolean`
## rowkeyName
- **详情:**
- **详情:** 行数据的 Key用来优化 Table 的渲染
- **默认值:** `'id'`
- **默认值:**
- **类型:**
- **类型:** `string`
- **说明:** 在使用 reserve-selection 功能与显示树形数据时,该属性是必填的
## border
- **详情:**
- **详情:** 是否显示边框
- **默认值:** `false`
- **默认值:**
- **类型:**
- **类型:** `boolean`

View File

@ -62,6 +62,59 @@ MenuButton 的[定义](https://github.com/Tencent/tmagic-editor/blob/239b5d3efea
### 二、左侧菜单栏
左侧菜单栏主要展示组件列表、组件树、代码块、数据源等内容。可以通过 `m-editor` 组件的 [sidebar](/api/editor/props.html#sidebar) `prop` 来进行配置。
#### 1. 自定义左侧面板
可以使用 `sidebar` slot 来完全自定义左侧面板:
```html
<m-editor>
<template #sidebar>
<your-sidebar></your-sidebar>
</template>
</m-editor>
```
#### 2. 扩展组件列表
通过 [componentGroupList](/api/editor/props.html#componentgrouplist) prop 配置组件分组和列表:
```js
const componentGroupList = [
{
title: '基础组件',
items: [
{
text: '文本',
type: 'text',
icon: 'text-icon'
},
{
text: '按钮',
type: 'button',
icon: 'button-icon'
}
]
},
{
title: '业务组件',
items: [
// 自定义业务组件
]
}
]
```
#### 3. 组件树扩展
组件树会自动根据页面配置生成,可以通过 `editorService` 监听组件树相关事件:
```js
editorService.on('select', (node) => {
console.log('选中组件:', node);
});
```
### 三、右侧属性配置栏
@ -118,6 +171,50 @@ propsService.usePlugin({
</m-editor>
```
### 四、中间工作区域
## 行为扩展
### 二、服务扩展
可以通过监听事件和使用插件来扩展 EditorService
```js
// 监听编辑器事件
editorService.on('add', (node) => {
console.log('添加组件:', node);
});
// 使用插件扩展
editorService.usePlugin({
beforeAdd(node) {
// 在添加组件前执行
return node;
},
afterAdd(node) {
// 在添加组件后执行
return node;
}
});
```
#### 2. PropsService 扩展
自定义属性配置的处理逻辑:
```js
propsService.usePlugin({
// 修改属性配置
beforeGetPropsConfig(type) {
console.log('获取配置前:', type);
},
afterGetPropsConfig(config, type) {
// 添加自定义配置
return config;
},
// 自定义配置填充逻辑
afterFillConfig(config, type) {
return config;
}
});
```

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "tmagic",
"private": true,
"type": "module",
@ -40,43 +40,44 @@
"url": "https://github.com/Tencent/tmagic-editor.git"
},
"devDependencies": {
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@rollup/plugin-alias": "^5.1.1",
"@commitlint/cli": "^20.1.0",
"@commitlint/config-conventional": "^20.0.0",
"@rollup/plugin-alias": "^6.0.0",
"@tmagic/eslint-config": "workspace:*",
"@types/node": "24.0.10",
"@vitejs/plugin-vue": "^5.2.3",
"@vitest/coverage-v8": "^2.1.9",
"@vitejs/plugin-vue": "^6.0.2",
"@vitest/coverage-v8": "^4.0.12",
"@vue/compiler-sfc": "catalog:",
"c8": "^7.14.0",
"c8": "^10.1.3",
"commitizen": "^4.3.1",
"conventional-changelog-cli": "^5.0.0",
"cosmiconfig": "^8.3.6",
"cosmiconfig": "^ 9.0.0 ",
"cz-conventional-changelog": "^3.3.0",
"element-plus": "^2.11.4",
"element-plus": "^2.11.8",
"enquirer": "^2.4.1",
"eslint": "^9.37.0",
"execa": "^4.1.0",
"eslint": "^9.39.1",
"execa": "^9.6.0",
"highlight.js": "^11.11.1",
"husky": "^9.1.7",
"jsdom": "^19.0.0",
"lint-staged": "^16.1.0",
"jsdom": "^27.2.0",
"lint-staged": "^16.2.7",
"minimist": "^1.2.8",
"picocolors": "^1.1.1",
"prettier": "^3.6.2",
"recast": "^0.23.11",
"rimraf": "^3.0.2",
"rollup": "4.44.1",
"rollup-plugin-dts": "^6.2.1",
"semver": "^7.7.1",
"serialize-javascript": "^6.0.2",
"rollup-plugin-dts": "^6.2.3",
"sass-embedded": "^1.93.3",
"semver": "^7.7.3",
"serialize-javascript": "^7.0.0",
"shx": "^0.3.4",
"typescript": "catalog:",
"vite": "catalog:",
"vitepress": "^1.6.4",
"vitest": "^3.2.4",
"vitest": "^4.0.12",
"vue": "catalog:",
"vue-tsc": "^3.1.1"
"vue-tsc": "^3.1.4"
},
"config": {
"commitizen": {

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/cli",
"main": "lib/index.js",
"types": "lib/index.d.ts",

View File

@ -2,8 +2,9 @@ import path from 'node:path';
import fs from 'fs-extra';
import { prepareEntryFile } from './utils/prepareEntryFile';
import { resolveAppPackages } from './utils/resolveAppPackages';
import { ModuleMainFilePath, UserConfig } from './types';
import { prepareEntryFile, resolveAppPackages } from './utils';
export default class Core {
// eslint-disable-next-line @typescript-eslint/no-require-imports

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/core",
"type": "module",
"main": "dist/tmagic-core.umd.cjs",

View File

@ -279,13 +279,13 @@ export default class EventHelper extends EventEmitter {
}
const toNodes = [];
const toNode = this.app.getNode(to);
const toNode = this.app.getNode(to, { strict: true });
if (toNode) {
toNodes.push(toNode);
}
for (const [, page] of this.app.pageFragments) {
const node = page.getNode(to);
const node = page.getNode(to, { strict: true });
if (node) {
toNodes.push(node);
}

View File

@ -191,7 +191,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);
const node = page.getNode(eventConfig.toId, { strict: true });
if (node && node === this) {
if (typeof instance[eventConfig.method] === 'function') {
await instance[eventConfig.method](eventConfig.fromCpt, ...eventConfig.args);

View File

@ -84,14 +84,16 @@ class Page extends Node {
public getNode<T extends TMagicNode = TMagicNode>(
id: Id,
{ iteratorContainerId, iteratorIndex, pageFragmentContainerId }: GetNodeOptions = {},
{ iteratorContainerId, iteratorIndex, pageFragmentContainerId, strict }: 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 });
return this.app.pageFragments
.get(pageFragmentContainerId)
?.getNode(id, { iteratorContainerId, iteratorIndex, strict: true });
}
if (Array.isArray(iteratorContainerId) && iteratorContainerId.length && Array.isArray(iteratorIndex)) {
@ -106,6 +108,14 @@ class Page extends Node {
return iteratorContainer?.getNode(id, iteratorIndex[iteratorIndex.length - 1]) as T;
}
if (!strict && this.app.pageFragments.size) {
for (const [, pageFragment] of this.app.pageFragments) {
if (pageFragment.nodes.has(id)) {
return pageFragment.nodes.get(id) as T;
}
}
}
}
public setNode(id: Id, node: TMagicNode) {

View File

@ -48,4 +48,6 @@ export interface GetNodeOptions {
iteratorContainerId?: Id[];
iteratorIndex?: number[];
pageFragmentContainerId?: Id;
/** 严格模式如果为true页面片中的节点必须指定pageFragmentContainerId为false时没有pageFragmentContainerId的时候获得第一个页面片容器中的节点 */
strict?: boolean;
}

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/data-source",
"type": "module",
"main": "dist/tmagic-data-source.umd.cjs",

View File

@ -78,13 +78,13 @@ export const createDataSourceManager = (app: TMagicApp, useMock?: boolean, initi
replaceChildNode(newNode, [app.page.data]);
}
app.getNode(node.id)?.setData(newNode);
app.getNode(node.id, { strict: true })?.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);
pageFragment.getNode(newNode.id, { strict: true })?.setData(newNode);
if (!pageFragment.instance) {
replaceChildNode(newNode, [pageFragment.data]);
}

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/dep",
"type": "module",
"main": "dist/tmagic-dep.umd.cjs",

View File

@ -55,8 +55,7 @@ export const isIncludeArrayField = (keys: string[], fields: DataSchema[]) => {
// 字段类型为数组并且后面没有数字索引
return (
field &&
field.type === 'array' &&
field?.type === 'array' &&
// 不是整数
/^(?!\d+$).*$/.test(`${keys[index + 1]}`) &&
index < keys.length - 1

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/design",
"type": "module",
"sideEffects": [

View File

@ -27,7 +27,7 @@
</template>
<script setup lang="ts">
import { computed, ref, watchEffect } from 'vue';
import { computed, ref, useTemplateRef, watchEffect } from 'vue';
import { getDesignConfig } from './config';
import type { AutocompleteProps } from './types';
@ -58,13 +58,13 @@ const updateModelValue = (...args: any[]) => {
emit('update:modelValue', ...args);
};
const autocomplete = ref<any>();
const autocompleteRef = useTemplateRef<any>('autocomplete');
const input = ref<HTMLInputElement>();
const inputRef = ref<any>();
watchEffect(() => {
inputRef.value = autocomplete.value?.inputRef;
input.value = autocomplete.value?.inputRef.input;
inputRef.value = autocompleteRef.value?.inputRef;
input.value = autocompleteRef.value?.inputRef.input;
});
defineExpose({
@ -72,10 +72,10 @@ defineExpose({
input,
blur: () => {
autocomplete.value?.blur();
autocompleteRef.value?.blur();
},
focus: () => {
autocomplete.value?.focus();
autocompleteRef.value?.focus();
},
});
</script>

View File

@ -24,7 +24,11 @@ defineOptions({
name: 'TMDialog',
});
const props = defineProps<DialogProps>();
const props = withDefaults(defineProps<DialogProps>(), {
closeOnClickModal: true,
closeOnPressEscape: true,
showClose: true,
});
const emit = defineEmits(['close', 'update:modelValue']);

View File

@ -3,7 +3,11 @@
<template #label>
<slot name="label"></slot>
</template>
<slot></slot>
<template #default>
<slot></slot>
<div v-if="adapterType === 'element-plus' && extra" v-html="extra" class="m-form-tip"></div>
</template>
</component>
</template>
@ -23,5 +27,10 @@ const ui = getDesignConfig('components')?.formItem;
const uiComponent = ui?.component || 'el-form-item';
const uiProps = computed<FormItemProps>(() => ui?.props(props) || props);
const adapterType = getDesignConfig('adapterType');
const uiProps = computed<FormItemProps>(() => {
const { extra, ...rest } = ui?.props(props) || props;
return rest;
});
</script>

View File

@ -8,6 +8,8 @@
@input="inputHandler"
@update:modelValue="updateModelValue"
@blur="blurHandler"
@focus="focusHandler"
@click="clickHandler"
>
<template #prepend v-if="$slots.prepend">
<slot name="prepend"></slot>
@ -42,7 +44,7 @@ const uiComponent = ui?.component || 'el-input';
const uiProps = computed<InputProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'input', 'blur', 'update:modelValue']);
const emit = defineEmits(['change', 'input', 'blur', 'focus', 'click', 'update:modelValue']);
const instance = ref<any>();
@ -62,6 +64,14 @@ const blurHandler = (...args: any[]) => {
emit('blur', ...args);
};
const focusHandler = (...args: any[]) => {
emit('focus', ...args);
};
const clickHandler = (...args: any[]) => {
emit('click', ...args);
};
defineExpose({
instance,
getInput() {

View File

@ -4,8 +4,9 @@
:is="uiComponent"
v-bind="uiProps"
@size-change="handleSizeChange"
@page-size-change="handleSizeChange"
@current-change="handleCurrentChange"
@update:current-page="updateCurrentPage"
@update:page-size="updatePageSize"
></component>
</template>
@ -21,7 +22,7 @@ defineOptions({
const props = defineProps<PaginationProps>();
const emit = defineEmits(['size-change', 'current-change']);
const emit = defineEmits(['size-change', 'current-change', 'update:current-page', 'update:page-size']);
const ui = getDesignConfig('components')?.pagination;
@ -35,4 +36,10 @@ const handleSizeChange = (...args: any[]) => {
const handleCurrentChange = (...args: any[]) => {
emit('current-change', ...args);
};
const updateCurrentPage = (...args: any[]) => {
emit('update:current-page', ...args);
};
const updatePageSize = (...args: any[]) => {
emit('update:page-size', ...args);
};
</script>

View File

@ -25,7 +25,7 @@ defineOptions({
const props = defineProps<SelectProps>();
const emit = defineEmits(['change', 'update:modelValue', 'visibleHandler']);
const emit = defineEmits(['change', 'update:modelValue', 'visible-change']);
const ui = getDesignConfig('components')?.select;
@ -44,7 +44,7 @@ const updateModelValue = (...args: any[]) => {
};
const visibleHandler = (...args: any[]) => {
emit('visibleHandler', ...args);
emit('visible-change', ...args);
};
const scrollbarWrap = ref<HTMLDivElement | undefined>();

View File

@ -54,21 +54,8 @@ export { default as TMagicTooltip } from './Tooltip.vue';
export { default as TMagicUpload } from './Upload.vue';
export { default as TMagicPopconfirm } from './Popconfirm.vue';
export const tMagicMessage = {
error: (msg: string) => {
console.error(msg);
},
success: (msg: string) => {
console.log(msg);
},
warning: (msg: string) => {
console.warn(msg);
},
info: (msg: string) => {
console.info(msg);
},
closeAll: (_msg: string) => {},
} as unknown as TMagicMessage;
// eslint-disable-next-line import/no-mutable-exports
export let tMagicMessage: TMagicMessage;
export const tMagicMessageBox = {
alert: (msg: string) => {
@ -108,13 +95,23 @@ export let useZIndex = (zIndexOverrides?: Ref<number>) => {
export default {
install(app: App, options: DesignPluginOptions) {
if (options.message) {
tMagicMessage.error = options.message?.error;
tMagicMessage.success = options.message?.success;
tMagicMessage.warning = options.message?.warning;
tMagicMessage.info = options.message?.info;
tMagicMessage.closeAll = options.message?.closeAll;
}
tMagicMessage =
options.message ||
({
error: (msg: string) => {
console.error(msg);
},
success: (msg: string) => {
console.log(msg);
},
warning: (msg: string) => {
console.warn(msg);
},
info: (msg: string) => {
console.info(msg);
},
closeAll: (_msg: string) => {},
} as unknown as TMagicMessage);
if (options.messageBox) {
tMagicMessageBox.alert = options.messageBox?.alert;
@ -131,6 +128,10 @@ export default {
useZIndex = options.useZIndex;
}
if (options.adapterType && globalThis.document?.documentElement) {
globalThis.document.documentElement.classList.add(`tmagic-adapter-${options.adapterType}`);
}
app.config.globalProperties.$MAGIC_DESIGN = options;
setDesignConfig(options);
},

View File

@ -6,7 +6,6 @@ export type FieldSize = 'large' | 'default' | 'small';
export interface AutocompleteProps {
modelValue?: string;
placeholder?: string;
label?: string;
clearable?: boolean;
disabled?: boolean;
triggerOnFocus?: boolean;
@ -128,6 +127,7 @@ export interface DialogProps {
closeOnClickModal?: boolean;
closeOnPressEscape?: boolean;
destroyOnClose?: boolean;
showClose?: boolean;
}
export interface DividerProps {
@ -184,6 +184,7 @@ export interface FormItemProps {
labelWidth?: string | number;
rules?: any;
extra?: string;
labelPosition?: 'top' | 'left' | 'right';
}
export interface InputProps {
@ -194,7 +195,7 @@ export interface InputProps {
rows?: number;
type?: string;
size?: FieldSize;
row?: number;
autosize?: boolean | { minRows: number; maxRows: number };
}
export interface InputNumberProps {
@ -225,8 +226,9 @@ export interface PaginationProps {
hideOnSinglePage?: boolean;
curPage?: number;
pageSizes?: number[];
pagesize?: number;
pageSize?: number;
total?: number;
size?: 'large' | 'default' | 'small';
}
export interface PopconfirmProps {
@ -345,6 +347,7 @@ export interface TableColumnOptions<T = any> {
selectable?: (row: T, index: number) => boolean;
};
cell?: (scope: { row: T; $index: number }) => any;
title?: (scope?: any) => any;
}
export interface TabPaneProps {
@ -400,7 +403,7 @@ export interface IconProps {
size?: string;
}
export interface TMagicMessage {
interface ExtraApi {
success: (msg: string) => void;
warning: (msg: string) => void;
info: (msg: string) => void;
@ -408,6 +411,14 @@ export interface TMagicMessage {
closeAll: () => void;
}
export type TMagicMessage = ExtraApi &
((options: {
type?: 'info' | 'success' | 'warning' | 'error';
message?: string;
dangerouslyUseHTMLString?: boolean;
duration?: number;
}) => void);
export type ElMessageBoxShortcutMethod = ((
message: string,
title: string,
@ -704,6 +715,7 @@ export interface Components {
}
export interface DesignPluginOptions {
adapterType?: string;
message?: TMagicMessage;
messageBox?: TMagicMessageBox;
components?: Components;

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/editor",
"type": "module",
"sideEffects": [
@ -46,31 +46,30 @@
"typescript"
],
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@element-plus/icons-vue": "^2.3.2",
"@tmagic/design": "workspace:*",
"@tmagic/form": "workspace:*",
"@tmagic/stage": "workspace:*",
"@tmagic/table": "workspace:*",
"@tmagic/utils": "workspace:*",
"buffer": "^6.0.3",
"color": "^3.1.3",
"deep-object-diff": "^1.1.9",
"emmet-monaco-es": "^5.5.0",
"emmet-monaco-es": "^5.6.1",
"events": "^3.3.0",
"gesto": "^1.19.4",
"keycon": "^1.4.0",
"lodash-es": "^4.17.21",
"moveable": "^0.53.0",
"serialize-javascript": "^6.0.2",
"serialize-javascript": "^7.0.0",
"sortablejs": "^1.15.6"
},
"devDependencies": {
"@types/events": "^3.0.3",
"@types/lodash-es": "^4.17.4",
"@types/serialize-javascript": "^5.0.4",
"@types/sortablejs": "^1.15.8",
"@types/sortablejs": "^1.15.9",
"@vue/test-utils": "^2.4.6",
"type-fest": "^4.10.3"
"type-fest": "^5.2.0"
},
"peerDependencies": {
"@tmagic/core": "workspace:*",

View File

@ -201,7 +201,7 @@ const functionConfig = computed<FormConfig>(() => [
name: 'content',
type: 'vs-code',
options: inject('codeOptions', {}),
height: '500px',
autosize: { minRows: 10, maxRows: 30 },
onChange: (formState: FormState | undefined, code: string) => {
try {
// js

View File

@ -7,6 +7,7 @@
...config.options,
readOnly: disabled,
}"
:autosize="config.autosize"
:parse="config.parse"
@save="save"
></MagicCodeEditor>

View File

@ -2,13 +2,15 @@
<div class="m-fields-code-select-col">
<div class="code-select-container">
<!-- 代码块下拉框 -->
<MContainer
<MSelect
class="select"
:config="selectConfig"
:name="name"
:model="model"
:size="size"
:prop="prop"
@change="onCodeIdChangeHandler"
></MContainer>
></MSelect>
<!-- 查看/编辑按钮 -->
<TMagicButton
@ -28,6 +30,7 @@
:key="model[name]"
:model="model"
:size="size"
:disabled="disabled"
:params-config="paramsConfig"
@change="onParamsChangeHandler"
></CodeParams>
@ -48,7 +51,8 @@ import {
type FieldProps,
filterFunction,
type FormState,
MContainer,
MSelect,
type SelectConfig,
} from '@tmagic/form';
import CodeParams from '@editor/components/CodeParams.vue';
@ -108,7 +112,7 @@ watch(
},
);
const selectConfig = {
const selectConfig: SelectConfig = {
type: 'select',
name: props.name,
disable: props.disabled,
@ -122,33 +126,26 @@ const selectConfig = {
}
return [];
},
onChange: (formState: any, codeId: Id, { setModel, model }: any) => {
// codeIdmodelcodeIdparams
paramsConfig.value = getParamItemsConfig(codeId);
if (paramsConfig.value.length) {
setModel('params', createValues(formState, paramsConfig.value, {}, model.params));
} else {
setModel('params', {});
}
return codeId;
},
};
const onCodeIdChangeHandler = (value: any, eventData: ContainerChangeEventData) => {
props.model.params = value.params;
const onCodeIdChangeHandler = (value: any) => {
// codeIdmodelcodeIdparams
paramsConfig.value = getParamItemsConfig(value);
emit('change', props.model, {
changeRecords: eventData.changeRecords?.map((item) => ({
prop: `${props.prop.replace(props.name, '')}${item.propPath}`,
value: item.value,
})) || [
{
propPath: props.prop,
value: value[props.name],
},
],
const changeRecords = [
{
propPath: props.prop,
value,
},
];
changeRecords.push({
propPath: props.prop.replace(`${props.name}`, 'params'),
value: paramsConfig.value.length ? createValues(mForm, paramsConfig.value, {}, props.model.params) : {},
});
emit('change', value, {
changeRecords,
});
};
@ -156,14 +153,10 @@ const onCodeIdChangeHandler = (value: any, eventData: ContainerChangeEventData)
* 参数值修改更新
*/
const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData) => {
props.model.params = value.params;
emit('change', props.model, {
...eventData,
changeRecords: (eventData.changeRecords || []).map((item) => ({
prop: `${props.prop.replace(props.name, '')}${item.propPath}`,
value: item.value,
})),
eventData.changeRecords?.forEach((record) => {
record.propPath = `${props.prop.replace(`${props.name}`, '')}${record.propPath}`;
});
emit('change', props.model[props.name], eventData);
};
const editCode = (id: string) => {

View File

@ -1,6 +1,6 @@
<template>
<TMagicSelect
v-model="model[name]"
:model-value="model[name]"
clearable
filterable
:size="size"

View File

@ -233,7 +233,6 @@ const dataSourceFieldsConfig: FormConfig = [
{
name: 'defaultValue',
text: '默认值',
height: '200px',
parse: true,
type: (mForm: FormState | undefined, { model }: any) => {
if (model.type === 'number') return 'number';
@ -242,6 +241,7 @@ const dataSourceFieldsConfig: FormConfig = [
return 'vs-code';
},
autosize: { minRows: 1, maxRows: 30 },
options: [
{ text: 'true', value: true },
{ text: 'false', value: false },
@ -267,7 +267,7 @@ const jsonFromConfig: FormConfig = [
type: 'vs-code',
labelWidth: '0',
language: 'json',
height: '600px',
autosize: { minRows: 30, maxRows: 50 },
options: inject('codeOptions', {}),
},
];

View File

@ -38,12 +38,29 @@
</template>
</component>
<div
:class="`tmagic-data-source-input-text el-input t-input t-size-${size?.[0]} el-input--${size}`"
@mouseup="mouseupHandler"
v-else
:class="{
'tmagic-data-source-input-text': true,
'el-input': adapterType === 'element-plus',
[`el-input--${size}`]: adapterType === 'element-plus',
't-input': adapterType === 'tdesign-vue-next',
[`t-size-${size?.[0]}`]: adapterType === 'tdesign-vue-next',
}"
@mouseup="mouseupHandler"
>
<div :class="`tmagic-data-source-input-text-wrapper el-input__wrapper ${isFocused ? ' is-focus' : ''}`">
<div class="el-input__inner t-input__inner">
<div
:class="{
'tmagic-data-source-input-text-wrapper': true,
'el-input__wrapper': adapterType === 'element-plus',
'is-focus': isFocused,
}"
>
<div
:class="{
'el-input__inner': adapterType === 'element-plus',
input__inner: adapterType === 'tdesign-vue-next',
}"
>
<template v-for="(item, index) in displayState">
<span :key="index" v-if="item.type === 'text'" style="margin-right: 2px">{{ item.value }}</span>
<TMagicTag :key="index" :size="size" v-if="item.type === 'var'">{{ item.value }}</TMagicTag>
@ -80,6 +97,8 @@ const emit = defineEmits<{
change: [value: string];
}>();
const adapterType = getDesignConfig('adapterType');
const { dataSourceService, propsService } = useServices();
const autocompleteRef = useTemplateRef<InstanceType<typeof TMagicAutocomplete>>('autocomplete');

View File

@ -1,13 +1,16 @@
<template>
<div class="m-fields-data-source-method-select">
<div class="data-source-method-select-container">
<MContainer
<MCascader
class="select"
:config="cascaderConfig"
:model="model"
:name="name"
:size="size"
:disabled="disabled"
:prop="prop"
@change="onChangeHandler"
></MContainer>
></MCascader>
<TMagicTooltip
v-if="model[name] && isCustomMethod && hasDataSourceSidePanel"
@ -22,11 +25,12 @@
<CodeParams
v-if="paramsConfig.length"
name="params"
:key="model[name]"
:model="model"
:size="size"
:disabled="disabled"
:params-config="paramsConfig"
@change="onChangeHandler"
@change="onParamsChangeHandler"
></CodeParams>
</div>
</template>
@ -38,13 +42,14 @@ import { Edit, View } from '@element-plus/icons-vue';
import type { Id } from '@tmagic/core';
import { TMagicButton, TMagicTooltip } from '@tmagic/design';
import {
type CascaderConfig,
type ContainerChangeEventData,
createValues,
type DataSourceMethodSelectConfig,
type FieldProps,
filterFunction,
type FormState,
MContainer,
type OnChangeHandlerData,
MCascader,
} from '@tmagic/form';
import CodeParams from '@editor/components/CodeParams.vue';
@ -100,21 +105,6 @@ const getParamItemsConfig = ([dataSourceId, methodName]: [Id, string] = ['', '']
const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model[props.name || 'dataSourceMethod']));
const setParamsConfig = (
dataSourceMethod: [Id, string],
formState: any = {},
setModel: OnChangeHandlerData['setModel'],
) => {
// codeIdmodelcodeIdparams
paramsConfig.value = dataSourceMethod ? getParamItemsConfig(dataSourceMethod) : [];
if (paramsConfig.value.length) {
setModel('params', createValues(formState, paramsConfig.value, {}, props.model.params));
} else {
setModel('params', {});
}
};
const methodsOptions = computed(
() =>
dataSources.value
@ -132,24 +122,42 @@ const methodsOptions = computed(
})) || [],
);
const cascaderConfig = computed(() => ({
const cascaderConfig = computed<CascaderConfig>(() => ({
type: 'cascader',
name: props.name,
options: methodsOptions.value,
disable: props.disabled,
onChange: (formState: any, dataSourceMethod: [Id, string], { setModel }: OnChangeHandlerData) => {
setParamsConfig(dataSourceMethod, formState, setModel);
return dataSourceMethod;
},
}));
/**
* 参数值修改更新
*/
const onChangeHandler = (value: any) => {
props.model.params = value.params;
emit('change', props.model);
paramsConfig.value = getParamItemsConfig(value);
const changeRecords = [
{
propPath: props.prop,
value,
},
];
changeRecords.push({
propPath: props.prop.replace(`${props.name}`, 'params'),
value: paramsConfig.value.length ? createValues(mForm, paramsConfig.value, {}, props.model.params) : {},
});
emit('change', value, {
changeRecords,
});
};
/**
* 参数值修改更新
*/
const onParamsChangeHandler = (value: any, eventData: ContainerChangeEventData) => {
eventData.changeRecords?.forEach((record) => {
record.propPath = `${props.prop.replace(`${props.name}`, '')}${record.propPath}`;
});
emit('change', props.model[props.name], eventData);
};
const editCodeHandler = () => {

View File

@ -96,7 +96,7 @@ const formConfig: FormConfig = [
language: 'json',
options: inject('codeOptions', {}),
defaultValue: '{}',
height: '400px',
autosize: { minRows: 30, maxRows: 50 },
onChange: (formState: FormState | undefined, v: string | any) => {
if (typeof v !== 'string') return v;
return JSON.parse(v);

View File

@ -219,7 +219,6 @@ const targetCompConfig = computed(() => {
display: (mForm: FormState, { model }: { model: Record<any, any> }) => model.actionType === ActionType.COMP,
onChange: (MForm: FormState, v: string, { setModel }: OnChangeHandlerData) => {
setModel('method', '');
return v;
},
};
return { ...defaultTargetCompConfig, ...props.config.targetCompConfig };

View File

@ -37,12 +37,15 @@
<MagicCodeEditor
v-if="config.advanced && showCode"
height="200px"
:init-values="model[name]"
language="javascript"
:options="{
readOnly: disabled,
}"
:autosize="{
minRows: 1,
maxRows: 20,
}"
:parse="true"
@save="save"
></MagicCodeEditor>

View File

@ -2,13 +2,16 @@
<div class="m-fields-page-fragment-select">
<div class="page-fragment-select-container">
<!-- 页面片下拉框 -->
<m-form-container
<MSelect
class="select"
:config="selectConfig"
:model="model"
:name="name"
:size="size"
:prop="prop"
:disabled="disabled"
@change="changeHandler"
></m-form-container>
></MSelect>
<!-- 编辑按钮 -->
<Icon v-if="model[name]" class="icon" :icon="Edit" @click="editPageFragment(model[name])"></Icon>
</div>
@ -20,7 +23,7 @@ import { computed } from 'vue';
import { Edit } from '@element-plus/icons-vue';
import { Id, NodeType } from '@tmagic/core';
import { FieldProps, type PageFragmentSelectConfig } from '@tmagic/form';
import { FieldProps, MSelect, type PageFragmentSelectConfig, type SelectConfig } from '@tmagic/form';
import Icon from '@editor/components/Icon.vue';
import { useServices } from '@editor/hooks/use-services';
@ -32,16 +35,16 @@ defineOptions({
const { editorService } = useServices();
const emit = defineEmits(['change']);
const props = withDefaults(defineProps<FieldProps<PageFragmentSelectConfig>>(), {
withDefaults(defineProps<FieldProps<PageFragmentSelectConfig>>(), {
disabled: false,
});
const pageList = computed(() =>
editorService.get('root')?.items.filter((item) => item.type === NodeType.PAGE_FRAGMENT),
);
const selectConfig = {
const selectConfig: SelectConfig = {
type: 'select',
name: props.name,
options: () => {
if (pageList.value) {
return pageList.value.map((item) => ({
@ -53,8 +56,8 @@ const selectConfig = {
return [];
},
};
const changeHandler = async () => {
emit('change', props.model[props.name]);
const changeHandler = (v: Id) => {
emit('change', v);
};
const editPageFragment = (id: Id) => {

View File

@ -79,7 +79,6 @@ const clickHandler = ({ detail }: Event & { detail: HTMLElement | MNode }) => {
id = getIdFromEl()(detail as HTMLElement) || id;
}
if (id) {
props.model[props.name] = id;
emit('change', id);
mForm?.$emit('field-change', props.prop, id);
}
@ -102,7 +101,6 @@ const startSelect = () => {
const deleteHandler = () => {
if (props.model) {
props.model[props.name] = '';
emit('change', '');
mForm?.$emit('field-change', props.prop, '');
}

View File

@ -92,6 +92,7 @@ export { default as KeyValue } from './fields/KeyValue.vue';
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
export { default as CodeBlockListPanel } from './layouts/sidebar/code-block/CodeBlockListPanel.vue';
export { default as DataSourceConfigPanel } from './layouts/sidebar/data-source/DataSourceConfigPanel.vue';
export { default as DataSourceAddButton } from './layouts/sidebar/data-source/DataSourceAddButton.vue';
export { default as PropsPanel } from './layouts/props-panel/PropsPanel.vue';
export { default as PropsFormPanel } from './layouts/props-panel/FormPanel.vue';
export { default as ToolButton } from './components/ToolButton.vue';

View File

@ -1,10 +1,7 @@
<template>
<div :class="`magic-code-editor`">
<Teleport to="body" :disabled="!fullScreen">
<div
:class="`magic-code-editor-wrapper${fullScreen ? ' full-screen' : ''}`"
:style="!fullScreen && height ? `height: ${height}` : '100%'"
>
<div :class="{ 'magic-code-editor-wrapper': true, 'full-screen': fullScreen }" :style="{ height: computeHeight }">
<TMagicButton
v-if="!disabledFullScreen"
class="magic-code-editor-full-screen-icon"
@ -20,7 +17,7 @@
</template>
<script lang="ts" setup>
import { nextTick, onBeforeUnmount, onMounted, onUnmounted, ref, useTemplateRef, watch } from 'vue';
import { computed, 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';
@ -46,6 +43,10 @@ const props = withDefaults(
autoSave?: boolean;
parse?: boolean;
disabledFullScreen?: boolean;
autosize?: {
minRows?: number;
maxRows?: number;
};
}>(),
{
initValues: '',
@ -61,6 +62,58 @@ const props = withDefaults(
const emit = defineEmits(['initd', 'save']);
const autoHeight = ref<string>('');
const computeHeight = computed(() => {
if (fullScreen.value) {
return '100%';
}
if (props.height) {
return props.height;
}
if (props.autosize) {
return autoHeight.value;
}
return '100%';
});
const setAutoHeight = (v = '') => {
let lines = Math.max(v.split('\n').length, props.autosize?.minRows || 1);
if (v) {
if (props.autosize?.maxRows) {
lines = Math.min(lines, props.autosize.maxRows);
}
}
// 使
let lineHeight = 20;
if (vsEditor) {
const editorOptions = vsEditor.getOptions();
lineHeight = editorOptions.get(monaco.editor.EditorOption.lineHeight) || 20;
}
const newHeight = `${lines * lineHeight + 10}px`;
//
if (autoHeight.value !== newHeight) {
autoHeight.value = newHeight;
//
nextTick(() => {
vsEditor?.layout();
//
if (vsEditor) {
vsEditor.setScrollTop(0);
vsEditor.revealLine(1);
}
});
}
};
const toString = (v: string | any, language: string): string => {
let value: string;
if (typeof v !== 'string') {
@ -110,6 +163,8 @@ const resizeObserver = new globalThis.ResizeObserver(
const setEditorValue = (v: string | any, m: string | any) => {
values.value = toString(v, props.language.toLocaleLowerCase());
setAutoHeight(values.value);
if (props.type === 'diff') {
const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
const modifiedModel = monaco.editor.createModel(toString(m, props.language), 'text/javascript');
@ -147,6 +202,7 @@ const handleKeyDown = (e: KeyboardEvent) => {
emit('save', props.parse ? parseCode(newValue, props.language) : newValue);
}
};
const init = async () => {
if (!codeEditorEl.value) return;
@ -163,8 +219,24 @@ const init = async () => {
if (props.type === 'diff') {
vsDiffEditor = getEditorConfig('customCreateMonacoDiffEditor')(monaco, codeEditorEl.value, options);
// diff
vsDiffEditor.getModifiedEditor().onDidChangeModelContent(() => {
// 使 autosize
if (props.autosize) {
setAutoHeight(getEditorValue());
}
});
} else {
vsEditor = getEditorConfig('customCreateMonacoEditor')(monaco, codeEditorEl.value, options);
//
vsEditor.onDidChangeModelContent(() => {
// 使 autosize
if (props.autosize) {
setAutoHeight(getEditorValue());
}
});
}
setEditorValue(props.initValues, props.modifiedValues);

View File

@ -0,0 +1,46 @@
<template>
<TMagicPopover
placement="right"
trigger="hover"
popper-class="data-source-list-panel-add-menu"
:destroy-on-close="true"
>
<template #reference>
<TMagicButton v-bind="addButtonConfig || {}">{{ addButtonText || '' }}</TMagicButton>
</template>
<ToolButton
v-for="(item, index) in datasourceTypeList"
:data="{
type: 'button',
text: item.text,
handler: () => {
$emit('add', item.type);
},
}"
:key="index"
></ToolButton>
</TMagicPopover>
</template>
<script setup lang="ts">
import { type ButtonProps, TMagicButton, TMagicPopover } from '@tmagic/design';
import ToolButton from '@editor/components/ToolButton.vue';
defineOptions({
name: 'MEditorDataSourceAddButton',
});
defineProps<{
datasourceTypeList: {
text: string;
type: string;
}[];
addButtonConfig?: ButtonProps;
addButtonText?: string;
}>();
defineEmits<{
add: [type: string];
}>();
</script>

View File

@ -2,28 +2,13 @@
<TMagicScrollbar class="data-source-list-panel m-editor-layer-panel">
<div class="search-wrapper">
<SearchInput @search="filterTextChangeHandler"></SearchInput>
<TMagicPopover
<DataSourceAddButton
v-if="editable"
placement="right"
trigger="hover"
popper-class="data-source-list-panel-add-menu"
:destroy-on-close="true"
>
<template #reference>
<TMagicButton type="primary" size="small">新增</TMagicButton>
</template>
<ToolButton
v-for="(item, index) in datasourceTypeList"
:data="{
type: 'button',
text: item.text,
handler: () => {
addHandler(item.type);
},
}"
:key="index"
></ToolButton>
</TMagicPopover>
add-button-text="新增"
:add-button-config="{ type: 'primary', size: 'small' }"
:datasource-type-list="datasourceTypeList"
@add="addHandler"
></DataSourceAddButton>
<slot name="data-source-panel-search"></slot>
</div>
@ -63,15 +48,15 @@
import { computed, inject, useTemplateRef, watch } from 'vue';
import { mergeWith } from 'lodash-es';
import { TMagicButton, tMagicMessageBox, TMagicPopover, TMagicScrollbar } from '@tmagic/design';
import { tMagicMessageBox, TMagicScrollbar } from '@tmagic/design';
import ContentMenu from '@editor/components/ContentMenu.vue';
import SearchInput from '@editor/components/SearchInput.vue';
import ToolButton from '@editor/components/ToolButton.vue';
import { useDataSourceEdit } from '@editor/hooks/use-data-source-edit';
import { useServices } from '@editor/hooks/use-services';
import type { CustomContentMenuFunction, DataSourceListSlots, EventBus, MenuButton, MenuComponent } from '@editor/type';
import DataSourceAddButton from './DataSourceAddButton.vue';
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
import DataSourceList from './DataSourceList.vue';
import { useContentMenu } from './useContentMenu';

View File

@ -1,7 +1,12 @@
<template>
<template v-if="data.type !== 'page'">
<MIcon v-if="data.visible === false" :icon="Hide" @click.stop="setNodeVisible(true)" title="点击显示"></MIcon>
<MIcon v-else :icon="View" @click.stop="setNodeVisible(false)" class="node-lock" title="点击隐藏"></MIcon>
<TMagicButton
link
:type="data.visible === false ? 'primary' : 'default'"
:icon="data.visible === false ? Hide : View"
:title="data.visible === false ? '点击显示' : '点击隐藏'"
@click.stop="setNodeVisible(data.visible === false)"
></TMagicButton>
</template>
</template>
@ -9,8 +14,8 @@
import { Hide, View } from '@element-plus/icons-vue';
import type { MNode } from '@tmagic/core';
import { TMagicButton } from '@tmagic/design';
import MIcon from '@editor/components/Icon.vue';
import { useServices } from '@editor/hooks/use-services';
const props = defineProps<{

View File

@ -167,7 +167,7 @@ watch(page, (page) => {
}
});
const resizeObserver = new ResizeObserver((entries) => {
const resizeObserver = new globalThis.ResizeObserver((entries) => {
for (const { contentRect } of entries) {
uiService.set('stageContainerRect', {
width: contentRect.width,

View File

@ -5,6 +5,22 @@
.m-editor-tree {
padding-top: 48px;
.tree-node-tool {
padding-right: 10px;
.tmagic-design-button + .tmagic-design-button {
margin-left: 2px;
}
}
.tree-node.selected {
.tree-node-tool {
.tmagic-design-button {
color: #fff;
}
}
}
}
.search-wrapper {

View File

@ -40,7 +40,7 @@
top: 0;
width: 100%;
background: #fff;
z-index: 2;
z-index: 3;
}
}
}

View File

@ -28,7 +28,7 @@
height: 14px;
border: 1px solid #1d1f24;
&.active {
background-color: var($theme-color);
background-color: $theme-color;
&::after {
border: 1px solid #fff;
}

View File

@ -64,7 +64,7 @@ const fillConfig = (config: FormConfig): FormConfig => [
name: 'beforeRequest',
type: 'vs-code',
parse: true,
height: '600px',
autosize: { minRows: 10, maxRows: 30 },
},
],
},
@ -76,7 +76,7 @@ const fillConfig = (config: FormConfig): FormConfig => [
name: 'afterResponse',
type: 'vs-code',
parse: true,
height: '600px',
autosize: { minRows: 10, maxRows: 30 },
},
],
},

View File

@ -29,11 +29,11 @@ import uiService from '@editor/services/ui';
globalThis.ResizeObserver =
globalThis.ResizeObserver ||
vi.fn().mockImplementation(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
unobserve: vi.fn(),
}));
class ResizeObserver {
disconnect = vi.fn();
observe = vi.fn();
unobserve = vi.fn();
};
describe('Stage.vue', () => {
(global as any).fetch = vi.fn(() =>

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/element-plus-adapter",
"type": "module",
"main": "dist/tmagic-element-plus-adapter.umd.cjs",
@ -37,6 +37,7 @@
"peerDependencies": {
"@tmagic/design": "workspace:*",
"element-plus": ">=2.9.0",
"vue": "catalog:",
"typescript": "catalog:"
},
"peerDependenciesMeta": {

View File

@ -1,26 +0,0 @@
<template>
<ElFormItem v-bind="itemProps">
<template #label>
<slot name="label"></slot>
</template>
<slot></slot>
<div v-if="extra" v-html="extra" class="m-form-tip"></div>
</ElFormItem>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { FormItemProps } from '@tmagic/design';
defineOptions({
name: 'TElAdapterFormItem',
});
const props = defineProps<FormItemProps>();
const itemProps = computed(() => {
const { extra, ...rest } = props;
return rest;
});
</script>

View File

@ -22,6 +22,9 @@
>
<template v-for="(item, columnIndex) in columns" :key="columnIndex">
<ElTableColumn v-bind="item.props || {}">
<template #header="scope" v-if="item.title">
<component :is="item.title(scope)"></component>
</template>
<template #default="scope" v-if="item.cell">
<component :is="item.cell(scope)"></component>
</template>

View File

@ -18,6 +18,7 @@ import {
ElDropdownItem,
ElDropdownMenu,
ElForm,
ElFormItem,
ElIcon,
ElInput,
ElInputNumber,
@ -90,10 +91,10 @@ import type {
UploadProps,
} from '@tmagic/design';
import FormItem from './FormItem.vue';
import Table from './Table.vue';
const adapter: DesignPluginOptions = {
adapterType: 'element-plus',
useZIndex,
message: ElMessage,
messageBox: ElMessageBox,
@ -110,7 +111,12 @@ const adapter: DesignPluginOptions = {
button: {
component: ElButton as any,
props: (props: ButtonProps) => props,
props: (props: ButtonProps) => {
return {
...props,
type: props.type === 'default' ? '' : props.type,
};
},
},
card: {
@ -194,7 +200,7 @@ const adapter: DesignPluginOptions = {
},
formItem: {
component: FormItem as any,
component: ElFormItem as any,
props: (props: FormItemProps) => props,
},

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/form-schema",
"type": "module",
"main": "dist/tmagic-form-schema.umd.cjs",

View File

@ -15,14 +15,14 @@ export interface ChangeRecord {
export interface OnChangeHandlerData {
model: FormValue;
values?: Readonly<FormValue>;
values?: Readonly<FormValue> | null;
parent?: FormValue;
formValue?: FormValue;
config: Readonly<any>;
prop: string;
changeRecords: ChangeRecord[];
setModel: (prop: string, value: any) => void;
setFromValue: (prop: string, value: any) => void;
setFormValue: (prop: string, value: any) => void;
}
export type FormValue = Record<string | number, any>;
@ -41,6 +41,7 @@ export interface FieldProps<T = any> {
disabled?: boolean;
size?: 'large' | 'default' | 'small';
lastValues?: Record<string, any>;
isCompare?: boolean;
}
/**
@ -361,7 +362,10 @@ export interface TextConfig extends FormItem, Input {
mForm: FormState | undefined,
data: {
model: any;
values: any;
values?: Readonly<FormValue> | null;
formValue?: FormValue;
setModel: (prop: string, value: any) => void;
setFormValue: (prop: string, value: any) => void;
},
) => void;
};
@ -373,6 +377,7 @@ export interface TextConfig extends FormItem, Input {
export interface TextareaConfig extends FormItem {
type: 'textarea';
placeholder?: string;
rows?: number;
}
/**
@ -552,7 +557,8 @@ export interface LinkConfig extends FormItem {
mForm: FormState | undefined,
data: {
model: Record<any, any>;
values: Record<any, any>;
values?: Readonly<FormValue> | null;
formValue?: FormValue;
},
) => FormConfig);
fullscreen?: boolean;
@ -715,13 +721,22 @@ export interface TableConfig extends FormItem {
defaultSort?: SortProp;
/** 是否支持拖拽排序 */
dropSort?: boolean;
dropSortHandle?: boolean;
dropActionButtonIcon?: any;
copyActionButtonIcon?: any;
deleteActionButtonIcon?: any;
/** 是否显示全屏按钮 */
enableFullscreen?: boolean;
fixed?: boolean;
fixed?: boolean | 'left' | 'right';
itemExtra?: string | FilterFunction<string>;
titleTip?: FilterFunction<string>;
rowKey?: string;
/** table 新增行时前置回调 */
beforeAddRow?: (mForm: FormState | undefined, data: any) => boolean;
addButtonConfig?: {
props?: Record<string, any>;
text?: string;
};
}
export interface GroupListConfig extends FormItem {
@ -753,6 +768,10 @@ export interface GroupListConfig extends FormItem {
groupModel: any,
) => boolean | boolean;
moveSpecifyLocation?: boolean;
addButtonConfig?: {
props?: Record<string, any>;
text?: string;
};
[key: string]: any;
}

View File

@ -39,6 +39,10 @@ export interface CodeConfig extends FormItem {
};
height?: string;
parse?: boolean;
autosize?: {
minRows?: number;
maxRows?: number;
};
}
export interface CodeLinkConfig extends FormItem {

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/form",
"type": "module",
"sideEffects": [
@ -37,15 +37,15 @@
"url": "https://github.com/Tencent/tmagic-editor.git"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@element-plus/icons-vue": "^2.3.2",
"@popperjs/core": "^2.11.8",
"dayjs": "^1.11.11",
"dayjs": "^1.11.19",
"lodash-es": "^4.17.21",
"sortablejs": "^1.15.2"
"sortablejs": "^1.15.6"
},
"devDependencies": {
"@types/lodash-es": "^4.17.4",
"@types/sortablejs": "^1.15.8",
"@types/sortablejs": "^1.15.9",
"@vue/test-utils": "^2.4.6"
},
"peerDependencies": {

View File

@ -196,6 +196,46 @@ const submitHandler = (e: SubmitEvent) => {
}
};
/**
* 通过 name config 中查找对应的 text
* @param name - 字段名支持点分隔的路径格式 'a.b.c'
* @param config - 表单配置数组
* @returns 找到的 text 如果未找到则返回 undefined
*/
const getTextByName = (name: string, config: FormConfig = props.config): string | undefined => {
if (!name || !Array.isArray(config)) return undefined;
const nameParts = name.split('.');
const findInConfig = (configs: FormConfig, parts: string[]): string | undefined => {
if (parts.length === 0) return undefined;
const [currentPart, ...remainingParts] = parts;
for (const item of configs) {
if (item.name === currentPart) {
if (remainingParts.length === 0) {
return typeof item.text === 'string' ? item.text : undefined;
}
if (item.items && Array.isArray(item.items)) {
const result = findInConfig(item.items, remainingParts);
if (result !== undefined) return result;
}
}
if (item.items && Array.isArray(item.items)) {
const result = findInConfig(item.items, parts);
if (result !== undefined) return result;
}
}
return undefined;
};
return findInConfig(config, nameParts);
};
defineExpose({
values,
lastValuesProcessed,
@ -212,7 +252,12 @@ defineExpose({
submitForm: async (native?: boolean): Promise<any> => {
try {
await tMagicFormRef.value?.validate();
const result = await tMagicFormRef.value?.validate();
// tdesign
// element-plus throw error
if (result !== true) {
throw result;
}
changeRecords.value = [];
return native ? values.value : cloneDeep(toRaw(values.value));
} catch (invalidFields: any) {
@ -220,16 +265,19 @@ defineExpose({
const error: string[] = [];
Object.entries(invalidFields).forEach(([, ValidateError]) => {
Object.entries(invalidFields).forEach(([prop, ValidateError]) => {
(ValidateError as ValidateError[]).forEach(({ field, message }) => {
if (field && message) error.push(`${field} -> ${message}`);
if (field && !message) error.push(`${field} -> 出现错误`);
if (!field && message) error.push(`${message}`);
const name = field || prop;
const text = getTextByName(name, props.config) || name;
error.push(`${text} -> ${message}`);
});
});
throw new Error(error.join('<br>'));
}
},
getTextByName,
});
</script>

View File

@ -101,8 +101,9 @@ watchEffect(() => {
const submitHandler = async () => {
try {
const changeRecords = form.value?.changeRecords;
const values = await form.value?.submitForm();
emit('submit', values, { changeRecords: form.value?.changeRecords });
emit('submit', values, { changeRecords });
} catch (e) {
emit('error', e);
}

View File

@ -8,7 +8,10 @@
:width="width"
:zIndex="zIndex"
:fullscreen="fullscreen"
:close-on-click-modal="false"
:close-on-click-modal="closeOnClickModal"
:close-on-press-escape="closeOnPressEscape"
:destroy-on-close="destroyOnClose"
:show-close="showClose"
@close="closeHandler"
>
<div
@ -42,7 +45,7 @@
</TMagicCol>
<TMagicCol :span="12">
<slot name="footer">
<TMagicButton @click="cancel" size="small"> </TMagicButton>
<TMagicButton v-if="showCancel" @click="cancel" size="small"> </TMagicButton>
<TMagicButton v-if="hasStep && stepActive > 1" type="info" size="small" @click="preStep"
>上一步</TMagicButton
>
@ -87,11 +90,21 @@ const props = withDefaults(
size?: 'small' | 'default' | 'large';
confirmText?: string;
preventSubmitDefault?: boolean;
closeOnClickModal?: boolean;
closeOnPressEscape?: boolean;
destroyOnClose?: boolean;
showClose?: boolean;
showCancel?: boolean;
}>(),
{
config: () => [],
values: () => ({}),
confirmText: '确定',
closeOnClickModal: false,
closeOnPressEscape: false,
destroyOnClose: false,
showClose: true,
showCancel: true,
},
);
@ -131,8 +144,9 @@ const closeHandler = () => {
const save = async () => {
try {
const changeRecords = form.value?.changeRecords;
const values = await form.value?.submitForm();
emit('submit', values, { changeRecords: form.value?.changeRecords });
emit('submit', values, { changeRecords });
} catch (e) {
emit('error', e);
}

View File

@ -109,8 +109,9 @@ watchEffect(() => {
const submitHandler = async () => {
try {
const changeRecords = form.value?.changeRecords;
const values = await form.value?.submitForm();
emit('submit', values, { changeRecords: form.value?.changeRecords });
emit('submit', values, { changeRecords });
} catch (e) {
emit('error', e);
}

View File

@ -24,15 +24,23 @@
<template v-else-if="type && display && !showDiff">
<TMagicFormItem v-bind="formItemProps" :class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text }">
<template #label
><span v-html="type === 'checkbox' && !config.useLabel ? '' : text" :title="config.labelTitle"></span
></template>
<template #label>
<FormLabel
:tip="config.tip"
:type="type"
:use-label="config.useLabel"
:label-title="config.labelTitle"
:text="text"
></FormLabel>
</template>
<TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
<component
v-bind="fieldsProps"
:is="tagName"
:model="model"
:last-values="lastValues"
:is-compare="isCompare"
@change="onChangeHandler"
@addDiffCount="onAddDiffCount"
></component>
@ -47,12 +55,13 @@
:is="tagName"
:model="model"
:last-values="lastValues"
:is-compare="isCompare"
@change="onChangeHandler"
@addDiffCount="onAddDiffCount"
></component>
</TMagicFormItem>
<TMagicTooltip v-if="config.tip" placement="left">
<TMagicTooltip v-if="config.tip && type === 'checkbox' && !config.useLabel" placement="top">
<TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
<template #content>
<div v-html="config.tip"></div>
@ -67,7 +76,15 @@
v-bind="formItemProps"
:class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-diff': true }"
>
<template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
<template #label>
<FormLabel
:tip="config.tip"
:type="type"
:use-label="config.useLabel"
:label-title="config.labelTitle"
:text="text"
></FormLabel>
</template>
<TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
<component v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
<template #content>
@ -78,7 +95,7 @@
<component v-else v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
</TMagicFormItem>
<TMagicTooltip v-if="config.tip" placement="left">
<TMagicTooltip v-if="config.tip && type === 'checkbox' && !config.useLabel" placement="top">
<TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
<template #content>
<div v-html="config.tip"></div>
@ -91,7 +108,15 @@
:style="config.tip ? 'flex: 1' : ''"
:class="{ 'tmagic-form-hidden': `${itemLabelWidth}` === '0' || !text, 'show-diff': true }"
>
<template #label><span v-html="type === 'checkbox' ? '' : text" :title="config.labelTitle"></span></template>
<template #label>
<FormLabel
:tip="config.tip"
:type="type"
:use-label="config.useLabel"
:label-title="config.labelTitle"
:text="text"
></FormLabel>
</template>
<TMagicTooltip v-if="tooltip.text" :placement="tooltip.placement">
<component v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
<template #content>
@ -102,7 +127,7 @@
<component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
</TMagicFormItem>
<TMagicTooltip v-if="config.tip" placement="left">
<TMagicTooltip v-if="config.tip && type === 'checkbox' && !config.useLabel" placement="top">
<TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
<template #content>
<div v-html="config.tip"></div>
@ -157,6 +182,8 @@ import type {
} from '../schema';
import { display as displayFunction, filterFunction, getRules } from '../utils/form';
import FormLabel from './FormLabel.vue';
defineOptions({
name: 'MFormContainer',
});
@ -208,10 +235,7 @@ const items = computed(() => (props.config as ContainerCommonConfig).items);
const itemProp = computed(() => {
let n: string | number = '';
const { names } = props.config as any;
if (names?.[0]) {
[n] = names;
} else if (name.value) {
if (name.value) {
n = name.value;
} else {
return props.prop;

View File

@ -0,0 +1,26 @@
<template>
<span v-if="tip" style="display: inline-flex; align-items: center">
<span v-html="type === 'checkbox' && !useLabel ? '' : text" :title="labelTitle"></span>
<TMagicTooltip v-if="tip && (type !== 'checkbox' || useLabel)" placement="top">
<TMagicIcon style="margin-left: 5px; display: flex"><warning-filled /></TMagicIcon>
<template #content>
<div v-html="tip"></div>
</template>
</TMagicTooltip>
</span>
<span v-else v-html="type === 'checkbox' && !useLabel ? '' : text" :title="labelTitle"></span>
</template>
<script setup lang="ts">
import { WarningFilled } from '@element-plus/icons-vue';
import { TMagicIcon, TMagicTooltip } from '@tmagic/design';
defineProps<{
tip?: string;
type?: string;
useLabel?: boolean;
text?: string;
labelTitle?: string;
}>();
</script>

View File

@ -2,7 +2,7 @@
<div class="m-fields-group-list">
<div v-if="config.extra" v-html="config.extra" style="color: rgba(0, 0, 0, 0.45)"></div>
<div v-if="!model[name] || !model[name].length" class="el-table__empty-block">
<span class="el-table__empty-text">暂无数据</span>
<span class="el-table__empty-text t-table__empty">暂无数据</span>
</div>
<MFieldsGroupListItem
@ -26,22 +26,28 @@
@addDiffCount="onAddDiffCount()"
></MFieldsGroupListItem>
<TMagicButton
v-if="addable"
type="primary"
:size="config.enableToggleMode ? 'small' : 'default'"
:disabled="disabled"
@click="addHandler"
>新增</TMagicButton
>
<TMagicButton :icon="Grid" size="small" @click="toggleMode" v-if="config.enableToggleMode">切换为表格</TMagicButton>
<div class="m-fields-group-list-footer">
<TMagicButton v-if="config.enableToggleMode" :icon="Grid" size="small" @click="toggleMode"
>切换为表格</TMagicButton
>
<div style="display: flex; justify-content: flex-end; flex: 1">
<TMagicButton
v-if="addable"
:size="config.enableToggleMode ? 'small' : 'default'"
:icon="Plus"
v-bind="config.addButtonConfig?.props || { type: 'primary' }"
:disabled="disabled"
@click="addHandler"
>{{ config.addButtonConfig?.text || '新增' }}</TMagicButton
>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, inject } from 'vue';
import { Grid } from '@element-plus/icons-vue';
import { Grid, Plus } from '@element-plus/icons-vue';
import { cloneDeep } from 'lodash-es';
import { TMagicButton } from '@tmagic/design';

View File

@ -1,90 +1,92 @@
<template>
<div class="m-fields-group-list-item">
<div>
<TMagicButton link :disabled="disabled" @click="expandHandler">
<TMagicIcon><CaretBottom v-if="expand" /><CaretRight v-else /></TMagicIcon>{{ title }}
</TMagicButton>
<TMagicCard class="m-fields-group-list-item" :body-style="{ display: expand ? 'block' : 'none' }">
<template #header>
<div>
<TMagicButton link :disabled="disabled" @click="expandHandler">
<TMagicIcon><CaretBottom v-if="expand" /><CaretRight v-else /></TMagicIcon>{{ title }}
</TMagicButton>
<TMagicButton
v-show="showDelete"
type="danger"
size="small"
link
:icon="Delete"
:disabled="disabled"
@click="removeHandler"
></TMagicButton>
<TMagicButton
v-if="copyable"
link
size="small"
type="primary"
:icon="DocumentCopy"
:disabled="disabled"
@click="copyHandler"
>复制</TMagicButton
>
<template v-if="movable">
<TMagicButton
v-show="index !== 0"
v-show="showDelete"
type="danger"
size="small"
link
:icon="Delete"
:disabled="disabled"
@click="removeHandler"
></TMagicButton>
<TMagicButton
v-if="copyable"
link
size="small"
type="primary"
:icon="DocumentCopy"
:disabled="disabled"
:icon="CaretTop"
@click="changeOrder(-1)"
>上移</TMagicButton
@click="copyHandler"
>复制</TMagicButton
>
<TMagicButton
v-show="index !== length - 1"
link
size="small"
:disabled="disabled"
:icon="CaretBottom"
@click="changeOrder(1)"
>下移</TMagicButton
>
</template>
<TMagicPopover
v-if="config.moveSpecifyLocation"
trigger="click"
placement="top"
width="200"
:visible="moveSpecifyLocationVisible"
>
<template #reference>
<template v-if="movable">
<TMagicButton
v-show="index !== 0"
link
size="small"
type="primary"
:icon="Position"
:disabled="disabled"
@click="moveSpecifyLocationVisible = true"
>移动至</TMagicButton
:icon="CaretTop"
@click="changeOrder(-1)"
>上移</TMagicButton
>
<TMagicButton
v-show="index !== length - 1"
link
size="small"
:disabled="disabled"
:icon="CaretBottom"
@click="changeOrder(1)"
>下移</TMagicButton
>
</template>
<div>
<div>
<TMagicInputNumber
style="margin: 0 5px"
v-model="moveSpecifyLocationIndex"
size="small"
:min="1"
:disabled="disabled"
></TMagicInputNumber
>
</div>
<div style="text-align: right; margin-top: 20px">
<TMagicButton size="small" text @click="moveSpecifyLocationVisible = false">取消</TMagicButton>
<TMagicButton size="small" type="primary" @click="moveSpecifyLocationHandler">确认</TMagicButton>
</div>
</div>
</TMagicPopover>
<span v-if="itemExtra" v-html="itemExtra" class="m-form-tip"></span>
</div>
<TMagicPopover
v-if="config.moveSpecifyLocation"
trigger="click"
placement="top"
width="200"
:visible="moveSpecifyLocationVisible"
>
<template #reference>
<TMagicButton
link
size="small"
type="primary"
:icon="Position"
:disabled="disabled"
@click="moveSpecifyLocationVisible = true"
>移动至</TMagicButton
>
</template>
<div>
<div>
<TMagicInputNumber
style="margin: 0 5px"
v-model="moveSpecifyLocationIndex"
size="small"
:min="1"
:disabled="disabled"
></TMagicInputNumber
>
</div>
<div style="text-align: right; margin-top: 20px">
<TMagicButton size="small" text @click="moveSpecifyLocationVisible = false">取消</TMagicButton>
<TMagicButton size="small" type="primary" @click="moveSpecifyLocationHandler">确认</TMagicButton>
</div>
</div>
</TMagicPopover>
<span v-if="itemExtra" v-html="itemExtra" class="m-form-tip"></span>
</div>
</template>
<Container
v-if="expand"
@ -99,14 +101,14 @@
@change="changeHandler"
@addDiffCount="onAddDiffCount()"
></Container>
</div>
</TMagicCard>
</template>
<script setup lang="ts">
import { computed, inject, ref } from 'vue';
import { CaretBottom, CaretRight, CaretTop, Delete, DocumentCopy, Position } from '@element-plus/icons-vue';
import { TMagicButton, TMagicIcon, TMagicInputNumber, TMagicPopover } from '@tmagic/design';
import { TMagicButton, TMagicCard, TMagicIcon, TMagicInputNumber, TMagicPopover } from '@tmagic/design';
import type { ContainerChangeEventData, FormState, GroupListConfig } from '../schema';
import { filterFunction } from '../utils/form';

View File

@ -8,8 +8,11 @@
<div style="width: 100%; display: flex; align-items: center">
<TMagicButton style="padding: 0" link :icon="expand ? CaretBottom : CaretRight" @click="expand = !expand">
</TMagicButton>
<slot name="header">
<span style="cursor: pointer" @click="expand = !expand">{{ filter(config.title) }}</span>
</slot>
<span v-if="config && config.extra" v-html="config.extra" class="m-form-tip"></span>
<slot name="header">{{ filter(config.title) }}</slot>
</div>
</template>
@ -101,4 +104,12 @@ const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
emit('change', props.model, eventData);
};
const onAddDiffCount = () => emit('addDiffCount');
defineExpose({
getExpand: () => expand.value,
setExpand: (v: boolean) => {
expand.value = v;
},
});
</script>

View File

@ -58,7 +58,13 @@
: lastValues
"
:is-compare="isCompare"
:prop="config.dynamic ? `${prop}${prop ? '.' : ''}${String(tabIndex)}` : prop"
:prop="
config.dynamic
? `${prop}${prop ? '.' : ''}${String(tabIndex)}`
: tab.name
? `${prop}${prop ? '.' : ''}${tab.name}`
: prop
"
:size="size"
:label-width="tab.labelWidth || labelWidth"
:expand-more="expandMore"
@ -88,6 +94,26 @@ type DiffCount = {
[tabIndex: number]: number;
};
const props = withDefaults(
defineProps<{
model: any;
lastValues?: any;
isCompare?: boolean;
config: TabConfig;
name: string;
size?: string;
labelWidth?: string;
prop?: string;
expandMore?: boolean;
disabled?: boolean;
}>(),
{
lastValues: () => ({}),
isCompare: false,
prop: '',
},
);
const tabPaneComponent = getDesignConfig('components')?.tabPane;
const tabsComponent = getDesignConfig('components')?.tabs;
@ -118,25 +144,6 @@ const tabClick = (mForm: FormState | undefined, tab: any, props: any) => {
}
};
const props = withDefaults(
defineProps<{
model: any;
lastValues?: any;
isCompare?: boolean;
config: TabConfig;
name: string;
size?: string;
labelWidth?: string;
prop?: string;
expandMore?: boolean;
disabled?: boolean;
}>(),
{
lastValues: () => ({}),
isCompare: false,
},
);
const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData];
addDiffCount: [];

View File

@ -9,6 +9,7 @@
:unlink-panels="true"
:disabled="disabled"
:default-time="config.defaultTime"
:format="`${config.dateFormat || 'YYYY/MM/DD'} ${config.timeFormat || 'HH:mm:ss'}`"
:value-format="config.valueFormat || 'YYYY/MM/DD HH:mm:ss'"
:date-format="config.dateFormat || 'YYYY/MM/DD'"
:time-format="config.timeFormat || 'HH:mm:ss'"
@ -17,11 +18,11 @@
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { onUnmounted, ref, watch } from 'vue';
import { TMagicDatePicker } from '@tmagic/design';
import type { DaterangeConfig, FieldProps } from '../schema';
import type { ChangeRecord, DaterangeConfig, FieldProps } from '../schema';
import { datetimeFormatter } from '../utils/form';
import { useAddField } from '../utils/useAddField';
@ -40,7 +41,7 @@ const value = ref<(Date | string | undefined)[] | null>([]);
if (props.model !== undefined) {
if (names?.length) {
watch(
const unWatch = watch(
[() => props.model[names[0]], () => props.model[names[1]]],
([start, end], [preStart, preEnd]) => {
if (!value.value) {
@ -56,8 +57,12 @@ if (props.model !== undefined) {
immediate: true,
},
);
onUnmounted(() => {
unWatch();
});
} else if (props.name && props.model[props.name]) {
watch(
const unWatch = watch(
() => props.model[props.name],
(start, preStart) => {
const format = `${props.config.dateFormat || 'YYYY/MM/DD'} ${props.config.timeFormat || 'HH:mm:ss'}`;
@ -71,32 +76,41 @@ if (props.model !== undefined) {
immediate: true,
},
);
onUnmounted(() => {
unWatch();
});
}
}
const setValue = (v: Date[] | Date) => {
names?.forEach((item, index) => {
if (!props.model) {
return;
}
if (Array.isArray(v)) {
props.model[item] = v[index];
} else {
props.model[item] = undefined;
}
});
};
const changeHandler = (v: Date[]) => {
const value = v || [];
if (props.name) {
emit('change', value);
} else {
if (names?.length) {
setValue(value);
if (props.config.names?.length) {
const newChangeRecords: ChangeRecord[] = [];
props.config.names.forEach((item, index) => {
if (!props.model) {
return;
}
if (Array.isArray(v)) {
newChangeRecords.push({
propPath: props.prop ? `${props.prop}.${item}` : item,
value: v[index],
});
} else {
newChangeRecords.push({
propPath: props.prop ? `${props.prop}.${item}` : item,
value: undefined,
});
}
});
emit('change', props.model, {
changeRecords: newChangeRecords,
});
}
emit('change', props.model);
}
};
</script>

View File

@ -17,7 +17,7 @@
</template>
<script lang="ts" setup>
import { computed, inject, ref } from 'vue';
import { computed, inject, readonly, ref } from 'vue';
import { TMagicButton } from '@tmagic/design';
@ -54,7 +54,8 @@ const formConfig = computed(() => {
if (typeof props.config.form === 'function') {
return props.config.form(mForm, {
model: props.model || {},
values: props.values || {},
values: mForm ? readonly(mForm.initValues) : null,
formValue: props.values || {},
});
}
return props.config.form;

View File

@ -1,7 +1,7 @@
<template>
<TMagicInputNumber
v-if="model"
:model-value="model[name]"
v-model="value"
clearable
controls-position="right"
:size="size"
@ -10,13 +10,13 @@
:step="config.step"
:placeholder="config.placeholder"
:disabled="disabled"
@update:model-value="changeHandler"
@change="changeHandler"
@input="inputHandler"
></TMagicInputNumber>
</template>
<script lang="ts" setup>
import { inject } from 'vue';
import { inject, ref, watch } from 'vue';
import { TMagicInputNumber } from '@tmagic/design';
@ -34,6 +34,18 @@ const emit = defineEmits<{
input: [values: number];
}>();
const value = ref<number>();
watch(
() => props.model[props.name],
(v) => {
value.value = v;
},
{
immediate: true,
},
);
useAddField(props.prop);
const mForm = inject<FormState | null>('mForm');

View File

@ -1,24 +1,26 @@
<template>
<div class="m-fields-number-range">
<TMagicInput
:model-value="model[name][0]"
v-model="firstValue"
:clearable="config.clearable ?? true"
:size="size"
:disabled="disabled"
@update:model-value="minChangeHandler"
@change="minChangeHandler"
></TMagicInput>
<span class="split-tag">-</span>
<TMagicInput
:model-value="model[name][1]"
v-model="secondValue"
:clearable="config.clearable ?? true"
:size="size"
:disabled="disabled"
@update:model-value="maxChangeHandler"
@change="maxChangeHandler"
></TMagicInput>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { TMagicInput } from '@tmagic/design';
import type { FieldProps, NumberRangeConfig } from '../schema';
@ -34,6 +36,21 @@ const emit = defineEmits<{
change: [values: [number, number]];
}>();
const firstValue = ref<number>();
const secondValue = ref<number>();
watch(
() => props.model[props.name],
([first, second]) => {
firstValue.value = first;
secondValue.value = second;
},
{
immediate: true,
deep: true,
},
);
useAddField(props.prop);
if (!Array.isArray(props.model[props.name])) {

View File

@ -1,13 +1,13 @@
<template>
<div class="m-fields-text">
<TMagicInput
:model-value="model[name]"
v-model="value"
ref="input"
clearable
:size="size"
:placeholder="config.placeholder"
:disabled="disabled"
@update:model-value="changeHandler"
@change="changeHandler"
@input="inputHandler"
@keyup="keyUpHandler($event)"
>
@ -41,7 +41,7 @@
</template>
<script lang="ts" setup>
import { computed, inject, ref, shallowRef, watch } from 'vue';
import { computed, inject, readonly, ref, shallowRef, watch } from 'vue';
import type { Instance } from '@popperjs/core';
import { createPopper } from '@popperjs/core';
import { debounce } from 'lodash-es';
@ -49,7 +49,7 @@ import { debounce } from 'lodash-es';
import { TMagicButton, TMagicInput } from '@tmagic/design';
import { isNumber } from '@tmagic/utils';
import type { FieldProps, FormState, TextConfig } from '../schema';
import type { ChangeRecord, ContainerChangeEventData, FieldProps, FormState, TextConfig } from '../schema';
import { useAddField } from '../utils/useAddField';
defineOptions({
@ -59,7 +59,7 @@ defineOptions({
const props = defineProps<FieldProps<TextConfig>>();
const emit = defineEmits<{
change: [value: string];
change: [value: string, eventData?: ContainerChangeEventData];
input: [value: string];
}>();
@ -67,6 +67,18 @@ useAddField(props.prop);
const mForm = inject<FormState | undefined>('mForm');
const value = ref('');
watch(
() => props.model[props.name],
(v) => {
value.value = v;
},
{
immediate: true,
},
);
const appendConfig = computed(() => {
if (typeof props.config.append === 'string') {
return {
@ -121,10 +133,28 @@ const inputHandler = (v: string) => {
const buttonClickHandler = () => {
if (!appendConfig.value) return;
if (typeof appendConfig.value.handler === 'function') {
const newChangeRecords: ChangeRecord[] = [];
const setModel = (key: string, value: any) => {
newChangeRecords.push({ propPath: props.prop.replace(`${props.name}`, key), value });
};
const setFormValue = (key: string, value: any) => {
newChangeRecords.push({ propPath: key, value });
};
appendConfig.value.handler(mForm, {
model: props.model,
values: mForm?.values,
values: mForm ? readonly(mForm.initValues) : null,
formValue: props.values || {},
setModel,
setFormValue,
});
if (newChangeRecords.length > 0) {
emit('change', props.model[props.name], {
changeRecords: newChangeRecords,
});
}
}
};

View File

@ -1,19 +1,20 @@
<template>
<TMagicInput
:model-value="model[name]"
v-model="value"
type="textarea"
:size="size"
clearable
:placeholder="config.placeholder"
:disabled="disabled"
@update:model-value="changeHandler"
:rows="config.rows"
@change="changeHandler"
@input="inputHandler"
>
</TMagicInput>
</template>
<script lang="ts" setup>
import { inject } from 'vue';
import { inject, ref, watch } from 'vue';
import { TMagicInput } from '@tmagic/design';
@ -31,6 +32,18 @@ const emit = defineEmits<{
input: [value: string];
}>();
const value = ref('');
watch(
() => props.model[props.name],
(v) => {
value.value = v;
},
{
immediate: true,
},
);
useAddField(props.prop);
const mForm = inject<FormState | null>('mForm');

View File

@ -1,12 +1,21 @@
<template>
<slot name="operateCol" :scope="{ $index: index, row: row }"></slot>
<TMagicTooltip v-if="config.dropSort && config.dropSortHandle" content="拖动排序" placement="left-start">
<TMagicButton
size="small"
link
:class="{ 'tmagic-form-table-drag-target': config.dropSortHandle }"
:icon="config.dropActionButtonIcon || Sort"
>
</TMagicButton>
</TMagicTooltip>
<TMagicButton
v-show="showDelete(index + 1 + currentPage * pageSize - 1)"
size="small"
type="danger"
link
title="删除"
:icon="Delete"
:icon="config.deleteActionButtonIcon || Delete"
@click="removeHandler(index + 1 + currentPage * pageSize - 1)"
></TMagicButton>
@ -16,7 +25,7 @@
size="small"
type="primary"
title="复制"
:icon="DocumentCopy"
:icon="config.copyActionButtonIcon || DocumentCopy"
:disabled="disabled"
@click="copyHandler(index + 1 + currentPage * pageSize - 1)"
></TMagicButton>
@ -24,10 +33,10 @@
<script setup lang="ts">
import { inject } from 'vue';
import { Delete, DocumentCopy } from '@element-plus/icons-vue';
import { Delete, DocumentCopy, Sort } from '@element-plus/icons-vue';
import { cloneDeep } from 'lodash-es';
import { TMagicButton } from '@tmagic/design';
import { TMagicButton, TMagicTooltip } from '@tmagic/design';
import type { FormState, TableConfig } from '../schema';

View File

@ -1,9 +1,18 @@
<template>
<div class="m-fields-table-wrap">
<teleport to="body" :disabled="!isFullscreen">
<div ref="mTable" class="m-fields-table" :class="{ 'm-fields-table-item-extra': config.itemExtra }">
<teleport to="body" :disabled="!isFullscreen">
<div
v-bind="$attrs"
class="m-fields-table-wrap"
:class="{ fixed: isFullscreen }"
:style="isFullscreen ? `z-index: ${nextZIndex()}` : ''"
>
<div class="m-fields-table" :class="{ 'm-fields-table-item-extra': config.itemExtra }">
<span v-if="config.extra" style="color: rgba(0, 0, 0, 0.45)" v-html="config.extra"></span>
<TMagicTooltip content="拖拽可排序" placement="left-start" :disabled="config.dropSort !== true">
<TMagicTooltip
content="拖拽可排序"
placement="left-start"
:disabled="config.dropSort !== true || config.dropSortHandle"
>
<TMagicTable
v-if="model[modelName]"
ref="tMagicTable"
@ -24,15 +33,10 @@
<slot></slot>
<div style="display: flex; justify-content: space-between; margin: 10px 0">
<TMagicButton v-if="addable" size="small" type="primary" :disabled="disabled" plain @click="newHandler()"
>新增一行</TMagicButton
>
<div style="display: flex">
<TMagicButton
:icon="Grid"
size="small"
type="primary"
@click="toggleMode"
v-if="enableToggleMode && config.enableToggleMode !== false && !isFullscreen"
>展开配置</TMagicButton
@ -40,7 +44,6 @@
<TMagicButton
:icon="FullScreen"
size="small"
type="primary"
@click="toggleFullscreen"
v-if="config.enableFullscreen !== false"
>
@ -61,6 +64,17 @@
>清空</TMagicButton
>
</div>
<TMagicButton
v-if="addable"
class="m-form-table-add-button"
size="small"
plain
:icon="Plus"
v-bind="config.addButtonConfig?.props || { type: 'primary' }"
:disabled="disabled"
@click="newHandler()"
>{{ config.addButtonConfig?.text || '新增一行' }}</TMagicButton
>
</div>
<div class="bottom" style="text-align: right" v-if="config.pagination">
@ -77,15 +91,15 @@
</TMagicPagination>
</div>
</div>
</teleport>
</div>
</div>
</teleport>
</template>
<script setup lang="ts">
import { computed, ref, useTemplateRef } from 'vue';
import { FullScreen, Grid } from '@element-plus/icons-vue';
import { FullScreen, Grid, Plus } from '@element-plus/icons-vue';
import { TMagicButton, TMagicPagination, TMagicTable, TMagicTooltip, TMagicUpload } from '@tmagic/design';
import { TMagicButton, TMagicPagination, TMagicTable, TMagicTooltip, TMagicUpload, useZIndex } from '@tmagic/design';
import type { SortProp } from '../schema';
import { sortChange } from '../utils/form';
@ -122,15 +136,16 @@ const { pageSize, currentPage, paginationData, handleSizeChange, handleCurrentCh
modelName,
);
const { nextZIndex } = useZIndex();
const updateKey = ref(1);
const { addable, newHandler } = useAdd(props, emit);
const { columns } = useTableColumns(props, emit, currentPage, pageSize, modelName);
useSortable(props, emit, tMagicTableRef, modelName);
useSortable(props, emit, tMagicTableRef, modelName, updateKey);
const { isFullscreen, toggleFullscreen } = useFullscreen();
const { importable, excelHandler, clearHandler } = useImport(props, emit, newHandler);
const { selectHandle, toggleRowSelection } = useSelection(props, emit, tMagicTableRef);
const updateKey = ref(1);
const data = computed(() => (props.config.pagination ? paginationData.value : props.model[modelName.value]));
const toggleMode = () => {

View File

@ -78,6 +78,7 @@ export const useAdd = (
if (typeof props.config.defaultAdd === 'function') {
inputs = await props.config.defaultAdd(mForm, {
model: props.model[modelName],
prop: props.prop,
formValue: mForm?.values,
});
} else if (props.config.defaultAdd) {

View File

@ -1,22 +1,12 @@
import { ref, useTemplateRef } from 'vue';
import { useZIndex } from '@tmagic/design';
import { ref } from 'vue';
export const useFullscreen = () => {
const isFullscreen = ref(false);
const mTableEl = useTemplateRef<HTMLDivElement>('mTable');
const { nextZIndex } = useZIndex();
const toggleFullscreen = () => {
if (!mTableEl.value) return;
if (isFullscreen.value) {
mTableEl.value.classList.remove('fixed');
isFullscreen.value = false;
} else {
mTableEl.value.classList.add('fixed');
mTableEl.value.style.zIndex = `${nextZIndex()}`;
isFullscreen.value = true;
}
};

View File

@ -1,4 +1,4 @@
import { inject, type Ref, type ShallowRef, watchEffect } from 'vue';
import { inject, nextTick, type Ref, type ShallowRef, watchEffect } from 'vue';
import Sortable, { type SortableEvent } from 'sortablejs';
import { type TMagicTable } from '@tmagic/design';
@ -13,6 +13,7 @@ export const useSortable = (
emit: (event: 'select' | 'change' | 'addDiffCount', ...args: any[]) => void,
tMagicTableRef: ShallowRef<InstanceType<typeof TMagicTable> | null>,
modelName: Ref<string | number>,
updateKey: Ref<number>,
) => {
const mForm = inject<FormState | undefined>('mForm');
@ -20,7 +21,7 @@ export const useSortable = (
const rowDrop = () => {
sortable?.destroy();
const tableEl = tMagicTableRef.value?.getEl();
const tBodyEl = tableEl?.querySelector('.el-table__body > tbody');
const tBodyEl = tableEl?.querySelector('.el-table__body > tbody') || tableEl?.querySelector('.t-table__body');
if (!tBodyEl) {
return;
}
@ -29,6 +30,7 @@ export const useSortable = (
filter: 'input', // 表单组件选字操作和触发拖拽会冲突,优先保证选字操作
preventOnFilter: false, // 允许选字
direction: 'vertical',
handle: props.config.dropSortHandle ? '.tmagic-form-table-drag-target' : undefined,
onEnd: ({ newIndex, oldIndex }: SortableEvent) => {
if (typeof newIndex === 'undefined') return;
if (typeof oldIndex === 'undefined') return;
@ -36,6 +38,12 @@ export const useSortable = (
emit('change', newData);
mForm?.$emit('field-change', newData);
nextTick(() => {
sortable?.destroy();
sortable = undefined;
updateKey.value += 1;
});
},
});
};

View File

@ -1,7 +1,8 @@
import { computed, h, inject, type Ref } from 'vue';
import { WarningFilled } from '@element-plus/icons-vue';
import { cloneDeep } from 'lodash-es';
import type { TableColumnOptions } from '@tmagic/design';
import { type TableColumnOptions, TMagicIcon, TMagicTooltip } from '@tmagic/design';
import type { FormState, TableColumnConfig } from '@tmagic/form-schema';
import Container from '../containers/Container.vue';
@ -43,6 +44,19 @@ export const useTableColumns = (
return fuc;
};
const titleTip = (fuc: any) => {
if (typeof fuc === 'function') {
return fuc(mForm, {
values: mForm?.initValues,
model: props.model,
formValue: mForm ? mForm.values : props.model,
prop: props.prop,
});
}
return fuc;
};
const selection = computed(() => {
if (typeof props.config.selection === 'function') {
return props.config.selection(mForm, { model: props.model[modelName.value] });
@ -87,11 +101,17 @@ export const useTableColumns = (
});
}
columns.push({
let actionFixed: 'left' | 'right' | undefined = props.config.fixed === false ? undefined : 'left';
if (typeof props.config.fixed === 'string' && ['left', 'right'].includes(props.config.fixed)) {
actionFixed = props.config.fixed;
}
const actionColumn = {
props: {
label: '操作',
fixed: props.config.fixed === false ? undefined : 'left',
width: props.config.operateColWidth || 112,
fixed: actionFixed,
width: props.config.operateColWidth ?? (props.config.dropSortHandle && props.config.dropSort ? 132 : 112),
align: 'center',
},
cell: ({ row, $index }: any) =>
@ -110,7 +130,11 @@ export const useTableColumns = (
emit('change', v);
},
}),
});
};
if (actionFixed !== 'right') {
columns.push(actionColumn);
}
if (props.sort && props.model[modelName.value] && props.model[modelName.value].length > 1) {
columns.push({
@ -158,6 +182,8 @@ export const useTableColumns = (
for (const column of props.config.items) {
if (column.type !== 'hidden' && display(column.display)) {
const titleTipValue = titleTip(column.titleTip);
columns.push({
props: {
prop: column.name,
@ -181,10 +207,39 @@ export const useTableColumns = (
onChange: changeHandler,
onAddDiffCount,
}),
title: titleTipValue
? () =>
h(
TMagicTooltip,
{ placement: 'top' },
{
default: () =>
h(
'span',
{
style: {
display: 'inline-flex',
alignItems: 'center',
gap: '5px',
},
},
[h('span', column.label), h(TMagicIcon, {}, { default: () => h(WarningFilled) })],
),
content: () =>
h('div', {
innerHTML: titleTipValue,
}),
},
)
: undefined,
});
}
}
if (actionFixed === 'right') {
columns.push(actionColumn);
}
return columns;
});

View File

@ -20,4 +20,19 @@
border-bottom: 0;
}
}
.tmagic-design-card {
.el-card__header {
padding: 5px 20px;
}
.t-card__header {
padding: 5px 0;
}
}
.m-fields-group-list-footer {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
}

View File

@ -1,9 +1,6 @@
.m-fields-table-wrap {
width: 100%;
}
.m-fields-table {
width: 100%;
&.fixed {
position: fixed;
height: 100%;
@ -14,6 +11,8 @@
bottom: 0;
z-index: 100;
background: rgba(0, 0, 0, 0.5);
align-items: center;
display: flex;
& > .el-form-item__content {
z-index: 101;
@ -25,6 +24,10 @@
width: 95vw !important;
}
}
}
.m-fields-table {
width: 100%;
th {
background-color: #f2f2f2 !important;
@ -62,4 +65,8 @@
.el-form-item {
margin-bottom: 0;
}
.tmagic-form-table-drag-target {
cursor: move;
}
}

View File

@ -16,7 +16,6 @@
* limitations under the License.
*/
import { toRaw } from 'vue';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import { cloneDeep } from 'lodash-es';
@ -121,12 +120,12 @@ const initValueItem = function (
asyncLoadConfig(value, initValue, item as HtmlField);
// 这种情况比较多,提前结束
if (name && !items && typeof initValue[name] !== 'undefined') {
if (name && !items && typeof initValue?.[name] !== 'undefined') {
if (typeof value[name] === 'undefined') {
if (type === 'number') {
value[name] = Number(initValue[name]);
} else {
value[name] = typeof initValue[name] === 'object' ? cloneDeep(initValue[name]) : initValue[name];
value[name] = typeof initValue[name] === 'object' ? initValue[name] : initValue[name];
}
}
@ -281,13 +280,15 @@ export const initValue = async (
) => {
if (!Array.isArray(config)) throw new Error('config应该为数组');
let valuesTmp = createValues(mForm, config, toRaw(initValues), {});
const initValuesCopy = cloneDeep(initValues);
let valuesTmp = createValues(mForm, config, initValuesCopy, {});
const [firstForm] = config as [ContainerCommonConfig];
if (firstForm && typeof firstForm.onInitValue === 'function') {
valuesTmp = await firstForm.onInitValue(mForm, {
formValue: valuesTmp,
initValue: initValues,
initValue: initValuesCopy,
});
}
@ -302,7 +303,7 @@ export const datetimeFormatter = (
if (v) {
let time: string | number;
if (['x', 'timestamp'].includes(format)) {
time = dayjs(v).valueOf();
time = dayjs(Number.isNaN(Number(v)) ? v : Number(v)).valueOf();
} else if ((typeof v === 'string' && v.includes('Z')) || v.constructor === Date) {
dayjs.extend(utc);
// UTC字符串时间或Date对象格式化为北京时间
@ -341,7 +342,7 @@ export const sortArray = (data: any[], newIndex: number, oldIndex: number, sortK
}
}
return newData;
return cloneDeep(newData);
};
export const sortChange = (data: any[], { prop, order }: SortProp) => {

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/schema",
"type": "module",
"main": "dist/tmagic-schema.umd.cjs",

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/stage",
"type": "module",
"main": "dist/tmagic-stage.umd.cjs",

View File

@ -50,7 +50,7 @@ Object.defineProperties(globalThis.HTMLElement.prototype, {
get() {
let parent = this.parentNode;
while (parent) {
if (parent.style && parent.style.position === 'absolute') {
if (parent.style?.position === 'absolute') {
return parent;
}
parent = parent.parentNode;

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/table",
"type": "module",
"sideEffects": [

View File

@ -29,12 +29,12 @@
row[config.prop]
}}</a>
<el-tooltip v-else-if="config.action === 'tip'" placement="left">
<TMagicTooltip v-else-if="config.action === 'tip'" placement="left">
<template #content>
<div>{{ formatter(config, row, { index: index }) }}</div>
</template>
<TMagicButton link type="primary">{{ config.buttonText || '扩展配置' }}</TMagicButton>
</el-tooltip>
</TMagicTooltip>
<TMagicTag
v-else-if="config.action === 'tag' && config.prop"
@ -46,7 +46,7 @@
</template>
<script lang="ts" setup>
import { TMagicButton, TMagicForm, TMagicTag } from '@tmagic/design';
import { TMagicButton, TMagicForm, TMagicTag, TMagicTooltip } from '@tmagic/design';
import { ColumnConfig } from './schema';
import { formatter } from './utils';

View File

@ -1,5 +1,5 @@
{
"version": "1.7.0-beta.0",
"version": "1.7.2",
"name": "@tmagic/tdesign-vue-next-adapter",
"type": "module",
"main": "dist/tmagic-tdesign-vue-next-adapter.umd.cjs",

View File

@ -0,0 +1,102 @@
<template>
<TAutoComplete
ref="autocomplete"
:model-value="modelValue"
:options="options"
:disabled="disabled"
:placeholder="placeholder"
:size="size === 'default' ? 'medium' : size"
:popupProps="{
trigger: props.triggerOnFocus ? 'focus' : 'hover',
}"
:filter="filterHandler"
@keypress="inputHandler"
@change="changeHandler"
@blur="blurHandler"
@focus="focusHandler"
@click="clickHandler"
@update:modelValue="updateModelValue"
>
<template #option="{ option }" v-if="$slots.default">
<slot name="default" :item="option"></slot>
</template>
<template #prepend v-if="$slots.prepend">
<slot name="prepend"></slot>
</template>
<template #append v-if="$slots.append">
<slot name="append"></slot>
</template>
<template #prefix v-if="$slots.prefix">
<slot name="prefix"></slot>
</template>
<template #suffix v-if="$slots.suffix">
<slot name="suffix"></slot>
</template>
</TAutoComplete>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { AutoComplete as TAutoComplete, type AutoCompleteOption } from 'tdesign-vue-next';
import type { AutocompleteProps } from '@tmagic/design';
defineOptions({
name: 'TTDesignAdapterAutoComplete',
});
const emit = defineEmits(['change', 'input', 'blur', 'focus', 'click', 'update:modelValue']);
const props = defineProps<AutocompleteProps>();
const options = ref<any[]>([]);
onMounted(() => {
if (typeof props.fetchSuggestions === 'function') {
props.fetchSuggestions('', (data: any[]) => {
options.value = data;
});
} else if (Array.isArray(props.fetchSuggestions)) {
options.value = props.fetchSuggestions;
}
});
const filterHandler = (keyword: string, _option: AutoCompleteOption) => {
if (typeof props.fetchSuggestions === 'function') {
props.fetchSuggestions(keyword, (data: any[]) => {
options.value = data;
});
}
return true;
};
const changeHandler = (...args: any[]) => {
emit('change', ...args);
};
const inputHandler = (...args: any[]) => {
emit('input', ...args);
};
const blurHandler = (...args: any[]) => {
emit('blur', ...args);
};
const focusHandler = (...args: any[]) => {
emit('focus', ...args);
};
const clickHandler = (...args: any[]) => {
emit('click', ...args);
};
const updateModelValue = (...args: any[]) => {
emit('update:modelValue', ...args);
};
defineExpose({
blur: () => {},
focus: () => {},
});
</script>

View File

@ -1,6 +1,9 @@
<template>
<TDateRangePicker
v-if="type.endsWith('range')"
allow-input
clearable
enable-time-picker
:modelValue="modelValue"
:mode="mode"
:placeholder="[startPlaceholder || '', endPlaceholder || '']"
@ -14,6 +17,7 @@
/>
<TDatePicker
v-else
clearable
:modelValue="modelValue"
:mode="mode"
:placeholder="placeholder"

View File

@ -5,6 +5,7 @@
:header="title"
:width="width"
:mode="fullscreen ? 'full-screen' : 'modal'"
:close-btn="showClose"
:close-on-overlay-click="closeOnClickModal"
:close-on-esc-keydown="closeOnPressEscape"
:destroy-on-close="destroyOnClose"

View File

@ -1,13 +1,19 @@
<template>
<TTextarea
v-if="type === 'textarea'"
ref="textarea"
:modelValue="modelValue"
:size="size === 'default' ? 'medium' : size"
:disabled="disabled"
:placeholder="placeholder"
:row="row"
:rows="rows"
:autosize="autosize"
@keypress="inputHandler"
@change="changeHandler"
@blur="blurHandler"
@focus="focusHandler"
@click="clickHandler"
@update:modelValue="updateModelValue"
></TTextarea>
<TInputAdornment v-else>
<template #prepend v-if="$slots.prepend">
@ -24,6 +30,9 @@
:placeholder="placeholder"
@keypress="inputHandler"
@change="changeHandler"
@blur="blurHandler"
@focus="focusHandler"
@click="clickHandler"
@update:modelValue="updateModelValue"
>
<template #prefix-icon v-if="$slots.prefix">
@ -37,6 +46,7 @@
</template>
<script lang="ts" setup>
import { useTemplateRef, watch } from 'vue';
import { Input as TInput, InputAdornment as TInputAdornment, Textarea as TTextarea } from 'tdesign-vue-next';
import type { InputProps } from '@tmagic/design';
@ -45,13 +55,28 @@ defineOptions({
name: 'TTDesignAdapterInput',
});
defineProps<
const props = defineProps<
InputProps & {
modelValue: string;
}
>();
const emit = defineEmits(['change', 'input', 'update:modelValue']);
const emit = defineEmits(['change', 'input', 'blur', 'focus', 'click', 'update:modelValue']);
const textareaRef = useTemplateRef('textarea');
watch(
[textareaRef, () => props.rows],
([val, rows]) => {
if (val && rows) {
const el = val.$el.querySelector('textarea');
if (el) {
el.rows = rows;
}
}
},
{ immediate: true },
);
const changeHandler = (...args: any[]) => {
emit('change', ...args);
@ -61,6 +86,18 @@ const inputHandler = (...args: any[]) => {
emit('input', ...args);
};
const blurHandler = (...args: any[]) => {
emit('blur', ...args);
};
const focusHandler = (...args: any[]) => {
emit('focus', ...args);
};
const clickHandler = (...args: any[]) => {
emit('click', ...args);
};
const updateModelValue = (...args: any[]) => {
emit('update:modelValue', ...args);
};

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