Compare commits

...

48 Commits

Author SHA1 Message Date
roymondchen
18524d89fb feat(core): 支持自定义Node类 2025-04-02 17:24:02 +08:00
roymondchen
840c2c3c7d fix(data-source): 数据源初始化时机比注册早回出现死循环 2025-04-02 17:17:30 +08:00
roymondchen
9b546645f3 chore: update deps 2025-03-31 20:31:29 +08:00
roymondchen
38ea57c6cb chore: update lockfile v1.5.12 2025-03-27 16:32:44 +08:00
roymondchen
c8fc2f7bfb chore: release v1.5.12 2025-03-27 16:30:51 +08:00
roymondchen
b715a87f40 feat(cli): 如果识别不要组件文件,则默认从npm包的default导入 2025-03-27 16:25:58 +08:00
roymondchen
f17ef325b1 chore: update vite 2025-03-27 11:29:18 +08:00
roymondchen
4ddc55aa6d fix(design): card没有slots时隐藏对应slot 2025-03-27 11:12:27 +08:00
roymondchen
2a714ae9cc fix(vue-components): button删除调试代码,text支持富文本 2025-03-18 16:54:55 +08:00
roymondchen
d53a479b21 style(vue-runtime-help): 更新ts定义 2025-03-18 16:52:21 +08:00
roymondchen
f5742c107a fix(editor): 获取关联组件的方法默认值 2025-03-13 14:30:28 +08:00
roymondchen
9c7d711c16 fix(editor): 数据源修改后,依赖重新收集不准确 2025-03-12 14:22:33 +08:00
roymondchen
11c027c71b chore: update lockfile v1.5.11 2025-03-11 17:14:17 +08:00
roymondchen
16f1c47db6 chore: release v1.5.11 2025-03-11 17:12:29 +08:00
roymondchen
72108e0019 fix(dep): 数据源方法收集判断 2025-03-11 17:01:32 +08:00
roymondchen
0c207c7b83 fix(dep): 依赖收集不能过滤id/name 2025-03-10 11:32:27 +08:00
roymondchen
f671c670db feat(vue-runtime-help): useDsl/useEditorDsl添加app默认参数 2025-03-07 14:33:13 +08:00
roymondchen
a293bd8d3f chore: update lockfile v1.5.10 2025-03-07 11:33:33 +08:00
roymondchen
7dad6720e4 chore: release v1.5.10 2025-03-07 11:31:46 +08:00
roymondchen
402f05f3d6 test(editor): 删除eventsService.init用例 2025-03-06 20:36:10 +08:00
roymondchen
0736646c63 feat(vue-components,react-components): 增加点击事件,使用组件状态hook 2025-03-06 20:29:38 +08:00
roymondchen
8d0040da53 chore(runtime): 更新runtime-help 2025-03-06 20:22:52 +08:00
roymondchen
6f5bb84c04 feat(reate-runtime-help,vue-runtime-help): 新增组件状态hook 2025-03-06 20:20:01 +08:00
roymondchen
63fe6ec68b fix(stage): 防止runtime重复注册 2025-03-06 20:10:52 +08:00
roymondchen
ff07147270 feat(core): node新增registerMethod方法,用于组件注册供其他组件通过事件配置调用 2025-03-06 20:08:47 +08:00
roymondchen
c8e1cffca9 feat(core, editor): 删除公共的点击事件实现,由组件自行添加 2025-03-06 20:05:49 +08:00
roymondchen
5ba2a73c70 feat(core): hook函数调用添加node参数 2025-03-04 19:11:36 +08:00
roymondchen
a4d021d2fb feat(core): app添加dsl-change事件 2025-03-04 17:07:45 +08:00
roymondchen
3c66319b03 fix(data-source): 处理异步动态加载数据源时,初始化数据源还未加载完毕的情况 2025-03-04 17:05:05 +08:00
roymondchen
775fcf5693 fix(editor): 样式配置宽度不能为负 2025-03-04 14:09:28 +08:00
roymondchen
f5315122e4 chore(vue-components): 添加name option 2025-02-27 19:33:57 +08:00
roymondchen
90abde57cc feat(runtime): vue2 h传参与vue3不一样,需要自定义render 2025-02-27 19:32:48 +08:00
roymondchen
91cde30d75 feat(vue-container): 支持自定义render 2025-02-27 19:31:07 +08:00
roymondchen
2114b71d47 style(vue-runtime-help): 添加UserRenderFunction定义 2025-02-27 19:28:08 +08:00
roymondchen
94d0ed1fd0 chore(playground): 恢复dsl本地存储的key 2025-02-27 11:31:20 +08:00
roymondchen
4026c0c305 feat(runtime): 支持页面切换 2025-02-26 15:20:32 +08:00
roymondchen
6d2b9d5f21 chore: update lockfile v1.5.9 2025-02-20 17:34:45 +08:00
roymondchen
f2f9044be0 chore: release v1.5.9 2025-02-20 17:32:53 +08:00
roymondchen
ebc873d6f3 style(stage): 完善Runtime定义 2025-02-20 15:16:20 +08:00
roymondchen
4d77b669ba feat(table): formatter添加index信息 2025-02-18 19:46:00 +08:00
roymondchen
6f2c6b151d feat(table): 支持type: index的序号列 2025-02-18 19:40:28 +08:00
roymondchen
6e2b5aea47 fix(editor): 编辑器销毁不应该移除依赖收集任务的事件监听 2025-02-12 14:08:27 +08:00
roymondchen
098351bb21 chore: update lockfile v1.5.8 2025-02-11 19:39:18 +08:00
roymondchen
a0e7ee7292 chore: release v1.5.8 2025-02-11 19:37:31 +08:00
roymondchen
3fbcbf25ca test(editor): 完善测试用例 2025-02-11 19:34:42 +08:00
roymondchen
413134b21d feat(editor): 优化依赖收集状态显示,新增剩余任务数量显示 2025-02-11 19:25:28 +08:00
roymondchen
6d82c0f730 fix(editor): 组件配置列大小缓存不生效 2025-02-11 17:25:58 +08:00
roymondchen
900b701c80 refactor(editor): 新增useServices hook,减少使用可选链 2025-02-11 16:24:24 +08:00
239 changed files with 3765 additions and 2727 deletions

View File

@ -1,3 +1,88 @@
## [1.5.12](https://github.com/Tencent/tmagic-editor/compare/v1.5.11...v1.5.12) (2025-03-27)
### Bug Fixes
* **design:** card没有slots时隐藏对应slot ([4ddc55a](https://github.com/Tencent/tmagic-editor/commit/4ddc55aa6de4bafb978a621ab9b69af1e0c36d78))
* **editor:** 数据源修改后,依赖重新收集不准确 ([9c7d711](https://github.com/Tencent/tmagic-editor/commit/9c7d711c167c5e8ee1e9d8a8e89d66d245070dee))
* **editor:** 获取关联组件的方法默认值 ([f5742c1](https://github.com/Tencent/tmagic-editor/commit/f5742c107a68389c0828de991e7f5e9745e20d67))
* **vue-components:** button删除调试代码text支持富文本 ([2a714ae](https://github.com/Tencent/tmagic-editor/commit/2a714ae9cc89b7c88528c8afe9270ad774d7755d))
### Features
* **cli:** 如果识别不要组件文件则默认从npm包的default导入 ([b715a87](https://github.com/Tencent/tmagic-editor/commit/b715a87f409856ed396b3e35eb4102776329531e))
## [1.5.11](https://github.com/Tencent/tmagic-editor/compare/v1.5.10...v1.5.11) (2025-03-11)
### Bug Fixes
* **dep:** 依赖收集不能过滤id/name ([0c207c7](https://github.com/Tencent/tmagic-editor/commit/0c207c7b834ae25c16038a8ca431154afb4ccda1))
* **dep:** 数据源方法收集判断 ([72108e0](https://github.com/Tencent/tmagic-editor/commit/72108e00193698c8e7087b115f36e10216a0f386))
### Features
* **vue-runtime-help:** useDsl/useEditorDsl添加app默认参数 ([f671c67](https://github.com/Tencent/tmagic-editor/commit/f671c670db84631abb1f7d9e4fbf432637de2947))
## [1.5.10](https://github.com/Tencent/tmagic-editor/compare/v1.5.9...v1.5.10) (2025-03-07)
### Bug Fixes
* **data-source:** 处理异步动态加载数据源时,初始化数据源还未加载完毕的情况 ([3c66319](https://github.com/Tencent/tmagic-editor/commit/3c66319b03c42305c66b0c6a87307fe747d9f90e))
* **editor:** 样式配置宽度不能为负 ([775fcf5](https://github.com/Tencent/tmagic-editor/commit/775fcf5693eef051e5899aceae7873c1afa0e60c))
* **stage:** 防止runtime重复注册 ([63fe6ec](https://github.com/Tencent/tmagic-editor/commit/63fe6ec68be444082cb16aac76d6b2fe9ee5724f))
### Features
* **core:** hook函数调用添加node参数 ([5ba2a73](https://github.com/Tencent/tmagic-editor/commit/5ba2a73c7035d84688be0b0f1c0b4467558518c3))
* **core, editor:** 删除公共的点击事件实现,由组件自行添加 ([c8e1cff](https://github.com/Tencent/tmagic-editor/commit/c8e1cffca9b12c124c34d75ee9187a03c6f61599))
* **core:** app添加dsl-change事件 ([a4d021d](https://github.com/Tencent/tmagic-editor/commit/a4d021d2fb1fec113496733380d99e0f9deb132f))
* **core:** node新增registerMethod方法用于组件注册供其他组件通过事件配置调用 ([ff07147](https://github.com/Tencent/tmagic-editor/commit/ff0714727044c41148babcec977ca89058169501))
* **reate-runtime-help,vue-runtime-help:** 新增组件状态hook ([6f5bb84](https://github.com/Tencent/tmagic-editor/commit/6f5bb84c04f9b4b5b2145ce6c559445563a60e6c))
* **runtime:** vue2 h传参与vue3不一样需要自定义render ([90abde5](https://github.com/Tencent/tmagic-editor/commit/90abde57cc01218b483c94faf29f88996fb75c20))
* **runtime:** 支持页面切换 ([4026c0c](https://github.com/Tencent/tmagic-editor/commit/4026c0c305937b060de21ec74355a1be46e25dbf))
* **vue-components,react-components:** 增加点击事件使用组件状态hook ([0736646](https://github.com/Tencent/tmagic-editor/commit/0736646c63067b3b19125b356f462d658673eaa7))
* **vue-container:** 支持自定义render ([91cde30](https://github.com/Tencent/tmagic-editor/commit/91cde30d75de866ade3f48f48b2c0d1251fe2b36))
## [1.5.9](https://github.com/Tencent/tmagic-editor/compare/v1.5.8...v1.5.9) (2025-02-20)
### Bug Fixes
* **editor:** 编辑器销毁不应该移除依赖收集任务的事件监听 ([6e2b5ae](https://github.com/Tencent/tmagic-editor/commit/6e2b5aea47cc1059d4b92a85b9801d7af4f9b3b9))
### Features
* **table:** formatter添加index信息 ([4d77b66](https://github.com/Tencent/tmagic-editor/commit/4d77b669baf68f3346aa64d52dea6f75712b1af4))
* **table:** 支持type: index的序号列 ([6f2c6b1](https://github.com/Tencent/tmagic-editor/commit/6f2c6b151d59aca8899742c724c44dbde7d01a89))
## [1.5.8](https://github.com/Tencent/tmagic-editor/compare/v1.5.7...v1.5.8) (2025-02-11)
### Bug Fixes
* **editor:** 组件配置列大小缓存不生效 ([6d82c0f](https://github.com/Tencent/tmagic-editor/commit/6d82c0f730ff0ad7dc36b39b7c0d2534672b05c3))
### Features
* **editor:** 优化依赖收集状态显示,新增剩余任务数量显示 ([413134b](https://github.com/Tencent/tmagic-editor/commit/413134b21d3966bf7f1dbb1da6e2596f2c582bff))
## [1.5.7](https://github.com/Tencent/tmagic-editor/compare/v1.5.6...v1.5.7) (2025-02-11) ## [1.5.7](https://github.com/Tencent/tmagic-editor/compare/v1.5.6...v1.5.7) (2025-02-11)

View File

@ -1,5 +1,5 @@
{ {
"version": "1.5.7", "version": "1.5.12",
"name": "tmagic", "name": "tmagic",
"private": true, "private": true,
"type": "module", "type": "module",
@ -49,22 +49,22 @@
"@types/node": "18.19.61", "@types/node": "18.19.61",
"@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0", "@typescript-eslint/parser": "^5.62.0",
"@vitejs/plugin-vue": "^5.2.1", "@vitejs/plugin-vue": "^5.2.3",
"@vitest/coverage-v8": "^2.1.8", "@vitest/coverage-v8": "^2.1.9",
"@vue/compiler-sfc": "^3.5.13", "@vue/compiler-sfc": "^3.5.13",
"c8": "^7.14.0", "c8": "^7.14.0",
"commitizen": "^4.3.1", "commitizen": "^4.3.1",
"conventional-changelog-cli": "^4.1.0", "conventional-changelog-cli": "^4.1.0",
"cosmiconfig": "^8.3.6", "cosmiconfig": "^8.3.6",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"element-plus": "^2.9.3", "element-plus": "^2.9.7",
"enquirer": "^2.4.1", "enquirer": "^2.4.1",
"eslint": "^8.57.1", "eslint": "^8.57.1",
"eslint-config-tencent": "^1.1.2", "eslint-config-tencent": "^1.1.2",
"eslint-plugin-import": "^2.31.0", "eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-simple-import-sort": "^10.0.0",
"eslint-plugin-vue": "^9.32.0", "eslint-plugin-vue": "^9.33.0",
"execa": "^4.1.0", "execa": "^4.1.0",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"husky": "^7.0.4", "husky": "^7.0.4",
@ -73,19 +73,19 @@
"minimist": "^1.2.8", "minimist": "^1.2.8",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"recast": "^0.20.5", "recast": "^0.23.11",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^4.31.0", "rollup": "^4.38.0",
"rollup-plugin-dts": "^6.1.1", "rollup-plugin-dts": "^6.2.1",
"semver": "^7.6.3", "semver": "^7.7.1",
"serialize-javascript": "^6.0.2", "serialize-javascript": "^6.0.2",
"shx": "^0.3.4", "shx": "^0.3.4",
"typescript": "^5.7.3", "typescript": "^5.8.2",
"vite": "^6.0.10", "vite": "^6.2.4",
"vitepress": "^1.6.1", "vitepress": "^1.6.3",
"vitest": "^3.0.2", "vitest": "^3.1.1",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-tsc": "^2.2.0" "vue-tsc": "^2.2.8"
}, },
"config": { "config": {
"commitizen": { "commitizen": {

View File

@ -1,5 +1,5 @@
{ {
"version": "1.5.7", "version": "1.5.12",
"name": "@tmagic/cli", "name": "@tmagic/cli",
"main": "lib/index.js", "main": "lib/index.js",
"types": "lib/index.d.ts", "types": "lib/index.d.ts",
@ -31,7 +31,7 @@
"esbuild": "^0.21.5", "esbuild": "^0.21.5",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"picocolors": "^1.1.1", "picocolors": "^1.1.1",
"recast": "^0.23.9", "recast": "^0.23.11",
"tslib": "^2.8.0" "tslib": "^2.8.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -11,11 +11,14 @@ export default class Core {
public options: UserConfig; public options: UserConfig;
public moduleMainFilePath: ModuleMainFilePath = { public moduleMainFilePath: ModuleMainFilePath = {
componentPackage: {},
componentMap: {}, componentMap: {},
pluginPakcage: {},
pluginMap: {}, pluginMap: {},
configMap: {}, configMap: {},
valueMap: {}, valueMap: {},
eventMap: {}, eventMap: {},
datasourcePackage: {},
datasourceMap: {}, datasourceMap: {},
dsConfigMap: {}, dsConfigMap: {},
dsValueMap: {}, dsValueMap: {},

View File

@ -54,11 +54,14 @@ export interface NpmConfig {
} }
export interface ModuleMainFilePath { export interface ModuleMainFilePath {
componentPackage: Record<string, string>;
componentMap: Record<string, string>; componentMap: Record<string, string>;
pluginPakcage: Record<string, string>;
pluginMap: Record<string, string>; pluginMap: Record<string, string>;
configMap: Record<string, string>; configMap: Record<string, string>;
valueMap: Record<string, string>; valueMap: Record<string, string>;
eventMap: Record<string, string>; eventMap: Record<string, string>;
datasourcePackage: Record<string, string>;
datasourceMap: Record<string, string>; datasourceMap: Record<string, string>;
dsConfigMap: Record<string, string>; dsConfigMap: Record<string, string>;
dsValueMap: Record<string, string>; dsValueMap: Record<string, string>;

View File

@ -5,33 +5,84 @@ import { EntryType } from '../types';
export const prepareEntryFile = async (app: App) => { export const prepareEntryFile = async (app: App) => {
const { moduleMainFilePath, options } = app; const { moduleMainFilePath, options } = app;
const { componentFileAffix, dynamicImport, hooks, useTs = true } = options; const { dynamicImport, hooks, useTs = true } = options;
let contentMap: Record<string, string> = { let contentMap: Record<string, string> = {
'comp-entry': generateContent(useTs, EntryType.COMPONENT, moduleMainFilePath.componentMap, componentFileAffix), 'comp-entry': generateContent(
useTs,
EntryType.COMPONENT,
moduleMainFilePath.componentPackage,
moduleMainFilePath.componentMap,
),
'async-comp-entry': generateContent( 'async-comp-entry': generateContent(
useTs, useTs,
EntryType.COMPONENT, EntryType.COMPONENT,
moduleMainFilePath.componentPackage,
moduleMainFilePath.componentMap, moduleMainFilePath.componentMap,
componentFileAffix,
dynamicImport, dynamicImport,
), ),
'plugin-entry': generateContent(useTs, EntryType.PLUGIN, moduleMainFilePath.pluginMap), 'plugin-entry': generateContent(
'async-plugin-entry': generateContent(useTs, EntryType.PLUGIN, moduleMainFilePath.pluginMap, '', dynamicImport), useTs,
'config-entry': generateContent(useTs, EntryType.CONFIG, moduleMainFilePath.configMap), EntryType.PLUGIN,
'value-entry': generateContent(useTs, EntryType.VALUE, moduleMainFilePath.valueMap), moduleMainFilePath.pluginPakcage,
'event-entry': generateContent(useTs, EntryType.EVENT, moduleMainFilePath.eventMap), moduleMainFilePath.pluginMap,
'datasource-entry': generateContent(useTs, EntryType.DATASOURCE, moduleMainFilePath.datasourceMap), ),
'async-plugin-entry': generateContent(
useTs,
EntryType.PLUGIN,
moduleMainFilePath.pluginPakcage,
moduleMainFilePath.pluginMap,
dynamicImport,
),
'config-entry': generateContent(
useTs,
EntryType.CONFIG,
moduleMainFilePath.componentPackage,
moduleMainFilePath.configMap,
),
'value-entry': generateContent(
useTs,
EntryType.VALUE,
moduleMainFilePath.componentPackage,
moduleMainFilePath.valueMap,
),
'event-entry': generateContent(
useTs,
EntryType.EVENT,
moduleMainFilePath.componentPackage,
moduleMainFilePath.eventMap,
),
'datasource-entry': generateContent(
useTs,
EntryType.DATASOURCE,
moduleMainFilePath.datasourcePackage,
moduleMainFilePath.datasourceMap,
),
'async-datasource-entry': generateContent( 'async-datasource-entry': generateContent(
useTs, useTs,
EntryType.DATASOURCE, EntryType.DATASOURCE,
moduleMainFilePath.datasourcePackage,
moduleMainFilePath.datasourceMap, moduleMainFilePath.datasourceMap,
'',
dynamicImport, dynamicImport,
), ),
'ds-config-entry': generateContent(useTs, EntryType.DS_CONFIG, moduleMainFilePath.dsConfigMap), 'ds-config-entry': generateContent(
'ds-value-entry': generateContent(useTs, EntryType.DS_VALUE, moduleMainFilePath.dsValueMap), useTs,
'ds-event-entry': generateContent(useTs, EntryType.DS_EVENT, moduleMainFilePath.dsEventMap), EntryType.DS_CONFIG,
moduleMainFilePath.datasourcePackage,
moduleMainFilePath.dsConfigMap,
),
'ds-value-entry': generateContent(
useTs,
EntryType.DS_VALUE,
moduleMainFilePath.datasourcePackage,
moduleMainFilePath.dsValueMap,
),
'ds-event-entry': generateContent(
useTs,
EntryType.DS_EVENT,
moduleMainFilePath.datasourcePackage,
moduleMainFilePath.dsEventMap,
),
}; };
if (typeof hooks?.beforeWriteEntry === 'function') { if (typeof hooks?.beforeWriteEntry === 'function') {
@ -53,8 +104,8 @@ export const prepareEntryFile = async (app: App) => {
export const generateContent = ( export const generateContent = (
useTs: boolean, useTs: boolean,
type: EntryType, type: EntryType,
packageMap: Record<string, string> = {},
map: Record<string, string> = {}, map: Record<string, string> = {},
componentFileAffix = '',
dynamicImport = false, dynamicImport = false,
) => { ) => {
const list: string[] = []; const list: string[] = [];
@ -62,14 +113,14 @@ export const generateContent = (
Object.entries(map).forEach(([key, packagePath]) => { Object.entries(map).forEach(([key, packagePath]) => {
const name = makeCamelCase(key); const name = makeCamelCase(key);
if (dynamicImport) {
list.push( if ([EntryType.CONFIG, EntryType.EVENT, EntryType.VALUE].includes(type) && packagePath === packageMap[key]) {
`'${key}': () => import('${packagePath}${packagePath.endsWith(componentFileAffix) ? '' : componentFileAffix}')`, importDeclarations.push(`import { ${type} as ${name} } from '${packageMap[key]}'`);
); list.push(`'${key}': ${name}`);
} else if (dynamicImport) {
list.push(`'${key}': () => import('${packagePath}')`);
} else { } else {
importDeclarations.push( importDeclarations.push(`import ${name} from '${packagePath}'`);
`import ${name} from '${packagePath}${packagePath.endsWith(componentFileAffix) ? '' : componentFileAffix}'`,
);
list.push(`'${key}': ${name}`); list.push(`'${key}': ${name}`);
} }
}); });

View File

@ -38,20 +38,37 @@ const getRelativePath = (str: string, base: string) => (path.isAbsolute(str) ? p
const npmInstall = function (dependencies: Record<string, string>, cwd: string, npmConfig: NpmConfig = {}) { const npmInstall = function (dependencies: Record<string, string>, cwd: string, npmConfig: NpmConfig = {}) {
try { try {
const { client = 'npm', registry, installArgs = '' } = npmConfig; const { client = 'npm', registry, installArgs = '', keepPackageJsonClean } = npmConfig;
const install = { const install = {
npm: 'install', npm: 'install',
yarn: 'add', yarn: 'add',
pnpm: 'add', pnpm: 'add',
}[client]; }[client];
const packages = Object.entries(dependencies) let packages = Object.entries(dependencies);
.map(([name, version]) => (version ? `${name}@${version}` : name))
.join(' '); const newPackages = Object.entries(dependencies).filter(([name]) => {
if (fs.existsSync(path.resolve(cwd, 'node_modules', name))) {
return false;
}
return true;
});
// keepPackageJsonClean会保留原始的package.json这样配置的packages就不会被写入dependencies中
// install 时会删除不在dependencies中的依赖所以需要install packages中配置的所有包
if (!keepPackageJsonClean || !newPackages.length) {
packages = newPackages;
}
if (!packages.length) {
return;
}
const packageNames = packages.map(([name, version]) => (version ? `${name}@${version}` : name)).join(' ');
const installArgsString = `${installArgs ? ` ${installArgs}` : ''}`; const installArgsString = `${installArgs ? ` ${installArgs}` : ''}`;
const registryString = `${registry ? ` --registry ${registry}` : ''}`; const registryString = `${registry ? ` --registry ${registry}` : ''}`;
const command = `${client} ${install}${installArgsString} ${packages}${registryString}`; const command = `${client} ${install}${installArgsString} ${packageNames}${registryString}`;
execInfo(cwd); execInfo(cwd);
execInfo(command); execInfo(command);
@ -318,25 +335,30 @@ const parseEntry = function ({ ast, package: module, indexPath }: ParseEntryOpti
const tokens = getASTTokenByTraverse({ ast, indexPath }); const tokens = getASTTokenByTraverse({ ast, indexPath });
let { config, value, event, component } = tokens; let { config, value, event, component } = tokens;
if (!config) { if (typeof config === 'undefined') {
info(`${module} 表单配置文件声明缺失`); info(`${module} 表单配置文件声明缺失`);
} }
if (!value) { if (typeof value === 'undefined') {
info(`${module} 初始化数据文件声明缺失`); info(`${module} 初始化数据文件声明缺失`);
} }
if (!event) { if (typeof event === 'undefined') {
info(`${module} 事件声明文件声明缺失`); info(`${module} 事件声明文件声明缺失`);
} }
if (!component) {
info(`${module} 组件或数据源文件声明不合法`);
exit(1);
}
const reg = /^.*[/\\]node_modules[/\\](.*)/; const reg = /^.*[/\\]node_modules[/\\](.*)/;
[, config] = config.match(reg) || [, config];
[, value] = value.match(reg) || [, value]; if (config) {
[, component] = component.match(reg) || [, component]; [, config] = config.match(reg) || [, config];
[, event] = event.match(reg) || [, event]; }
if (value) {
[, value] = value.match(reg) || [, value];
}
if (component) {
[, component] = component.match(reg) || [, component];
}
if (event) {
[, event] = event.match(reg) || [, event];
}
return { return {
config, config,
@ -347,10 +369,10 @@ const parseEntry = function ({ ast, package: module, indexPath }: ParseEntryOpti
}; };
const getASTTokenByTraverse = ({ ast, indexPath }: { ast: any; indexPath: string }) => { const getASTTokenByTraverse = ({ ast, indexPath }: { ast: any; indexPath: string }) => {
let config = ''; let config: string | undefined;
let value = ''; let value: string | undefined;
let event = ''; let event: string | undefined;
let component = ''; let component: string | undefined;
const importSpecifiersMap: { [key: string]: string } = {}; const importSpecifiersMap: { [key: string]: string } = {};
const exportSpecifiersMap: { [key: string]: string | undefined } = {}; const exportSpecifiersMap: { [key: string]: string | undefined } = {};
@ -396,7 +418,11 @@ const getASTTokenByTraverse = ({ ast, indexPath }: { ast: any; indexPath: string
visitExportDefaultDeclaration(p) { visitExportDefaultDeclaration(p) {
const { node } = p; const { node } = p;
const { declaration } = node as any; const { declaration } = node as any;
component = path.resolve(path.dirname(indexPath), importSpecifiersMap[declaration.name]);
if (importSpecifiersMap[declaration.name]) {
component = path.resolve(path.dirname(indexPath), importSpecifiersMap[declaration.name]);
}
this.traverse(p); this.traverse(p);
}, },
}); });
@ -405,7 +431,10 @@ const getASTTokenByTraverse = ({ ast, indexPath }: { ast: any; indexPath: string
const exportValue = exportSpecifiersMap[exportName]; const exportValue = exportSpecifiersMap[exportName];
const importValue = importSpecifiersMap[exportName]; const importValue = importSpecifiersMap[exportName];
const connectValue = exportValue ? importSpecifiersMap[exportValue] : ''; const connectValue = exportValue ? importSpecifiersMap[exportValue] : '';
const filePath = path.resolve(path.dirname(indexPath), connectValue || importValue || exportValue || '');
const fileName = connectValue || importValue || exportValue || '';
const filePath = fileName ? path.resolve(path.dirname(indexPath), fileName) : '';
if (exportName === EntryType.VALUE) { if (exportName === EntryType.VALUE) {
value = filePath; value = filePath;
@ -463,7 +492,7 @@ const getDependencies = (dependencies: Record<string, string>, packagePath: stri
const setPackages = (packages: ModuleMainFilePath, app: App, packagePath: string, cwd: string, key?: string) => { const setPackages = (packages: ModuleMainFilePath, app: App, packagePath: string, cwd: string, key?: string) => {
const { options } = app; const { options } = app;
const { temp, componentFileAffix, datasoucreSuperClass } = options; const { temp, componentFileAffix = '', datasoucreSuperClass } = options;
let { name: moduleName } = splitNameVersion(packagePath); let { name: moduleName } = splitNameVersion(packagePath);
@ -513,15 +542,45 @@ const setPackages = (packages: ModuleMainFilePath, app: App, packagePath: string
if (!key) return; if (!key) return;
if (result.type === PackageType.COMPONENT) { if (result.type === PackageType.COMPONENT || !result.type) {
packages.componentPackage[key] = moduleName;
// 组件 // 组件
const entry = parseEntry({ ast, package: moduleName, indexPath }); const entry = parseEntry({ ast, package: moduleName, indexPath });
if (entry.component) packages.componentMap[key] = getRelativePath(entry.component, temp); if (entry.component) {
if (entry.config) packages.configMap[key] = getRelativePath(entry.config, temp); const packagePath = getRelativePath(entry.component, temp);
if (entry.event) packages.eventMap[key] = getRelativePath(entry.event, temp); packages.componentMap[key] = `${packagePath}${
if (entry.value) packages.valueMap[key] = getRelativePath(entry.value, temp); packagePath.endsWith(componentFileAffix) ? '' : componentFileAffix
}`;
} else {
packages.componentMap[key] = moduleName;
}
if (typeof entry.config === 'string') {
if (entry.config) {
packages.configMap[key] = getRelativePath(entry.config, temp);
} else {
packages.configMap[key] = moduleName;
}
}
if (typeof entry.event === 'string') {
if (entry.event) {
packages.eventMap[key] = getRelativePath(entry.event, temp);
} else {
packages.eventMap[key] = moduleName;
}
}
if (typeof entry.value === 'string') {
if (entry.value) {
packages.valueMap[key] = getRelativePath(entry.value, temp);
} else {
packages.valueMap[key] = moduleName;
}
}
} else if (result.type === PackageType.DATASOURCE) { } else if (result.type === PackageType.DATASOURCE) {
packages.datasourcePackage[key] = moduleName;
// 数据源 // 数据源
const entry = parseEntry({ ast, package: moduleName, indexPath }); const entry = parseEntry({ ast, package: moduleName, indexPath });
@ -530,6 +589,7 @@ const setPackages = (packages: ModuleMainFilePath, app: App, packagePath: string
if (entry.event) packages.dsEventMap[key] = getRelativePath(entry.event, temp); if (entry.event) packages.dsEventMap[key] = getRelativePath(entry.event, temp);
if (entry.value) packages.dsValueMap[key] = getRelativePath(entry.value, temp); if (entry.value) packages.dsValueMap[key] = getRelativePath(entry.value, temp);
} else if (result.type === PackageType.PLUGIN) { } else if (result.type === PackageType.PLUGIN) {
packages.pluginPakcage[key] = moduleName;
// 插件 // 插件
packages.pluginMap[key] = getRelativePath(moduleName, temp); packages.pluginMap[key] = getRelativePath(moduleName, temp);
} }
@ -573,11 +633,14 @@ export const resolveAppPackages = (app: App): ModuleMainFilePath => {
} }
const packagesMap: ModuleMainFilePath = { const packagesMap: ModuleMainFilePath = {
componentPackage: {},
componentMap: {}, componentMap: {},
configMap: {}, configMap: {},
eventMap: {}, eventMap: {},
valueMap: {}, valueMap: {},
pluginPakcage: {},
pluginMap: {}, pluginMap: {},
datasourcePackage: {},
datasourceMap: {}, datasourceMap: {},
dsConfigMap: {}, dsConfigMap: {},
dsEventMap: {}, dsEventMap: {},

View File

@ -7,6 +7,7 @@
"rootDir": "./src", "rootDir": "./src",
"outDir": "./lib", "outDir": "./lib",
"declaration": true, "declaration": true,
"types": ["node"], "types": ["node"],
}, },
"include": ["./src"], "include": ["./src"],

View File

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

View File

@ -49,6 +49,12 @@ export interface AppOptionsConfig {
class App extends EventEmitter { class App extends EventEmitter {
[x: string]: any; [x: string]: any;
static nodeClassMap = new Map<string, typeof Node>();
public static registerNode<T extends typeof Node = typeof Node>(type: string, NodeClass: T) {
App.nodeClassMap.set(type, NodeClass);
}
public env: Env = new Env(); public env: Env = new Env();
public dsl?: MApp; public dsl?: MApp;
public codeDsl?: CodeBlockDSL; public codeDsl?: CodeBlockDSL;
@ -144,7 +150,12 @@ class App extends EventEmitter {
this.dataSourceManager = createDataSourceManager(this, this.useMock); this.dataSourceManager = createDataSourceManager(this, this.useMock);
this.codeDsl = config.codeBlocks; this.codeDsl = config.codeBlocks;
this.setPage(curPage || this.page?.data?.id);
const pageId = curPage || this.page?.data?.id;
super.emit('dsl-change', { dsl: config, curPage: pageId });
this.setPage(pageId);
if (this.dataSourceManager) { if (this.dataSourceManager) {
const dataSourceList = Array.from(this.dataSourceManager.dataSourceMap.values()); const dataSourceList = Array.from(this.dataSourceManager.dataSourceMap.values());
@ -162,19 +173,22 @@ class App extends EventEmitter {
return; return;
} }
if (pageConfig === this.page?.data) return; if (this.page) {
if (pageConfig === this.page.data) return;
this.page?.destroy(); this.page.destroy();
}
this.page = new Page({ this.page = new Page({
config: pageConfig, config: pageConfig,
app: this, app: this,
}); });
this.eventHelper?.removeNodeEvents(); if (this.eventHelper) {
this.page.nodes.forEach((node) => { this.eventHelper.removeNodeEvents();
this.eventHelper?.bindNodeEvents(node); for (const [, node] of this.page.nodes) {
}); this.eventHelper.bindNodeEvents(node);
}
}
super.emit('page-change', this.page); super.emit('page-change', this.page);
} }

View File

@ -30,7 +30,11 @@ class Env {
isWeb = false; isWeb = false;
isOpenHarmony = false; isOpenHarmony = false;
constructor(ua = globalThis.navigator.userAgent, options: Record<string, boolean | string> = {}) { constructor(ua = globalThis.navigator?.userAgent ?? '', options: Record<string, boolean | string> = {}) {
if (!ua) {
return;
}
this.isIphone = ua.indexOf('iPhone') >= 0; this.isIphone = ua.indexOf('iPhone') >= 0;
this.isIpad = /(iPad).*OS\s([\d_]+)/.test(ua); this.isIpad = /(iPad).*OS\s([\d_]+)/.test(ua);

View File

@ -32,38 +32,11 @@ import {
type EventActionItem, type EventActionItem,
type EventConfig, type EventConfig,
} from '@tmagic/schema'; } from '@tmagic/schema';
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, getIdFromEl } from '@tmagic/utils'; import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX } from '@tmagic/utils';
import type { default as TMagicApp } from './App'; import type { default as TMagicApp } from './App';
import FlowState from './FlowState'; import FlowState from './FlowState';
import type { default as TMagicNode } from './Node'; import type { default as TMagicNode } from './Node';
import { COMMON_EVENT_PREFIX, isCommonMethod, triggerCommonMethod } from './utils';
const getCommonEventName = (commonEventName: string) => {
if (commonEventName.startsWith(COMMON_EVENT_PREFIX)) return commonEventName;
return `${COMMON_EVENT_PREFIX}${commonEventName}`;
};
// 点击在组件内的某个元素上,需要向上寻找到当前组件
const getDirectComponent = (element: HTMLElement | null, app: TMagicApp): TMagicNode | undefined => {
if (!element) {
return;
}
const id = getIdFromEl()(element);
if (!id) {
return getDirectComponent(element.parentElement, app);
}
const node = app.getNode(
id,
element.dataset.tmagicIteratorContainerId?.split(','),
element.dataset.tmagicIteratorIndex?.split(',').map((i) => globalThis.parseInt(i, 10)),
);
return node;
};
export default class EventHelper extends EventEmitter { export default class EventHelper extends EventEmitter {
public app: TMagicApp; public app: TMagicApp;
@ -75,19 +48,11 @@ export default class EventHelper extends EventEmitter {
super(); super();
this.app = app; this.app = app;
if (app.jsEngine === 'browser') {
globalThis.document.body.addEventListener('click', this.commonClickEventHandler);
}
} }
public destroy() { public destroy() {
this.removeNodeEvents(); this.removeNodeEvents();
this.removeAllListeners(); this.removeAllListeners();
if (this.app.jsEngine === 'browser') {
globalThis.document.body.removeEventListener('click', this.commonClickEventHandler);
}
} }
public bindNodeEvents(node: TMagicNode) { public bindNodeEvents(node: TMagicNode) {
@ -243,10 +208,6 @@ export default class EventHelper extends EventEmitter {
const toNode = this.app.getNode(to); const toNode = this.app.getNode(to);
if (!toNode) throw `ID为${to}的组件不存在`; if (!toNode) throw `ID为${to}的组件不存在`;
if (isCommonMethod(methodName)) {
return triggerCommonMethod(methodName, toNode);
}
if (toNode.instance) { if (toNode.instance) {
if (typeof toNode.instance[methodName] === 'function') { if (typeof toNode.instance[methodName] === 'function') {
await toNode.instance[methodName](fromCpt, ...args); await toNode.instance[methodName](fromCpt, ...args);
@ -259,17 +220,4 @@ export default class EventHelper extends EventEmitter {
}); });
} }
} }
private commonClickEventHandler = (e: MouseEvent) => {
if (!e.target) {
return;
}
const node = getDirectComponent(e.target as HTMLElement, this.app);
const eventName = `${getCommonEventName('click')}_${node?.data.id}`;
if (node?.eventKeys.has(eventName)) {
this.emit(node.eventKeys.get(eventName)!, node);
}
};
} }

View File

@ -32,6 +32,10 @@ interface EventCache {
args: any[]; args: any[];
} }
interface Methods {
[key: string]: (...args: any[]) => any;
}
export interface NodeOptions { export interface NodeOptions {
config: MNode; config: MNode;
page?: Page; page?: Page;
@ -45,7 +49,7 @@ class Node extends EventEmitter {
[key: string]: any; [key: string]: any;
}; };
public events: EventConfig[] = []; public events: EventConfig[] = [];
public instance?: any; public instance?: any = {};
public page?: Page; public page?: Page;
public parent?: Node; public parent?: Node;
public app: TMagicApp; public app: TMagicApp;
@ -69,6 +73,7 @@ class Node extends EventEmitter {
const { events, style } = data; const { events, style } = data;
this.events = events || []; this.events = events || [];
this.style = style || {}; this.style = style || {};
this.instance.config = data;
this.emit('update-data', data); this.emit('update-data', data);
} }
@ -76,6 +81,22 @@ class Node extends EventEmitter {
this.eventQueue.push(event); this.eventQueue.push(event);
} }
public registerMethod(methods: Methods) {
if (!methods) {
return;
}
if (!this.instance) {
this.instance = {};
}
for (const [key, fn] of Object.entries(methods)) {
if (typeof fn === 'function') {
this.instance[key] = fn;
}
}
}
public async runHookCode(hook: string, params?: Record<string, any>) { public async runHookCode(hook: string, params?: Record<string, any>) {
if (typeof this.data[hook] === 'function') { if (typeof this.data[hook] === 'function') {
// 兼容旧的数据格式 // 兼容旧的数据格式
@ -102,8 +123,9 @@ class Node extends EventEmitter {
const { codeType = HookCodeType.CODE, codeId, params: itemParams = {} } = item; const { codeType = HookCodeType.CODE, codeId, params: itemParams = {} } = item;
let functionContent: ((...args: any[]) => any) | string | undefined; let functionContent: ((...args: any[]) => any) | string | undefined;
const functionParams: { app: TMagicApp; params: Record<string, any>; dataSource?: DataSource } = { const functionParams: { app: TMagicApp; node: Node; params: Record<string, any>; dataSource?: DataSource } = {
app: this.app, app: this.app,
node: this,
params: params || itemParams, params: params || itemParams,
}; };
@ -136,12 +158,23 @@ class Node extends EventEmitter {
this.listenLifeSafe(); this.listenLifeSafe();
}); });
this.instance = instance; if (instance) {
this.registerMethod(instance);
if (instance.config) {
this.setData(instance.config);
}
}
await this.runHookCode('created'); await this.runHookCode('created');
}); });
this.once('mounted', async (instance: any) => { this.once('mounted', async (instance: any) => {
this.instance = instance; if (instance) {
this.registerMethod(instance);
if (instance.config) {
this.setData(instance.config);
}
}
for (let eventConfig = this.eventQueue.shift(); eventConfig; eventConfig = this.eventQueue.shift()) { for (let eventConfig = this.eventQueue.shift(); eventConfig; eventConfig = this.eventQueue.shift()) {
if (typeof instance[eventConfig.method] === 'function') { if (typeof instance[eventConfig.method] === 'function') {

View File

@ -18,7 +18,7 @@
import type { Id, MComponent, MContainer, MPage, MPageFragment } from '@tmagic/schema'; import type { Id, MComponent, MContainer, MPage, MPageFragment } from '@tmagic/schema';
import type App from './App'; import App from './App';
import IteratorContainer from './IteratorContainer'; import IteratorContainer from './IteratorContainer';
import type { default as TMagicNode } from './Node'; import type { default as TMagicNode } from './Node';
import Node from './Node'; import Node from './Node';
@ -53,7 +53,7 @@ class Page extends Node {
return; return;
} }
const node = new Node({ const node = new ((config.type && App.nodeClassMap.get(config.type)) || Node)({
config, config,
parent, parent,
page: this, page: this,

View File

@ -18,8 +18,6 @@
import { JsEngine } from '@tmagic/schema'; import { JsEngine } from '@tmagic/schema';
import { isNumber } from '@tmagic/utils'; import { isNumber } from '@tmagic/utils';
import type { default as TMagicNode } from './Node';
export const style2Obj = (style: string) => { export const style2Obj = (style: string) => {
if (typeof style !== 'string') { if (typeof style !== 'string') {
return style; return style;
@ -119,47 +117,7 @@ export const transformStyle = (style: Record<string, any> | string, jsEngine: Js
export const COMMON_EVENT_PREFIX = 'magic:common:events:'; export const COMMON_EVENT_PREFIX = 'magic:common:events:';
export const COMMON_METHOD_PREFIX = 'magic:common:actions:'; export const COMMON_METHOD_PREFIX = 'magic:common:actions:';
export const CommonMethod = {
SHOW: 'show',
HIDE: 'hide',
SCROLL_TO_VIEW: 'scrollIntoView',
SCROLL_TO_TOP: 'scrollToTop',
};
export const isCommonMethod = (methodName: string) => methodName.startsWith(COMMON_METHOD_PREFIX);
export const triggerCommonMethod = (methodName: string, node: TMagicNode) => {
const { instance } = node;
if (!instance) return;
switch (methodName.replace(COMMON_METHOD_PREFIX, '')) {
case CommonMethod.SHOW:
instance.show();
break;
case CommonMethod.HIDE:
instance.hide();
break;
case CommonMethod.SCROLL_TO_VIEW:
instance.$el?.scrollIntoView({ behavior: 'smooth' });
break;
case CommonMethod.SCROLL_TO_TOP:
window.scrollTo({ top: 0, behavior: 'smooth' });
break;
default:
break;
}
};
export interface EventOption { export interface EventOption {
label: string; label: string;
value: string; value: string;
} }
export const DEFAULT_EVENTS: EventOption[] = [{ label: '点击', value: `${COMMON_EVENT_PREFIX}click` }];
export const DEFAULT_METHODS: EventOption[] = [];

View File

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

View File

@ -32,9 +32,16 @@ import { compiledNodeField, compliedConditions, compliedIteratorItem, createIter
class DataSourceManager extends EventEmitter { class DataSourceManager extends EventEmitter {
private static dataSourceClassMap = new Map<string, typeof DataSource>(); private static dataSourceClassMap = new Map<string, typeof DataSource>();
private static ObservedDataClass: ObservedDataClass = SimpleObservedData; private static ObservedDataClass: ObservedDataClass = SimpleObservedData;
private static waitInitSchemaList = new Map<DataSourceManager, Record<string, DataSourceSchema[]>>();
public static register<T extends typeof DataSource = typeof DataSource>(type: string, dataSource: T) { public static register<T extends typeof DataSource = typeof DataSource>(type: string, dataSource: T) {
DataSourceManager.dataSourceClassMap.set(type, dataSource); DataSourceManager.dataSourceClassMap.set(type, dataSource);
DataSourceManager.waitInitSchemaList?.forEach((listMap, app) => {
const list = listMap[type] || [];
for (let config = list.shift(); config; config = list.shift()) {
app.addDataSource(config);
}
});
} }
public static getDataSourceClass(type: string) { public static getDataSourceClass(type: string) {
@ -55,6 +62,8 @@ class DataSourceManager extends EventEmitter {
constructor({ app, useMock, initialData }: DataSourceManagerOptions) { constructor({ app, useMock, initialData }: DataSourceManagerOptions) {
super(); super();
DataSourceManager.waitInitSchemaList.set(this, {});
this.app = app; this.app = app;
this.useMock = useMock; this.useMock = useMock;
@ -133,7 +142,24 @@ class DataSourceManager extends EventEmitter {
public async addDataSource(config?: DataSourceSchema) { public async addDataSource(config?: DataSourceSchema) {
if (!config) return; if (!config) return;
const DataSourceClass = DataSourceManager.dataSourceClassMap.get(config.type) || DataSource; const DataSourceClass = DataSourceManager.dataSourceClassMap.get(config.type);
if (!DataSourceClass) {
let listMap = DataSourceManager.waitInitSchemaList.get(this);
if (!listMap) {
listMap = {};
DataSourceManager.waitInitSchemaList.set(this, listMap);
}
if (listMap[config.type]) {
listMap[config.type].push(config);
} else {
listMap[config.type] = [config];
}
return;
}
const ds = new DataSourceClass({ const ds = new DataSourceClass({
app: this.app, app: this.app,
@ -276,6 +302,7 @@ class DataSourceManager extends EventEmitter {
ds.destroy(); ds.destroy();
}); });
this.dataSourceMap.clear(); this.dataSourceMap.clear();
DataSourceManager.waitInitSchemaList.delete(this);
} }
public onDataChange(id: string, path: string, callback: (newVal: any) => void) { public onDataChange(id: string, path: string, callback: (newVal: any) => void) {
@ -288,5 +315,6 @@ class DataSourceManager extends EventEmitter {
} }
DataSourceManager.register('http', HttpDataSource as any); DataSourceManager.register('http', HttpDataSource as any);
DataSourceManager.register('base', DataSource as any);
export default DataSourceManager; export default DataSourceManager;

View File

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

View File

@ -222,7 +222,6 @@ export default class Watcher {
for (const [key, value] of Object.entries(config)) { for (const [key, value] of Object.entries(config)) {
if (typeof value === 'undefined' || value === '') continue; if (typeof value === 'undefined' || value === '') continue;
if (key === 'id' || key === 'name') continue;
doCollect(key, value); doCollect(key, value);
} }
}; };

View File

@ -263,20 +263,20 @@ export const createDataSourceCondTarget = (ds: Pick<DataSourceSchema, 'id' | 'fi
isTarget: (key: string | number, value: any) => isDataSourceCondTarget(ds, key, value), isTarget: (key: string | number, value: any) => isDataSourceCondTarget(ds, key, value),
}); });
export const createDataSourceMethodTarget = (ds: Pick<DataSourceSchema, 'id' | 'fields'>, initialDeps: DepData = {}) => export const createDataSourceMethodTarget = (ds: Pick<DataSourceSchema, 'id' | 'methods'>, initialDeps: DepData = {}) =>
new Target({ new Target({
type: DepTargetType.DATA_SOURCE_METHOD, type: DepTargetType.DATA_SOURCE_METHOD,
id: ds.id, id: ds.id,
initialDeps, initialDeps,
isTarget: (_key: string | number, value: any) => { isTarget: (_key: string | number, value: any) => {
// 使用data-source-method-select 可以配置出来 // 使用data-source-method-select 可以配置出来
if (!Array.isArray(value) || !ds) { if (!Array.isArray(value) || !ds?.methods) {
return false; return false;
} }
const [dsId, ...keys] = value; const [dsId, methodName] = value;
if (dsId !== ds.id || ds.fields?.find((field) => field.name === keys[0])) { if (!methodName || dsId !== ds.id || !ds.methods.find((field) => field.name === methodName)) {
return false; return false;
} }

View File

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

View File

@ -42,7 +42,7 @@ const ui = getDesignConfig('components')?.autocomplete;
const uiComponent = ui?.component || 'el-autocomplete'; const uiComponent = ui?.component || 'el-autocomplete';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<AutocompleteProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'select', 'update:modelValue']); const emit = defineEmits(['change', 'select', 'update:modelValue']);

View File

@ -19,5 +19,5 @@ const props = defineProps<BadgeProps>();
const ui = getDesignConfig('components')?.badge; const ui = getDesignConfig('components')?.badge;
const uiComponent = ui?.component || 'el-badge'; const uiComponent = ui?.component || 'el-badge';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<BadgeProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -22,7 +22,7 @@ const ui = getDesignConfig('components')?.button;
const uiComponent = ui?.component || 'el-button'; const uiComponent = ui?.component || 'el-button';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<ButtonProps>(() => ui?.props(props) || props);
const emit = defineEmits(['click']); const emit = defineEmits(['click']);

View File

@ -1,10 +1,10 @@
<template> <template>
<component class="tmagic-design-card" :is="uiComponent" v-bind="uiProps"> <component class="tmagic-design-card" :is="uiComponent" v-bind="uiProps">
<template #header> <template #header v-if="$slots.header">
<slot name="header" class="header"></slot> <slot name="header" class="header"></slot>
</template> </template>
<template #default> <template #default v-if="$slots.default">
<slot name="default"></slot> <slot name="default"></slot>
</template> </template>
</component> </component>
@ -26,5 +26,5 @@ const ui = getDesignConfig('components')?.card;
const uiComponent = ui?.component || 'el-card'; const uiComponent = ui?.component || 'el-card';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<CardProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -25,7 +25,7 @@ const ui = getDesignConfig('components')?.cascader;
const uiComponent = ui?.component || 'el-cascader'; const uiComponent = ui?.component || 'el-cascader';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<CascaderProps>(() => ui?.props(props) || props);
const cascader = ref<any>(); const cascader = ref<any>();

View File

@ -6,7 +6,7 @@
@update:modelValue="updateModelValue" @update:modelValue="updateModelValue"
@change="changeHandler" @change="changeHandler"
> >
<template #default> <template #default v-if="$slots.default">
<slot></slot> <slot></slot>
</template> </template>
</component> </component>
@ -31,7 +31,7 @@ const ui = getDesignConfig('components')?.checkbox;
const uiComponent = ui?.component || 'el-checkbox'; const uiComponent = ui?.component || 'el-checkbox';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<CheckboxProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -26,7 +26,7 @@ const ui = getDesignConfig('components')?.checkboxGroup;
const uiComponent = ui?.component || 'el-checkbox-group'; const uiComponent = ui?.component || 'el-checkbox-group';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<CheckboxGroupProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.col;
const uiComponent = ui?.component || 'el-col'; const uiComponent = ui?.component || 'el-col';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<ColProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -26,7 +26,7 @@ const ui = getDesignConfig('components')?.collapse;
const uiComponent = ui?.component || 'el-collapse'; const uiComponent = ui?.component || 'el-collapse';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<CollapseProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -32,7 +32,7 @@ const ui = getDesignConfig('components')?.collapseItem;
const uiComponent = ui?.component || 'el-collapse-item'; const uiComponent = ui?.component || 'el-collapse-item';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<CollapseItemProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -28,7 +28,7 @@ const ui = getDesignConfig('components')?.colorPicker;
const uiComponent = ui?.component || 'el-color-picker'; const uiComponent = ui?.component || 'el-color-picker';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<ColorPickerProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -27,7 +27,7 @@ const ui = getDesignConfig('components')?.datePicker;
const uiComponent = ui?.component || 'el-date-picker'; const uiComponent = ui?.component || 'el-date-picker';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<DatePickerProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -32,7 +32,7 @@ const ui = getDesignConfig('components')?.dialog;
const uiComponent = ui?.component || 'el-dialog'; const uiComponent = ui?.component || 'el-dialog';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<DialogProps>(() => ui?.props(props) || props);
const closeHandler = (...args: any[]) => { const closeHandler = (...args: any[]) => {
emit('close', ...args); emit('close', ...args);

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.divider;
const uiComponent = ui?.component || 'el-divider'; const uiComponent = ui?.component || 'el-divider';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<DividerProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -40,7 +40,7 @@ const ui = getDesignConfig('components')?.drawer;
const uiComponent = ui?.component || 'el-drawer'; const uiComponent = ui?.component || 'el-drawer';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<DrawerProps>(() => ui?.props(props) || props);
const drawer = ref<any>(); const drawer = ref<any>();

View File

@ -24,7 +24,7 @@ const ui = getDesignConfig('components')?.dropdown;
const uiComponent = ui?.component || 'el-dropdown'; const uiComponent = ui?.component || 'el-dropdown';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<DropdownProps>(() => ui?.props(props) || props);
const emit = defineEmits(['command']); const emit = defineEmits(['command']);

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.dropdownItem;
const uiComponent = ui?.component || 'el-dropdown-item'; const uiComponent = ui?.component || 'el-dropdown-item';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<DropdownItemProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -26,7 +26,7 @@ const ui = getDesignConfig('components')?.form;
const uiComponent = ui?.component || 'el-form'; const uiComponent = ui?.component || 'el-form';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<FormProps>(() => ui?.props(props) || props);
const form = ref<any>(); const form = ref<any>();

View File

@ -23,5 +23,5 @@ const ui = getDesignConfig('components')?.formItem;
const uiComponent = ui?.component || 'el-form-item'; const uiComponent = ui?.component || 'el-form-item';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<FormItemProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -17,5 +17,5 @@ defineOptions({
const ui = getDesignConfig('components')?.icon; const ui = getDesignConfig('components')?.icon;
const uiComponent = ui?.component || 'el-icon'; const uiComponent = ui?.component || 'el-icon';
const props = defineProps<IconProps>(); const props = defineProps<IconProps>();
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<IconProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -39,7 +39,7 @@ const ui = getDesignConfig('components')?.input;
const uiComponent = ui?.component || 'el-input'; const uiComponent = ui?.component || 'el-input';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<InputProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'input', 'update:modelValue']); const emit = defineEmits(['change', 'input', 'update:modelValue']);

View File

@ -25,7 +25,7 @@ const ui = getDesignConfig('components')?.inputNumber;
const uiComponent = ui?.component || 'el-input-number'; const uiComponent = ui?.component || 'el-input-number';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<InputNumberProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'input', 'update:modelValue']); const emit = defineEmits(['change', 'input', 'update:modelValue']);

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.option;
const uiComponent = ui?.component || 'el-option'; const uiComponent = ui?.component || 'el-option';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<OptionProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -20,7 +20,7 @@ const ui = getDesignConfig('components')?.optionGroup;
const uiComponent = ui?.component || 'el-option-group'; const uiComponent = ui?.component || 'el-option-group';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<OptionGroupProps>(() => ui?.props(props) || props);
const optionGroup = ref<any>(); const optionGroup = ref<any>();
</script> </script>

View File

@ -27,7 +27,7 @@ const ui = getDesignConfig('components')?.pagination;
const uiComponent = ui?.component || 'el-pagination'; const uiComponent = ui?.component || 'el-pagination';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<PaginationProps>(() => ui?.props(props) || props);
const handleSizeChange = (...args: any[]) => { const handleSizeChange = (...args: any[]) => {
emit('size-change', ...args); emit('size-change', ...args);

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.radio;
const uiComponent = ui?.component || 'el-radio'; const uiComponent = ui?.component || 'el-radio';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<RadioProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.radioButton;
const uiComponent = ui?.component || 'el-radio-button'; const uiComponent = ui?.component || 'el-radio-button';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<RadioButtonProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -26,7 +26,7 @@ const ui = getDesignConfig('components')?.radioGroup;
const uiComponent = ui?.component || 'el-radio-group'; const uiComponent = ui?.component || 'el-radio-group';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<RadioGroupProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -31,7 +31,7 @@ const ui = getDesignConfig('components')?.select;
const uiComponent = ui?.component || 'el-select'; const uiComponent = ui?.component || 'el-select';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<SelectProps>(() => ui?.props(props) || props);
const select = ref<any>(); const select = ref<any>();

View File

@ -26,5 +26,5 @@ const ui = getDesignConfig('components')?.step;
const uiComponent = ui?.component || 'el-step'; const uiComponent = ui?.component || 'el-step';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<StepProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.steps;
const uiComponent = ui?.component || 'el-steps'; const uiComponent = ui?.component || 'el-steps';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<StepsProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -28,7 +28,7 @@ const ui = getDesignConfig('components')?.switch;
const uiComponent = ui?.component || 'el-switch'; const uiComponent = ui?.component || 'el-switch';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<SwitchProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -26,5 +26,5 @@ const ui = getDesignConfig('components')?.tabPane;
const uiComponent = ui?.component || 'el-tab-pane'; const uiComponent = ui?.component || 'el-tab-pane';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TabPaneProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -33,7 +33,7 @@ const ui = getDesignConfig('components')?.table;
const uiComponent = ui?.component || 'el-table'; const uiComponent = ui?.component || 'el-table';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TableProps>(() => ui?.props(props) || props);
const emit = defineEmits(['select', 'sort-change', 'expand-change', 'cell-click']); const emit = defineEmits(['select', 'sort-change', 'expand-change', 'cell-click']);

View File

@ -23,5 +23,5 @@ const ui = getDesignConfig('components')?.tableColumn;
const uiComponent = ui?.component || 'el-table-column'; const uiComponent = ui?.component || 'el-table-column';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TableColumnProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -30,7 +30,7 @@ const ui = getDesignConfig('components')?.tabs;
const uiComponent = ui?.component || 'el-tabs'; const uiComponent = ui?.component || 'el-tabs';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TabsProps>(() => ui?.props(props) || props);
const emit = defineEmits(['tab-click', 'tab-add', 'tab-remove', 'update:model-value']); const emit = defineEmits(['tab-click', 'tab-add', 'tab-remove', 'update:model-value']);

View File

@ -20,5 +20,5 @@ const ui = getDesignConfig('components')?.tag;
const uiComponent = ui?.component || 'el-tag'; const uiComponent = ui?.component || 'el-tag';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TagProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -25,7 +25,7 @@ const ui = getDesignConfig('components')?.timePicker;
const uiComponent = ui?.component || 'el-time-picker'; const uiComponent = ui?.component || 'el-time-picker';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TimePickerProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'update:modelValue']); const emit = defineEmits(['change', 'update:modelValue']);

View File

@ -23,5 +23,5 @@ const ui = getDesignConfig('components')?.tooltip;
const uiComponent = ui?.component || 'el-tooltip'; const uiComponent = ui?.component || 'el-tooltip';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TooltipProps>(() => ui?.props(props) || props);
</script> </script>

View File

@ -35,7 +35,7 @@ const ui = getDesignConfig('components')?.tree;
const uiComponent = ui?.component || 'el-tree'; const uiComponent = ui?.component || 'el-tree';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<TreeProps>(() => ui?.props(props) || props);
const emit = defineEmits([ const emit = defineEmits([
'node-click', 'node-click',

View File

@ -29,7 +29,7 @@ const ui = getDesignConfig('components')?.upload;
const uiComponent = ui?.component || 'el-upload'; const uiComponent = ui?.component || 'el-upload';
const uiProps = computed(() => ui?.props(props) || props); const uiProps = computed<UploadProps>(() => ui?.props(props) || props);
const upload = ref<any>(); const upload = ref<any>();

View File

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

View File

@ -74,9 +74,9 @@ import {
import FloatingBox from '@editor/components/FloatingBox.vue'; import FloatingBox from '@editor/components/FloatingBox.vue';
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height'; import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position'; import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
import { useServices } from '@editor/hooks/use-services';
import { useWindowRect } from '@editor/hooks/use-window-rect'; import { useWindowRect } from '@editor/hooks/use-window-rect';
import CodeEditor from '@editor/layouts/CodeEditor.vue'; import CodeEditor from '@editor/layouts/CodeEditor.vue';
import type { Services } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config'; import { getEditorConfig } from '@editor/utils/config';
defineOptions({ defineOptions({
@ -97,7 +97,7 @@ const emit = defineEmits<{
submit: [values: CodeBlockContent, eventData: ContainerChangeEventData]; submit: [values: CodeBlockContent, eventData: ContainerChangeEventData];
}>(); }>();
const services = inject<Services>('services'); const { codeBlockService, uiService } = useServices();
const { height: codeBlockEditorHeight } = useEditorContentHeight(); const { height: codeBlockEditorHeight } = useEditorContentHeight();
@ -192,7 +192,7 @@ const functionConfig = computed<FormConfig>(() => [
label: '描述', label: '描述',
name: 'extra', name: 'extra',
}, },
services?.codeBlockService.getParamsColConfig() || defaultParamColConfig, codeBlockService.getParamsColConfig() || defaultParamColConfig,
], ],
}, },
{ {
@ -259,7 +259,7 @@ const closedHandler = () => {
}; };
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null)); const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating); const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
defineExpose({ defineExpose({
async show() { async show() {

View File

@ -37,7 +37,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef } from 'vue'; import { computed, nextTick, onBeforeUnmount, onMounted, type Ref, ref, useTemplateRef } from 'vue';
import { useZIndex } from '@tmagic/design'; import { useZIndex } from '@tmagic/design';
@ -73,7 +73,7 @@ const menuEl = useTemplateRef<HTMLDivElement>('menu');
const buttonRefs = useTemplateRef<InstanceType<typeof ToolButton>[]>('buttons'); const buttonRefs = useTemplateRef<InstanceType<typeof ToolButton>[]>('buttons');
const subMenuRef = useTemplateRef<any>('subMenu'); const subMenuRef = useTemplateRef<any>('subMenu');
const visible = ref(false); const visible = ref(false);
const subMenuData = ref<(MenuButton | MenuComponent)[]>([]); const subMenuData: Ref<(MenuButton | MenuComponent)[]> = ref<(MenuButton | MenuComponent)[]>([]);
const zIndex = useZIndex(); const zIndex = useZIndex();
const curZIndex = ref<number>(0); const curZIndex = ref<number>(0);

View File

@ -17,14 +17,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue'; import { computed, nextTick, onBeforeUnmount, provide, ref, useTemplateRef, watch } from 'vue';
import { Close } from '@element-plus/icons-vue'; import { Close } from '@element-plus/icons-vue';
import VanillaMoveable from 'moveable'; import VanillaMoveable from 'moveable';
import { TMagicButton, useZIndex } from '@tmagic/design'; import { TMagicButton, useZIndex } from '@tmagic/design';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
interface Position { interface Position {
left: number; left: number;
@ -66,8 +66,8 @@ const bodyHeight = computed(() => {
return 'auto'; return 'auto';
}); });
const services = inject<Services>('services'); const { uiService } = useServices();
const frameworkWidth = computed(() => services?.uiService.get('frameworkRect').width || 0); const frameworkWidth = computed(() => uiService.get('frameworkRect').width || 0);
const style = computed(() => { const style = computed(() => {
let { left } = props.position; let { left } = props.position;
if (width.value) { if (width.value) {

View File

@ -49,7 +49,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject } from 'vue'; import { computed } from 'vue';
import { ArrowDown } from '@element-plus/icons-vue'; import { ArrowDown } from '@element-plus/icons-vue';
import { import {
@ -62,8 +62,10 @@ import {
TMagicTooltip, TMagicTooltip,
} from '@tmagic/design'; } from '@tmagic/design';
import { useServices } from '@editor/hooks/use-services';
import MIcon from '../components/Icon.vue'; import MIcon from '../components/Icon.vue';
import type { MenuButton, MenuComponent, Services } from '../type'; import type { MenuButton, MenuComponent } from '../type';
defineOptions({ defineOptions({
name: 'MEditorToolButton', name: 'MEditorToolButton',
@ -82,7 +84,7 @@ const props = withDefaults(
eventType: 'click', eventType: 'click',
}, },
); );
const services = inject<Services>('services'); const services = useServices();
const disabled = computed(() => { const disabled = computed(() => {
if (typeof props.data === 'string') return false; if (typeof props.data === 'string') return false;
@ -104,7 +106,7 @@ const display = computed(() => {
const buttonHandler = (item: MenuButton | MenuComponent, event: MouseEvent) => { const buttonHandler = (item: MenuButton | MenuComponent, event: MouseEvent) => {
if (disabled.value) return; if (disabled.value) return;
if (typeof (item as MenuButton).handler === 'function' && services) { if (typeof (item as MenuButton).handler === 'function') {
(item as MenuButton).handler?.(services, event); (item as MenuButton).handler?.(services, event);
} }
}; };

View File

@ -15,7 +15,7 @@
class="tree-node" class="tree-node"
:class="{ selected, expanded }" :class="{ selected, expanded }"
:style="`padding-left: ${indent}px`" :style="`padding-left: ${indent}px`"
@contextmenu="nodeContentmenuHandler" @contextmenu="nodeContextmenuHandler"
@mouseenter="mouseenterHandler" @mouseenter="mouseenterHandler"
> >
<MIcon <MIcon
@ -139,7 +139,7 @@ const handleDragEnd = (event: DragEvent) => {
treeEmit?.('node-dragend', event, props.data); treeEmit?.('node-dragend', event, props.data);
}; };
const nodeContentmenuHandler = (event: MouseEvent) => { const nodeContextmenuHandler = (event: MouseEvent) => {
treeEmit?.('node-contextmenu', event, props.data); treeEmit?.('node-contextmenu', event, props.data);
}; };

View File

@ -16,7 +16,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, watch } from 'vue'; import { computed, watch } from 'vue';
import { isEmpty } from 'lodash-es'; import { isEmpty } from 'lodash-es';
import { HookCodeType, HookType } from '@tmagic/core'; import { HookCodeType, HookType } from '@tmagic/core';
@ -24,7 +24,7 @@ import { TMagicCard } from '@tmagic/design';
import type { ContainerChangeEventData, FieldProps, FormItem, GroupListConfig } from '@tmagic/form'; import type { ContainerChangeEventData, FieldProps, FormItem, GroupListConfig } from '@tmagic/form';
import { MContainer } from '@tmagic/form'; import { MContainer } from '@tmagic/form';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
defineOptions({ defineOptions({
name: 'MFieldsCodeSelect', name: 'MFieldsCodeSelect',
@ -34,7 +34,7 @@ const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData]; change: [v: any, eventData: ContainerChangeEventData];
}>(); }>();
const services = inject<Services>('services'); const { dataSourceService, codeBlockService } = useServices();
const props = withDefaults( const props = withDefaults(
defineProps< defineProps<
@ -59,7 +59,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
return index; return index;
} }
const ds = services?.dataSourceService.getDataSourceById(model.codeId[0]); const ds = dataSourceService.getDataSourceById(model.codeId[0]);
return `${ds?.title} / ${model.codeId[1]}`; return `${ds?.title} / ${model.codeId[1]}`;
} }
@ -98,7 +98,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
span: 18, span: 18,
labelWidth: 0, labelWidth: 0,
display: (mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD, display: (mForm, { model }) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
notEditable: () => !services?.codeBlockService.getEditStatus(), notEditable: () => !codeBlockService.getEditStatus(),
}, },
{ {
type: 'data-source-method-select', type: 'data-source-method-select',
@ -106,7 +106,7 @@ const codeConfig = computed<GroupListConfig>(() => ({
span: 18, span: 18,
labelWidth: 0, labelWidth: 0,
display: (mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD, display: (mForm, { model }) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
notEditable: () => !services?.dataSourceService.get('editable'), notEditable: () => !dataSourceService.get('editable'),
}, },
], ],
}, },

View File

@ -52,7 +52,8 @@ import {
import CodeParams from '@editor/components/CodeParams.vue'; import CodeParams from '@editor/components/CodeParams.vue';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import type { CodeParamStatement, CodeSelectColConfig, EventBus, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { CodeParamStatement, CodeSelectColConfig, EventBus } from '@editor/type';
import { SideItemKey } from '@editor/type'; import { SideItemKey } from '@editor/type';
defineOptions({ defineOptions({
@ -60,7 +61,7 @@ defineOptions({
}); });
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const services = inject<Services>('services'); const { codeBlockService, uiService } = useServices();
const eventBus = inject<EventBus>('eventBus'); const eventBus = inject<EventBus>('eventBus');
const emit = defineEmits<{ const emit = defineEmits<{
change: [v: any, eventData: ContainerChangeEventData]; change: [v: any, eventData: ContainerChangeEventData];
@ -73,7 +74,7 @@ const props = withDefaults(defineProps<FieldProps<CodeSelectColConfig>>(), {
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props)); const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
const hasCodeBlockSidePanel = computed(() => const hasCodeBlockSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.CODE_BLOCK), (uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.CODE_BLOCK),
); );
/** /**
@ -94,7 +95,7 @@ const getParamItemsConfig = (codeId?: Id): CodeParamStatement[] => {
})); }));
}; };
const codeDsl = computed(() => services?.codeBlockService.getCodeDsl()); const codeDsl = computed(() => codeBlockService.getCodeDsl());
const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model[props.name])); const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model[props.name]));
watch( watch(

View File

@ -27,12 +27,13 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject } from 'vue'; import { computed } from 'vue';
import { getDesignConfig, TMagicSelect } from '@tmagic/design'; import { getDesignConfig, TMagicSelect } from '@tmagic/design';
import type { FieldProps } from '@tmagic/form'; import type { FieldProps } from '@tmagic/form';
import type { CondOpSelectConfig, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { CondOpSelectConfig } from '@editor/type';
import { arrayOptions, eqOptions, numberOptions } from '@editor/utils'; import { arrayOptions, eqOptions, numberOptions } from '@editor/utils';
defineOptions({ defineOptions({
@ -43,7 +44,7 @@ const emit = defineEmits<{
change: [value: string]; change: [value: string];
}>(); }>();
const { dataSourceService } = inject<Services>('services') || {}; const { dataSourceService } = useServices();
const props = defineProps<FieldProps<CondOpSelectConfig>>(); const props = defineProps<FieldProps<CondOpSelectConfig>>();
@ -52,7 +53,7 @@ const optionComponent = getDesignConfig('components')?.option;
const options = computed(() => { const options = computed(() => {
const [id, ...fieldNames] = [...(props.config.parentFields || []), ...props.model.field]; const [id, ...fieldNames] = [...(props.config.parentFields || []), ...props.model.field];
const ds = dataSourceService?.getDataSourceById(id); const ds = dataSourceService.getDataSourceById(id);
let fields = ds?.fields || []; let fields = ds?.fields || [];
let type = ''; let type = '';

View File

@ -69,13 +69,14 @@
import { computed, inject, ref, watch } from 'vue'; import { computed, inject, ref, watch } from 'vue';
import { Edit, View } from '@element-plus/icons-vue'; import { Edit, View } from '@element-plus/icons-vue';
import { DataSourceFieldType } from '@tmagic/core'; import type { DataSourceFieldType } from '@tmagic/core';
import { getDesignConfig, TMagicButton, TMagicCascader, TMagicSelect, TMagicTooltip } from '@tmagic/design'; import { getDesignConfig, TMagicButton, TMagicCascader, TMagicSelect, TMagicTooltip } from '@tmagic/design';
import { type FilterFunction, filterFunction, type FormState, type SelectOption } from '@tmagic/form'; import { type FilterFunction, filterFunction, type FormState, type SelectOption } from '@tmagic/form';
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils'; import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import { type EventBus, type Services, SideItemKey } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { type EventBus, SideItemKey } from '@editor/type';
import { getCascaderOptionsFromFields, removeDataSourceFieldPrefix } from '@editor/utils'; import { getCascaderOptionsFromFields, removeDataSourceFieldPrefix } from '@editor/utils';
const props = defineProps<{ const props = defineProps<{
@ -101,11 +102,11 @@ const modelValue = defineModel<string[] | any>('modelValue', { default: [] });
const optionComponent = getDesignConfig('components')?.option; const optionComponent = getDesignConfig('components')?.option;
const services = inject<Services>('services'); const { dataSourceService, uiService } = useServices();
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const eventBus = inject<EventBus>('eventBus'); const eventBus = inject<EventBus>('eventBus');
const dataSources = computed(() => services?.dataSourceService.get('dataSources') || []); const dataSources = computed(() => dataSourceService.get('dataSources') || []);
const valueIsKey = computed(() => props.value === 'key'); const valueIsKey = computed(() => props.value === 'key');
const notEditable = computed(() => filterFunction(mForm, props.notEditable, props)); const notEditable = computed(() => filterFunction(mForm, props.notEditable, props));
@ -172,7 +173,7 @@ const onChangeHandler = (v: string[] = []) => {
}; };
const hasDataSourceSidePanel = computed(() => const hasDataSourceSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE), uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
); );
const editHandler = (id: string) => { const editHandler = (id: string) => {

View File

@ -48,7 +48,8 @@ import type { ContainerChangeEventData, FieldProps, FormState } from '@tmagic/fo
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils'; import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import type { DataSourceFieldSelectConfig, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { DataSourceFieldSelectConfig } from '@editor/type';
import { removeDataSourceFieldPrefix } from '@editor/utils'; import { removeDataSourceFieldPrefix } from '@editor/utils';
import FieldSelect from './FieldSelect.vue'; import FieldSelect from './FieldSelect.vue';
@ -83,10 +84,10 @@ watch(
}, },
); );
const services = inject<Services>('services'); const { dataSourceService } = useServices();
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const dataSources = computed(() => services?.dataSourceService.get('dataSources') || []); const dataSources = computed(() => dataSourceService.get('dataSources') || []);
const type = computed((): string => { const type = computed((): string => {
let type = props.config.fieldConfig?.type; let type = props.config.fieldConfig?.type;

View File

@ -64,7 +64,7 @@ import { getDefaultValueFromFields } from '@tmagic/utils';
import FloatingBox from '@editor/components/FloatingBox.vue'; import FloatingBox from '@editor/components/FloatingBox.vue';
import { useEditorContentHeight } from '@editor/hooks'; import { useEditorContentHeight } from '@editor/hooks';
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position'; import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
defineOptions({ defineOptions({
name: 'MFieldsDataSourceFields', name: 'MFieldsDataSourceFields',
@ -85,7 +85,7 @@ const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData]; change: [v: any, eventData?: ContainerChangeEventData];
}>(); }>();
const services = inject<Services>('services'); const { uiService } = useServices();
const fieldValues = ref<Record<string, any>>({}); const fieldValues = ref<Record<string, any>>({});
const fieldTitle = ref(''); const fieldTitle = ref('');
@ -344,5 +344,5 @@ const addFromJsonDialogVisible = defineModel<boolean>('visible1', { default: fal
const { height: editorHeight } = useEditorContentHeight(); const { height: editorHeight } = useEditorContentHeight();
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null)); const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating); const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
</script> </script>

View File

@ -48,7 +48,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, nextTick, ref, useTemplateRef, watch } from 'vue'; import { computed, nextTick, ref, useTemplateRef, watch } from 'vue';
import { Coin } from '@element-plus/icons-vue'; import { Coin } from '@element-plus/icons-vue';
import type { DataSchema, DataSourceSchema } from '@tmagic/core'; import type { DataSchema, DataSourceSchema } from '@tmagic/core';
@ -57,7 +57,7 @@ import type { FieldProps, FormItem } from '@tmagic/form';
import { getKeysArray, isNumber } from '@tmagic/utils'; import { getKeysArray, isNumber } from '@tmagic/utils';
import Icon from '@editor/components/Icon.vue'; import Icon from '@editor/components/Icon.vue';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { getDisplayField } from '@editor/utils/data-source'; import { getDisplayField } from '@editor/utils/data-source';
defineOptions({ defineOptions({
@ -81,7 +81,7 @@ const emit = defineEmits<{
change: [value: string]; change: [value: string];
}>(); }>();
const { dataSourceService } = inject<Services>('services') || {}; const { dataSourceService } = useServices();
const autocompleteRef = useTemplateRef<InstanceType<typeof TMagicAutocomplete>>('autocomplete'); const autocompleteRef = useTemplateRef<InstanceType<typeof TMagicAutocomplete>>('autocomplete');
const isFocused = ref(false); const isFocused = ref(false);
@ -89,7 +89,7 @@ const state = ref('');
const displayState = ref<{ value: string; type: 'var' | 'text' }[]>([]); const displayState = ref<{ value: string; type: 'var' | 'text' }[]>([]);
const input = computed<HTMLInputElement>(() => autocompleteRef.value?.inputRef?.input); const input = computed<HTMLInputElement>(() => autocompleteRef.value?.inputRef?.input);
const dataSources = computed(() => dataSourceService?.get('dataSources') || []); const dataSources = computed(() => dataSourceService.get('dataSources'));
const setDisplayState = () => { const setDisplayState = () => {
displayState.value = getDisplayField(dataSources.value, state.value); displayState.value = getDisplayField(dataSources.value, state.value);

View File

@ -41,37 +41,36 @@ import { createValues, type FieldProps, filterFunction, type FormState, MContain
import CodeParams from '@editor/components/CodeParams.vue'; import CodeParams from '@editor/components/CodeParams.vue';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import type { CodeParamStatement, DataSourceMethodSelectConfig, EventBus, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { CodeParamStatement, DataSourceMethodSelectConfig, EventBus } from '@editor/type';
import { SideItemKey } from '@editor/type'; import { SideItemKey } from '@editor/type';
defineOptions({ defineOptions({
name: 'MFieldsDataSourceMethodSelect', name: 'MFieldsDataSourceMethodSelect',
}); });
const { dataSourceService, uiService } = useServices();
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const services = inject<Services>('services');
const eventBus = inject<EventBus>('eventBus'); const eventBus = inject<EventBus>('eventBus');
const emit = defineEmits(['change']); const emit = defineEmits(['change']);
const dataSourceService = services?.dataSourceService;
const props = withDefaults(defineProps<FieldProps<DataSourceMethodSelectConfig>>(), { const props = withDefaults(defineProps<FieldProps<DataSourceMethodSelectConfig>>(), {
disabled: false, disabled: false,
}); });
const hasDataSourceSidePanel = computed(() => const hasDataSourceSidePanel = computed(() =>
(services?.uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE), (uiService.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE),
); );
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props)); const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
const dataSources = computed(() => dataSourceService?.get('dataSources')); const dataSources = computed(() => dataSourceService.get('dataSources'));
const isCustomMethod = computed(() => { const isCustomMethod = computed(() => {
const [id, name] = props.model[props.name]; const [id, name] = props.model[props.name];
const dataSource = dataSourceService?.getDataSourceById(id); const dataSource = dataSourceService.getDataSourceById(id);
return Boolean(dataSource?.methods.find((method) => method.name === name)); return Boolean(dataSource?.methods.find((method) => method.name === name));
}); });
@ -107,7 +106,7 @@ const setParamsConfig = (dataSourceMethod: [Id, string], formState: any = {}) =>
const methodsOptions = computed( const methodsOptions = computed(
() => () =>
dataSources.value dataSources.value
?.filter((ds) => ds.methods?.length || dataSourceService?.getFormMethod(ds.type).length) ?.filter((ds) => ds.methods?.length || dataSourceService.getFormMethod(ds.type).length)
?.map((ds) => ({ ?.map((ds) => ({
label: ds.title || ds.id, label: ds.title || ds.id,
value: ds.id, value: ds.id,
@ -144,7 +143,7 @@ const onChangeHandler = (value: any) => {
const editCodeHandler = () => { const editCodeHandler = () => {
const [id] = props.model[props.name]; const [id] = props.model[props.name];
const dataSource = dataSourceService?.getDataSourceById(id); const dataSource = dataSourceService.getDataSourceById(id);
if (!dataSource) return; if (!dataSource) return;

View File

@ -38,8 +38,8 @@ import { getDefaultValueFromFields } from '@tmagic/utils';
import FloatingBox from '@editor/components/FloatingBox.vue'; import FloatingBox from '@editor/components/FloatingBox.vue';
import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position'; import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
import { useServices } from '@editor/hooks/use-services';
import CodeEditor from '@editor/layouts/CodeEditor.vue'; import CodeEditor from '@editor/layouts/CodeEditor.vue';
import { Services } from '@editor/type';
import { useEditorContentHeight } from '..'; import { useEditorContentHeight } from '..';
@ -60,7 +60,7 @@ const props = withDefaults(
const emit = defineEmits(['change']); const emit = defineEmits(['change']);
const services = inject<Services>('services'); const { uiService } = useServices();
const width = defineModel<number>('width', { default: 670 }); const width = defineModel<number>('width', { default: 670 });
const drawerTitle = ref(''); const drawerTitle = ref('');
@ -250,5 +250,5 @@ const toggleValue = (row: MockSchema, key: 'enable' | 'useInEditor', value: bool
const addDialogVisible = defineModel<boolean>('visible', { default: false }); const addDialogVisible = defineModel<boolean>('visible', { default: false });
const { height: editorHeight } = useEditorContentHeight(); const { height: editorHeight } = useEditorContentHeight();
const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null)); const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating); const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(uiService, parentFloating);
</script> </script>

View File

@ -27,7 +27,8 @@ import { TMagicButton, TMagicTooltip } from '@tmagic/design';
import { type FieldProps, filterFunction, type FormState, MSelect, type SelectConfig } from '@tmagic/form'; import { type FieldProps, filterFunction, type FormState, MSelect, type SelectConfig } from '@tmagic/form';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import type { DataSourceSelect, EventBus, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { DataSourceSelect, EventBus } from '@editor/type';
import { SideItemKey } from '@editor/type'; import { SideItemKey } from '@editor/type';
defineOptions({ defineOptions({
@ -41,15 +42,15 @@ const props = withDefaults(defineProps<FieldProps<DataSourceSelect>>(), {
}); });
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const { dataSourceService, uiService } = inject<Services>('services') || {}; const { dataSourceService, uiService } = useServices();
const eventBus = inject<EventBus>('eventBus'); const eventBus = inject<EventBus>('eventBus');
const dataSources = computed(() => dataSourceService?.get('dataSources') || []); const dataSources = computed(() => dataSourceService.get('dataSources'));
const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props)); const notEditable = computed(() => filterFunction(mForm, props.config.notEditable, props));
const hasDataSourceSidePanel = computed(() => const hasDataSourceSidePanel = computed(() =>
(uiService?.get('sideBarItems') || []).find((item) => item.$key === SideItemKey.DATA_SOURCE), uiService.get('sideBarItems').find((item) => item.$key === SideItemKey.DATA_SOURCE),
); );
const selectConfig = computed<SelectConfig>(() => { const selectConfig = computed<SelectConfig>(() => {
@ -87,7 +88,7 @@ const editHandler = () => {
const id = typeof value === 'string' ? value : value.dataSourceId; const id = typeof value === 'string' ? value : value.dataSourceId;
const dataSource = dataSourceService?.getDataSourceById(id); const dataSource = dataSourceService.getDataSourceById(id);
if (!dataSource) return; if (!dataSource) return;

View File

@ -26,7 +26,7 @@ import {
MGroupList, MGroupList,
} from '@tmagic/form'; } from '@tmagic/form';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { getCascaderOptionsFromFields } from '@editor/utils'; import { getCascaderOptionsFromFields } from '@editor/utils';
defineOptions({ defineOptions({
@ -49,7 +49,7 @@ const props = withDefaults(
}, },
); );
const { dataSourceService } = inject<Services>('services') || {}; const { dataSourceService } = useServices();
const mForm = inject<FormState | undefined>('mForm'); const mForm = inject<FormState | undefined>('mForm');
const parentFields = computed(() => filterFunction<string[]>(mForm, props.config.parentFields, props) || []); const parentFields = computed(() => filterFunction<string[]>(mForm, props.config.parentFields, props) || []);
@ -71,7 +71,7 @@ const config = computed<GroupListConfig>(() => ({
type: 'cascader', type: 'cascader',
options: () => { options: () => {
const [dsId, ...keys] = parentFields.value; const [dsId, ...keys] = parentFields.value;
const ds = dataSourceService?.getDataSourceById(dsId); const ds = dataSourceService.getDataSourceById(dsId);
if (!ds) { if (!ds) {
return []; return [];
} }
@ -113,7 +113,7 @@ const config = computed<GroupListConfig>(() => ({
type: (mForm, { model }) => { type: (mForm, { model }) => {
const [id, ...fieldNames] = [...parentFields.value, ...model.field]; const [id, ...fieldNames] = [...parentFields.value, ...model.field];
const ds = dataSourceService?.getDataSourceById(id); const ds = dataSourceService.getDataSourceById(id);
let fields = ds?.fields || []; let fields = ds?.fields || [];
let type = ''; let type = '';

View File

@ -50,7 +50,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject } from 'vue'; import { computed } from 'vue';
import { Delete } from '@element-plus/icons-vue'; import { Delete } from '@element-plus/icons-vue';
import { has } from 'lodash-es'; import { has } from 'lodash-es';
@ -68,7 +68,8 @@ import type {
import { MContainer as MFormContainer, MPanel } from '@tmagic/form'; import { MContainer as MFormContainer, MPanel } from '@tmagic/form';
import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils'; import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX, traverseNode } from '@tmagic/utils';
import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { CodeSelectColConfig, DataSourceMethodSelectConfig, EventSelectConfig } from '@editor/type';
import { getCascaderOptionsFromFields } from '@editor/utils'; import { getCascaderOptionsFromFields } from '@editor/utils';
defineOptions({ defineOptions({
@ -81,12 +82,7 @@ const emit = defineEmits<{
change: [v: any, eventData?: ContainerChangeEventData]; change: [v: any, eventData?: ContainerChangeEventData];
}>(); }>();
const services = inject<Services>('services'); const { editorService, dataSourceService, eventsService, codeBlockService } = useServices();
const editorService = services?.editorService;
const dataSourceService = services?.dataSourceService;
const eventsService = services?.eventsService;
const codeBlockService = services?.codeBlockService;
// //
const eventNameConfig = computed(() => { const eventNameConfig = computed(() => {
@ -108,13 +104,11 @@ const eventNameConfig = computed(() => {
options: (mForm: FormState, { formValue }: any) => { options: (mForm: FormState, { formValue }: any) => {
let events: EventOption[] | CascaderOption[] = []; let events: EventOption[] | CascaderOption[] = [];
if (!eventsService || !dataSourceService) return events;
if (props.config.src === 'component') { if (props.config.src === 'component') {
events = eventsService.getEvent(formValue.type); events = eventsService.getEvent(formValue.type);
if (formValue.type === 'page-fragment-container' && formValue.pageFragmentId) { if (formValue.type === 'page-fragment-container' && formValue.pageFragmentId) {
const pageFragment = editorService?.get('root')?.items?.find((page) => page.id === formValue.pageFragmentId); const pageFragment = editorService.get('root')?.items?.find((page) => page.id === formValue.pageFragmentId);
if (pageFragment) { if (pageFragment) {
events = [ events = [
{ {
@ -185,7 +179,7 @@ const actionTypeConfig = computed(() => {
{ {
text: '代码', text: '代码',
label: '代码', label: '代码',
disabled: !Object.keys(codeBlockService?.getCodeDsl() || {}).length, disabled: !Object.keys(codeBlockService.getCodeDsl() || {}).length,
value: ActionType.CODE, value: ActionType.CODE,
}, },
{ {
@ -193,7 +187,7 @@ const actionTypeConfig = computed(() => {
label: '数据源', label: '数据源',
disabled: !dataSourceService disabled: !dataSourceService
?.get('dataSources') ?.get('dataSources')
?.filter((ds) => ds.methods?.length || dataSourceService?.getFormMethod(ds.type).length).length, ?.filter((ds) => ds.methods?.length || dataSourceService.getFormMethod(ds.type).length).length,
value: ActionType.DATA_SOURCE, value: ActionType.DATA_SOURCE,
}, },
], ],
@ -222,7 +216,7 @@ const compActionConfig = computed(() => {
name: 'method', name: 'method',
text: '动作', text: '动作',
type: (mForm, { model }: any) => { type: (mForm, { model }: any) => {
const to = editorService?.getNodeById(model.to); const to = editorService.getNodeById(model.to);
if (to && to.type === 'page-fragment-container' && to.pageFragmentId) { if (to && to.type === 'page-fragment-container' && to.pageFragmentId) {
return 'cascader'; return 'cascader';
@ -233,20 +227,20 @@ const compActionConfig = computed(() => {
checkStrictly: () => props.config.src !== 'component', checkStrictly: () => props.config.src !== 'component',
display: (mForm, { model }: any) => model.actionType === ActionType.COMP, display: (mForm, { model }: any) => model.actionType === ActionType.COMP,
options: (mForm: FormState, { model }: any) => { options: (mForm: FormState, { model }: any) => {
const node = editorService?.getNodeById(model.to); const node = editorService.getNodeById(model.to);
if (!node?.type) return []; if (!node?.type) return [];
let methods: EventOption[] | CascaderOption[] = []; let methods: EventOption[] | CascaderOption[] = [];
methods = eventsService?.getMethod(node.type) || []; methods = eventsService.getMethod(node.type);
if (node.type === 'page-fragment-container' && node.pageFragmentId) { if (node.type === 'page-fragment-container' && node.pageFragmentId) {
const pageFragment = editorService?.get('root')?.items?.find((page) => page.id === node.pageFragmentId); const pageFragment = editorService.get('root')?.items?.find((page) => page.id === node.pageFragmentId);
if (pageFragment) { if (pageFragment) {
methods = []; methods = [];
pageFragment.items.forEach((node: MComponent | MContainer) => { pageFragment.items.forEach((node: MComponent | MContainer) => {
traverseNode<MComponent | MContainer>(node, (node) => { traverseNode<MComponent | MContainer>(node, (node) => {
const nodeMethods = (node.type && eventsService?.getMethod(node.type)) || []; const nodeMethods = (node.type && eventsService.getMethod(node.type)) || [];
if (nodeMethods.length) { if (nodeMethods.length) {
methods.push({ methods.push({
@ -277,7 +271,7 @@ const codeActionConfig = computed(() => {
type: 'code-select-col', type: 'code-select-col',
text: '代码块', text: '代码块',
name: 'codeId', name: 'codeId',
notEditable: () => !codeBlockService?.getEditStatus(), notEditable: () => !codeBlockService.getEditStatus(),
display: (mForm, { model }) => model.actionType === ActionType.CODE, display: (mForm, { model }) => model.actionType === ActionType.CODE,
}; };
return { ...defaultCodeActionConfig, ...props.config.codeActionConfig }; return { ...defaultCodeActionConfig, ...props.config.codeActionConfig };
@ -289,7 +283,7 @@ const dataSourceActionConfig = computed(() => {
type: 'data-source-method-select', type: 'data-source-method-select',
text: '数据源方法', text: '数据源方法',
name: 'dataSourceMethod', name: 'dataSourceMethod',
notEditable: () => !services?.dataSourceService.get('editable'), notEditable: () => !dataSourceService.get('editable'),
display: (mForm, { model }) => model.actionType === ActionType.DATA_SOURCE, display: (mForm, { model }) => model.actionType === ActionType.DATA_SOURCE,
}; };
return { ...defaultDataSourceActionConfig, ...props.config.dataSourceActionConfig }; return { ...defaultDataSourceActionConfig, ...props.config.dataSourceActionConfig };
@ -305,7 +299,7 @@ const tableConfig = computed(() => ({
label: '事件名', label: '事件名',
type: eventNameConfig.value.type, type: eventNameConfig.value.type,
options: (mForm: FormState, { formValue }: any) => options: (mForm: FormState, { formValue }: any) =>
eventsService?.getEvent(formValue.type).map((option: any) => ({ eventsService.getEvent(formValue.type).map((option: any) => ({
text: option.label, text: option.label,
value: option.value, value: option.value,
})), })),
@ -320,10 +314,10 @@ const tableConfig = computed(() => ({
label: '动作', label: '动作',
type: compActionConfig.value.type, type: compActionConfig.value.type,
options: (mForm: FormState, { model }: any) => { options: (mForm: FormState, { model }: any) => {
const node = editorService?.getNodeById(model.to); const node = editorService.getNodeById(model.to);
if (!node?.type) return []; if (!node?.type) return [];
return eventsService?.getMethod(node.type).map((option: any) => ({ return eventsService.getMethod(node.type).map((option: any) => ({
text: option.label, text: option.label,
value: option.value, value: option.value,
})); }));

View File

@ -16,27 +16,28 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject } from 'vue'; import { computed } from 'vue';
import { Edit } from '@element-plus/icons-vue'; import { Edit } from '@element-plus/icons-vue';
import { Id, NodeType } from '@tmagic/core'; import { Id, NodeType } from '@tmagic/core';
import { FieldProps } from '@tmagic/form'; import { FieldProps } from '@tmagic/form';
import Icon from '@editor/components/Icon.vue'; import Icon from '@editor/components/Icon.vue';
import type { PageFragmentSelectConfig, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { PageFragmentSelectConfig } from '@editor/type';
defineOptions({ defineOptions({
name: 'MFieldsPageFragmentSelect', name: 'MFieldsPageFragmentSelect',
}); });
const services = inject<Services>('services'); const { editorService } = useServices();
const emit = defineEmits(['change']); const emit = defineEmits(['change']);
const props = withDefaults(defineProps<FieldProps<PageFragmentSelectConfig>>(), { const props = withDefaults(defineProps<FieldProps<PageFragmentSelectConfig>>(), {
disabled: false, disabled: false,
}); });
const pageList = computed(() => const pageList = computed(() =>
services?.editorService.get('root')?.items.filter((item) => item.type === NodeType.PAGE_FRAGMENT), editorService.get('root')?.items.filter((item) => item.type === NodeType.PAGE_FRAGMENT),
); );
const selectConfig = { const selectConfig = {
@ -58,6 +59,6 @@ const changeHandler = async () => {
}; };
const editPageFragment = (id: Id) => { const editPageFragment = (id: Id) => {
services?.editorService.select(id); editorService.select(id);
}; };
</script> </script>

View File

@ -50,7 +50,8 @@ import { TMagicButton, TMagicTooltip } from '@tmagic/design';
import type { FieldProps, FormItem, FormState } from '@tmagic/form'; import type { FieldProps, FormItem, FormState } from '@tmagic/form';
import { getIdFromEl } from '@tmagic/utils'; import { getIdFromEl } from '@tmagic/utils';
import { type Services, UI_SELECT_MODE_EVENT_NAME } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { UI_SELECT_MODE_EVENT_NAME } from '@editor/utils/const';
defineOptions({ defineOptions({
name: 'MFieldsUISelect', name: 'MFieldsUISelect',
@ -60,14 +61,14 @@ const props = defineProps<FieldProps<{ type: 'ui-select' } & FormItem>>();
const emit = defineEmits(['change']); const emit = defineEmits(['change']);
const services = inject<Services>('services'); const { editorService, uiService, stageOverlayService } = useServices();
const mForm = inject<FormState>('mForm'); const mForm = inject<FormState>('mForm');
const val = computed(() => props.model[props.name]); const val = computed(() => props.model[props.name]);
const uiSelectMode = ref(false); const uiSelectMode = ref(false);
const cancelHandler = () => { const cancelHandler = () => {
if (!services?.uiService) return; uiService.set('uiSelectMode', false);
services.uiService.set('uiSelectMode', false);
uiSelectMode.value = false; uiSelectMode.value = false;
globalThis.document.removeEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener); globalThis.document.removeEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener);
}; };
@ -89,13 +90,12 @@ const clickHandler = ({ detail }: Event & { detail: HTMLElement | MNode }) => {
}; };
const toName = computed(() => { const toName = computed(() => {
const config = services?.editorService.getNodeById(val.value); const config = editorService.getNodeById(val.value);
return config?.name || ''; return config?.name || '';
}); });
const startSelect = () => { const startSelect = () => {
if (!services?.uiService) return; uiService.set('uiSelectMode', true);
services.uiService.set('uiSelectMode', true);
uiSelectMode.value = true; uiSelectMode.value = true;
globalThis.document.addEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener); globalThis.document.addEventListener(UI_SELECT_MODE_EVENT_NAME, clickHandler as EventListener);
}; };
@ -109,24 +109,21 @@ const deleteHandler = () => {
}; };
const selectNode = async (id: Id) => { const selectNode = async (id: Id) => {
if (!services) return; await editorService.select(id);
await services.editorService.select(id); editorService.get('stage')?.select(id);
services.editorService.get('stage')?.select(id); stageOverlayService.get('stage')?.select(id);
services.stageOverlayService.get('stage')?.select(id);
}; };
const highlight = throttle((id: Id) => { const highlight = throttle((id: Id) => {
if (!services) return; editorService.highlight(id);
services.editorService.highlight(id); editorService.get('stage')?.highlight(id);
services.editorService.get('stage')?.highlight(id); stageOverlayService.get('stage')?.highlight(id);
services.stageOverlayService.get('stage')?.highlight(id);
}, 150); }, 150);
const unhighlight = () => { const unhighlight = () => {
if (!services) return; editorService.set('highlightNode', null);
services.editorService.set('highlightNode', null); editorService.get('stage')?.clearHighlight();
services.editorService.get('stage')?.clearHighlight(); stageOverlayService.get('stage')?.clearHighlight();
services.stageOverlayService.get('stage')?.clearHighlight();
}; };
</script> </script>

View File

@ -28,3 +28,4 @@ export * from './use-next-float-box-position';
export * from './use-node-status'; export * from './use-node-status';
export * from './use-stage'; export * from './use-stage';
export * from './use-window-rect'; export * from './use-window-rect';
export * from './use-services';

View File

@ -5,20 +5,15 @@ import type { CodeBlockContent } from '@tmagic/core';
import { tMagicMessage } from '@tmagic/design'; import { tMagicMessage } from '@tmagic/design';
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue'; import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
import type { CodeBlockService } from '@editor/services/codeBlock'; import type { Services } from '@editor/type';
export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => { export const useCodeBlockEdit = (codeBlockService: Services['codeBlockService']) => {
const codeConfig = ref<CodeBlockContent>(); const codeConfig = ref<CodeBlockContent>();
const codeId = ref<string>(); const codeId = ref<string>();
const codeBlockEditorRef = useTemplateRef<InstanceType<typeof CodeBlockEditor>>('codeBlockEditor'); const codeBlockEditorRef = useTemplateRef<InstanceType<typeof CodeBlockEditor>>('codeBlockEditor');
// 新增代码块 // 新增代码块
const createCodeBlock = async () => { const createCodeBlock = async () => {
if (!codeBlockService) {
tMagicMessage.error('新增代码块失败');
return;
}
codeConfig.value = { codeConfig.value = {
name: '', name: '',
content: `({app, params, flowState}) => {\n // place your code here\n}`, content: `({app, params, flowState}) => {\n // place your code here\n}`,
@ -34,7 +29,7 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
// 编辑代码块 // 编辑代码块
const editCode = async (id: string) => { const editCode = async (id: string) => {
const codeBlock = await codeBlockService?.getCodeContentById(id); const codeBlock = await codeBlockService.getCodeContentById(id);
if (!codeBlock) { if (!codeBlock) {
tMagicMessage.error('获取代码块内容失败'); tMagicMessage.error('获取代码块内容失败');
@ -59,13 +54,13 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
// 删除代码块 // 删除代码块
const deleteCode = async (key: string) => { const deleteCode = async (key: string) => {
codeBlockService?.deleteCodeDslByIds([key]); codeBlockService.deleteCodeDslByIds([key]);
}; };
const submitCodeBlockHandler = async (values: CodeBlockContent) => { const submitCodeBlockHandler = async (values: CodeBlockContent) => {
if (!codeId.value) return; if (!codeId.value) return;
await codeBlockService?.setCodeDslById(codeId.value, values); await codeBlockService.setCodeDslById(codeId.value, values);
codeBlockEditorRef.value?.hide(); codeBlockEditorRef.value?.hide();
}; };

View File

@ -4,20 +4,20 @@ import type { DataSourceSchema } from '@tmagic/core';
import type { ContainerChangeEventData } from '@tmagic/form'; import type { ContainerChangeEventData } from '@tmagic/form';
import DataSourceConfigPanel from '@editor/layouts/sidebar/data-source/DataSourceConfigPanel.vue'; import DataSourceConfigPanel from '@editor/layouts/sidebar/data-source/DataSourceConfigPanel.vue';
import type { DataSourceService } from '@editor/services/dataSource'; import type { Services } from '@editor/type';
export const useDataSourceEdit = (dataSourceService?: DataSourceService) => { export const useDataSourceEdit = (dataSourceService: Services['dataSourceService']) => {
const dialogTitle = ref(''); const dialogTitle = ref('');
const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>(); const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
const dataSourceValues = ref<Record<string, any>>({}); const dataSourceValues = ref<Record<string, any>>({});
const editable = computed(() => dataSourceService?.get('editable') ?? true); const editable = computed(() => dataSourceService.get('editable'));
const editHandler = (id: string) => { const editHandler = (id: string) => {
if (!editDialog.value) return; if (!editDialog.value) return;
dataSourceValues.value = { dataSourceValues.value = {
...dataSourceService?.getDataSourceById(id), ...(dataSourceService.getDataSourceById(id) || {}),
}; };
dialogTitle.value = `编辑${dataSourceValues.value.title || ''}`; dialogTitle.value = `编辑${dataSourceValues.value.title || ''}`;
@ -27,9 +27,9 @@ export const useDataSourceEdit = (dataSourceService?: DataSourceService) => {
const submitDataSourceHandler = (value: DataSourceSchema, eventData: ContainerChangeEventData) => { const submitDataSourceHandler = (value: DataSourceSchema, eventData: ContainerChangeEventData) => {
if (value.id) { if (value.id) {
dataSourceService?.update(value, { changeRecords: eventData.changeRecords }); dataSourceService.update(value, { changeRecords: eventData.changeRecords });
} else { } else {
dataSourceService?.add(value); dataSourceService.add(value);
} }
editDialog.value?.hide(); editDialog.value?.hide();

View File

@ -1,11 +1,11 @@
import { computed, inject, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import type { Services } from '@editor/type'; import { useServices } from './use-services';
export const useEditorContentHeight = () => { export const useEditorContentHeight = () => {
const services = inject<Services>('services'); const { uiService } = useServices();
const frameworkHeight = computed(() => services?.uiService.get('frameworkRect').height || 0); const frameworkHeight = computed(() => uiService.get('frameworkRect').height);
const navMenuHeight = computed(() => services?.uiService.get('navMenuRect').height || 0); const navMenuHeight = computed(() => uiService.get('navMenuRect').height);
const editorContentHeight = computed(() => frameworkHeight.value - navMenuHeight.value); const editorContentHeight = computed(() => frameworkHeight.value - navMenuHeight.value);
const height = ref(0); const height = ref(0);

View File

@ -1,6 +1,6 @@
import { computed, ComputedRef, inject, onBeforeUnmount, ref, watch } from 'vue'; import { computed, ComputedRef, onBeforeUnmount, ref, watch } from 'vue';
import type { Services } from '@editor/type'; import { useServices } from './use-services';
interface State { interface State {
status: boolean; status: boolean;
@ -9,7 +9,7 @@ interface State {
} }
export const useFloatBox = (slideKeys: ComputedRef<string[]>) => { export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
const services = inject<Services>('services'); const { uiService } = useServices();
const floatBoxStates = ref<{ const floatBoxStates = ref<{
[key in (typeof slideKeys.value)[number]]: State; [key in (typeof slideKeys.value)[number]]: State;
@ -57,10 +57,10 @@ export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
Math.abs(startOffset.x - e.clientX) > effectiveDistance || Math.abs(startOffset.x - e.clientX) > effectiveDistance ||
Math.abs(startOffset.y - e.clientY) > effectiveDistance Math.abs(startOffset.y - e.clientY) > effectiveDistance
) { ) {
const navMenuRect = services?.uiService?.get('navMenuRect'); const navMenuRect = uiService.get('navMenuRect');
floatBoxStates.value[key] = { floatBoxStates.value[key] = {
left: e.clientX, left: e.clientX,
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0), top: navMenuRect.top + navMenuRect.height,
status: true, status: true,
}; };
} }

View File

@ -1,24 +1,24 @@
import { Ref, ref } from 'vue'; import { type Ref, ref } from 'vue';
import { UiService } from '@editor/services/ui'; import type { Services } from '@editor/type';
export const useNextFloatBoxPosition = (uiService?: UiService, parent?: Ref<HTMLDivElement | null>) => { export const useNextFloatBoxPosition = (uiService: Services['uiService'], parent?: Ref<HTMLDivElement | null>) => {
const boxPosition = ref({ const boxPosition = ref({
left: 0, left: 0,
top: 0, top: 0,
}); });
const calcBoxPosition = () => { const calcBoxPosition = () => {
const columnWidth = uiService?.get('columnWidth'); const columnWidth = uiService.get('columnWidth');
const navMenuRect = uiService?.get('navMenuRect'); const navMenuRect = uiService.get('navMenuRect');
let left = columnWidth?.left ?? 0; let left = columnWidth.left ?? 0;
if (parent?.value) { if (parent?.value) {
const rect = parent?.value?.getBoundingClientRect(); const rect = parent.value.getBoundingClientRect();
left = (rect?.left ?? 0) + (rect?.width ?? 0); left = rect.left + rect.width;
} }
boxPosition.value = { boxPosition.value = {
left, left,
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0), top: navMenuRect.top + navMenuRect.height,
}; };
}; };

View File

@ -0,0 +1,13 @@
import { inject } from 'vue';
import type { Services } from '@editor/type';
export const useServices = () => {
const services = inject<Services>('services');
if (!services) {
throw new Error('services is required');
}
return services;
};

View File

@ -6,12 +6,8 @@ import { getIdFromEl } from '@tmagic/utils';
import editorService from '@editor/services/editor'; import editorService from '@editor/services/editor';
import uiService from '@editor/services/ui'; import uiService from '@editor/services/ui';
import { import type { StageOptions } from '@editor/type';
H_GUIDE_LINE_STORAGE_KEY, import { H_GUIDE_LINE_STORAGE_KEY, UI_SELECT_MODE_EVENT_NAME, V_GUIDE_LINE_STORAGE_KEY } from '@editor/utils/const';
StageOptions,
UI_SELECT_MODE_EVENT_NAME,
V_GUIDE_LINE_STORAGE_KEY,
} from '@editor/type';
import { getGuideLineFromCache } from '@editor/utils/editor'; import { getGuideLineFromCache } from '@editor/utils/editor';
const root = computed(() => editorService.get('root')); const root = computed(() => editorService.get('root'));

View File

@ -502,6 +502,7 @@ export const initServiceEvents = (
isModifyField = isModifyField =
changeRecord.propPath === 'fields' || changeRecord.propPath === 'fields' ||
/fields.(\d)+.name/.test(changeRecord.propPath) || /fields.(\d)+.name/.test(changeRecord.propPath) ||
/fields.(\d)+.defaultValue/.test(changeRecord.propPath) ||
/fields.(\d)+$/.test(changeRecord.propPath); /fields.(\d)+$/.test(changeRecord.propPath);
isModifyMock = changeRecord.propPath === 'mocks'; isModifyMock = changeRecord.propPath === 'mocks';
@ -523,25 +524,35 @@ export const initServiceEvents = (
if (Array.isArray(root?.items)) { if (Array.isArray(root?.items)) {
depService.clearIdleTasks(); depService.clearIdleTasks();
removeDataSourceTarget(config.id);
initDataSourceDepTarget(config);
let collectIdlePromises: Promise<void[]>[] = []; let collectIdlePromises: Promise<void[]>[] = [];
if (isModifyField) { if (isModifyField) {
depService.removeTarget(config.id, DepTargetType.DATA_SOURCE);
depService.removeTarget(config.id, DepTargetType.DATA_SOURCE_COND);
depService.addTarget(createDataSourceTarget(config, reactive({})));
depService.addTarget(createDataSourceCondTarget(config, reactive({})));
collectIdlePromises = [ collectIdlePromises = [
collectIdle(root.items, true, DepTargetType.DATA_SOURCE), collectIdle(root.items, true, DepTargetType.DATA_SOURCE),
collectIdle(root.items, true, DepTargetType.DATA_SOURCE_COND), collectIdle(root.items, true, DepTargetType.DATA_SOURCE_COND),
]; ];
} else if (isModifyMock) { } else if (isModifyMock) {
depService.removeTarget(config.id, DepTargetType.DATA_SOURCE);
depService.addTarget(createDataSourceTarget(config, reactive({})));
collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE)]; collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE)];
} else if (isModifyMethod) { } else if (isModifyMethod) {
depService.removeTarget(config.id, DepTargetType.DATA_SOURCE_METHOD);
depService.addTarget(createDataSourceMethodTarget(config, reactive({})));
collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE_METHOD)]; collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE_METHOD)];
} }
Promise.all(collectIdlePromises).then(() => { Promise.all(collectIdlePromises)
updateDataSourceSchema(); .then(() => updateDataSourceSchema())
updateDsData(); .then(() => updateDsData())
updateStageNodes(root.items); .then(() => updateStageNodes(root.items));
});
} }
} else if (root?.dataSources) { } else if (root?.dataSources) {
updateDsData(); updateDsData();
@ -616,20 +627,31 @@ export const initServiceEvents = (
} }
root.dataSourceCondDeps[target.id] = target.deps; root.dataSourceCondDeps[target.id] = target.deps;
} }
if (target.type === DepTargetType.DATA_SOURCE_METHOD) {
if (!root.dataSourceMethodDeps) {
root.dataSourceMethodDeps = {};
}
root.dataSourceMethodDeps[target.id] = target.deps;
}
}; };
const targetRemoveHandler = (id: string | number) => { const targetRemoveHandler = (id: string | number, type: DepTargetType) => {
const root = editorService.get('root'); const root = editorService.get('root');
if (!root) return; if (!root) return;
if (root.dataSourceDeps) { if (root.dataSourceDeps && type === DepTargetType.DATA_SOURCE) {
delete root.dataSourceDeps[id]; delete root.dataSourceDeps[id];
} }
if (root.dataSourceCondDeps) { if (root.dataSourceCondDeps && type === DepTargetType.DATA_SOURCE_COND) {
delete root.dataSourceCondDeps[id]; delete root.dataSourceCondDeps[id];
} }
if (root.dataSourceMethodDeps && type === DepTargetType.DATA_SOURCE_METHOD) {
delete root.dataSourceMethodDeps[id];
}
}; };
depService.on('add-target', targetAddHandler); depService.on('add-target', targetAddHandler);

View File

@ -19,13 +19,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { inject, toRaw } from 'vue'; import { toRaw } from 'vue';
import { Plus } from '@element-plus/icons-vue'; import { Plus } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core'; import { NodeType } from '@tmagic/core';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { generatePageNameByApp } from '@editor/utils'; import { generatePageNameByApp } from '@editor/utils';
defineOptions({ defineOptions({
@ -36,13 +36,9 @@ defineProps<{
disabledPageFragment: boolean; disabledPageFragment: boolean;
}>(); }>();
const services = inject<Services>('services'); const { editorService } = useServices();
const clickHandler = (type: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => { const clickHandler = (type: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => {
const { editorService } = services || {};
if (!editorService) return;
const root = toRaw(editorService.get('root')); const root = toRaw(editorService.get('root'));
if (!root) throw new Error('root 不能为空'); if (!root) throw new Error('root 不能为空');

View File

@ -19,10 +19,10 @@
right-class="m-editor-framework-right" right-class="m-editor-framework-right"
:left="columnWidth.left" :left="columnWidth.left"
:right="columnWidth.right" :right="columnWidth.right"
:min-left="200" :min-left="MIN_LEFT_COLUMN_WIDTH"
:min-right="300" :min-right="MIN_RIGHT_COLUMN_WIDTH"
:min-center="400" :min-center="MIN_CENTER_COLUMN_WIDTH"
:width="frameworkRect?.width || 0" :width="frameworkRect.width"
@change="columnWidthChange" @change="columnWidthChange"
> >
<template #left> <template #left>
@ -65,11 +65,16 @@ import { computed, inject, onBeforeUnmount, onMounted, useTemplateRef, watch } f
import type { MPage, MPageFragment } from '@tmagic/core'; import type { MPage, MPageFragment } from '@tmagic/core';
import SplitView from '@editor/components/SplitView.vue'; import SplitView from '@editor/components/SplitView.vue';
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { Protocol } from '@editor/services/storage';
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions } from '@editor/type';
import { getEditorConfig } from '@editor/utils/config'; import { getEditorConfig } from '@editor/utils/config';
import { import {
DEFAULT_LEFT_COLUMN_WIDTH, DEFAULT_LEFT_COLUMN_WIDTH,
LEFT_COLUMN_WIDTH_STORAGE_KEY, LEFT_COLUMN_WIDTH_STORAGE_KEY,
MIN_CENTER_COLUMN_WIDTH,
MIN_LEFT_COLUMN_WIDTH,
MIN_RIGHT_COLUMN_WIDTH,
RIGHT_COLUMN_WIDTH_STORAGE_KEY, RIGHT_COLUMN_WIDTH_STORAGE_KEY,
} from '@editor/utils/const'; } from '@editor/utils/const';
@ -90,53 +95,48 @@ defineProps<{
}>(); }>();
const codeOptions = inject('codeOptions', {}); const codeOptions = inject('codeOptions', {});
const { editorService, uiService } = inject<Services>('services') || {}; const { editorService, uiService, storageService } = useServices();
const contentEl = useTemplateRef<HTMLDivElement>('content'); const contentEl = useTemplateRef<HTMLDivElement>('content');
const splitViewRef = useTemplateRef<InstanceType<typeof SplitView>>('splitView'); const splitViewRef = useTemplateRef<InstanceType<typeof SplitView>>('splitView');
const root = computed(() => editorService?.get('root')); const root = computed(() => editorService.get('root'));
const page = computed(() => editorService?.get('page')); const page = computed(() => editorService.get('page'));
const pageLength = computed(() => editorService?.get('pageLength') || 0); const pageLength = computed(() => editorService.get('pageLength') || 0);
const showSrc = computed(() => uiService?.get('showSrc')); const showSrc = computed(() => uiService.get('showSrc'));
const columnWidth = computed( const columnWidth = computed(() => uiService.get('columnWidth'));
() =>
uiService?.get('columnWidth') || {
left: 0,
center: 0,
right: 0,
},
);
watch(pageLength, () => { watch(pageLength, () => {
splitViewRef.value?.updateWidth(); splitViewRef.value?.updateWidth();
}); });
watch( watch(
() => uiService?.get('hideSlideBar'), () => uiService.get('hideSlideBar'),
(hideSlideBar) => { (hideSlideBar) => {
uiService?.set('columnWidth', { uiService.set('columnWidth', {
...columnWidth.value, ...columnWidth.value,
left: hideSlideBar left: hideSlideBar
? 0 ? 0
: Number(globalThis.localStorage.getItem(LEFT_COLUMN_WIDTH_STORAGE_KEY)) || DEFAULT_LEFT_COLUMN_WIDTH, : storageService.getItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, { protocol: Protocol.NUMBER }) ||
DEFAULT_LEFT_COLUMN_WIDTH,
}); });
}, },
); );
const columnWidthChange = (columnW: GetColumnWidth) => { const columnWidthChange = (columnW: GetColumnWidth) => {
globalThis.localStorage.setItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, `${columnW.left}`); storageService.setItem(LEFT_COLUMN_WIDTH_STORAGE_KEY, columnW.left, { protocol: Protocol.NUMBER });
globalThis.localStorage.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, `${columnW.right}`); storageService.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, columnW.right, { protocol: Protocol.NUMBER });
uiService?.set('columnWidth', columnW);
uiService.set('columnWidth', columnW);
}; };
const frameworkRect = computed(() => uiService?.get('frameworkRect')); const frameworkRect = computed(() => uiService.get('frameworkRect'));
const resizerObserver = new ResizeObserver((entries) => { const resizerObserver = new ResizeObserver((entries) => {
const { contentRect } = entries[0]; const { contentRect } = entries[0];
uiService?.set('frameworkRect', { uiService.set('frameworkRect', {
width: contentRect.width, width: contentRect.width,
height: contentRect.height, height: contentRect.height,
left: contentRect.left, left: contentRect.left,
@ -157,7 +157,7 @@ onBeforeUnmount(() => {
const saveCode = (value: string) => { const saveCode = (value: string) => {
try { try {
const parseDSL = getEditorConfig('parseDSL'); const parseDSL = getEditorConfig('parseDSL');
editorService?.set('root', parseDSL(value)); editorService.set('root', parseDSL(value));
} catch (e: any) { } catch (e: any) {
console.error(e); console.error(e);
} }

View File

@ -7,13 +7,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, markRaw, onBeforeUnmount, onMounted, useTemplateRef } from 'vue'; import { computed, markRaw, onBeforeUnmount, onMounted, useTemplateRef } from 'vue';
import { Back, Delete, FullScreen, Grid, Memo, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons-vue'; import { Back, Delete, FullScreen, Grid, Memo, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core'; import { NodeType } from '@tmagic/core';
import ToolButton from '@editor/components/ToolButton.vue'; import ToolButton from '@editor/components/ToolButton.vue';
import { ColumnLayout, MenuBarData, MenuButton, MenuComponent, MenuItem, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { ColumnLayout, MenuBarData, MenuButton, MenuComponent, MenuItem } from '@editor/type';
defineOptions({ defineOptions({
name: 'MEditorNavMenu', name: 'MEditorNavMenu',
@ -30,15 +31,14 @@ const props = withDefaults(
}, },
); );
const services = inject<Services>('services'); const { uiService, editorService, historyService } = useServices();
const uiService = services?.uiService;
const columnWidth = computed(() => services?.uiService.get('columnWidth')); const columnWidth = computed(() => uiService.get('columnWidth'));
const keys = Object.values(ColumnLayout); const keys = Object.values(ColumnLayout);
const showGuides = computed((): boolean => uiService?.get('showGuides') ?? true); const showGuides = computed((): boolean => uiService.get('showGuides'));
const showRule = computed((): boolean => uiService?.get('showRule') ?? true); const showRule = computed((): boolean => uiService.get('showRule'));
const zoom = computed((): number => uiService?.get('zoom') ?? 1); const zoom = computed((): number => uiService.get('zoom'));
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase()); const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
const ctrl = isMac ? 'Command' : 'Ctrl'; const ctrl = isMac ? 'Command' : 'Ctrl';
@ -70,10 +70,10 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
className: 'delete', className: 'delete',
icon: markRaw(Delete), icon: markRaw(Delete),
tooltip: `刪除(Delete)`, tooltip: `刪除(Delete)`,
disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE, disabled: () => editorService.get('node')?.type === NodeType.PAGE,
handler: () => { handler: () => {
const node = services?.editorService.get('node'); const node = editorService.get('node');
node && services?.editorService.remove(node); node && editorService.remove(node);
}, },
}); });
break; break;
@ -83,8 +83,8 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
className: 'undo', className: 'undo',
icon: markRaw(Back), icon: markRaw(Back),
tooltip: `后退(${ctrl}+z)`, tooltip: `后退(${ctrl}+z)`,
disabled: () => !services?.historyService.state.canUndo, disabled: () => !historyService.state.canUndo,
handler: () => services?.editorService.undo(), handler: () => editorService.undo(),
}); });
break; break;
case 'redo': case 'redo':
@ -93,8 +93,8 @@ const getConfig = (item: MenuItem): (MenuButton | MenuComponent)[] => {
className: 'redo', className: 'redo',
icon: markRaw(Right), icon: markRaw(Right),
tooltip: `前进(${ctrl}+Shift+z)`, tooltip: `前进(${ctrl}+Shift+z)`,
disabled: () => !services?.historyService.state.canRedo, disabled: () => !historyService.state.canRedo,
handler: () => services?.editorService.redo(), handler: () => editorService.redo(),
}); });
break; break;
case 'zoom-in': case 'zoom-in':
@ -184,7 +184,7 @@ const navMenuEl = useTemplateRef<HTMLDivElement>('navMenu');
const resizeObserver = new ResizeObserver(() => { const resizeObserver = new ResizeObserver(() => {
const rect = navMenuEl.value?.getBoundingClientRect(); const rect = navMenuEl.value?.getBoundingClientRect();
if (rect) { if (rect) {
uiService?.set('navMenuRect', { uiService.set('navMenuRect', {
left: rect.left, left: rect.left,
top: rect.top, top: rect.top,
width: rect.width, width: rect.width,

View File

@ -33,7 +33,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, toRaw } from 'vue'; import { computed, toRaw } from 'vue';
import { Plus } from '@element-plus/icons-vue'; import { Plus } from '@element-plus/icons-vue';
import { NodeType } from '@tmagic/core'; import { NodeType } from '@tmagic/core';
@ -41,21 +41,18 @@ import { TMagicPopover } from '@tmagic/design';
import Icon from '@editor/components/Icon.vue'; import Icon from '@editor/components/Icon.vue';
import ToolButton from '@editor/components/ToolButton.vue'; import ToolButton from '@editor/components/ToolButton.vue';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { generatePageNameByApp } from '@editor/utils/editor'; import { generatePageNameByApp } from '@editor/utils/editor';
defineOptions({ defineOptions({
name: 'MEditorPageBarAddButton', name: 'MEditorPageBarAddButton',
}); });
const services = inject<Services>('services'); const { editorService, uiService } = useServices();
const uiService = services?.uiService;
const editorService = services?.editorService;
const showAddPageButton = computed(() => uiService?.get('showAddPageButton')); const showAddPageButton = computed(() => uiService.get('showAddPageButton'));
const addPage = (type: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => { const addPage = (type: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => {
if (!editorService) return;
const root = toRaw(editorService.get('root')); const root = toRaw(editorService.get('root'));
if (!root) throw new Error('root 不能为空'); if (!root) throw new Error('root 不能为空');
const pageConfig = { const pageConfig = {

View File

@ -68,14 +68,15 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, ref, useTemplateRef, watch } from 'vue'; import { computed, ref, useTemplateRef, watch } from 'vue';
import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue'; import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
import { type Id, type MPage, type MPageFragment, NodeType } from '@tmagic/core'; import { type Id, type MPage, type MPageFragment, NodeType } from '@tmagic/core';
import { TMagicIcon, TMagicPopover } from '@tmagic/design'; import { TMagicIcon, TMagicPopover } from '@tmagic/design';
import ToolButton from '@editor/components/ToolButton.vue'; import ToolButton from '@editor/components/ToolButton.vue';
import type { PageBarSortOptions, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { PageBarSortOptions } from '@editor/type';
import AddButton from './AddButton.vue'; import AddButton from './AddButton.vue';
import PageBarScrollContainer from './PageBarScrollContainer.vue'; import PageBarScrollContainer from './PageBarScrollContainer.vue';
@ -97,11 +98,10 @@ const props = withDefaults(
}, },
); );
const services = inject<Services>('services'); const { editorService } = useServices();
const editorService = services?.editorService;
const root = computed(() => editorService?.get('root')); const root = computed(() => editorService.get('root'));
const page = computed(() => editorService?.get('page')); const page = computed(() => editorService.get('page'));
const query = ref<{ const query = ref<{
pageType: NodeType[]; pageType: NodeType[];
@ -129,19 +129,19 @@ const list = computed(() => {
}); });
const switchPage = (id: Id) => { const switchPage = (id: Id) => {
editorService?.select(id); editorService.select(id);
}; };
const copy = (node: MPage | MPageFragment) => { const copy = (node: MPage | MPageFragment) => {
node && editorService?.copy(node); node && editorService.copy(node);
editorService?.paste({ editorService.paste({
left: 0, left: 0,
top: 0, top: 0,
}); });
}; };
const remove = (node: MPage | MPageFragment) => { const remove = (node: MPage | MPageFragment) => {
editorService?.remove(node); editorService.remove(node);
}; };
const pageBarScrollContainerRef = useTemplateRef<InstanceType<typeof PageBarScrollContainer>>('pageBarScrollContainer'); const pageBarScrollContainerRef = useTemplateRef<InstanceType<typeof PageBarScrollContainer>>('pageBarScrollContainer');

View File

@ -24,14 +24,15 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue'; import { computed, nextTick, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue'; import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
import Sortable, { type SortableEvent } from 'sortablejs'; import Sortable, { type SortableEvent } from 'sortablejs';
import type { Id } from '@tmagic/core'; import type { Id } from '@tmagic/core';
import Icon from '@editor/components/Icon.vue'; import Icon from '@editor/components/Icon.vue';
import type { PageBarSortOptions, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import type { PageBarSortOptions } from '@editor/type';
defineOptions({ defineOptions({
name: 'MEditorPageBarScrollContainer', name: 'MEditorPageBarScrollContainer',
@ -42,15 +43,13 @@ const props = defineProps<{
length: number; length: number;
}>(); }>();
const services = inject<Services>('services'); const { editorService, uiService } = useServices();
const editorService = services?.editorService;
const uiService = services?.uiService;
const itemsContainerEl = useTemplateRef<HTMLElement>('itemsContainer'); const itemsContainerEl = useTemplateRef<HTMLElement>('itemsContainer');
const canScroll = ref(false); const canScroll = ref(false);
const showAddPageButton = computed(() => uiService?.get('showAddPageButton')); const showAddPageButton = computed(() => uiService.get('showAddPageButton'));
const showPageListButton = computed(() => uiService?.get('showPageListButton')); const showPageListButton = computed(() => uiService.get('showPageListButton'));
const itemsContainerWidth = ref(0); const itemsContainerWidth = ref(0);
@ -145,7 +144,7 @@ watch(
beforeDragList = sortable.toArray(); beforeDragList = sortable.toArray();
}, },
onUpdate: async (event: SortableEvent) => { onUpdate: async (event: SortableEvent) => {
await editorService?.sort( await editorService.sort(
beforeDragList[event.oldIndex as number], beforeDragList[event.oldIndex as number],
beforeDragList[event.newIndex as number], beforeDragList[event.newIndex as number],
); );

View File

@ -35,14 +35,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, inject } from 'vue'; import { computed } from 'vue';
import { Files } from '@element-plus/icons-vue'; import { Files } from '@element-plus/icons-vue';
import { Id, MPage, MPageFragment } from '@tmagic/core'; import { Id, MPage, MPageFragment } from '@tmagic/core';
import { TMagicIcon, TMagicPopover } from '@tmagic/design'; import { TMagicIcon, TMagicPopover } from '@tmagic/design';
import ToolButton from '@editor/components/ToolButton.vue'; import ToolButton from '@editor/components/ToolButton.vue';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
defineOptions({ defineOptions({
name: 'MEditorPageList', name: 'MEditorPageList',
}); });
@ -51,13 +51,11 @@ defineProps<{
list: (MPage | MPageFragment)[]; list: (MPage | MPageFragment)[];
}>(); }>();
const services = inject<Services>('services'); const { editorService, uiService } = useServices();
const uiService = services?.uiService;
const editorService = services?.editorService;
const showPageListButton = computed(() => uiService?.get('showPageListButton')); const showPageListButton = computed(() => uiService.get('showPageListButton'));
const page = computed(() => editorService?.get('page')); const page = computed(() => editorService.get('page'));
const switchPage = async (id: Id) => { const switchPage = async (id: Id) => {
await editorService?.select(id); await editorService.select(id);
}; };
</script> </script>

View File

@ -51,7 +51,7 @@ import { MForm } from '@tmagic/form';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height'; import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
import type { Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import CodeEditor from '../CodeEditor.vue'; import CodeEditor from '../CodeEditor.vue';
@ -80,13 +80,15 @@ const emit = defineEmits<{
mounted: [internalInstance: any]; mounted: [internalInstance: any];
}>(); }>();
const services = inject<Services>('services'); const services = useServices();
const { editorService, uiService } = services;
const codeOptions = inject('codeOptions', {}); const codeOptions = inject('codeOptions', {});
const showSrc = ref(false); const showSrc = ref(false);
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small'); const propsPanelSize = computed(() => uiService.get('propsPanelSize') || 'small');
const { height: editorContentHeight } = useEditorContentHeight(); const { height: editorContentHeight } = useEditorContentHeight();
const stage = computed(() => services?.editorService.get('stage')); const stage = computed(() => editorService.get('stage'));
const configFormRef = useTemplateRef<InstanceType<typeof MForm>>('configForm'); const configFormRef = useTemplateRef<InstanceType<typeof MForm>>('configForm');

View File

@ -34,7 +34,9 @@
<div class="m-editor-props-style-panel-title"> <div class="m-editor-props-style-panel-title">
<span>样式</span> <span>样式</span>
<div> <div>
<TMagicButton link size="small" @click="closeStylePanelHandler"><MIcon :icon="Close"></MIcon></TMagicButton> <TMagicButton link size="small" @click="toggleStylePanel(false)"
><MIcon :icon="Close"></MIcon
></TMagicButton>
</div> </div>
</div> </div>
</template> </template>
@ -44,7 +46,7 @@
v-if="showStylePanelToggleButton && !showStylePanel" v-if="showStylePanelToggleButton && !showStylePanel"
class="m-editor-props-panel-style-icon" class="m-editor-props-panel-style-icon"
circle circle
@click="showStylePanelHandler" @click="toggleStylePanel(true)"
> >
<MIcon :icon="Sugar"></MIcon> <MIcon :icon="Sugar"></MIcon>
</TMagicButton> </TMagicButton>
@ -52,7 +54,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, onBeforeUnmount, ref, useTemplateRef, watch, watchEffect } from 'vue'; import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef, watch, watchEffect } from 'vue';
import { Close, Sugar } from '@element-plus/icons-vue'; import { Close, Sugar } from '@element-plus/icons-vue';
import type { OnDrag } from 'gesto'; import type { OnDrag } from 'gesto';
@ -63,9 +65,11 @@ import { setValueByKeyPath } from '@tmagic/utils';
import MIcon from '@editor/components/Icon.vue'; import MIcon from '@editor/components/Icon.vue';
import Resizer from '@editor/components/Resizer.vue'; import Resizer from '@editor/components/Resizer.vue';
import type { PropsPanelSlots, Services } from '@editor/type'; import { useServices } from '@editor/hooks/use-services';
import { Protocol } from '@editor/services/storage';
import type { PropsPanelSlots } from '@editor/type';
import { styleTabConfig } from '@editor/utils'; import { styleTabConfig } from '@editor/utils';
import { RIGHT_COLUMN_WIDTH_STORAGE_KEY } from '@editor/utils/const'; import { PROPS_PANEL_WIDTH_STORAGE_KEY } from '@editor/utils/const';
import FormPanel from './FormPanel.vue'; import FormPanel from './FormPanel.vue';
import { useStylePanel } from './use-style-panel'; import { useStylePanel } from './use-style-panel';
@ -87,13 +91,13 @@ const emit = defineEmits<{
mounted: [internalInstance: InstanceType<typeof FormPanel>]; mounted: [internalInstance: InstanceType<typeof FormPanel>];
}>(); }>();
const services = inject<Services>('services'); const { editorService, uiService, propsService, storageService } = useServices();
const values = ref<FormValue>({}); const values = ref<FormValue>({});
// tsFormConfig any // tsFormConfig any
const curFormConfig = ref<any>([]); const curFormConfig = ref<any>([]);
const node = computed(() => services?.editorService.get('node')); const node = computed(() => editorService.get('node'));
const nodes = computed(() => services?.editorService.get('nodes') || []); const nodes = computed(() => editorService.get('nodes'));
const styleFormConfig = [ const styleFormConfig = [
{ {
@ -109,15 +113,15 @@ const init = async () => {
} }
const type = node.value.type || (node.value.items ? 'container' : 'text'); const type = node.value.type || (node.value.items ? 'container' : 'text');
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || []; curFormConfig.value = await propsService.getPropsConfig(type);
values.value = node.value; values.value = node.value;
}; };
watchEffect(init); watchEffect(init);
services?.propsService.on('props-configs-change', init); propsService.on('props-configs-change', init);
onBeforeUnmount(() => { onBeforeUnmount(() => {
services?.propsService.off('props-configs-change', init); propsService.off('props-configs-change', init);
}); });
const submit = async (v: MNode, eventData?: ContainerChangeEventData) => { const submit = async (v: MNode, eventData?: ContainerChangeEventData) => {
@ -145,7 +149,7 @@ const submit = async (v: MNode, eventData?: ContainerChangeEventData) => {
}); });
} }
services?.editorService.update(newValue, { changeRecords: eventData?.changeRecords }); editorService.update(newValue, { changeRecords: eventData?.changeRecords });
} catch (e: any) { } catch (e: any) {
emit('submit-error', e); emit('submit-error', e);
} }
@ -162,8 +166,16 @@ const mountedHandler = () => {
}; };
const propsPanelEl = useTemplateRef('propsPanel'); const propsPanelEl = useTemplateRef('propsPanel');
const propsPanelWidth = ref(
storageService.getItem(PROPS_PANEL_WIDTH_STORAGE_KEY, { protocol: Protocol.NUMBER }) || 300,
);
onMounted(() => {
propsPanelEl.value?.style.setProperty('--props-style-panel-width', `${Math.max(propsPanelWidth.value, 0)}px`);
});
const widthChange = ({ deltaX }: OnDrag) => { const widthChange = ({ deltaX }: OnDrag) => {
if (!propsPanelEl.value || !services) { if (!propsPanelEl.value) {
return; return;
} }
@ -172,47 +184,25 @@ const widthChange = ({ deltaX }: OnDrag) => {
); );
let value = width - deltaX; let value = width - deltaX;
if (value > services.uiService.get('columnWidth').right) { if (value > uiService.get('columnWidth').right) {
value = services.uiService.get('columnWidth').right - 40; value = uiService.get('columnWidth').right - 40;
} }
propsPanelEl.value.style.setProperty('--props-style-panel-width', `${value}px`); propsPanelWidth.value = Math.max(value, 0);
}; };
const { showStylePanel, showStylePanelToggleButton, showStylePanelHandler, closeStylePanelHandler } = watch(propsPanelWidth, (value) => {
useStylePanel(services); propsPanelEl.value?.style.setProperty('--props-style-panel-width', `${value}px`);
storageService.setItem(PROPS_PANEL_WIDTH_STORAGE_KEY, value, { protocol: Protocol.NUMBER });
watch(showStylePanel, (showStylePanel) => {
if (!propsPanelEl.value || !services) {
return;
}
const columnWidth = {
...services.uiService.get('columnWidth'),
};
const width = globalThis.parseFloat(
getComputedStyle(propsPanelEl.value).getPropertyValue('--props-style-panel-width'),
);
if (showStylePanel) {
columnWidth.right += width;
columnWidth.center -= width;
} else {
columnWidth.right -= width;
columnWidth.center += width;
}
if (columnWidth.center < 0) {
columnWidth.right = columnWidth.right + columnWidth.center - 400;
columnWidth.center = 400;
propsPanelEl.value.style.setProperty('--props-style-panel-width', `${columnWidth.right / 2}px`);
}
globalThis.localStorage.setItem(RIGHT_COLUMN_WIDTH_STORAGE_KEY, `${columnWidth.right}`);
services.uiService.set('columnWidth', columnWidth);
}); });
const { showStylePanel, showStylePanelToggleButton, toggleStylePanel } = useStylePanel(
{
storageService,
uiService,
},
propsPanelWidth,
);
const propertyFormPanelRef = useTemplateRef<InstanceType<typeof FormPanel>>('propertyFormPanel'); const propertyFormPanelRef = useTemplateRef<InstanceType<typeof FormPanel>>('propertyFormPanel');
defineExpose({ defineExpose({
getFormState() { getFormState() {

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