diff --git a/.gitignore b/.gitignore index 3d706bf102b463746dafe46263172fa348e92153..0c76a4b37117ac56270bb5a2d9e4d742d1ebc4f5 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,10 @@ dist-ssr /packages/ui-vue/theme-default /packages/mobile-ui-vue/package /packages/mobile-command-services/package +/packages/mobile-command-services/dist +/packages/mobile-command-services/dist-rollup +/packages/mobile-render/publish /packages/expression-engine/package /packages/expression-engine/dist-rollup /publish.sh -/packages/mobile-command-services/dist + diff --git a/.ls-lint.yml b/.ls-lint.yml index 01db09cca21cc29fac5503fd7bea6ca2f344102e..ac8a63998db51b447e9463922f967cf38cd8b4ec 100644 --- a/.ls-lint.yml +++ b/.ls-lint.yml @@ -3,9 +3,9 @@ ls: # .dir: kebab-case | regex:__[A-Za-z0-9]+__ .scss: kebab-case .vue: kebab-case - .js: kebab-case - .ts: kebab-case - .tsx: kebab-case + .js: kebab-case + .ts: kebab-case + .tsx: kebab-case .component.tsx: kebab-case .design.component.tsx: kebab-case .item.component.tsx: kebab-case @@ -101,6 +101,7 @@ ignore: - packages/mobile-command-services/.vscode - packages/mobile-command-services/node_modules - packages/mobile-command-services/dist + - packages/mobile-command-services/dist-rollup - packages/mobile-command-services/public - packages/mobile-command-services/src - packages/mobile-command-services/lib @@ -138,10 +139,20 @@ ignore: - packages/code-editor/package - packages/code-editor/demos - packages/code-editor/types + # charts-vue + - packages/charts-vue/.vscode + - packages/charts-vue/docs/.vitepress + - packages/charts-vue/node_modules + - packages/charts-vue/dist + - packages/charts-vue/public + - packages/charts-vue/docs + - packages/charts-vue/package + - packages/charts-vue/demos # admin - packages/farris-admin/node_modules # mobile-render - packages/mobile-render/dist - packages/mobile-render/node_modules - - packages/mobile-render/public + - packages/mobile-render/public/** + - packages/mobile-render/publish/** - packages/mobile-render/scripts diff --git a/eslint.config.mjs b/eslint.config.mjs index 3512846a8e77e9080fbd3b5950079571eca86805..11e490e2e96029e198b38ab49586702272fc742b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -23,11 +23,14 @@ export default [ "packages/**/package/*", "packages/ui-vue/docs/*", "packages/ui-vue/demos/*", + "packages/charts-vue/docs/*", + "packages/charts-vue/demos/*", "packages/farris-theme/*", "packages/f-theme-editor-project/*", "packages/mobile-ui-vue/docs/*", "packages/devkit/public/*", - "packages/mobile-command-services/public/*" + "packages/mobile-command-services/public/*", + "packages/mobile-render/publish/*" ] }, { @@ -134,7 +137,8 @@ export default [ 'vue/require-default-prop': 'off', 'vue/no-unused-components': 'off', 'vue/multi-word-component-names': 'off', - 'vue/return-in-computed-property': 'off' + 'vue/return-in-computed-property': 'off', + 'vue/no-ref-as-operand': 'off' } }, // { plugins: { farrislint }, rules: { "prefer-object-spread": "off" } } diff --git a/how_to_develop_component.md b/how_to_develop_component.md index 32ce3b98364ab8805bdeb5c668d76f80ce63026c..a4aee81ae28e944cd951284f7137d5495a3bc59f 100644 --- a/how_to_develop_component.md +++ b/how_to_develop_component.md @@ -420,7 +420,7 @@ export { componentMap, componentPropsConverter }; { "id": "HtmlTemplate", "type": "HtmlTemplate", - "name": "模版容器", + "name": "模板容器", "category": "container" }, { diff --git a/lerna.json b/lerna.json index c12be3c6dc4f48455ddfbda1f9c879e991fd0d94..1ba83d1ac88cd3e332b1fb685698d0f5a1e85929 100644 --- a/lerna.json +++ b/lerna.json @@ -6,6 +6,7 @@ "packages/farris-theme", "packages/renderer", "packages/ui-vue", + "packages/charts-vue", "packages/mobile-ui-vue", "packages/devkit", "packages/designer", diff --git a/package.json b/package.json index b390fc74c90d812384bc12b60a951314f5f732e9..d919dcf236a1cb63423d03f8a38ff9576ec17a97 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "prepare": "husky install", "farrisTheme": "node ./packages/f-theme-editor-project/farris-themebuilder-service/farris.js", "docs:dev": "pnpm --filter ui-vue run docs:dev", + "charts-docs:dev": "pnpm --filter charts-vue run docs:dev", "designer": "pnpm --filter designer run dev", "demo": "pnpm --filter ui-vue dev", "build:devkit-vue": "pnpm --filter devkit-vue run build:lib && pnpm --filter devkit-vue run rollup", @@ -14,6 +15,7 @@ "build:ui-binding-vue": "pnpm --filter ui-binding-vue run build:lib && pnpm --filter ui-binding-vue run rollup", "build:renderer": "pnpm --filter renderer run build:system", "build:ui-vue": "pnpm --filter ui-vue run build:lib && pnpm --filter ui-vue run build:system", + "build:charts-vue": "pnpm --filter charts-vue run build:lib && pnpm --filter charts-vue run build:system", "build:designer": "pnpm --filter designer run build:system", "build:expression-engine-vue": "pnpm --filter expression-engine-vue run rollup", "admin": "pnpm --filter farris-admin dev", @@ -25,109 +27,111 @@ "pnpm": ">=9" }, "devDependencies": { - "@babel/parser": "^7.19.0", - "@babel/preset-env": "^7.19.0", - "@babel/preset-typescript": "^7.18.0", - "@babel/traverse": "^7.19.0", - "@changesets/cli": "^2.27.7", - "@commitlint/cli": "^19.3.0", - "@commitlint/config-conventional": "^19.2.0", - "@docsearch/css": "^3.6.0", + "@babel/parser": "^7.28.0", + "@babel/preset-env": "^7.28.0", + "@babel/preset-typescript": "^7.27.1", + "@babel/traverse": "^7.28.0", + "@changesets/cli": "^2.29.5", + "@commitlint/cli": "^19.8.1", + "@commitlint/config-conventional": "^19.8.1", + "@docsearch/css": "^3.9.0", "@farris/designer-dragula": "0.0.5", "@jest/globals": "^29.7.0", - "@ls-lint/ls-lint": "^2.2.0", + "@ls-lint/ls-lint": "^2.3.1", "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-node-resolve": "^15.2.3", - "@testing-library/vue": "^8.0.0", + "@rollup/plugin-node-resolve": "^15.3.1", + "@testing-library/vue": "^8.1.0", "@types/crypto-js": "^4.2.2", - "@types/echarts": "^4.1.11", - "@types/jasmine": "~5.1.4", - "@types/jasminewd2": "~2.0.3", - "@types/jest": "^29.5.12", + "@types/echarts": "^4.9.22", + "@types/jasmine": "~5.1.8", + "@types/jasminewd2": "~2.0.13", + "@types/jest": "^29.5.14", "@types/jsonp": "^0.2.3", - "@types/lodash": "^4.14.182", + "@types/lodash": "^4.17.20", "@types/lodash-es": "^4.17.12", - "@types/node": "^18.15.0", + "@types/node": "^18.19.119", "@types/urlencode": "^1.1.4", - "@typescript-eslint/eslint-plugin": "^7.15.0", - "@typescript-eslint/parser": "^7.15.0", - "@vitejs/plugin-vue": "^5.0.0", - "@vitejs/plugin-vue-jsx": "^4.0.0", - "@vue/babel-plugin-jsx": "^1.2.3", - "@vue/compiler-sfc": "^3.2.0", - "@vue/test-utils": "^2.0.0", - "@vuedx/typecheck": "^0.7.5", - "@vuedx/typescript-plugin-vue": "^0.7.5", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", + "@vitejs/plugin-vue": "^5.2.4", + "@vitejs/plugin-vue-jsx": "^4.2.0", + "@vue/babel-plugin-jsx": "^1.4.0", + "@vue/compiler-sfc": "^3.5.17", + "@vue/test-utils": "^2.4.6", + "@vuedx/typecheck": "^0.7.6", + "@vuedx/typescript-plugin-vue": "^0.7.6", "babel-cli": "^6.26.0", - "babel-jest": "^29.0.3", - "body-parser": "^1.18.3", + "babel-jest": "^29.7.0", + "body-parser": "^1.20.3", "body-scroll-lock": "4.0.0-beta.0", - "chalk": "^5.0.0", - "clean-css-cli": "^5.6.0", - "commander": "^12.0.0", - "compressing": "^1.4.0", - "compression": "^1.7.4", + "chalk": "^5.4.1", + "clean-css-cli": "^5.6.3", + "commander": "^12.1.0", + "compressing": "^1.10.3", + "compression": "^1.8.0", "conventional-changelog-cli": "^5.0.0", - "cors": "^2.8.4", + "cors": "^2.8.5", "cpy-cli": "^5.0.0", "crypto-js": "^4.2.0", "csstype": "^3.1.3", "date-fns": "^3.6.0", "del-cli": "^5.1.0", - "esbuild-register": "^3.3.0", - "eslint": "^9.19.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-vue": "^9.29.1", - "express": "^4.16.3", - "fs-extra": "^11.2.0", - "happy-dom": "^14.12.0", - "highlight.js": "^11.9.0", - "husky": "^9.0.0", - "inquirer": "^9.1.1", + "esbuild-register": "^3.6.0", + "eslint": "^9.31.0", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-vue": "^9.33.0", + "express": "^4.21.2", + "fs-extra": "^11.3.0", + "happy-dom": "^14.12.3", + "highlight.js": "^11.11.1", + "husky": "^9.1.7", + "inquirer": "^9.3.7", "intersection-observer": "^0.12.2", - "jest": "^29.0.0", - "jest-environment-jsdom": "^29.5.0", - "lint-staged": "^15.0.0", - "lodash-es": "^4.17.11", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "lint-staged": "^15.5.2", + "lodash-es": "^4.17.21", "make-dir-cli": "^4.0.0", "merge-stream": "^2.0.0", - "npm-run-all": "^4.1.2", - "ora": "^8.0.0", + "npm-run-all": "^4.1.5", + "ora": "^8.2.0", "patch-vue-directive-ssr": "^0.0.1", - "rollup": "^4.18.0", - "sass": "^1.77.6", - "shelljs": "^0.8.4", + "rollup": "^4.45.1", + "sass": "^1.89.2", + "shelljs": "^0.8.5", "shiki": "^0.11.1", - "stylelint": "^16.6.0", - "stylelint-config-recommended-scss": "^14.0.0", - "stylelint-config-standard": "^36.0.0", - "stylelint-order": "^6.0.0", - "stylelint-scss": "^6.3.0", + "stylelint": "^16.21.1", + "stylelint-config-recommended-scss": "^14.1.0", + "stylelint-config-standard": "^36.0.1", + "stylelint-order": "^6.0.4", + "stylelint-scss": "^6.12.1", "ts-node": "~10.9.2", - "typescript": "^5.5.0", + "typescript": "^5.8.3", "urlencode": "^2.0.0", - "vite": "^5.3.3", + "vite": "^5.4.19", "vite-plugin-dts": "3.9.1", "vite-plugin-md": "^0.21.5", "vite-svg-loader": "^5.1.0", - "vitepress": "^1.0.0-alpha.8", + "vitepress": "^1.6.3", "vitepress-theme-demoblock": "1.4.2", - "vitest": "^1.4.0", - "vue-tsc": "^2.0.0" + "vitest": "^1.6.1", + "vue-tsc": "^2.2.12" }, "lint-staged": { "packages/ui-vue/{*.vue,*.js,*.ts,*.jsx,*.tsx}": "eslint --fix", - "packages/ui-vue/{*.scss,*.css}": "stylelint --fix" + "packages/ui-vue/{*.scss,*.css}": "stylelint --fix", + "packages/charts-vue/{*.vue,*.js,*.ts,*.jsx,*.tsx}": "eslint --fix", + "packages/charts-vue/{*.scss,*.css}": "stylelint --fix" }, "dependencies": { - "@eslint/js": "^9.19.0", - "axios": "^1.7.2", - "echarts": "^5.5.0", - "globals": "^15.13.0", + "@eslint/js": "^9.31.0", + "axios": "^1.10.0", + "echarts": "^5.6.0", + "globals": "^15.15.0", "jsonp": "^0.2.1", "markdown-it": "^14.1.0", "markdown-it-container": "^4.0.0", - "ts-jest": "^29.1.2", - "typescript-eslint": "^8.18.0" + "ts-jest": "^29.4.0", + "typescript-eslint": "^8.37.0" } -} \ No newline at end of file +} diff --git a/packages/bef/lib/change/bef-change-detail-builder.ts b/packages/bef/lib/change/bef-change-detail-builder.ts index df795628cd2a6f7d49973e9b10a746cf0ddecbf1..dec818c6d5c891668dcb2e9173471f8cbaf3e294 100644 --- a/packages/bef/lib/change/bef-change-detail-builder.ts +++ b/packages/bef/lib/change/bef-change-detail-builder.ts @@ -1,4 +1,4 @@ -import { FieldType, Entity, EntityList, EntityState, EntityStore, EntityChange, ChangeValueChange, EntityPathNodeType, EntityFieldSchema } from '@farris/devkit-vue'; +import { FieldType, Entity, EntityList, EntityState, EntityStore, EntityChange, ChangeValueChange, EntityPathNodeType, EntityFieldSchema, PrimitiveFieldSchema } from '@farris/devkit-vue'; import { BefRepository } from "../bef-repository"; import { ChangeDetailType, ChangeDetail, ChangeDetailInfo } from './types'; @@ -132,7 +132,10 @@ class BefChangeDetailBuilder { if (!parentChangeDetail) { throw new Error(`Field(name=${fieldName}) can not be found`); } - parentChangeDetail[fieldName] = (entityChange as ChangeValueChange).newValue; + const primitiveFieldSchema = fieldSchema as PrimitiveFieldSchema; + const multiLanguage = primitiveFieldSchema.multiLanguage; + const dataField = multiLanguage ? `${fieldName}_MULTILANGUAGE`: fieldName; + parentChangeDetail[dataField] = (entityChange as ChangeValueChange).newValue; currentChangeDetails = null; currentEntities = null; diff --git a/packages/bef/lib/framework-session.service.ts b/packages/bef/lib/framework-session.service.ts index 35632ad1bbf447daa749ccaa98f57f2b256c7ac0..c80a0cef1f113c2aec3f2aa2ca020ea99f400a6c 100644 --- a/packages/bef/lib/framework-session.service.ts +++ b/packages/bef/lib/framework-session.service.ts @@ -2,8 +2,21 @@ * 框架Session服务 */ class FrameworkSessionService { + + /** + * 框架服务 + * @summary + * 等同于frameworkService的rtf属性 + */ private rtfService: any; + + /** + * 框架服务 + * @summary + * 等同于框架向Window注册的gspframeworkService对象 + */ private frameworkService: any = null; + /** * 构造函数 */ @@ -14,44 +27,68 @@ class FrameworkSessionService { /** * 获取当前会话id */ - getCurrentSessionId(): string { + public getCurrentSessionId(): string { + // TODO: 存在页签未激活获取token错误的问题 return this.formToken || ''; } + public get tabId() { return this.params && this.params['tabId'] || null; } + /** * 获取formToken */ public get formToken() { return this.params && this.params['cvft'] || null; } + + /** + * 从URL中获取查询参数 + */ public get params() { + // if (this.rtfService && this.rtfService.hasOwnProperty('session') && typeof this.rtfService['session']['getCommonVariable'] === 'function') { // return this.rtfService['session']['getCommonVariable'](); // } // return null; - // 从url中获取 + const hash = window.location.hash; const params = this.parseQueryString(hash); return params; } + + /** + * 获取框架服务 + */ private getRuntimeFrameworkService() { const frameworkService = this.gspFrameworkService; return frameworkService && frameworkService.rtf || {}; } + + /** + * 获取框架服务 + * @summary + * 从Window上获取框架的gspframeworkService对象 + */ private get gspFrameworkService() { if (this.frameworkService) { return this.frameworkService; } + let env: Window = window; while (!env['gspframeworkService'] && env !== window.top && this.isSameOrigin(env)) { env = env.parent; } + this.frameworkService = env['gspframeworkService']; return this.frameworkService; } + + /** + * 当前Window是否和表单是同源 + */ private isSameOrigin(environment: Window) { const host = window.location.host; try { @@ -64,6 +101,10 @@ class FrameworkSessionService { return false; } + + /** + * 将查询参数字符串转换成对象 + */ private parseQueryString(queryString: string): { [propName: string]: any } { if (!queryString) { return {}; diff --git a/packages/bef/lib/session/bef_session_manager.ts b/packages/bef/lib/session/bef_session_manager.ts index 14263a40a744272f44fade970c4aeb9c9d5bc01a..f36cc33932adfadc12b87844a9e75c705ea35416 100644 --- a/packages/bef/lib/session/bef_session_manager.ts +++ b/packages/bef/lib/session/bef_session_manager.ts @@ -2,15 +2,35 @@ import { RuntimeContext } from '../types'; import { BefSessionService } from './bef_session_service'; export class BefSessionManager { - private static history: string[] = []; - public static getSessionId(moduleId: string, sessionService: BefSessionService, beBaseUri: string, runtimeContext?: RuntimeContext): Promise { - const key = `${moduleId}_${beBaseUri}`; - const createSessionIsInvoked = this.history.includes(key); - if (createSessionIsInvoked) { - return Promise.resolve(null); - } else { - this.history.push(key); - return sessionService.getBeSessionId(runtimeContext); - } + + /** + * 会话创建历史记录 + */ + private static history: string[] = []; + + /** + * 获取会话 + */ + public static getSessionId(moduleId: string, sessionService: BefSessionService, beBaseUri: string, runtimeContext?: RuntimeContext): Promise { + const key = `${moduleId}_${beBaseUri}`; + const createSessionIsInvoked = this.history.includes(key); + if (createSessionIsInvoked) { + return Promise.resolve(null); + } else { + this.history.push(key); + return sessionService.getBeSessionId(runtimeContext); + } + } + + /** + * 清理会话 + */ + public static clearSessionId(moduleId: string, sessionService: BefSessionService, beBaseUri: string, runtimeContext?: RuntimeContext) { + const key = `${moduleId}_${beBaseUri}`; + const keyIndex = this.history.indexOf(key); + if (keyIndex !== -1) { + this.history.splice(keyIndex, 1); } - } \ No newline at end of file + sessionService.clearBeSessionId(runtimeContext); + } +} \ No newline at end of file diff --git a/packages/bef/lib/session/bef_session_service.ts b/packages/bef/lib/session/bef_session_service.ts index 85eb16d4dd94a8206924eae9bca7873bdb983217..9b06e360a391c5a62414b026587335205cccc420 100644 --- a/packages/bef/lib/session/bef_session_service.ts +++ b/packages/bef/lib/session/bef_session_service.ts @@ -34,7 +34,6 @@ class BefSessionService { /** * 设置sessionId - * @param sessionId sessionId */ public setBeSessionId(sessionId: string, runtimeContext?: RuntimeContext) { this.handlingStrategy.setSessionId(sessionId, runtimeContext); diff --git a/packages/bef/lib/session/handling-strategies/bef-separated-session-handling-strategy.ts b/packages/bef/lib/session/handling-strategies/bef-separated-session-handling-strategy.ts index 42156ab0cd632bc60208500f281a8ceb3fcd3c7b..b6bf7cf3d9e8bcbb631a09e377c01dd6f9413334 100644 --- a/packages/bef/lib/session/handling-strategies/bef-separated-session-handling-strategy.ts +++ b/packages/bef/lib/session/handling-strategies/bef-separated-session-handling-strategy.ts @@ -9,6 +9,7 @@ import { RuntimeContext } from '../../types'; * 隔离的BeSession处理策略 */ class BefSeparatedSessionHandlingStrategy extends SessionHandlingStrategy { + /** * 构造函数 */ @@ -24,6 +25,9 @@ class BefSeparatedSessionHandlingStrategy extends SessionHandlingStrategy { /** * 获取BeSessionId + * @summary + * 1、如果前端缓存存在:优先使用缓存中的BE会话; + * 2、如果缓存中的不存在:如果存在上次打开菜单时遗留的BE会话先关闭老BE会话,再创建新的BE会话 */ public getSessionId(runtimeContext?: RuntimeContext): Promise { const beSessionId = this.getSessionIdFromStorage(runtimeContext); @@ -35,6 +39,7 @@ class BefSeparatedSessionHandlingStrategy extends SessionHandlingStrategy { }); return sessionIdPromise; } + /** * 创建BeSessionId */ @@ -71,11 +76,14 @@ class BefSeparatedSessionHandlingStrategy extends SessionHandlingStrategy { requestConfig.headers = BefHttpUtil.appendCafRuntimeContext(requestConfig.headers, this.lastTimeUsedSessionId); requestConfig.headers = BefHttpUtil.appendCafRuntimeCommonVariable(requestConfig.headers, this.frameworkSessionId); - // 无论是否成功,统一置空cleardBeSessionId + // 无论关闭是否成功,不重复关闭 return this.httpClient.post(this.closeSessionUrl, null, requestConfig).then(() => { return true; + }).finally(() => { + this.lastTimeUsedSessionId = null; }); } + /** * 设置BeSessionId */ @@ -89,8 +97,13 @@ class BefSeparatedSessionHandlingStrategy extends SessionHandlingStrategy { */ public clearSessionId(runtimeContext?: RuntimeContext) { if (BefEnvUtil.isInFramework() === true) { + + // 菜单打开:清理该菜单相关的全部会话(风险:1、PC端不应该触发该逻辑,否则弹窗表单会有问题;2、PC端没有formToken参数恰好没有触发) + // TODO: 待优化 this.storageStrategy.removeItemsByScope(this.frameworkSessionId); } else { + + // 菜单外打开:1、先记录上次遗留的会话ID,以便对其进行关闭;2、移除上次的会话ID,以便重新创建 const sessionKey = this.getSessionStorageKey(runtimeContext); this.lastTimeUsedSessionId = this.getSessionIdFromStorage(runtimeContext); this.storageStrategy.removeItem(sessionKey); @@ -104,7 +117,10 @@ class BefSeparatedSessionHandlingStrategy extends SessionHandlingStrategy { const frmSessionId = this.getFrameworkSessionId(); const beSessionId = this.getSessionIdFromStorage(runtimeContext); + // 追加框架会话ID headers = BefHttpUtil.appendCafRuntimeCommonVariable(headers, frmSessionId); + + // 追加BE会话ID if (beSessionId) { headers = BefHttpUtil.appendCafRuntimeContext(headers, beSessionId); headers = BefHttpUtil.appendSessionId(headers, beSessionId); @@ -122,14 +138,19 @@ class BefSeparatedSessionHandlingStrategy extends SessionHandlingStrategy { * 获取某个Repository对应的BeSession的唯一key */ protected getSessionStorageKey(runtimeContext?: RuntimeContext): string { + + // 获取会话ID let sessionId: string = this.frameworkSessionId; if (runtimeContext) { sessionId = this.getFrameworkSessionId(); } + + // 获取标签页ID const tabId = runtimeContext && runtimeContext.tabId; if (tabId) { return `${sessionId}_${tabId}_${this.baseUrl}`; } + return `${sessionId}_${this.baseUrl}`; } } diff --git a/packages/bef/lib/session/handling-strategies/handling-strategy-factory.ts b/packages/bef/lib/session/handling-strategies/handling-strategy-factory.ts index e46a2ec8b238c517d4a517a5d7964ebb81f310ef..4b4b684531c0697c628bc04b4ad4902c4402b6f9 100644 --- a/packages/bef/lib/session/handling-strategies/handling-strategy-factory.ts +++ b/packages/bef/lib/session/handling-strategies/handling-strategy-factory.ts @@ -9,6 +9,7 @@ import { BefUnifiedSessionHandlingStrategy } from './bef-unified-session-handlin * BeSession处理策略工厂 */ class BefSessionHandlingStrategyFactory { + /** * 创建BeSession处理策略 */ @@ -23,6 +24,8 @@ class BefSessionHandlingStrategyFactory { if (handlingStrategyName === 'UnifiedSession') { return new BefUnifiedSessionHandlingStrategy(storageStrategy, frmSessionService, httpClient, beBaseUrl, injector); } else { + + // 默认策略 return new BefSeparatedSessionHandlingStrategy(storageStrategy, frmSessionService, httpClient, beBaseUrl, injector); } } diff --git a/packages/bef/lib/session/handling-strategies/handling-strategy.ts b/packages/bef/lib/session/handling-strategies/handling-strategy.ts index 6a19181f55913010ec943e3f5159feb74463469a..3eea06cb945754eb1d80755b7bf1cda36a895ecd 100644 --- a/packages/bef/lib/session/handling-strategies/handling-strategy.ts +++ b/packages/bef/lib/session/handling-strategies/handling-strategy.ts @@ -40,7 +40,7 @@ abstract class SessionHandlingStrategy { /** * 上次使用的会话id */ - protected lastTimeUsedSessionId: string; + protected lastTimeUsedSessionId: string | null; /** * injector */ @@ -75,6 +75,8 @@ abstract class SessionHandlingStrategy { /** * 获取框架SessionId * TODO: 暂不支持runtimeContext + * @summary + * 获取框架的会话,即框架参数中的cvft的值 */ public getFrameworkSessionId() { return this.frameworkSessionId; diff --git a/packages/bef/lib/session/storage-strategies/session-storage-strategy.ts b/packages/bef/lib/session/storage-strategies/session-storage-strategy.ts index 965ec1d267b7a786d2c87599075b86ebe34c9a03..01de5335a568e58d59cb821f98a578889983c226 100644 --- a/packages/bef/lib/session/storage-strategies/session-storage-strategy.ts +++ b/packages/bef/lib/session/storage-strategies/session-storage-strategy.ts @@ -87,7 +87,7 @@ class SessionStorageStrategy implements StorageStrategy { /** * 设置全部BeSessions到SessionStorage */ - setAllBeSessions(beSessions: any): void { + private setAllBeSessions(beSessions: any): void { const beSessionsString = JSON.stringify(beSessions); window.sessionStorage.setItem(this.sessionStorageKey, beSessionsString); } diff --git a/packages/charts-vue/.gitignore b/packages/charts-vue/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ca8018c48b20a44abef75a8ca13ce6f2aa0c518a --- /dev/null +++ b/packages/charts-vue/.gitignore @@ -0,0 +1,31 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +!/docs/.vitepress/dist +*.local +coverage +package + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Vitepress +*/.vitepress/cache +*/.vitepress/dist diff --git a/packages/charts-vue/CHANGELOG.md b/packages/charts-vue/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..62c3b1a1db15a31e79348a8149d9eef5973ca238 --- /dev/null +++ b/packages/charts-vue/CHANGELOG.md @@ -0,0 +1,7 @@ +# @farris/charts-vue + +## 0.0.1 + +### Patch Changes + +- update ui libs diff --git a/packages/charts-vue/PROPERTY.md b/packages/charts-vue/PROPERTY.md new file mode 100644 index 0000000000000000000000000000000000000000..90d6835cdced41ce924e92aa2ab9b1893d85f4de --- /dev/null +++ b/packages/charts-vue/PROPERTY.md @@ -0,0 +1,59 @@ +# 组件的属性与属性面板结合 + +## 可以获取的服务有哪些 +1. 服务的定义,在路径:packages/designer/src/composition下 +2. 注入位置:designer.component.tsx和form-designer/form-designer.component.tsx下,注意两个位置的注入服务不要重复。 +3. 可以通过在上述位置inject注入更多的服务 +4. 在DesignerCanvas中可以获取的服务,构造位置:packages/ui-vue/components/designer-canvas/designer-canvas.component.tsx +``` +provide('design-host-service',....) +``` + +通过更改DesignHostService类型,追加新属性,丰富服务 + +## 组件设计时属性的基础类 +1. 在路径:packages/ui-vue/common/property/base-property.ts +2. 上述BaseControlProperty类,被具体组件的属性类所继承,可以在此位置定义通用的方法、属性 + +## 组件如何获取复杂服务 +以data-grid为例,在路径:packages/ui-vue/components/data-grid +1. designer/data-grid.design.component.tsx +``` + const designerHostService=inject('designer-host-service'); + // 构造属性配置方法 + componentInstance.value['getPropertyConfig'] = (componentId:string) => { + const dataGridProp = new DataGridProperty(componentId,designerHostService); + return dataGridProp.getPropertyConfig(componentInstance.value.schema); + } + +``` +- 传递服务、组件ID参数,初始化DataGridProperty +- 在类内部可以取到继承BaseControlProperty自的方法、属性,比如获取viewModelId、formSchemaUtils等。 + +2. property-config/data-grid.property-config.ts + - 这个文件处理组件的设计时属性,构造基本信息、外观、事件。 +``` +export class DataGridProperty extends BaseControlProperty { + getPropertyConfig(propertyData: any) { + // 基本信息 + this.getBasicPropConfig(propertyData); + // 外观 + this.getAppearanceProperties(propertyData); + // 事件 + this.getEventPropConfig(propertyData); + return this.propertyConfig; + } +} + +``` +3. 注意改造data-grid.props.ts +``` +export const propsResolver = createPropsResolver(dataGridProps, dataGridSchema, schemaMapper, schemaResolver, DataGridProperty); + +``` +更改为 +``` +export const propsResolver = createPropsResolver(dataGridProps, dataGridSchema, schemaMapper, schemaResolver); +``` + + diff --git a/packages/charts-vue/README.md b/packages/charts-vue/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c2079988ec552cf04a6a6bf90266d9ad117eaafc --- /dev/null +++ b/packages/charts-vue/README.md @@ -0,0 +1,51 @@ +

+ + Farris UI Logo + +

+ +

Farris Charts Vue

+ +

Farris Charts Vue 是一套基于 Farris Design 的前端组件库。

+ +## 1. 编写组件 + 在components目录下,以组件名创建源代码目录,参考[贡献指南(https://gitee.com/ubml/farris-vue/blob/master/style-guide/vue_component_style_guide.md)](https://gitee.com/ubml/farris-vue/blob/master/style-guide/vue_component_style_guide.md)实现组件。 + +## 2. 提供示例页面 + 开发者在charts-vue/demo目录按照{组件名}/{组件特性}.vue实现示例页面。 + +## 3. 注册示例页面路由 + 在`charts-vue/src/app.vue`组件,编辑`routes`对象,添加路由信息,例如: +```typescript +import GridLayoutWrapper from '../demos/grid-layout/grid-layout.vue'; + +const routes: Record = { + '/gridlayout/basic': GridLayoutWrapper, +}; +``` + +## 4. 运行示例页面 + 开发者执行`npx vite dev --open #{示例页面路由}`在浏览器打开示例页面,例如: +``` +npx vite dev --open #grid-layout/basic +``` +## 5. 编写组件说明文档 + 开发者在`charts-vue/docs/components`目录下,已markdown格式编写说明文档,以`{示例页面路径}`方式引用示例页面,例如: + + +\# GridLayout 栅栏布局 + +GridLayout 组件提供了栅栏式、响应式布局。 + +\## 基本用法 + +:::vdemo + +\```vue + +{demos/grid-layout/grid-layout.vue} + +\``` + +::: + diff --git a/packages/charts-vue/__mocks__/style-mock.ts b/packages/charts-vue/__mocks__/style-mock.ts new file mode 100644 index 0000000000000000000000000000000000000000..ff8b4c56321a3362fc00224b01800f62466f9a1f --- /dev/null +++ b/packages/charts-vue/__mocks__/style-mock.ts @@ -0,0 +1 @@ +export default {}; diff --git a/packages/charts-vue/components/charts-bar/index.ts b/packages/charts-vue/components/charts-bar/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a489598686f94f4f701686a33f62a6a5ec7c93bb --- /dev/null +++ b/packages/charts-vue/components/charts-bar/index.ts @@ -0,0 +1,13 @@ +import FChartsBar from './src/charts-bar.component'; +// import { propsResolver } from './src/charts-bar.props'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-bar.props'; + +// FChartsBar.register = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record) => { +// componentMap['charts-bar'] = FChartsBar; +// propsResolverMap['charts-bar'] = propsResolver; +// }; + +export { FChartsBar }; +export default withInstall(FChartsBar); diff --git a/packages/charts-vue/components/charts-bar/src/charts-bar.component.tsx b/packages/charts-vue/components/charts-bar/src/charts-bar.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c67badb15a894f0d0bf8435f55f1ce796c878560 --- /dev/null +++ b/packages/charts-vue/components/charts-bar/src/charts-bar.component.tsx @@ -0,0 +1,129 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsBarProps, chartsBarProps } from './charts-bar.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '../../charts-common'; + +export default defineComponent({ + name: 'FChartsBar', + props: chartsBarProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsBarProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + xAxis: props.xAxis || {}, + yAxis: props.yAxis || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const barseries = { ...series }; + barseries.type = 'bar'; + return barseries; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + const resizeChart = () => { + chartInstance?.resize(); + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-bar/src/charts-bar.props.ts b/packages/charts-vue/components/charts-bar/src/charts-bar.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb0ff26eab252b2189311b3bdde9cb9a88307c89 --- /dev/null +++ b/packages/charts-vue/components/charts-bar/src/charts-bar.props.ts @@ -0,0 +1,160 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + AxisProps, animationProps, ToolboxProps, DataProps, ColorByType, StackStrategyType, LabelProps, + LabelLineProps, LabelLayoutProps, ItemStyleProps, LineStyleProps, AreaStyleProps, BlurProps, + SelectProps, TooltipProps, SelectedModeType, SamplingType, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps, EmphasisProps +} from '@farris/charts-vue/components/charts-common'; + +export const barSeriesProps = { + + type: { type: String, default: 'bar' }, + + id: { type: String, default: '' }, + + name: { type: String, default: '' }, + + colorBy: { type: String as PropType }, + + legendHoverLink: { type: Boolean, default: true }, + + // 坐标系类型,暂时不考虑极坐标 + // coordinateSystem: { type: String as PropType<>, default: 'cartesian2d' }, + // polarIndex: { type: Number, default: 0 }, + + xAxisIndex: { type: Number }, + + yAxisIndex: { type: Number }, + + // 动态排序 + realtimeSort: { type: Boolean, default: false}, + + // 背景色,暂时不提供 + // showBackground: { type: Boolean, default: false }, + // backgroundStyle: { type: Object as PropType, default: () => ({}) }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + emphasis: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + selectedMode: { type: String as PropType }, + + /** 数据堆叠,目前只支持堆叠 value 和 log */ + stack: { type: String }, + + /** 堆积数值的策略,前提是stack属性已被设置 */ + stackStrategy: { type: String as PropType }, + + // 采样方式,默认关闭 + sampling: { type: String as PropType }, + + cursor: { type: String, default: 'pointer' }, + + barWidth: { type: [Number, String] }, + + barMaxWidth: { type: [Number, String] }, + + barMinWidth: { type: [Number, String] }, + + barMinHeight: { type: Number }, + + barGap: { type: String }, + + barCategoryGap: { type: String }, + + large: { type: Boolean, default: false }, + + largeThreshold: { type: Number, default: 400 }, + + /** 使用 dimensions 定义 series.data 或者 dataset.source 的每个维度的信息。 */ + dimensions: { type: Array }, + + /** 可以定义 data 的哪个维度被编码成什么。 */ + encode: { type: Object }, + + /** 当使用 dataset 时,seriesLayoutBy 指定了 dataset 中用行还是列对应到系列上。column row */ + seriesLayoutBy: { type: String }, + + /** 如果 series.data 没有指定,并且 dataset 存在,那么就会使用 dataset。datasetIndex 指定本系列使用哪个 dataset */ + datasetIndex: { type: Number }, + + dataGroupId: { type: String }, + + data: { type: Object as PropType }, + + /** 是否裁剪超出坐标系部分的图形 */ + clip: { type: Boolean, default: true }, + + // 是否需要封装为公共的props + // markPoint: { type: Object as PropType }, + // markLine: { type: Object as PropType }, + // markArea: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number } + +}; + +export type BarSeriesProps = ExtractPropTypes; + +// Bar组件的属性 +export const chartsBarProps = { + // /** + // * 组件标识 + // */ + // id: { type: String as PropType, default: 'charts-bar', required: true }, + // /** + // * 组件类型,默认是 'bar' + // */ + // type: { type: String as PropType, default: 'bar', required: true }, + + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + textStyle: { type: Object as PropType }, + + xAxis: { type: Object as PropType }, + + yAxis: { type: Object as PropType }, + /** + * 数据系列 + */ + series: { type: Array as PropType, default: [{ ...barSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps + +} as Record; + +export type ChartsBarProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-bar/src/schema/charts-bar.schema.json b/packages/charts-vue/components/charts-bar/src/schema/charts-bar.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..4650e6a7580d64bf0f42e408db50caf0723234b1 --- /dev/null +++ b/packages/charts-vue/components/charts-bar/src/schema/charts-bar.schema.json @@ -0,0 +1,893 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://farris-design.gitee.io/charts-bar.schema.json", + "title": "charts-bar", + "description": "A Farris Bar Component", + "type": "object", + "properties": { + "id": { + "description": "The unique identifier for a Input Group", + "type": "string" + }, + "type": { + "description": "The type string of Input Group component", + "type": "string", + "default": "bar" + }, + "name": { + "type": "string" + }, + "code": { + "type": "string" + }, + "color": { + "type": "array", + "default": [ + "#5470c6", + "#91cc75", + "#fac858", + "#ee6666", + "#73c0de", + "#3ba272", + "#fc8452", + "#9a60b4", + "#ea7ccc" + ] + }, + "textStyle": { + "type": "object", + "properties": { + "color": { + "type": "string", + "default": "#808080" + }, + "fontStyle": { + "type": "string", + "enum": [ + "normal", + "italic", + "oblique" + ], + "default": "normal" + }, + "fontWeight": { + "type": "string", + "enum": [ + "normal", + "bold", + "bolder", + "lighter" + ], + "default": "normal" + }, + "fontFamily": { + "type": "string", + "enum": [ + "serif", + "monospace", + "Arial", + "Courier New" + ], + "default": "serif" + }, + "fontSize": { + "type": "number", + "default": 12 + }, + "lineHeight": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "overflow": { + "type": "string", + "enum": [ + "none", + "truncate", + "break", + "breakAll" + ], + "default": "none" + }, + "ellipsis": { + "type": "string", + "description": "在overflow配置为'truncate'的时候,可以通过该属性配置末尾显示的文本", + "default": "..." + } + } + }, + "title": { + "type": "object", + "properties": { + "id": { + "type": "string", + "default": "" + }, + "show": { + "type": "boolean", + "default": true + }, + "text": { + "type": "string", + "default": "" + }, + "link": { + "type": "string", + "default": "" + }, + "target": { + "type": "string", + "enum": [ + "blank", + "self" + ], + "default": "blank" + }, + "textStyle": { + "type": "object", + "properties": { + "color": { + "type": "string", + "default": "#2D2F33" + }, + "fontStyle": { + "type": "string", + "enum": [ + "normal", + "italic", + "oblique" + ], + "default": "normal" + }, + "fontWeight": { + "type": [ + "string", + "number" + ], + "enum": [ + "normal", + "bold", + "bolder", + "lighter" + ], + "default": 500 + }, + "fontFamily": { + "type": "string", + "enum": [ + "serif", + "monospace", + "Arial", + "Courier New" + ], + "default": "serif" + }, + "lineHeight": { + "type": "number", + "default": 26 + } + } + }, + "subtext": { + "type": "string", + "default": "" + }, + "sublink": { + "type": "string", + "default": "" + }, + "subtarget": { + "type": "string", + "enum": [ + "blank", + "self" + ], + "default": "blank" + }, + "subtextStyle": { + "type": "object", + "properties": { + "color": { + "type": "string", + "default": "#2D2F33" + }, + "fontStyle": { + "type": "string", + "enum": [ + "normal", + "italic", + "oblique" + ], + "default": "normal" + }, + "fontWeight": { + "type": [ + "string", + "number" + ], + "enum": [ + "normal", + "bold", + "bolder", + "lighter" + ], + "default": "normal" + }, + "fontFamily": { + "type": "string", + "enum": [ + "serif", + "monospace", + "Arial", + "Courier New" + ], + "default": "serif" + }, + "lineHeight": { + "type": "number", + "default": 20 + } + } + }, + "triggerEvent": { + "type": "boolean", + "default": true + }, + "padding": { + "type": "array", + "items": { + "type": "number" + }, + "default": [ + 5, + 5, + 5, + 5 + ] + }, + "itemGap": { + "type": "number", + "default": 10 + }, + "top": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "bottom": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "left": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "right": { + "type": [ + "string", + "number" + ], + "default": "auto" + } + } + }, + "legend": { + "type": "object", + "properties": { + "id": { + "type": "string", + "default": "" + }, + "show": { + "type": "boolean", + "default": true + }, + "type": { + "type": "string", + "enum": [ + "plain", + "scroll" + ], + "default": "plain" + }, + "top": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "bottom": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "left": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "right": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "width": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "height": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "orient": { + "type": [ + "string" + ], + "enum": [ + "horizontal", + "vertical" + ], + "default": "horizontal" + }, + "align": { + "type": "string", + "enum": [ + "auto", + "left", + "right" + ], + "default": "auto" + }, + "padding": { + "type": "number", + "default": 5 + }, + "itemGap": { + "type": "number", + "default": 10 + }, + "itemWidth": { + "type": "number", + "default": 12 + }, + "itemHeight": { + "type": "number", + "default": 12 + }, + "itemStyle": { + "type": "object", + "properties": { + "color": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "lineStyle": { + "type": "object", + "properties": { + "color": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "textStyle": { + "type": "object", + "properties": { + "color": { + "type": "string", + "default": "#666666" + }, + "fontSize": { + "type": "number", + "default": 12 + }, + "fontStyle": { + "type": "string", + "enum": [ + "normal", + "italic", + "oblique" + ], + "default": "normal" + }, + "fontWeight": { + "type": [ + "string", + "number" + ], + "enum": [ + "normal", + "bold", + "bolder", + "lighter" + ], + "default": 400 + }, + "fontFamily": { + "type": "string", + "enum": [ + "serif", + "monospace", + "Arial", + "Courier New" + ], + "default": "serif" + }, + "lineHeight": { + "type": "number", + "default": 16 + } + } + }, + "formatter": { + "type": "object", + "default": "" + }, + "tooltip": { + "type": "boolean", + "default": false + }, + "icon": { + "type": "string", + "default": "" + }, + "data": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "textStyle": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "fontStyle": { + "type": "string", + "enum": [ + "normal", + "italic", + "oblique" + ] + }, + "fontWeight": { + "type": [ + "string", + "number" + ], + "enum": [ + "normal", + "bold", + "bolder", + "lighter" + ] + }, + "fontFamily": { + "type": "string", + "enum": [ + "serif", + "monospace", + "Arial", + "Courier New" + ] + }, + "lineHeight": { + "type": "number" + } + } + } + } + }, + "borderRadius": { + "type": [ + "number", + "array" + ], + "default": 0 + } + } + }, + "grid": { + "type": "object", + "properties": { + "id": { + "type": "string", + "default": "" + }, + "show": { + "type": "boolean", + "default": true + }, + "zlevel": { + "type": "number", + "default": 0 + }, + "z": { + "type": "number", + "default": 0 + }, + "top": { + "type": [ + "string", + "number" + ], + "default": 60 + }, + "bottom": { + "type": [ + "string", + "number" + ], + "default": 60 + }, + "left": { + "type": [ + "string", + "number" + ], + "default": "10%" + }, + "right": { + "type": [ + "string", + "number" + ], + "default": "10%" + }, + "width": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "height": { + "type": [ + "string", + "number" + ], + "default": "auto" + }, + "containLabel": { + "type": "boolean", + "description": "grid 区域是否包含坐标轴的刻度标签。containLabel 为 false 的时候:grid.left grid.right grid.top grid.bottom grid.width grid.height 决定的是由坐标轴形成的矩形的尺寸和位置。这比较适用于多个 grid 进行对齐的场景,因为往往多个 grid 对齐的时候,是依据坐标轴来对齐的。containLabel 为 true 的时候:grid.left grid.right grid.top grid.bottom grid.width grid.height 决定的是包括了坐标轴标签在内的所有内容所形成的矩形的位置。这常用于『防止标签溢出』的场景,标签溢出指的是,标签长度动态变化时,可能会溢出容器或者覆盖其他组件", + "default": false + }, + "backgroundColor": { + "type": "string", + "default": "rgba(1,51,100,0.05)" + }, + "borderColor": { + "type": "string", + "default": "#FFFFFF" + }, + "borderWidth": { + "type": "number", + "default": 0 + }, + "tooltip": { + "type": "object", + "properties": { + "show": { + "type": "boolean" + }, + "padding": { + "type": "number" + }, + "trigger": { + "type": "string", + "enum": [ + "item", + "axis", + "none" + ] + }, + "formatter": { + "type": "object" + }, + "valueFormater": { + "type": "object" + }, + "textStyle": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "fontStyle": { + "type": "string", + "enum": [ + "normal", + "italic", + "oblique" + ] + }, + "fontWeight": { + "type": [ + "string", + "number" + ], + "enum": [ + "normal", + "bold", + "bolder", + "lighter" + ] + }, + "fontFamily": { + "type": "string", + "enum": [ + "serif", + "monospace", + "Arial", + "Courier New" + ] + }, + "lineHeight": { + "type": "number" + } + } + }, + "axisPointer": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "line", + "shadow", + "none", + "cross" + ], + "description": "指示器类型。" + }, + "axis": { + "type": "string", + "enum": [ + "x", + "y", + "radius", + "angle" + ], + "description": "指示器的坐标轴。默认情况,坐标系会自动选择显示哪个轴的 axisPointer(默认取类目轴或者时间轴)" + }, + "label": { + "type": "object", + "properties": { + "show": { + "type": "boolean", + "description": "是否显示文本标签。" + }, + "precision": { + "type": [ + "number", + "string" + ], + "description": "文本标签中数值的小数点精度。默认根据当前轴的值自动判断。也可以指定如 2 表示保留两位小数" + }, + "margin": { + "type": "number", + "description": "label 距离轴的距离" + }, + "color": { + "type": "string", + "description": "文字的颜色" + }, + "fontStyle": { + "type": "string", + "enum": [ + "normal", + "italic", + "oblique" + ], + "description": "文字字体的风格" + }, + "fontWeight": { + "type": [ + "number", + "string" + ], + "description": "文字字体的粗细. normal bold bolder lighter, 100|200|300|400" + }, + "fontFamily": { + "type": "string", + "description": "文字的字体系列. 'serif' , 'monospace', 'Arial', 'Courier New', 'Microsoft YaHei', ..." + }, + "fontSize": { + "type": "number" + }, + "lineHeight": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "overflow": { + "type": "string", + "enum": [ + "none", + "truncate", + "break", + "breakAll" + ], + "description": "文字超出宽度是否截断或者换行。配置width时有效" + }, + "padding": { + "type": "array" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "borderWidth": { + "type": "number" + } + } + }, + "lineStyle": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "width": { + "type": "number" + }, + "type": { + "type": [ + "string", + "array" + ], + "description": "线的类型" + }, + "cap": { + "type": "string", + "enum": [ + "butt", + "round", + "square" + ], + "description": "用于指定线段的绘制方式" + }, + "join": { + "type": "string", + "enum": [ + "bevel", + "round", + "miter" + ], + "description": "用于设置2个长度不为0的相连部分(线段,圆弧,曲线)如何连接在一起的属性" + } + } + } + } + } + } + } + } + }, + "appearance": { + "description": "", + "type": "object", + "properties": { + "class": { + "type": "string" + }, + "style": { + "type": "string" + } + }, + "default": {} + }, + "xAxis": { + "type": "object", + "properties": { + "type": { + "type": "string", + "default": "category" + }, + "data": { + "type": "array", + "default": [] + } + } + }, + "yAxis": { + "type": "object", + "properties": { + "type": { + "type": "string", + "default": "value" + } + } + }, + "series": { + "type": "array", + "properties": { + "type": { + "type": "string", + "default": "bar" + }, + "id": { + "type": "string", + "default": "" + }, + "name": { + "type": "string", + "default": "" + }, + "xAxisIndex": { + "type": "number", + "default": 0 + }, + "yAxisIndex": { + "type": "number", + "default": 0 + }, + "clip": { + "type": "boolean", + "default": true + }, + "data": { + "type": "array", + "properties" : { + "name": { + "type": "string" + }, + "value": { + "type": "number" + } + }, + "default": [] + } + + }, + "default": [] + } + + }, + "required": [ + "id", + "type" + ] +} \ No newline at end of file diff --git a/packages/charts-vue/components/charts-combined/index.ts b/packages/charts-vue/components/charts-combined/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc3958212fd0ce35f08ce4712886cd6d87b61101 --- /dev/null +++ b/packages/charts-vue/components/charts-combined/index.ts @@ -0,0 +1,6 @@ +import FCharstCombined from './src/charts-combined.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-combined.props'; +export { FCharstCombined }; +export default withInstall(FCharstCombined); diff --git a/packages/charts-vue/components/charts-combined/src/charts-combined.component.tsx b/packages/charts-vue/components/charts-combined/src/charts-combined.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6b87d1cb160039c63480dc585a1e063e411a16f4 --- /dev/null +++ b/packages/charts-vue/components/charts-combined/src/charts-combined.component.tsx @@ -0,0 +1,133 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsCombinedProps, chartsCombinedProps } from './charts-combined.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '@farris/charts-vue/components/charts-common'; + +export default defineComponent({ + name: 'FChartsCombined', + props: chartsCombinedProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsCombinedProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + xAxis: props.xAxis || {}, + yAxis: props.yAxis || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + if (s.type !== 'line' && s.type !== 'bar') { + throw 'The combination chart only supports line charts and bar charts.'; + } + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-combined/src/charts-combined.props.ts b/packages/charts-vue/components/charts-combined/src/charts-combined.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..b64e52819ad7f277109fe7305913597f632fd446 --- /dev/null +++ b/packages/charts-vue/components/charts-combined/src/charts-combined.props.ts @@ -0,0 +1,138 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + AxisProps, animationProps, ToolboxProps, DataProps, ColorByType, StackStrategyType, LabelProps, + LabelLineProps, LabelLayoutProps, ItemStyleProps, LineStyleProps, AreaStyleProps, BlurProps, + SelectProps, TooltipProps, SelectedModeType, SamplingType, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps, + CombinedType +} from '@farris/charts-vue/components/charts-common'; + +export const combinedSeriesProps = { + type: { type: String as PropType }, + + id: { type: String }, + + name: { type: String }, + + colorBy: { type: String as PropType }, + + xAxisIndex: { type: Number }, + + yAxisIndex: { type: Number }, + + symbol: { type: String, Array, default: 'none' }, + + symbolSize: { type: Array }, + + symbolOffset: { type: Array }, + + symbolRotate: { type: Number }, + + legendHoverLink: { type: Boolean, default: true }, + /** 数据堆叠,目前只支持堆叠 value 和 log */ + stack: { type: String }, + /** 堆积数值的策略,前提是stack属性已被设置 */ + stackStrategy: { type: String as PropType }, + + cursor: { type: String, default: 'pointer' }, + /** 是否裁剪超出坐标系部分的图形 */ + clip: { type: Boolean, default: true }, + + triggerLineEvent: { type: Boolean, default: false }, + + step: { type: Boolean, default: false }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + areaStyle: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + selectedMode: { type: String as PropType }, + + smooth: { type: [Boolean, Number] }, + + sampling: { type: String as PropType }, + + data: { type: Object as PropType }, + /** 使用 dimensions 定义 series.data 或者 dataset.source 的每个维度的信息。 */ + dimensions: { type: Array }, + /** 可以定义 data 的哪个维度被编码成什么。 */ + encode: { type: Object }, + /** 当使用 dataset 时,seriesLayoutBy 指定了 dataset 中用行还是列对应到系列上。column row */ + seriesLayoutBy: { type: String }, + /** 如果 series.data 没有指定,并且 dataset 存在,那么就会使用 dataset。datasetIndex 指定本系列使用哪个 dataset */ + datasetIndex: { type: Number }, + + roundCap: { type: Boolean, default: false }, + + realtimeSort: { type: Boolean, default: false }, + + barWidth: { type: [Number, String] }, + + barMaxWidth: { type: [Number, String] }, + + barMinWidth: { type: [Number, String] }, + + barMaxHeight: { type: [Number, String] }, + + barMinHeight: { type: [Number, String] }, + + barGap: { type: String }, + + ...animationProps +}; + +export type CombinedSeriesProps = ExtractPropTypes; + +export const chartsCombinedProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [Number, String] }, + + height: { type: [Number, String] }, + + textStyle: { type: Object as PropType }, + + series: { type: Array as PropType, default: [{ ...combinedSeriesProps }] }, + + xAxis: { type: Object as PropType }, + + yAxis: { type: Object as PropType }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps +}; + +export type ChartsCombinedProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/index.ts b/packages/charts-vue/components/charts-common/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3091d74020c3318a65d61da5bc720f8d77062b8 --- /dev/null +++ b/packages/charts-vue/components/charts-common/index.ts @@ -0,0 +1,33 @@ +export * from './src/props/type/common.type'; +export * from './src/props/common/blur.props'; +export * from './src/props/common/datazoom.props'; +export * from './src/props/common/grid.props'; +export * from './src/props/common/itemstyle.props'; +export * from './src/props/common/label.props'; +export * from './src/props/common/labellayout.props'; +export * from './src/props/common/labelline.props'; +export * from './src/props/common/legend.props'; +export * from './src/props/common/select.props'; +export * from './src/props/common/textstyle.props'; +export * from './src/props/common/title.props'; +export * from './src/props/common/axis.props'; +export * from './src/props/common/data.props'; +export * from './src/props/common/border.props'; +export * from './src/props/common/animation.props'; +export * from './src/props/common/toolbox.props'; +export * from './src/props/common/emphasis.props'; +export * from './src/props/common/scalelimit.props'; +export * from './src/compositions/type'; +export * from './src/compositions/use-action-event'; +export * from './src/compositions/use-brush-event'; +export * from './src/compositions/use-datazoom-event'; +export * from './src/compositions/use-geo-event'; +export * from './src/compositions/use-georoam-event'; +export * from './src/compositions/use-graph-event'; +export * from './src/compositions/use-legend-event'; +export * from './src/compositions/use-mouse-event'; +export * from './src/compositions/use-parallel-event'; +export * from './src/compositions/use-render-event'; +export * from './src/compositions/use-timeline-event'; +export * from './src/compositions/use-theme'; +export * from './src/utils/with-install'; diff --git a/packages/charts-vue/components/charts-common/src/compositions/type.ts b/packages/charts-vue/components/charts-common/src/compositions/type.ts new file mode 100644 index 0000000000000000000000000000000000000000..adca6d5327b766594678d885834e719eb4cdc939 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/type.ts @@ -0,0 +1,141 @@ +export const MouseEventArray = [ + 'click','dblclick','mousedown','mousemove','mouseup','mouseover','mouseout','globalout','contextmenu' +]; + +export const ActionEventArray = ['highlight','downplay','selectchanged']; + +export const LegendEventArray = [ + 'legendselectchanged','legendselected','legendunselected','legendselectall','legendinverseselect','legendscroll' +]; + +export const DatazoomEventArray = ['datazoom']; + +export const VisualMapEventArray = ['datarangeselected']; + +export const GraphEventArray = ['graphroam']; + +export const GeoroamEventArray = ['georoam']; + +export const TreeEventArray = ['treeroam']; + +export const TimeLineEventArray = ['timelinechanged','timelineplaychanged']; + +export const ToolboxEventArray = ['restore','dataviewchanged','magictypechanged']; + +export const GeoEventArray = ['geoselectchanged','geoselected','geounselected']; + +export const ParallelEventArray = ['axisareaselected']; + +export const BrushEventArray = ['brush','brushEnd','brushselected']; + +export const RenderEventArray = ['rendered','finished']; + +export interface UseMouseEvent { + onClick: () => void; + + onDbClick: () => void; + + onMousedown: () => void; + + onMousemove: () => void; + + onMouseup: () => void; + + onMouseover: () => void; + + onMouseout: () => void; + + onGlobalout: () => void; + + onContextmenu: () => void; +} + +export interface UseActionEvent { + onHighLight: () => void; + + onDownPlay: () => void; + + onSelectChanged: () => void; +} + +export interface UseLegendEvent { + onLegendSelectChanged: () => void; + + onLegendSelected: () => void; + + onLegendUnSelected: () => void; + + onLegendSelectAll: () => void; + + onLegendInverseSelect: () => void; + + onLegendScroll: () => void; +} + +export interface UseDatazoomEvent { + onDatazoom: () => void; +} + +export interface UseVisualMapEvent { + onDataRangeSelected: () => void; +} + +export interface UseGraphEvent { + onGraphroam: () => void; +} + +export interface UseGeoroamEvent { + onGeoroam: () => void; +} + +export interface UseTreeEvent { + onTreeroam: () => void; +} + +export interface UseTimeLineEvent { + onTimeLineChanged: () => void; + + onTimeLinePlayChanged: () => void; +} + +export interface UseToolboxEvent { + onRestore: () => void; + + onDataViewChanged: () => void; + + onMagicTypeChanged: () => void; +} + +export interface UseGeoEvent { + onGeoSelectChanged: () => void; + + onGeoSelected: () => void; + + onGeounselected: () => void; +} + +export interface UseParallelEvent { + onAxisAreaSelected: () => void; +} + +export interface UseBrushEvent { + onBrush: () => void; + + onBrushEnd: () => void; + + onBrushSelected: () => void; +} + +export interface UseRenderEvent { + onRendered: () => void; + + onFinished: () => void; +} + +export interface UseChartsTheme { + registerTheme: () => void; + + getTheme: () => string; + + getThemeByName: (theme: string) => object; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-action-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-action-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..8001d3c16d759bf2545149cb394f2acbb256a87d --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-action-event.ts @@ -0,0 +1,28 @@ +import { SetupContext } from 'vue'; +import { UseActionEvent } from './type'; + +export function useActionEvent(instance: any, context: SetupContext): UseActionEvent { + function onHighLight(): void { + instance.on('highlight', function ($event) { + context.emit('highlight', $event); + }); + } + + function onDownPlay(): void { + instance.on('downplay', function ($event) { + context.emit('downplay', $event); + }); + } + + function onSelectChanged(): void { + instance.on('selectchanged', function ($event) { + context.emit('selectchanged', $event); + }); + } + + return { + onHighLight, + onDownPlay, + onSelectChanged + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-brush-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-brush-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..4de4960d84d9da341046a39b2cdab34f30bd5777 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-brush-event.ts @@ -0,0 +1,28 @@ +import { SetupContext } from 'vue'; +import { UseBrushEvent } from './type'; + +export function useBrushEvent(instance: any, context: SetupContext): UseBrushEvent { + function onBrush(): void { + instance.on('brush', function ($event) { + context.emit('brush', $event); + }); + } + + function onBrushEnd(): void { + instance.on('brushEnd', function ($event) { + context.emit('brushEnd', $event); + }); + } + + function onBrushSelected(): void { + instance.on('brushselected', function ($event) { + context.emit('brushselected', $event); + }); + } + + return { + onBrush, + onBrushEnd, + onBrushSelected + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-datazoom-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-datazoom-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..f1854bca19de647a07c2fdc8201df670c0ffbd77 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-datazoom-event.ts @@ -0,0 +1,14 @@ +import { SetupContext } from 'vue'; +import { UseDatazoomEvent } from './type'; + +export function useDatazoomEvent(instance: any, context: SetupContext): UseDatazoomEvent { + function onDatazoom(): void { + instance.on('datazoom', function ($event) { + context.emit('datazoom', $event); + }); + } + + return { + onDatazoom + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-geo-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-geo-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..0d558983b5e4ace9edb7f650a4133b035abc87af --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-geo-event.ts @@ -0,0 +1,28 @@ +import { SetupContext } from 'vue'; +import { UseGeoEvent } from './type'; + +export function useGeoEvent(instance: any, context: SetupContext): UseGeoEvent { + function onGeoSelectChanged(): void { + instance.on('geoselectchanged', function ($event) { + context.emit('geoselectchanged', $event); + }); + } + + function onGeoSelected(): void { + instance.on('geoselected', function ($event) { + context.emit('geoselected', $event); + }); + } + + function onGeounselected(): void { + instance.on('geounselected', function ($event) { + context.emit('geounselected', $event); + }); + } + + return { + onGeoSelectChanged, + onGeoSelected, + onGeounselected + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-georoam-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-georoam-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..7887ca9735a32512e5d3b6feaed29ebf7eba77b6 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-georoam-event.ts @@ -0,0 +1,14 @@ +import { SetupContext } from 'vue'; +import { UseGeoroamEvent } from './type'; + +export function useGeoroamEvent(instance: any, context: SetupContext): UseGeoroamEvent { + function onGeoroam(): void { + instance.on('georoam', function ($event) { + context.emit('georoam', $event); + }); + } + + return { + onGeoroam + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-graph-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-graph-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..3aec74901907be3ce9e929be01933b609c027384 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-graph-event.ts @@ -0,0 +1,14 @@ +import { SetupContext } from 'vue'; +import { UseGraphEvent } from './type'; + +export function useGraphEvent(instance: any, context: SetupContext): UseGraphEvent { + function onGraphroam(): void { + instance.on('graphroam', function ($event) { + context.emit('graphroam', $event); + }); + } + + return { + onGraphroam + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-legend-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-legend-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..96062831c2d7f3768e18a725e373b4a910db5da8 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-legend-event.ts @@ -0,0 +1,49 @@ +import { SetupContext } from 'vue'; +import { UseLegendEvent } from './type'; + +export function useLegendEvent(instance: any, context: SetupContext): UseLegendEvent { + function onLegendSelectChanged(): void { + instance.on('legendselectchanged', function ($event) { + context.emit('legendselectchanged', $event); + }); + } + + function onLegendSelected(): void { + instance.on('legendselected', function ($event) { + context.emit('legendselected', $event); + }); + } + + function onLegendUnSelected(): void { + instance.on('legendunselected', function ($event) { + context.emit('legendunselected', $event); + }); + } + + function onLegendSelectAll(): void { + instance.on('legendselectall', function ($event) { + context.emit('legendselectall', $event); + }); + } + + function onLegendInverseSelect(): void { + instance.on('legendinverseselect', function ($event) { + context.emit('legendinverseselect', $event); + }); + } + + function onLegendScroll(): void { + instance.on('legendscroll', function ($event) { + context.emit('legendscroll', $event); + }); + } + + return { + onLegendSelectChanged, + onLegendSelected, + onLegendUnSelected, + onLegendSelectAll, + onLegendInverseSelect, + onLegendScroll + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-mouse-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-mouse-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..4a571692e63860340de0ea7be7973f25631ffba7 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-mouse-event.ts @@ -0,0 +1,70 @@ +import { SetupContext } from 'vue'; +import { UseMouseEvent } from './type'; + +export function useMouseEvent(instance: any, context: SetupContext): UseMouseEvent { + function onClick(): void { + instance.on('click', function ($event) { + context.emit('click', $event); + }); + } + + function onDbClick(): void { + instance.on('dblclick', function ($event) { + context.emit('dblclick', $event); + }); + } + + function onMousedown(): void { + instance.on('mousedown', function ($event) { + context.emit('mousedown', $event); + }); + } + + function onMousemove(): void { + instance.on('mousemove', function ($event) { + context.emit('mousemove', $event); + }); + } + + function onMouseup(): void { + instance.on('mouseup', function ($event) { + context.emit('mouseup', $event); + }); + } + + function onMouseover(): void { + instance.on('mouseover', function ($event) { + context.emit('mouseover', $event); + }); + } + + function onMouseout(): void { + instance.on('mouseout', function ($event) { + context.emit('mouseout', $event); + }); + } + + function onGlobalout(): void { + instance.on('globalout', function ($event) { + context.emit('globalout', $event); + }); + } + + function onContextmenu(): void { + instance.on('contextmenu', function ($event) { + context.emit('contextmenu', $event); + }); + } + + return { + onClick, + onDbClick, + onMousedown, + onMousemove, + onMouseup, + onMouseover, + onMouseout, + onGlobalout, + onContextmenu + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-parallel-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-parallel-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..5c6679f6cae1f8646f01d9ce1ace641f183232d0 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-parallel-event.ts @@ -0,0 +1,14 @@ +import { SetupContext } from 'vue'; +import { UseParallelEvent } from './type'; + +export function useParallelEvent(instance: any, context: SetupContext): UseParallelEvent { + function onAxisAreaSelected(): void { + instance.on('axisareaselected', function ($event) { + context.emit('axisareaselected', $event); + }); + } + + return { + onAxisAreaSelected + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-render-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-render-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..d9acdd9bed71a57b8463f9c35b1a1cf9d1ed5dd5 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-render-event.ts @@ -0,0 +1,21 @@ +import { SetupContext } from 'vue'; +import { UseRenderEvent } from './type'; + +export function useRenderEvent(instance: any, context: SetupContext): UseRenderEvent { + function onRendered(): void { + instance.on('rendered', function ($event) { + context.emit('rendered', $event); + }); + } + + function onFinished(): void { + instance.on('finished', function ($event) { + context.emit('finished', $event); + }); + } + + return { + onRendered, + onFinished + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-theme.ts b/packages/charts-vue/components/charts-common/src/compositions/use-theme.ts new file mode 100644 index 0000000000000000000000000000000000000000..278b6b72049210fa7eb1aea575d7d8cd5a2a42c3 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-theme.ts @@ -0,0 +1,56 @@ +import * as echarts from 'echarts'; +import { SetupContext } from 'vue'; + +import { defaultTheme } from '../theme/default'; +import { defaultSolidcolorTheme } from '../theme/default-solidcolor'; +import { auroraTheme } from '../theme/aurora'; +import { auroraSolidcolorTheme } from '../theme/aurora-solidcolor'; +import { sunshineTheme } from '../theme/sunshine'; +import { sunshineSolidcolorTheme } from '../theme/sunshine-solidcolor'; +import { UseChartsTheme } from './type'; + +export function useChartsTheme(): UseChartsTheme { + function registerTheme() { + echarts.registerTheme('defaultTheme', defaultTheme); + echarts.registerTheme('defaultSolidcolorTheme', defaultSolidcolorTheme); + echarts.registerTheme('auroraTheme', auroraTheme); + echarts.registerTheme('auroraSolidcolorTheme', auroraSolidcolorTheme); + echarts.registerTheme('sunshineTheme', sunshineTheme); + echarts.registerTheme('sunshineSolidcolorTheme', sunshineSolidcolorTheme); + } + + function getTheme() { + return 'defaultTheme'; + } + + function getThemeByName(themeName: string) { + let theme: any = null; + switch (themeName) { + case 'defaultTheme': + theme = defaultTheme; + break; + case 'defaultSolidcolorTheme': + theme = defaultSolidcolorTheme; + break; + case 'auroraTheme': + theme = auroraTheme; + break; + case 'auroraSolidcolorTheme': + theme = auroraSolidcolorTheme; + break; + case 'sunshineTheme': + theme = sunshineTheme; + break; + case 'sunshineSolidcolorTheme': + theme = sunshineSolidcolorTheme; + break; + } + return theme; + } + + return { + registerTheme, + getTheme, + getThemeByName + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-timeline-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-timeline-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..6e795730a1bfdc27b488e0555a268b13c2cb9457 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-timeline-event.ts @@ -0,0 +1,21 @@ +import { SetupContext } from 'vue'; +import { UseTimeLineEvent } from './type'; + +export function useTimeLineEvent(instance: any, context: SetupContext): UseTimeLineEvent { + function onTimeLineChanged(): void { + instance.on('timelinechanged', function ($event) { + context.emit('timelinechanged', $event); + }); + } + + function onTimeLinePlayChanged(): void { + instance.on('timelineplaychanged', function ($event) { + context.emit('timelineplaychanged', $event); + }); + } + + return { + onTimeLineChanged, + onTimeLinePlayChanged + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-toolbox-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-toolbox-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..60d810814627c9e2110ee8cf5e992b0248cc7696 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-toolbox-event.ts @@ -0,0 +1,28 @@ +import { SetupContext } from 'vue'; +import { UseToolboxEvent } from './type'; + +export function useToolboxEvent(instance: any, context: SetupContext): UseToolboxEvent { + function onRestore(): void { + instance.on('restore', function ($event) { + context.emit('restore', $event); + }); + } + + function onDataViewChanged(): void { + instance.on('dataviewchanged', function ($event) { + context.emit('dataviewchanged', $event); + }); + } + + function onMagicTypeChanged(): void { + instance.on('magictypechanged', function ($event) { + context.emit('magictypechanged', $event); + }); + } + + return { + onRestore, + onDataViewChanged, + onMagicTypeChanged + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-tree-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-tree-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc077c83cd3eaea28dfbf922e59ca369b364e111 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-tree-event.ts @@ -0,0 +1,14 @@ +import { SetupContext } from 'vue'; +import { UseTreeEvent } from './type'; + +export function useTreeEvent(instance: any, context: SetupContext): UseTreeEvent { + function onTreeroam(): void { + instance.on('treeroam', function ($event) { + context.emit('treeroam', $event); + }); + } + + return { + onTreeroam + }; +} diff --git a/packages/charts-vue/components/charts-common/src/compositions/use-visualmap-event.ts b/packages/charts-vue/components/charts-common/src/compositions/use-visualmap-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..92c1de881fd2cff1f3b9d4cdd3d591627176db21 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/compositions/use-visualmap-event.ts @@ -0,0 +1,14 @@ +import { SetupContext } from 'vue'; +import { UseVisualMapEvent } from './type'; + +export function useVisualMapEvent(instance: any, context: SetupContext): UseVisualMapEvent { + function onDataRangeSelected(): void { + instance.on('datarangeselected', function ($event) { + context.emit('datarangeselected', $event); + }); + } + + return { + onDataRangeSelected + }; +} diff --git a/packages/charts-vue/components/charts-common/src/props/common/animation.props.ts b/packages/charts-vue/components/charts-common/src/props/common/animation.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..4487045e938d58ae0262ce14f11afc390089d627 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/animation.props.ts @@ -0,0 +1,16 @@ +import { ExtractPropTypes, PropType } from 'vue'; + +export const animationProps = { + /** 是否开启动画。 */ + animation: { type: Boolean, default: true }, + /** 是否开启动画的阈值,当单个系列显示的图形数量大于这个阈值时会关闭动画。 */ + animationThreshold: { type: Number, default: 2000 }, + /** 初始动画的时长,支持回调函数 */ + animationDuration: { type: Number, default: 1000 }, + /** 初始动画的缓动效果 */ + animationEasingUpdate: { type: String, default: 'linear' }, + /** 初始动画的延迟,支持回调函数 */ + animationDelayUpdate: { type: [Number, Function] } +}; + +export type AnimationProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/axis.props.ts b/packages/charts-vue/components/charts-common/src/props/common/axis.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..fbc4ef8994cfaf4d1db1e9c6a22cf1f1231cba8f --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/axis.props.ts @@ -0,0 +1,211 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { AlignLabelType, AlignType, AxisPointerType, AxisType, BorderType, FontStyleType, FontWeightType, LocationType, OverFlowType, PositionType, VerticalAlignType } from '../type/common.type'; +import { TextStyleProps } from './textstyle.props'; +import { LineStyleProps } from './legend.props'; +import { TooltipProps } from './grid.props'; +import { AreaStyleProps } from './datazoom.props'; +import { LabelProps } from './label.props'; + +export const nameTruncateProps = { + maxWidth: { type: Number }, + + ellipsis: { type: String } +}; + +export type NameTruncateProps = ExtractPropTypes; + +export const axisLineProps = { + show: { type: Boolean, default: true }, + + onZero: { type: Boolean, default: true }, + + onZeroAxisIndex: { type: Number }, + + symbol: { type: String, Array, default: 'none' }, + + symbolSize: { type: Array }, + + symbolOffset: { type: Array }, + + lineStyle: { type: Object as PropType } +}; + +export type AxisLineProps = ExtractPropTypes; + +export const axisTickProps = { + show: { type: Boolean, default: true }, + + alignWithLabel: { type: Boolean, default: false }, + + interval: { type: [Number, Function], default: 'auto' }, + + inside: { type: Boolean, default: false }, + + length: { type: Number, default: 5 }, + + lineStyle: { type: Object as PropType } +}; + +export type AxisTickProps = ExtractPropTypes; + +export const axisLabelProps = { + show: { type: Boolean, default: true }, + + interval: { type: [Number, Function], default: 'auto' }, + + inside: { type: Boolean, default: false }, + + rotate: { type: Number, default: 0 }, + + margin: { type: Number, default: 8 }, + + formatter: { type: [String, Function] }, + + showMinLabel: { type: Boolean, default: null }, + + showMaxLabel: { type: Boolean, default: null }, + + alignMinLabel: { type: String as PropType, default: null }, + + alignMaxLabel: { type: String as PropType, default: null }, + + hideOverlap: { type: Boolean }, + + customValues: { type: Array }, + + color: { type: Function as PropType<(val: string) => string> }, + + fontStyle: { type: String as PropType }, + + fontWeight: { type: String as PropType , Number }, + + fontFamily: { type: String }, + + fontSize: { type: Number }, + + width: { type: Number }, + + height: { type: Number }, + + align: { type: String as PropType }, + + verticalAlign: { type: String as PropType }, + + borderColor: { type: String }, + + borderWidth: { type: Number }, + + borderType: { type: String as PropType }, + + borderRadius: { type: Number }, + + padding: { type: [Number, Array] }, + + overflow: { type: String as PropType } +}; + +export type AxisLabelProps = ExtractPropTypes; + +export const splitAreaProps = { + show: { type: Boolean }, + + interval: { type: [Number, Function], default: 'auto' }, + + areaStyle: { type: Object as PropType }, +}; + +export type SplitAreaProps = ExtractPropTypes; + +export const axisPointerProps = { + show: { type: Boolean }, + + type: { type: String as PropType }, + + snap: { type: Boolean }, + + z: { type: Number }, + + label: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + triggerEmphasis: { type: Boolean, default: true }, + + triggerTooltip: { type: Boolean, default: true }, + + value: { type: Number } +}; + +export type AxisPointerProps = ExtractPropTypes; + +export const axisProps = { + id: { type: String }, + + show: { type: Boolean, default: true }, + + gridIndex: { type: Number, default: 0 }, + + alignTicks: { type: Boolean, default: false }, + + position: { type: String as PropType }, + + offset: { type: Number, default: 0 }, + + type: { type: String as PropType, default: 'value' }, + + name: { type: String }, + + nameLocation: { type: String as PropType, default: 'end' }, + + nameTextStyle: { type: Object as PropType }, + + nameGap: { type: Number }, + + nameRotate: { type: Number }, + + nameTruncate: { type: Object as PropType }, + + inverse: { type: Boolean, default: false }, + + boundaryGap: { type: [Boolean, Array] }, + + min: { type: [Number, String, Function] }, + + max: { type: [Number, String, Function] }, + + scale: { type: Boolean, default: false }, + + splitNumber: { type: Number, default: 5 }, + + minInterval: { type: Number }, + + maxInterval: { type: Number }, + + logBase: { type: Number, default: 10 }, + + startValue: { type: Number }, + + silent: { type: Boolean, default: false }, + + triggerEvent: { type: Boolean, default: false }, + + axisLine: { type: Object as ExtractPropTypes }, + + axisTick: { type: Object as PropType }, + + axisLabel: { type: Object as PropType }, + + splitArea: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + axisPointer: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + data: { type: Array } +}; + +export type AxisProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/blur.props.ts b/packages/charts-vue/components/charts-common/src/props/common/blur.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..c96792d5a281c2f4dbffb3b77af9dc6de14f9d74 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/blur.props.ts @@ -0,0 +1,20 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { LabelProps } from './label.props'; +import { LabelLineProps } from './labelline.props'; +import { ItemStyleProps } from './itemstyle.props'; +import { LineStyleProps } from './legend.props'; +import { AreaStyleProps } from './datazoom.props'; + +export const blurProps = { + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + areaStyle: { type: Object as PropType } +}; + +export type BlurProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/border.props.ts b/packages/charts-vue/components/charts-common/src/props/common/border.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..d13cf386fdf190a8708a496392b86eb287d6b320 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/border.props.ts @@ -0,0 +1,15 @@ +import { ExtractPropTypes, PropType } from 'vue'; + +export const borderProps = { + borderColor: { type: String }, + + borderWidth: { type: Number }, + /** 边框类型: solid dashed dotted */ + borderType: { type: [String, Number, Array] }, + + borderDashOffset: { type: Number }, + + borderRadius: { type: [Number, Array] } +}; + +export type BorderProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/data.props.ts b/packages/charts-vue/components/charts-common/src/props/common/data.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..0361289f10250e1d0709d4bd5a11b23f32ba0c3c --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/data.props.ts @@ -0,0 +1,39 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { LabelProps } from './label.props'; +import { LabelLineProps } from './labelline.props'; +import { ItemStyleProps } from './itemstyle.props'; +import { BlurProps } from './blur.props'; +import { TooltipProps } from './grid.props'; +import { SelectProps } from './select.props'; + +export const dataProps = { + name: { type: String }, + + value: { type: String }, + + groupId: { type: String }, + + childGroupId: { type: String }, + + symbol: { type: [String, Array], default: 'none' }, + + symbolSize: { type: Array }, + + symbolOffset: { type: Array }, + + symbolRotate: { type: Number }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + select: { type: Object as PropType } +}; + +export type DataProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/datazoom.props.ts b/packages/charts-vue/components/charts-common/src/props/common/datazoom.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..e71620e1cf3fc5eb97d89c80f7aff6b42935f658 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/datazoom.props.ts @@ -0,0 +1,89 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { LineStyleProps } from './legend.props'; +import { TextStyleProps } from './textstyle.props'; + +export const areaStyleProps = { + color: { type: String }, + + opacity: { type: Number } +}; + +export type AreaStyleProps = ExtractPropTypes; + +export const dataBackgroundProps = { + color: { type: String }, + + width: { type: Number }, + + type: { type: [String, Number, Array] }, + + lineStyle: { type: Object as PropType }, + + areaStyle: { type: Object as PropType }, +}; + +export type DataBackgroundProps = ExtractPropTypes; + +export const selectedDataBackgroundProps = { + lineStyle: { type: Object as PropType }, + + areaStyle: { type: Object as PropType } +}; + +export type SelectedDataBackgroundProps = ExtractPropTypes; + +export const dataZoomProps = { + type: { type: String, default: 'slider' }, + + id: { type: String }, + + show: { type: Boolean, default: true }, + + backgroundColor: { type: String }, + + dataBackground: { type: Object as PropType }, + + selectedDataBackground: { type: Object as PropType }, + + fillerColor: { type: String }, + + borderColor: { type: String }, + + borderRadius: { type: Number }, + + showDetails: { type: Boolean, default: true }, + + realtime: { type: Boolean, default: true }, + + textStyle: { type: Object as PropType }, + + xAxisIndex: { type: [Number, Array] }, + + yAxisIndex: { type: [Number, Array] }, + + start: { type: Number }, + + end: { type: Number }, + + minSpan: { type: Number }, + + maxSpan: { type: Number }, + + zlevel: { type: Number }, + + z: { type: Number }, + + top: { type: [String, Number] }, + + bottom: { type: [String, Number] }, + + left: { type: [String, Number] }, + + right: { type: [String, Number] }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, +}; + +export type DataZoomProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/emphasis.props.ts b/packages/charts-vue/components/charts-common/src/props/common/emphasis.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..dee43a1144b8006f84468f749b901e9273444bd7 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/emphasis.props.ts @@ -0,0 +1,23 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { FocusType, BlurScopeType } from '../type/common.type'; +import { LabelProps } from './label.props'; +import { LabelLineProps } from './labelline.props'; +import { ItemStyleProps } from './itemstyle.props'; + + +export const emphasisProps = { + + disabled: { type: Boolean, default: false }, + + focus: { type: String as PropType, default: 'series' }, + + blurScope: { type: String as PropType, default: 'coordinateSystem' }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + itemStyle: { type: Object as PropType } +}; + +export type EmphasisProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/grid.props.ts b/packages/charts-vue/components/charts-common/src/props/common/grid.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..cf066a7d0456d437d693e0b4b6c1502c180260bc --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/grid.props.ts @@ -0,0 +1,56 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { TriggerType } from '../type/common.type'; + +export const tooltipProps = { + show: { type: Boolean, default: true }, + + padding: { type: Array as unknown as PropType }, + + trigger: { type: String as PropType , default: 'item' }, + + formatter: { type: [String, Function] }, + + valueFormatter: { type: Object as PropType<(value: number | string, dataIndex: number) => string> }, + + backgroundColor: { type: String }, + + borderColor: { type: String }, + + borderWidth: { type: Number } +}; + +export type TooltipProps = ExtractPropTypes; + +export const gridProps = { + id: { type: String }, + + show: { type: Boolean, default: true }, + + zlevel: { type: Number }, + + z: { type: Number }, + + top: { type: [String, Number] }, + + bottom: { type: [String, Number] }, + + left: { type: [String, Number] }, + + right: { type: [String, Number] }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + containLabel: { type: Boolean }, + + backgroundColor: { type: String }, + + borderColor: { type: String }, + + borderWidth: { type: Number }, + + tooltip: { type: Object as PropType } +}; + +export type GridProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/itemstyle.props.ts b/packages/charts-vue/components/charts-common/src/props/common/itemstyle.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..badab5b84466ea83486abcc8532804f55c6f994b --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/itemstyle.props.ts @@ -0,0 +1,18 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { BorderType } from '../type/common.type'; + +export const itemStyleProps = { + color: { type: String, default: 'auto' }, + + borderColor: { type: String }, + + borderWidth: { type: Number }, + + borderType: { type: String as PropType }, + + borderRadius: { type: Number }, + + opacity: { type: Number } +}; + +export type ItemStyleProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/label.props.ts b/packages/charts-vue/components/charts-common/src/props/common/label.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed4d880438abf2bd6a781176ef4f4910f02d243b --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/label.props.ts @@ -0,0 +1,48 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { AlignType, BorderType, FontStyleType, FontWeightType, OverFlowType, PositionType, VerticalAlignType } from '../type/common.type'; + +export const labelProps = { + show: { type: Boolean, default: false }, + + distance: { type: Number, default: 5 }, + + rotate: { type: Number }, + + offset: { type: Array }, + + formatter: { type: [String, Function] }, + + color: { type: String }, + + fontStyle: { type: String as PropType }, + + fontWeight: { type: [String as PropType , Number] }, + + fontFamily: { type: String }, + + fontSize: { type: Number }, + + align: { type: String as PropType }, + + verticalAlign: { type: String as PropType }, + + borderColor: { type: String }, + + borderWidth: { type: Number }, + + borderType: { type: String as PropType }, + + borderRadius: { type: Number }, + + padding: { type: [Number, Array] }, + + width: { type: Number }, + + height: { type: Number }, + + overflow: { type: String as PropType }, + + position: { type: String as PropType } +}; + +export type LabelProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/labellayout.props.ts b/packages/charts-vue/components/charts-common/src/props/common/labellayout.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..e789d3c3cd9c2d0263a690a98ce164470b067c0e --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/labellayout.props.ts @@ -0,0 +1,34 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { AlignType, MoveOverlapType, VerticalAlignType } from '../type/common.type'; + +export const labelLayoutProps = { + hideOverlap: { type: Boolean }, + + moveOverlap: { type: String as PropType }, + + x: { type: [String, Number] }, + + y: { type: [String, Number] }, + + dx: { type: Number }, + + dy: { type: Number }, + + rotate: { type: Number }, + + width: { type: Number }, + + height: { type: Number }, + + align: { type: String as PropType }, + + verticalAlign: { type: String as PropType }, + + fontSize: { type: Number }, + + draggable: { type: Boolean }, + + labelLinePoints: { type: Array } +}; + +export type LabelLayoutProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/labelline.props.ts b/packages/charts-vue/components/charts-common/src/props/common/labelline.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..3064b42682c7491f776f5b1375965f7c052f9879 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/labelline.props.ts @@ -0,0 +1,10 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { LineStyleProps } from './legend.props'; + +export const labelLineProps = { + show: { type: Boolean }, + + lineStyle: { type: Object as PropType } +}; + +export type LabelLineProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/legend.props.ts b/packages/charts-vue/components/charts-common/src/props/common/legend.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..be320467cc5c0a7e7b7cbea5ac1979547528bd35 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/legend.props.ts @@ -0,0 +1,85 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { LegendType, AlignType, OrientType, BorderType, CapType, JoinType } from '../type/common.type'; +import { TextStyleProps } from './textstyle.props'; +import { ItemStyleProps } from './itemstyle.props'; +import { TooltipProps } from './grid.props'; + +export const lineStyleProps = { + color: { type: String }, + + width: { type: Number }, + + type: { type: String as PropType }, + + dashOffset: { type: Number }, + + cap: { type: String as PropType }, + + join: { type: String as PropType }, + + opacity: { type: Number } +}; + +export type LineStyleProps = ExtractPropTypes; + +export const legendDataProps = { + name: { type: String }, + + icon: { type: String }, + + value: { type: String }, + + textStyle: { type: Object as ExtractPropTypes } +}; + +export type LegendDataProps = ExtractPropTypes; + +export const legendProps = { + id: { type: String }, + + show: { type: Boolean, default: true }, + + type: { type: String as PropType }, + + top: { type: [String, Number], default: 'auto' }, + + bottom: { type: [String, Number], default: 'auto' }, + + left: { type: [String, Number], default: 'auto' }, + + right: { type: [String, Number], default: 'auto' }, + + width: { type: [String, Number], default: 'auto' }, + + height: { type: [String, Number], default: 'auto' }, + + orient: { type: String as PropType }, + + align: { type: String as PropType, default: 'auto' }, + + itemGap: { type: Number, default: 10 }, + + itemWidth: { type: Number, default: 25 }, + + itemHeight: { type: Number, default: 14 }, + + itemStyle: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + formatter: { type: [String, Function as PropType<(name: string) => string>] }, + + selectedMode: { type: Boolean, default: true }, + + textStyle: { type: Object as PropType }, + + tooltip: { type: Object as PropType, default: true }, + + icon: { type: String }, + + data: { type: Array }, + + borderRadius: { type: [String, Number] } +}; + +export type LegendProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/scalelimit.props.ts b/packages/charts-vue/components/charts-common/src/props/common/scalelimit.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..86ff928ec97e2c0fbe483a11e7fe5ba83e3fbaba --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/scalelimit.props.ts @@ -0,0 +1,11 @@ +import { ExtractPropTypes } from 'vue'; + +export const scaleLimitProps = { + + min: { type: Number}, + + max: { type: Number} + +}; + +export type ScaleLimitProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/select.props.ts b/packages/charts-vue/components/charts-common/src/props/common/select.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..be6a009fbb73c8f5c5fa306fffdfb0e98ad204a0 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/select.props.ts @@ -0,0 +1,16 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { LabelProps } from './label.props'; +import { LabelLineProps } from './labelline.props'; +import { ItemStyleProps } from './itemstyle.props'; + +export const selectProps = { + disabled: { type: Boolean }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + itemStyle: { type: Object as PropType } +}; + +export type SelectProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/textstyle.props.ts b/packages/charts-vue/components/charts-common/src/props/common/textstyle.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..aed62f958d2217b5436d4966777ac146c0bb5705 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/textstyle.props.ts @@ -0,0 +1,32 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { FontStyleType, FontWeightType, OverFlowType, AlignType, VerticalAlignType } from '../type/common.type'; + +export const textStyleProps = { + color: { type: String }, + + fontStyle: { type: String as PropType }, + + fontWeight: { type: String as PropType , Number }, + + fontFamily: { type: String }, + + lineHeight: { type: Number }, + + width: { type: Number }, + + height: { type: Number }, + + overflow: { type: String as PropType } +}; + +export type TextStyleProps = ExtractPropTypes; + +export const subTextStyleProps = { + ...textStyleProps, + + align: { type: String as PropType }, + + verticalAlign: { type: String as PropType } +}; + +export type SubTextStyleProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/title.props.ts b/packages/charts-vue/components/charts-common/src/props/common/title.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..09483965b0d62546e4605a52363858567922cb60 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/title.props.ts @@ -0,0 +1,44 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { SubTextStyleProps, TextStyleProps } from './textstyle.props'; +import { LinkType } from '../type/common.type'; +import { borderProps } from './border.props'; + +export const titleProps = { + id: { type: String }, + + show: { type: Boolean, default: true }, + + text: { type: String }, + + target: { type: String as PropType, default: 'blank' }, + + textStyle: { type: Object as PropType }, + + subtext: { type: String }, + + sublink: { type: String }, + + subtarget: { type: String as PropType, default: 'blank' }, + + subtextStyle: { type: Object as PropType }, + + triggerEvent: { type: Boolean, default: true }, + + padding: { type: Array as unknown as PropType }, + + itemGap: { type: Number, default: 10 }, + + top: { type: [String, Number], default: 'auto' }, + + bottom: { type: [String, Number], default: 'auto' }, + + left: { type: [String, Number], default: 'auto' }, + + right: { type: [String, Number], default: 'auto' }, + + backgroundColor: { type: String }, + + ...borderProps +}; + +export type TitleProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/common/toolbox.props.ts b/packages/charts-vue/components/charts-common/src/props/common/toolbox.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4f96fe37ab351f88cb6f8464903a5ea1c351550 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/common/toolbox.props.ts @@ -0,0 +1,42 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { OrientType } from '../type/common.type'; + +export const toolboxFeatureProps = { + dataZoom: { type: Object, default: {} }, + + restore: { type: Object, default: {} }, + + saveAsImage: { type: Object, default: {} }, +}; + +export type toolboxFeatureProps = ExtractPropTypes; + +export const toolboxProps = { + id: { type: String }, + + show: { type: Boolean, default: true }, + + orient: { type: String as PropType, default: 'horizontal' }, + + showTitle: { type: Boolean, default: true }, + + top: { type: [String, Number] }, + + bottom: { type: [String, Number] }, + + left: { type: [String, Number] }, + + right: { type: [String, Number] }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + zlevel: { type: Number }, + + z: { type: Number }, + + feature: { type: Object as PropType } +}; + +export type ToolboxProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-common/src/props/type/common.type.ts b/packages/charts-vue/components/charts-common/src/props/type/common.type.ts new file mode 100644 index 0000000000000000000000000000000000000000..a77f33f2bbb8729b1f97fe89d5442eebb1ad59c2 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/props/type/common.type.ts @@ -0,0 +1,49 @@ +export type FontStyleType = 'normal' | 'italic' | 'oblique'; + +export type FontWeightType = 'normal' | 'bold' | 'bolder' | 'lighter'; + +export type OverFlowType = 'none' | 'truncate' | 'break' | 'breakAll'; + +export type AlignType = 'left' | 'center' | 'right' | 'auto'; + +export type VerticalAlignType = 'top' | 'middle' | 'bottom' | 'auto'; + +export type LinkType = 'blank' | 'self'; + +export type LegendType = 'plain' | 'scroll'; + +export type OrientType = 'horizontal' | 'vertical'; + +export type PositionType = 'top' | 'bottom'; + +export type AxisType = 'value' | 'category' | 'time' | 'log'; + +export type LocationType = 'start' | 'middle' | 'center' | 'end'; + +export type BorderType = 'solid' | 'dashed' | 'dotted'; + +export type CapType = 'butt' | 'round' | 'square'; + +export type JoinType = 'bevel' | 'round' | 'miter'; + +export type MoveOverlapType = 'shiftX' | 'shifY'; + +export type AlignLabelType = 'left' | 'center' | 'right' | null; + +export type AxisPointerType = 'line' | 'shadow' | 'none'; + +export type ColorByType = 'series' | 'data'; + +export type StackStrategyType = 'samesign' | 'all' | 'positive' | 'negative'; + +export type SelectedModeType = 'single' | 'multiple' | 'series'; + +export type SamplingType = 'lttb' | 'average' | 'min' | 'max' | 'minmax' | 'sum'; + +export type TriggerType = 'item' | 'axis' | 'none'; + +export type CombinedType = 'line' | 'bar'; + +export type FocusType = 'self' | 'series' | 'none'; + +export type BlurScopeType = 'coordinateSystem' | 'series' | 'global'; diff --git a/packages/charts-vue/components/charts-common/src/theme/aurora-solidcolor.ts b/packages/charts-vue/components/charts-common/src/theme/aurora-solidcolor.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3b1af2303b65849d48a08cd81c1a443062731c9 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/theme/aurora-solidcolor.ts @@ -0,0 +1,79 @@ +import * as echarts from 'echarts'; + +const color1 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#5C90F9' // 0% 处的颜色 +}, { + offset: 1, color: '#5C90F9' // 100% 处的颜色 +}]); + +const color2 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#04BED6' // 0% 处的颜色 +}, { + offset: 1, color: '#04BED6' // 100% 处的颜色 +}]); + +const color3 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#56BF7D' // 0% 处的颜色 +}, { + offset: 1, color: '#56BF7D' // 100% 处的颜色 +}]); + +const color4 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#6F74DC' // 0% 处的颜色 +}, { + offset: 1, color: '#6F74DC' // 100% 处的颜色 +}]); + +const color5 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#739AE1' // 0% 处的颜色 +}, { + offset: 1, color: '#739AE1' // 100% 处的颜色 +}]); + +const color6 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#22C3AA' // 0% 处的颜色 +}, { + offset: 1, color: '#22C3AA' // 100% 处的颜色 +}]); + +const color7 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#4F79F6' // 0% 处的颜色 +}, { + offset: 1, color: '#4F79F6' // 100% 处的颜色 +}]); + +const color8 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#8E7CE1' // 0% 处的颜色 +}, { + offset: 1, color: '#8E7CE1' // 100% 处的颜色 +}]); + +const color9 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#8B9EC5' // 0% 处的颜色 +}, { + offset: 1, color: '#8B9EC5' // 100% 处的颜色 +}]); + +const color10 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#6E85B4' // 0% 处的颜色 +}, { + offset: 1, color: '#6E85B4' // 100% 处的颜色 +}]); + +const colorPalette: any = [ + color1, + color2, + color3, + color4, + color5, + color6, + color7, + color8, + color9, + color10 +]; +export const auroraSolidcolorTheme: any = { + color: colorPalette +}; + +// echarts.registerTheme('dark', theme); diff --git a/packages/charts-vue/components/charts-common/src/theme/aurora.ts b/packages/charts-vue/components/charts-common/src/theme/aurora.ts new file mode 100644 index 0000000000000000000000000000000000000000..d21eeca556a79550c96b20912821737d7799690e --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/theme/aurora.ts @@ -0,0 +1,79 @@ +import * as echarts from 'echarts'; + +const color1 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#5C90F9' // 0% 处的颜色 +}, { + offset: 1, color: '#5CB4F9' // 100% 处的颜色 +}]); + +const color2 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#04BED6' // 0% 处的颜色 +}, { + offset: 1, color: '#19DAE2' // 100% 处的颜色 +}]); + +const color3 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#56BF7D' // 0% 处的颜色 +}, { + offset: 1, color: '#69D691' // 100% 处的颜色 +}]); + +const color4 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#6F74DC' // 0% 处的颜色 +}, { + offset: 1, color: '#8196F9' // 100% 处的颜色 +}]); + +const color5 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#739AE1' // 0% 处的颜色 +}, { + offset: 1, color: '#87AFF7' // 100% 处的颜色 +}]); + +const color6 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#22C3AA' // 0% 处的颜色 +}, { + offset: 1, color: '#31DFA2' // 100% 处的颜色 +}]); + +const color7 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#4F79F6' // 0% 处的颜色 +}, { + offset: 1, color: '#5193EF' // 100% 处的颜色 +}]); + +const color8 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#8E7CE1' // 0% 处的颜色 +}, { + offset: 1, color: '#A394FE' // 100% 处的颜色 +}]); + +const color9 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#8B9EC5' // 0% 处的颜色 +}, { + offset: 1, color: '#9EBCD4' // 100% 处的颜色 +}]); + +const color10 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#6E85B4' // 0% 处的颜色 +}, { + offset: 1, color: '#849BC8' // 100% 处的颜色 +}]); + +const colorPalette: any = [ + color1, + color2, + color3, + color4, + color5, + color6, + color7, + color8, + color9, + color10 +]; +export const auroraTheme: any = { + color: colorPalette +}; + +// echarts.registerTheme('dark', theme); diff --git a/packages/charts-vue/components/charts-common/src/theme/default-solidcolor.ts b/packages/charts-vue/components/charts-common/src/theme/default-solidcolor.ts new file mode 100644 index 0000000000000000000000000000000000000000..90c3f8fdb57fb3f5a5334c024f66e4c5639033c8 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/theme/default-solidcolor.ts @@ -0,0 +1,79 @@ +import * as echarts from 'echarts'; + +const color1 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#5C90F9' // 0% 处的颜色 +}, { + offset: 1, color: '#5C90F9' // 100% 处的颜色 +}]); + +const color2 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#22C3AA' // 0% 处的颜色 +}, { + offset: 1, color: '#22C3AA' // 100% 处的颜色 +}]); + +const color3 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FFB45A' // 0% 处的颜色 +}, { + offset: 1, color: '#FFB45A' // 100% 处的颜色 +}]); + +const color4 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#7C80E1' // 0% 处的颜色 +}, { + offset: 1, color: '#7C80E1' // 100% 处的颜色 +}]); + +const color5 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#1CE0E9' // 0% 处的颜色 +}, { + offset: 1, color: '#1CE0E9' // 100% 处的颜色 +}]); + +const color6 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FFC45C' // 0% 处的颜色 +}, { + offset: 1, color: '#FFC45C' // 100% 处的颜色 +}]); + +const color7 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#87AFF7' // 0% 处的颜色 +}, { + offset: 1, color: '#87AFF7' // 100% 处的颜色 +}]); + +const color8 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#72DD9A' // 0% 处的颜色 +}, { + offset: 1, color: '#72DD9A' // 100% 处的颜色 +}]); + +const color9 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#D199E2' // 0% 处的颜色 +}, { + offset: 1, color: '#D199E2' // 100% 处的颜色 +}]); + +const color10 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#849BC8' // 0% 处的颜色 +}, { + offset: 1, color: '#849BC8' // 100% 处的颜色 +}]); + +const colorPalette: any = [ + color1, + color2, + color3, + color4, + color5, + color6, + color7, + color8, + color9, + color10 +]; +export const defaultSolidcolorTheme: any = { + color: colorPalette +}; + +// echarts.registerTheme('dark', theme); diff --git a/packages/charts-vue/components/charts-common/src/theme/default.ts b/packages/charts-vue/components/charts-common/src/theme/default.ts new file mode 100644 index 0000000000000000000000000000000000000000..92026cf054ba2a24c1e1c59c4f4ecb1085394e38 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/theme/default.ts @@ -0,0 +1,79 @@ +import * as echarts from 'echarts'; + +const color1 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#5C90F9' // 0% 处的颜色 +}, { + offset: 1, color: '#5CB4F9' // 100% 处的颜色 +}]); + +const color2 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#22C3AA' // 0% 处的颜色 +}, { + offset: 1, color: '#31DFA2' // 100% 处的颜色 +}]); + +const color3 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FFB45A' // 0% 处的颜色 +}, { + offset: 1, color: '#FC9657' // 100% 处的颜色 +}]); + +const color4 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#7C80E1' // 0% 处的颜色 +}, { + offset: 1, color: '#8A9EFE' // 100% 处的颜色 +}]); + +const color5 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#1CE0E9' // 0% 处的颜色 +}, { + offset: 1, color: '#04BED6' // 100% 处的颜色 +}]); + +const color6 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FFC45C' // 0% 处的颜色 +}, { + offset: 1, color: '#F1B141' // 100% 处的颜色 +}]); + +const color7 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#87AFF7' // 0% 处的颜色 +}, { + offset: 1, color: '#739AE1' // 100% 处的颜色 +}]); + +const color8 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#72DD9A' // 0% 处的颜色 +}, { + offset: 1, color: '#56BF7D' // 100% 处的颜色 +}]); + +const color9 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#D199E2' // 0% 处的颜色 +}, { + offset: 1, color: '#BF88D0' // 100% 处的颜色 +}]); + +const color10 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#849BC8' // 0% 处的颜色 +}, { + offset: 1, color: '#6E85B4' // 100% 处的颜色 +}]); + +const colorPalette: any = [ + color1, + color2, + color3, + color4, + color5, + color6, + color7, + color8, + color9, + color10 +]; +export const defaultTheme: any = { + color: colorPalette +}; + +// echarts.registerTheme('dark', theme); diff --git a/packages/charts-vue/components/charts-common/src/theme/sunshine-solidcolor.ts b/packages/charts-vue/components/charts-common/src/theme/sunshine-solidcolor.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e169345d7cdb7840468972930d537cce0ccf2d1 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/theme/sunshine-solidcolor.ts @@ -0,0 +1,79 @@ +import * as echarts from 'echarts'; + +const color1 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#3B91FF' // 0% 处的颜色 +}, { + offset: 1, color: '#3B91FF' // 100% 处的颜色 +}]); + +const color2 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FF8B00' // 0% 处的颜色 +}, { + offset: 1, color: '#FF8B00' // 100% 处的颜色 +}]); + +const color3 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#01C7C7' // 0% 处的颜色 +}, { + offset: 1, color: '#01C7C7' // 100% 处的颜色 +}]); + +const color4 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FFB400' // 0% 处的颜色 +}, { + offset: 1, color: '#FFB400' // 100% 处的颜色 +}]); + +const color5 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#4EC87A' // 0% 处的颜色 +}, { + offset: 1, color: '#4EC87A' // 100% 处的颜色 +}]); + +const color6 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#878BFF' // 0% 处的颜色 +}, { + offset: 1, color: '#878BFF' // 100% 处的颜色 +}]); + +const color7 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#4F7CFF' // 0% 处的颜色 +}, { + offset: 1, color: '#4F7CFF' // 100% 处的颜色 +}]); + +const color8 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#F47A12' // 0% 处的颜色 +}, { + offset: 1, color: '#F47A12' // 100% 处的颜色 +}]); + +const color9 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#739AE1' // 0% 处的颜色 +}, { + offset: 1, color: '#739AE1' // 100% 处的颜色 +}]); + +const color10 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#6E85B4' // 0% 处的颜色 +}, { + offset: 1, color: '#6E85B4' // 100% 处的颜色 +}]); + +const colorPalette: any = [ + color1, + color2, + color3, + color4, + color5, + color6, + color7, + color8, + color9, + color10 +]; +export const sunshineSolidcolorTheme: any = { + color: colorPalette +}; + +// echarts.registerTheme('dark', theme); diff --git a/packages/charts-vue/components/charts-common/src/theme/sunshine.ts b/packages/charts-vue/components/charts-common/src/theme/sunshine.ts new file mode 100644 index 0000000000000000000000000000000000000000..82886c62826681d20dd1a784979803f97c30b855 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/theme/sunshine.ts @@ -0,0 +1,79 @@ +import * as echarts from 'echarts'; + +const color1 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#3B91FF' // 0% 处的颜色 +}, { + offset: 1, color: '#6AC1FF' // 100% 处的颜色 +}]); + +const color2 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FF8B00' // 0% 处的颜色 +}, { + offset: 1, color: '#FFB046' // 100% 处的颜色 +}]); + +const color3 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#01C7C7' // 0% 处的颜色 +}, { + offset: 1, color: '#3CE7C8' // 100% 处的颜色 +}]); + +const color4 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#FFB400' // 0% 处的颜色 +}, { + offset: 1, color: '#EFC80A' // 100% 处的颜色 +}]); + +const color5 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#4EC87A' // 0% 处的颜色 +}, { + offset: 1, color: '#58E092' // 100% 处的颜色 +}]); + +const color6 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#878BFF' // 0% 处的颜色 +}, { + offset: 1, color: '#8E9CFF' // 100% 处的颜色 +}]); + +const color7 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#4F7CFF' // 0% 处的颜色 +}, { + offset: 1, color: '#65B0FF' // 100% 处的颜色 +}]); + +const color8 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#F47A12' // 0% 处的颜色 +}, { + offset: 1, color: '#FF9373' // 100% 处的颜色 +}]); + +const color9 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#739AE1' // 0% 处的颜色 +}, { + offset: 1, color: '#87AFF7' // 100% 处的颜色 +}]); + +const color10 = new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ + offset: 0, color: '#6E85B4' // 0% 处的颜色 +}, { + offset: 1, color: '#849BC8' // 100% 处的颜色 +}]); + +const colorPalette: any = [ + color1, + color2, + color3, + color4, + color5, + color6, + color7, + color8, + color9, + color10 +]; +export const sunshineTheme: any = { + color: colorPalette +}; + +// echarts.registerTheme('dark', theme); diff --git a/packages/charts-vue/components/charts-common/src/utils/with-install.ts b/packages/charts-vue/components/charts-common/src/utils/with-install.ts new file mode 100644 index 0000000000000000000000000000000000000000..3bce8e707a3f40c9a79f95a9c4a7c093f0805aa7 --- /dev/null +++ b/packages/charts-vue/components/charts-common/src/utils/with-install.ts @@ -0,0 +1,10 @@ +import type { App, Component, Plugin } from 'vue'; + +export const withInstall = (component: T) => { + const c = component as any; + c.install = function (app: App) { + app.component(c.name, component); + }; + + return component as T & Plugin; +}; diff --git a/packages/charts-vue/components/charts-funnel/index.ts b/packages/charts-vue/components/charts-funnel/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c1c6541f0c819f7a80f84a66118b2c085717e27b --- /dev/null +++ b/packages/charts-vue/components/charts-funnel/index.ts @@ -0,0 +1,6 @@ +import FChartsFunnel from './src/charts-funnel.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-funnel.props'; +export { FChartsFunnel }; +export default withInstall(FChartsFunnel); diff --git a/packages/charts-vue/components/charts-funnel/src/charts-funnel.component.tsx b/packages/charts-vue/components/charts-funnel/src/charts-funnel.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c03f66c8cc18e060775ab3346d9e485843d47f26 --- /dev/null +++ b/packages/charts-vue/components/charts-funnel/src/charts-funnel.component.tsx @@ -0,0 +1,130 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsFunnelProps, chartsFunnelProps } from './charts-funnel.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '../../charts-common'; + +export default defineComponent({ + name: 'FChartsFunnel', + props: chartsFunnelProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsFunnelProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'funnel'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-funnel/src/charts-funnel.props.ts b/packages/charts-vue/components/charts-funnel/src/charts-funnel.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..077626ce6e95da5f7badbe09b11d6a7fb82bdef4 --- /dev/null +++ b/packages/charts-vue/components/charts-funnel/src/charts-funnel.props.ts @@ -0,0 +1,134 @@ +import { ExtractPropTypes, PropType } from 'vue'; + +import { + OrientType, animationProps, ToolboxProps, DataProps, ColorByType, LabelProps, + LabelLineProps, LabelLayoutProps, ItemStyleProps, BlurProps, + SelectProps, TooltipProps, SelectedModeType, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps, EmphasisProps +} from '@farris/charts-vue/components/charts-common'; + +export type SortType = 'ascending' | 'descending' | 'none'; + +export type FunnelAlignType = 'left' | 'right' | 'center'; + +export const funnelSeriesProps = { + + type: { type: String, default: 'funnel' }, + + id: { type: String }, + + name: { type: String }, + + colorBy: { type: String as PropType }, + + min: { type: Number }, + + max: { type: Number }, + + minSize: { type: [String, Array] }, + + maxSize: { type: [String, Array] }, + + orient: { type: String as PropType, default: 'vertical' }, + + sort: { type: String as PropType , default: 'descending' }, + + gap: { type: Number, default: 0 }, + + legendHoverLink: { type: Boolean, default: true }, + + funnelAlign: { type: String as PropType, default: 'center' }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + emphasis: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + selectedMode: { type: String as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + left: { type: [String, Number] }, + + right: { type: [String, Number] }, + + top: { type: [String, Number] }, + + bottom: { type: [String, Number] }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + /** 当使用 dataset 时,seriesLayoutBy 指定了 dataset 中用行还是列对应到系列上。column row */ + seriesLayoutBy: { type: String }, + + /** 如果 series.data 没有指定,并且 dataset 存在,那么就会使用 dataset。datasetIndex 指定本系列使用哪个 dataset */ + datasetIndex: { type: Number }, + + /** 使用 dimensions 定义 series.data 或者 dataset.source 的每个维度的信息。 */ + dimensions: { type: Array }, + + /** 可以定义 data 的哪个维度被编码成什么。 */ + encode: { type: Object }, + + dataGroupId: { type: String }, + + data: { type: Object as PropType }, + + // 是否需要封装为公共的props + // markPoint: { type: Object as PropType }, + // markLine: { type: Object as PropType }, + // markArea: { type: Object as PropType }, + + silent: { type: Boolean }, + + ...animationProps, + + tooltip: { type: Object as PropType }, +}; + +export type FunnelSeriesProps = ExtractPropTypes; + +export const chartsFunnelProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + textStyle: { type: Object as PropType }, + + series: { type: Array as PropType, default: [{ ...funnelSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps +}; + +export type ChartsFunnelProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-graph/index.ts b/packages/charts-vue/components/charts-graph/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..64c55107ae1bda6638fe47d66e3f33ce0393c896 --- /dev/null +++ b/packages/charts-vue/components/charts-graph/index.ts @@ -0,0 +1,6 @@ +import FChartsGraph from './src/charts-graph.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-graph.props'; +export { FChartsGraph }; +export default withInstall(FChartsGraph); diff --git a/packages/charts-vue/components/charts-graph/src/charts-graph.component.tsx b/packages/charts-vue/components/charts-graph/src/charts-graph.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cbaa6675d7e609ffb8522b701726edca3eac7e73 --- /dev/null +++ b/packages/charts-vue/components/charts-graph/src/charts-graph.component.tsx @@ -0,0 +1,131 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsGraphProps, chartsGraphProps } from './charts-graph.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '../../charts-common'; + +export default defineComponent({ + name: 'FChartsGraph', + props: chartsGraphProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsGraphProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + // xAxis: props.xAxis || {}, + // yAxis: props.yAxis || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'graph'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-graph/src/charts-graph.props.ts b/packages/charts-vue/components/charts-graph/src/charts-graph.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..74cf8359b9bb012229da1a37869a25e57808266e --- /dev/null +++ b/packages/charts-vue/components/charts-graph/src/charts-graph.props.ts @@ -0,0 +1,202 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + dataProps, animationProps, ToolboxProps, LabelProps, ScaleLimitProps, EmphasisProps, + LabelLayoutProps, ItemStyleProps, LineStyleProps, BlurProps, + SelectProps, TooltipProps, SelectedModeType, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps +} from '@farris/charts-vue/components/charts-common'; + +export const graphDataProps = { + + ...dataProps, + + x: { type: Number }, + + y: { type: Number }, + + fixed: { type: Boolean }, + + category: { type: Number } +}; + +export type GraphDataProps = ExtractPropTypes; + +export const circularProps = { + rotateLabel: { type: Boolean } +}; + +export type CircularProps = ExtractPropTypes; + +export const forceProps = { + + initLayout: { type: String }, + + repulsion: { type: Number }, + + gravity: { type: Number }, + + edgeLength: { type: [Number, Array] }, + + layoutAnimation: { type: Boolean }, + + friction: { type: Number } + +}; + +export type ForceProps = ExtractPropTypes; + +export const graphSeriesProps = { + type: { type: String, default: 'graph' }, + + id: { type: String }, + + name: { type: String }, + + legendHoverLink: { type: Boolean, default: true }, + + // 使用的坐标系,暂时用字符串,后续考虑是否封装为公共的枚举 + coordinateSystem: { type: String }, + + xAxisIndex: { type: Number }, + + yAxisIndex: { type: Number }, + + polarIndex: { type: Number }, + + geoIndex: { type: Number }, + + calendarIndex: { type: Number }, + + center: { type: Array }, + + zoom: { type: Number }, + + layout: { type: String }, + + circular: { type: Object as PropType }, + + force: { type: Object as PropType }, + + roam: { type: [Boolean, String] }, + + scaleLimit: { type: Object as PropType }, + + nodeScaleRatio: { type: Number }, + + draggable: { type: Boolean }, + + symbol: { type: String, Array, default: 'none' }, + + symbolSize: { type: Array }, + + symbolOffset: { type: Array }, + + symbolRotate: { type: Number }, + + symbolKeepAspect: { type: Boolean }, + + edgeSymbol: { type: [String, Array] }, + + edgeSymbolSize: { type: [Array, Number] }, + + cursor: { type: String, default: 'pointer' }, + + itemStyle: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + label: { type: Object as PropType }, + + edgeLabel: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + emphasis: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + selectedMode: { type: String as PropType }, + + // 后续考虑将category封装成公共props + categories: { type: Array }, + + autoCurveness: { type: [Boolean, Number, Array] }, + + data: { type: Object as PropType }, + + // 别名 同data + nodes: { type: Array }, + + // Todo 单独封装Props + links: { type: Array }, + + // 别名 同links + edges: { type: Array }, + + // 是否需要封装为公共的props + // markPoint: { type: Object as PropType }, + // markLine: { type: Object as PropType }, + // markArea: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + left: { type: [String, Number] }, + + top: { type: [String, Number] }, + + right: { type: [String, Number] }, + + bottom: { type: [String, Number] }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + silent: { type: Boolean }, + + ...animationProps, + + tooltip: { type: Object as PropType } +}; + +export type GraphSeriesProps = ExtractPropTypes; + +export const chartsGraphProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + textStyle: { type: Object as PropType }, + + series: { type: Array as PropType, default: [{ ...graphSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + // xAxis: { type: Object as PropType }, + + // yAxis: { type: Object as PropType }, + + ...animationProps +}; + +export type ChartsGraphProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-line/index.ts b/packages/charts-vue/components/charts-line/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..86e86dd5e97baae64c07cc1dec616cf4b42a1217 --- /dev/null +++ b/packages/charts-vue/components/charts-line/index.ts @@ -0,0 +1,6 @@ +import FCharstLine from './src/charts-line.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-line.props'; +export { FCharstLine }; +export default withInstall(FCharstLine); diff --git a/packages/charts-vue/components/charts-line/src/charts-line.component.tsx b/packages/charts-vue/components/charts-line/src/charts-line.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2a3c8e5c31edb2de054629e95ad71e654ff5b03b --- /dev/null +++ b/packages/charts-vue/components/charts-line/src/charts-line.component.tsx @@ -0,0 +1,131 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsLineProps, chartsLineProps } from './charts-line.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '@farris/charts-vue/components/charts-common'; + +export default defineComponent({ + name: 'FChartsLine', + props: chartsLineProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsLineProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + xAxis: props.xAxis || {}, + yAxis: props.yAxis || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'line'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-line/src/charts-line.props.ts b/packages/charts-vue/components/charts-line/src/charts-line.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..f85ec555d28d41df4ef525ac8d3c868b6479bfcb --- /dev/null +++ b/packages/charts-vue/components/charts-line/src/charts-line.props.ts @@ -0,0 +1,121 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + AxisProps, animationProps, ToolboxProps, DataProps, ColorByType, StackStrategyType, LabelProps, + LabelLineProps, LabelLayoutProps, ItemStyleProps, LineStyleProps, AreaStyleProps, BlurProps, + SelectProps, TooltipProps, SelectedModeType, SamplingType, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps +} from '@farris/charts-vue/components/charts-common'; + +export const lineSeriesProps = { + type: { type: String, default: 'line' }, + + id: { type: String }, + + name: { type: String }, + + colorBy: { type: String as PropType }, + + xAxisIndex: { type: Number }, + + yAxisIndex: { type: Number }, + + symbol: { type: String, Array, default: 'none' }, + + symbolSize: { type: Array }, + + symbolOffset: { type: Array }, + + symbolRotate: { type: Number }, + + legendHoverLink: { type: Boolean, default: true }, + /** 数据堆叠,目前只支持堆叠 value 和 log */ + stack: { type: String }, + /** 堆积数值的策略,前提是stack属性已被设置 */ + stackStrategy: { type: String as PropType }, + + cursor: { type: String, default: 'pointer' }, + /** 是否裁剪超出坐标系部分的图形 */ + clip: { type: Boolean, default: true }, + + triggerLineEvent: { type: Boolean, default: false }, + + step: { type: Boolean, default: false }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + areaStyle: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + selectedMode: { type: String as PropType }, + + smooth: { type: [Boolean, Number] }, + + sampling: { type: String as PropType }, + + data: { type: Object as PropType }, + /** 使用 dimensions 定义 series.data 或者 dataset.source 的每个维度的信息。 */ + dimensions: { type: Array }, + /** 可以定义 data 的哪个维度被编码成什么。 */ + encode: { type: Object }, + /** 当使用 dataset 时,seriesLayoutBy 指定了 dataset 中用行还是列对应到系列上。column row */ + seriesLayoutBy: { type: String }, + /** 如果 series.data 没有指定,并且 dataset 存在,那么就会使用 dataset。datasetIndex 指定本系列使用哪个 dataset */ + datasetIndex: { type: Number }, + + ...animationProps +}; + +export type LineSeriesProps = ExtractPropTypes; + +export const chartsLineProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [Number, String] }, + + height: { type: [Number, String] }, + + textStyle: { type: Object as PropType }, + + series: { type: Array as PropType, default: [{ ...lineSeriesProps }] }, + + xAxis: { type: Object as PropType }, + + yAxis: { type: Object as PropType }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps +}; + +export type ChartsLineProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-pie/index.ts b/packages/charts-vue/components/charts-pie/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..bea1f1b6cbc02979c1891d8691f0c813a7032ec8 --- /dev/null +++ b/packages/charts-vue/components/charts-pie/index.ts @@ -0,0 +1,6 @@ +import FCharstPie from './src/charts-pie.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-pie.props'; +export { FCharstPie }; +export default withInstall(FCharstPie); diff --git a/packages/charts-vue/components/charts-pie/src/charts-pie.component.tsx b/packages/charts-vue/components/charts-pie/src/charts-pie.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aa16d6a2f472a5c8a9719d9b5ad49bc738f694d1 --- /dev/null +++ b/packages/charts-vue/components/charts-pie/src/charts-pie.component.tsx @@ -0,0 +1,128 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsPieProps, chartsPieProps } from './charts-pie.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '@farris/charts-vue/components/charts-common'; + +export default defineComponent({ + name: 'FChartsPie', + props: chartsPieProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsPieProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'pie'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-pie/src/charts-pie.props.ts b/packages/charts-vue/components/charts-pie/src/charts-pie.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..10d51635c310d5c7b687a3cb032e3a6804ddf347 --- /dev/null +++ b/packages/charts-vue/components/charts-pie/src/charts-pie.props.ts @@ -0,0 +1,108 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + DataProps, ColorByType, StackStrategyType, LabelProps, LabelLineProps, LabelLayoutProps, ItemStyleProps, + animationProps, ToolboxProps, BlurProps, SelectProps, TooltipProps, SelectedModeType, SamplingType, + TextStyleProps, LegendProps, TitleProps, GridProps, DataZoomProps +} from '@farris/charts-vue/components/charts-common'; + +export const pieSeriesProps = { + type: { type: String, default: 'pie' }, + + id: { type: String }, + + name: { type: String }, + + colorBy: { type: String as PropType }, + + symbol: { type: String, Array, default: 'none' }, + + symbolSize: { type: Array }, + + symbolOffset: { type: Array }, + + symbolRotate: { type: Number }, + + legendHoverLink: { type: Boolean, default: true }, + /** 数据堆叠,目前只支持堆叠 value 和 log */ + stack: { type: String }, + /** 堆积数值的策略,前提是stack属性已被设置 */ + stackStrategy: { type: String as PropType }, + + cursor: { type: String, default: 'pointer' }, + /** 是否裁剪超出坐标系部分的图形 */ + clip: { type: Boolean, default: true }, + + triggerLineEvent: { type: Boolean, default: false }, + + step: { type: Boolean, default: false }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + selectedMode: { type: String as PropType }, + + smooth: { type: [Boolean, Number] }, + + sampling: { type: String as PropType }, + + data: { type: Object as PropType }, + /** 使用 dimensions 定义 series.data 或者 dataset.source 的每个维度的信息。 */ + dimensions: { type: Array }, + /** 可以定义 data 的哪个维度被编码成什么。 */ + encode: { type: Object }, + /** 当使用 dataset 时,seriesLayoutBy 指定了 dataset 中用行还是列对应到系列上。column row */ + seriesLayoutBy: { type: String }, + /** 如果 series.data 没有指定,并且 dataset 存在,那么就会使用 dataset。datasetIndex 指定本系列使用哪个 dataset */ + datasetIndex: { type: Number }, + + ...animationProps +}; + +export type PieSeriesProps = ExtractPropTypes; + +export const chartsPieProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + textStyle: { type: Object as PropType }, + + series: { type: Array as PropType, default: [{ ...pieSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps +}; + +export type ChartsPieProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-process-gauge/index.ts b/packages/charts-vue/components/charts-process-gauge/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d53242c11e5b2373c3711f320badd4c26ae52ad0 --- /dev/null +++ b/packages/charts-vue/components/charts-process-gauge/index.ts @@ -0,0 +1,6 @@ +import FChartsProcessGauge from './src/charts-process-gauge.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-process-gauge.props'; +export { FChartsProcessGauge }; +export default withInstall(FChartsProcessGauge); diff --git a/packages/charts-vue/components/charts-process-gauge/src/charts-process-gauge.component.tsx b/packages/charts-vue/components/charts-process-gauge/src/charts-process-gauge.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..23a9c76fff6a44856a780a71bfa64f8ee86a1f53 --- /dev/null +++ b/packages/charts-vue/components/charts-process-gauge/src/charts-process-gauge.component.tsx @@ -0,0 +1,129 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsProcessGaugeProps, chartsProcessGaugeProps } from './charts-process-gauge.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '@farris/charts-vue/components/charts-common'; + +export default defineComponent({ + name: 'FChartsProcessGauge', + props: chartsProcessGaugeProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsProcessGaugeProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'gauge'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-process-gauge/src/charts-process-gauge.props.ts b/packages/charts-vue/components/charts-process-gauge/src/charts-process-gauge.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..b8e777683269f1f72b0a11c39da4c946117477ac --- /dev/null +++ b/packages/charts-vue/components/charts-process-gauge/src/charts-process-gauge.props.ts @@ -0,0 +1,159 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + DataProps, ColorByType, ItemStyleProps, TooltipProps, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps, LineStyleProps, textStyleProps, borderProps, AxisLabelProps, animationProps, + ToolboxProps +} from '@farris/charts-vue/components/charts-common'; + +export const gaugeAxisLineProps = { + show: { type: Boolean, default: true }, + + roundCap: { type: Boolean, default: true }, + + lineStyle: { type: Object as PropType<{ show: true, width: 30}> } +}; + +export type GaugeAxisLineProps = ExtractPropTypes; + +export const progressProps = { + show: { type: Boolean, default: true }, + + width: { type: Number, default: 30 }, + + roundCap: { type: Boolean, default: true }, + + overlap: { type: Boolean, default: true }, + + itemStyle: { type: Object as PropType } +}; + +export type ProgressProps = ExtractPropTypes; + +export const splitLineProps = { + show: { type: Boolean, default: false }, + + length: { type: Number, default: 10 }, + + distance: { type: Number, default: 10 }, + + lineStyle: { type: Object as PropType } +}; + +export type SplitLineProps = ExtractPropTypes; + +export const axisTickProps = { + show: { type: Boolean, default: false }, + + length: { type: Number }, + + splitNumber: { type: Number }, + + distance: { type: Number }, + + lineStyle: { type: Object as PropType } +}; + +export type AxisTickProps = ExtractPropTypes; + +export const detailProps = { + ...textStyleProps, + ...borderProps, + + show: { type: Boolean, default: true }, + + color: { type: String }, + + padding: { type: Number }, + + overflow: { type: String, default: 'none' }, + + formatter: { type: [Function, String] }, + + valueAnimation: { type: Boolean, default: true } +}; + +export type DetailProps = ExtractPropTypes; + +export const processGaugeSeriesProps = { + type: { type: String, default: 'gauge' }, + + id: { type: String }, + + name: { type: String }, + + colorBy: { type: String as PropType }, + + startAngle: { type: Number, default: 220 }, + + endAngle: { type: Number, default: -40 }, + + min: { type: Number, default: 0 }, + + max: { type: Number, default: 100 }, + + splitNumber: { type: Number, default: 100 }, + + clockwise: { type: Boolean, default: true }, + + legendHoverLink: { type: Boolean, default: true }, + + cursor: { type: String, default: 'pointer' }, + + itemStyle: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + axisLine: { type: Object as PropType }, + + process: { type: Object as PropType }, + + splitLine: { type: Object as PropType }, + + axisTick: { type: Object as PropType }, + + axisLabel: { type: Object as PropType }, + + detail: { type: Object as PropType }, + + data: { type: Object as PropType }, + + ...animationProps +}; + +export type ProcessGaugeSeriesProps = ExtractPropTypes; + +export const chartsProcessGaugeProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [Number, String] }, + + height: { type: [Number, String] }, + + textStyle: { type: Object as PropType }, + + series: { type: Array as PropType, default: [{ ...processGaugeSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps +}; + +export type ChartsProcessGaugeProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-radar/index.ts b/packages/charts-vue/components/charts-radar/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..73e50adda0a0af8e164ebc11db07f99cc2618eff --- /dev/null +++ b/packages/charts-vue/components/charts-radar/index.ts @@ -0,0 +1,6 @@ +import FChartsRadar from './src/charts-radar.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-radar.props'; +export { FChartsRadar }; +export default withInstall(FChartsRadar); diff --git a/packages/charts-vue/components/charts-radar/src/charts-radar.component.tsx b/packages/charts-vue/components/charts-radar/src/charts-radar.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..4371bde3f004659d1ce52edb2b0bbf78550742cc --- /dev/null +++ b/packages/charts-vue/components/charts-radar/src/charts-radar.component.tsx @@ -0,0 +1,130 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsRadarProps, chartsRadarProps } from './charts-radar.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '../../charts-common'; + +export default defineComponent({ + name: 'FChartsRadar', + props: chartsRadarProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsRadarProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + radar: props.radar || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'radar'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-radar/src/charts-radar.props.ts b/packages/charts-vue/components/charts-radar/src/charts-radar.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee42685406b76ff797eac82af0bcd7c213b1bebb --- /dev/null +++ b/packages/charts-vue/components/charts-radar/src/charts-radar.props.ts @@ -0,0 +1,185 @@ +import { ExtractPropTypes, PropType } from 'vue'; + +import { + OverFlowType, FontWeightType, FontStyleType, SplitAreaProps, AxisLabelProps, AxisTickProps, AxisLineProps, + EmphasisProps, animationProps, ToolboxProps, DataProps, ColorByType, LabelProps, + LabelLineProps, LabelLayoutProps, ItemStyleProps, LineStyleProps, AreaStyleProps, BlurProps, + SelectProps, TooltipProps, SelectedModeType, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps +} from '@farris/charts-vue/components/charts-common'; + +export type RadarType = 'polygon' | 'circle'; + +export const radarSeriesProps = { + + type: { type: String, default: 'radar' }, + + id: { type: String }, + + name: { type: String }, + + colorBy: { type: String as PropType }, + + radarIndex: { type: Number }, + + symbol: { type: String, Array, default: 'none' }, + + symbolSize: { type: Array }, + + symbolRotate: { type: Number }, + + symbolKeepAspect: { type: Boolean }, + + symbolOffset: { type: Array }, + + label: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + areaStyle: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + emphasis: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + selectedMode: { type: String as PropType }, + + dataGroupId: { type: String }, + + data: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + silent: { type: Boolean }, + + ...animationProps, + + tooltip: { type: Object as PropType }, +}; + +export const indicatorProps = { + name: { type: String }, + + max: { type: Number }, + + min: { type: Number }, + + color: { type: String } +}; + +export const axisNameProps = { + show: { type: Boolean, default: true }, + + formatter: { type: String }, + + color: { type: String }, + + fontStyle: { type: String as PropType }, + + fontWeight: { type: String as PropType, Number }, + + fontFamily: { type: String }, + + fontSize: { type: Number }, + + lineHeight: { type: Number }, + + width: { type: Number }, + + height: { type: Number }, + + overflow: { type: String as PropType } + + +}; + +export type IndicatorProps = ExtractPropTypes; + +export type AxisNameProps = ExtractPropTypes; + +export const radarConfigProps = { + id: { type: String }, + + zlevel: { type: Number }, + + z: { type: Number }, + + center: { type: Array }, + + radius: { type: Object }, + + startAngle: { type: Number }, + + axisName: { type: Object as PropType }, + + nameGap: { type: Number }, + + splitNumber: { type: Number }, + + shape: { type: String as PropType, default: 'polygon' }, + + scale: { type: Boolean }, + + silent: { type: Boolean }, + + triggerEvent: { type: Boolean }, + + axisLine: { type: Object as ExtractPropTypes }, + + axisTick: { type: Object as PropType }, + + axisLabel: { type: Object as PropType }, + + splitArea: { type: Object as PropType }, + + indicator: { type: Array as PropType, default: () => [] } + +}; + +export type RadarSeriesProps = ExtractPropTypes; + +export type RadarConfigProps = ExtractPropTypes; + +export const chartsRadarProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + textStyle: { type: Object as PropType }, + + radar: { type: Object as PropType, default: () => ({ ...radarConfigProps }) }, + + series: { type: Array as PropType, default: [{ ...radarSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps +}; + +export type ChartsRadarProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-scatter/index.ts b/packages/charts-vue/components/charts-scatter/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a9684bb46beaba64cd124aa8190306b6082241f --- /dev/null +++ b/packages/charts-vue/components/charts-scatter/index.ts @@ -0,0 +1,6 @@ +import FCharstScatter from './src/charts-scatter.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-scatter.props'; +export { FCharstScatter }; +export default withInstall(FCharstScatter); diff --git a/packages/charts-vue/components/charts-scatter/src/charts-scatter.component.tsx b/packages/charts-vue/components/charts-scatter/src/charts-scatter.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b96703205514adea970cbbab48b685fb12f0373f --- /dev/null +++ b/packages/charts-vue/components/charts-scatter/src/charts-scatter.component.tsx @@ -0,0 +1,131 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsScatterProps, chartsScatterProps } from './charts-scatter.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '@farris/charts-vue/components/charts-common'; + +export default defineComponent({ + name: 'FChartsScatter', + props: chartsScatterProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsScatterProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + xAxis: props.xAxis || {}, + yAxis: props.yAxis || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'scatter'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-scatter/src/charts-scatter.props.ts b/packages/charts-vue/components/charts-scatter/src/charts-scatter.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee3d0d6d9d8891e1044625d51fcf5fa28399cfac --- /dev/null +++ b/packages/charts-vue/components/charts-scatter/src/charts-scatter.props.ts @@ -0,0 +1,124 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + DataProps, ColorByType, StackStrategyType, LabelProps, LabelLineProps, LabelLayoutProps, ItemStyleProps, + LineStyleProps, animationProps, BlurProps, SelectProps, TooltipProps, SelectedModeType, SamplingType, + TextStyleProps, LegendProps, TitleProps, GridProps, DataZoomProps, AxisProps, ToolboxProps +} from '@farris/charts-vue/components/charts-common'; + +export const scatterSeriesProps = { + type: { type: String, default: 'scatter' }, + + id: { type: String }, + + name: { type: String }, + + colorBy: { type: String as PropType }, + + xAxisIndex: { type: Number }, + + yAxisIndex: { type: Number }, + + polarIndex: { type: Number, default: 0 }, + + geoIndex: { type: Number, default: 0 }, + + calendarIndex: { type: Number, default: 0 }, + + symbol: { type: String, Array, default: 'none' }, + + symbolSize: { type: Array }, + + symbolOffset: { type: Array }, + + symbolRotate: { type: Number }, + + legendHoverLink: { type: Boolean, default: true }, + /** 数据堆叠,目前只支持堆叠 value 和 log */ + stack: { type: String }, + /** 堆积数值的策略,前提是stack属性已被设置 */ + stackStrategy: { type: String as PropType }, + + cursor: { type: String, default: 'pointer' }, + /** 是否裁剪超出坐标系部分的图形 */ + clip: { type: Boolean, default: true }, + + triggerLineEvent: { type: Boolean, default: false }, + + step: { type: Boolean, default: false }, + + label: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + lineStyle: { type: Object as PropType }, + + // areaStyle: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + zlevel: { type: Number }, + + z: { type: Number }, + + selectedMode: { type: String as PropType }, + + sampling: { type: String as PropType }, + + data: { type: Object as PropType }, + /** 使用 dimensions 定义 series.data 或者 dataset.source 的每个维度的信息。 */ + dimensions: { type: Array }, + /** 可以定义 data 的哪个维度被编码成什么。 */ + encode: { type: Object }, + /** 当使用 dataset 时,seriesLayoutBy 指定了 dataset 中用行还是列对应到系列上。column row */ + seriesLayoutBy: { type: String }, + /** 如果 series.data 没有指定,并且 dataset 存在,那么就会使用 dataset。datasetIndex 指定本系列使用哪个 dataset */ + datasetIndex: { type: Number }, + + ...animationProps +}; + +export type ScatterSeriesProps = ExtractPropTypes; + +export const chartsScatterProps = { + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + textStyle: { type: Object as PropType }, + + xAxis: { type: Object as PropType }, + + yAxis: { type: Object as PropType }, + + series: { type: Array as PropType, default: [{ ...scatterSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + ...animationProps +}; + +export type ChartsScatterProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/charts-treemap/index.ts b/packages/charts-vue/components/charts-treemap/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d7e575c84b5c97a0016c53a6816795ef3ee64a3 --- /dev/null +++ b/packages/charts-vue/components/charts-treemap/index.ts @@ -0,0 +1,6 @@ +import FChartsTreemap from './src/charts-treemap.component'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; + +export * from './src/charts-treemap.props'; +export { FChartsTreemap }; +export default withInstall(FChartsTreemap); diff --git a/packages/charts-vue/components/charts-treemap/src/charts-treemap.component.tsx b/packages/charts-vue/components/charts-treemap/src/charts-treemap.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..2a70d84354b9c02197f7b858843c1db791fbe065 --- /dev/null +++ b/packages/charts-vue/components/charts-treemap/src/charts-treemap.component.tsx @@ -0,0 +1,131 @@ +import { defineComponent, onMounted, onBeforeUnmount, ref, watch, SetupContext } from 'vue'; +import * as echarts from 'echarts'; +import type { ECharts } from 'echarts'; +import { ChartsTreemapProps, chartsTreemapProps } from './charts-treemap.props'; +import { ActionEventArray, DatazoomEventArray, MouseEventArray, RenderEventArray, useActionEvent, useMouseEvent, useChartsTheme, useDatazoomEvent, useRenderEvent } from '@farris/charts-vue/components/charts-common'; + +export default defineComponent({ + name: 'FChartsTreemap', + props: chartsTreemapProps, + emits: [ + ...MouseEventArray, + ...ActionEventArray, + ...DatazoomEventArray, + ...RenderEventArray + ], + setup(props: ChartsTreemapProps, context: SetupContext) { + const chartRef = ref(null); + let chartInstance: ECharts | null = null; + let observer: ResizeObserver | null = null; + + const { getTheme, getThemeByName, registerTheme } = useChartsTheme(); + + const initChart = () => { + registerTheme(); + + if (!chartRef.value) { + return; + } + + const theme = getTheme(); + + chartInstance = echarts.init(chartRef.value, theme, {}); + + // eslint-disable-next-line no-use-before-define + updateChart(); + + const { onClick, onDbClick, onMousedown, onMousemove, onMouseup, onMouseover, onMouseout, onGlobalout, onContextmenu } = useMouseEvent(chartInstance, context); + onClick(), onDbClick(), onMousedown(), onMousemove(), onMouseup(), onMouseover(), onMouseout(), onGlobalout(), onContextmenu(); + + const { onHighLight, onDownPlay, onSelectChanged } = useActionEvent(chartInstance, context); + onHighLight(), onDownPlay(), onSelectChanged(); + + const { onDatazoom } = useDatazoomEvent(chartInstance, context); + onDatazoom(); + + const { onFinished, onRendered } = useRenderEvent(chartInstance, context); + onFinished(), onRendered(); + }; + + function updateChart() { + if (!chartInstance) { + return; + } + + let theme: any = null; + if (props.theme !== getTheme()) { + theme = getThemeByName(props.theme); + } + + const baseOption: any = { + ...theme, + title: props.title || {}, + grid: props.grid || {}, + // xAxis: props.xAxis || {}, + // yAxis: props.yAxis || {}, + tooltip: props.tooltip || {}, + legend: props.legend || {}, + toolbox: props.toolbox || {}, + animation: props.animation, + animationDuration: props.animationDuration, + animationEasingUpdate: props.animationEasingUpdate, + series: props.series.map((series) => { + const s = { ...series }; + s.type = 'treemap'; + return s; + }) + }; + + chartInstance.setOption(baseOption, true); + } + + const resizeChart = () => { + if (chartInstance) { + chartInstance.resize(); + } + }; + + // 监听容器大小变化 + const initResizeObserver = () => { + observer = new ResizeObserver(() => { + resizeChart(); + }); + if (chartRef.value) { + observer.observe(chartRef.value); + } + }; + + onMounted(() => { + initChart(); + initResizeObserver(); + }); + + onBeforeUnmount(() => { + if (observer) { + observer.disconnect(); + } + if (chartInstance) { + chartInstance.dispose(); + chartInstance = null; + } + }); + + watch( + () => props, + () => { + updateChart(); + }, + { deep: true } + ); + + return () => ( +
+ ); + } +}); diff --git a/packages/charts-vue/components/charts-treemap/src/charts-treemap.props.ts b/packages/charts-vue/components/charts-treemap/src/charts-treemap.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..00a5ba801d04b5cd83453d2de185e18e92ad26dd --- /dev/null +++ b/packages/charts-vue/components/charts-treemap/src/charts-treemap.props.ts @@ -0,0 +1,160 @@ +import { ExtractPropTypes, PropType } from 'vue'; +import { + animationProps, ToolboxProps, DataProps, LabelProps, + LabelLineProps, LabelLayoutProps, ItemStyleProps, EmphasisProps, BlurProps, + SelectProps, TooltipProps, SelectedModeType, ScaleLimitProps, TextStyleProps, LegendProps, TitleProps, + GridProps, DataZoomProps +} from '@farris/charts-vue/components/charts-common'; + +export const breadcrumbProps = { + + show: { type: Boolean }, + + top: { type: [String, Number] }, + + left: { type: [String, Number] }, + + right: { type: [String, Number] }, + + bottom: { type: [String, Number] }, + + height: { type: Number }, + + emptyItemWidth: { type: Number } + +}; + +export type BreadcrumbProps = ExtractPropTypes; + +export const treemapSeriesProps = { + + type: { type: String, default: 'treemap' }, + + id: { type: String, default: '' }, + + name: { type: String, default: '' }, + + zlevel: { type: Number }, + + z: { type: Number }, + + left: { type: [String, Number] }, + + right: { type: [String, Number] }, + + top: { type: [String, Number] }, + + bottom: { type: [String, Number] }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + squareRatio: { type: Number }, + + leafDepth: { type: Number }, + + drillDownIcon: { type: String }, + + roam: { type: [Boolean, String] }, + + scaleLimit: { type: Object as PropType }, + + nodeClick: { type: [String, Boolean] }, + + zoomToNodeRatio: { type: Number }, + + // treemap 中支持对数据其他维度进行视觉映射 + visualDimension: { type: Number }, + + visualMin: { type: Number }, + + visualMax: { type: Number }, + + colorAlpha: { type: Array }, + + colorSaturation: { type: Number }, + + colorMappingBy: { type: String }, + + visibleMin: { type: Number }, + + childrenVisibleMin: { type: Number }, + + label: { type: Object as PropType }, + + upperLabel: { type: Object as PropType }, + + itemStyle: { type: Object as PropType }, + + emphasis: { type: Object as PropType }, + + blur: { type: Object as PropType }, + + select: { type: Object as PropType }, + + selectedMode: { type: String as PropType }, + + breadcrumb: { type: Object as PropType }, + + labelLine: { type: Object as PropType }, + + labelLayout: { type: Object as PropType }, + + levels: { type: Array }, + + silent: { type: Boolean }, + + ...animationProps, + + tooltip: { type: Object as PropType }, + + cursor: { type: String}, + + data: { type: Object as PropType } + +}; + +export type TreemapSeriesProps = ExtractPropTypes; + +// Treemap组件的属性 +export const chartsTreemapProps = { + + title: { type: Object as PropType }, + + legend: { type: Object as PropType }, + + grid: { type: Object as PropType }, + + // xAxis: { type: Object as PropType }, + + // yAxis: { type: Object as PropType }, + + dataZoom: { type: Object as PropType }, + + tooltip: { type: Object as PropType }, + + toolbox: { type: Object as PropType }, + + /** + * 数据系列 + */ + series: { type: Array as PropType, default: [{ ...treemapSeriesProps }] }, + + theme: { type: String, default: 'defaultTheme' }, + + color: { type: Array }, + + backgroundColor: { type: String }, + + textStyle: { type: Object as PropType }, + + width: { type: [String, Number] }, + + height: { type: [String, Number] }, + + ...animationProps + +} as Record; + +export type ChartsTreemapProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/components.ts b/packages/charts-vue/components/components.ts new file mode 100644 index 0000000000000000000000000000000000000000..110fed723f1cdaff666571f089656c8b4bc562ef --- /dev/null +++ b/packages/charts-vue/components/components.ts @@ -0,0 +1,22 @@ +export { default as FGridLayoutWrapper } from './grid-layout'; +export type { GridLayoutProps } from './grid-layout'; +export { default as FChartsLine } from './charts-line'; +export type { ChartsLineProps } from './charts-line'; +export { default as FCharstPie } from './charts-pie'; +export type { ChartsPieProps } from './charts-pie'; +export { default as FCharstScatter } from './charts-scatter'; +export type { ChartsScatterProps } from './charts-scatter'; +export { default as FChartsProcessGauge } from './charts-process-gauge'; +export type { ChartsProcessGaugeProps } from './charts-process-gauge'; +export { default as FCharstCombined } from './charts-combined'; +export type { ChartsCombinedProps } from './charts-combined'; +export { default as FChartsBar } from './charts-bar'; +export type { ChartsBarProps } from './charts-bar'; +export { default as FChartsRadar } from './charts-radar'; +export type { ChartsRadarProps } from './charts-radar'; +export { default as FChartsFunnel } from './charts-funnel'; +export type { ChartsFunnelProps } from './charts-funnel'; +export { default as FChartsTreemap } from './charts-treemap'; +export type { ChartsTreemapProps } from './charts-treemap'; +export { default as FChartsGraph } from './charts-graph'; +export type { ChartsGraphProps } from './charts-graph'; diff --git a/packages/mobile-ui-vue/components/property-panel/index.ts b/packages/charts-vue/components/grid-layout/index.ts similarity index 64% rename from packages/mobile-ui-vue/components/property-panel/index.ts rename to packages/charts-vue/components/grid-layout/index.ts index 385785eb69478e590aceb36235e0ca51e37c3cfd..57f8bb62dd8b069c59802cb4d9d06fe55d1fd509 100644 --- a/packages/mobile-ui-vue/components/property-panel/index.ts +++ b/packages/charts-vue/components/grid-layout/index.ts @@ -1,3 +1,4 @@ + /** * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. * @@ -13,9 +14,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { BaseControlProperty } from './src/composition/entity/base-property'; -import { SchemaDOMMapping } from './src/composition/entity/schema-dom-mapping'; +import FGridLayoutWrapper from './src/gridlayoutwrapper.component'; +import LayoutItem from './src/composition/type'; +import { withInstall } from '@farris/charts-vue/components/charts-common'; -export * from './src/composition/type'; +export * from './src/gridlayout.props'; -export { BaseControlProperty, SchemaDOMMapping }; +export { FGridLayoutWrapper }; +export type { LayoutItem }; +export default withInstall(FGridLayoutWrapper); diff --git a/packages/charts-vue/components/grid-layout/src/composition/type.ts b/packages/charts-vue/components/grid-layout/src/composition/type.ts new file mode 100644 index 0000000000000000000000000000000000000000..fbc0c09724706adbb6532071c8bea792798371a5 --- /dev/null +++ b/packages/charts-vue/components/grid-layout/src/composition/type.ts @@ -0,0 +1,37 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * row: 在栅栏布局中第几行 + * column:在栅栏布局中第几列 + * width:在栅栏布局中元素初始宽度 + * height:在栅栏布局中元数初始高度 + */ +export default interface LayoutItem { + x: number + y: number + w: number + h: number + i: string | number + minW?: number + maxW?: number + minH?: number + maxH?: number + draggable?: boolean + resizable?: boolean + [key: string]: any +}; diff --git a/packages/charts-vue/components/grid-layout/src/gridlayout.props.ts b/packages/charts-vue/components/grid-layout/src/gridlayout.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..6543601a25122f65b117d10325f57f293e4c58a6 --- /dev/null +++ b/packages/charts-vue/components/grid-layout/src/gridlayout.props.ts @@ -0,0 +1,67 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import LayoutItem from './composition/type'; +import { ExtractPropTypes, PropType } from 'vue'; + +export const gridLayoutProps = { + /** 数据源,参与布局的item */ + layout: { + type: Array as PropType, + required: true, + validator: (value: unknown) => + Array.isArray(value) && value.every(item => + typeof item === 'object' && 'i' in item + ) + }, + /** 定义栅栏布局的列数 */ + columnNumber: { type: Number, default: 12 }, + /** 行高,单位像素 */ + rowHeight: { type: Number, default: 50 }, + /** 定义栅栏元素是否可以拖拽 */ + isDraggable: { type: Boolean, default: true }, + /** 定义栅栏元素是否可以调整大小 */ + isResizable: { type: Boolean, default: true }, + /** 定义栅格中的元素边距 */ + margin: { type: Array as unknown as PropType<[number, number]>, default: () => [10, 10] }, + /** 标识布局容器是否自动调整大小 */ + autoSize: { type: Boolean, default: true }, + /** 标识布局是否为响应式 */ + responsive: { type: Boolean, default: true }, + verticalCompact: { type: Boolean, default: true }, + useCssTransforms: { type: Boolean, default: true }, + /** 对应Vue生命周期的created */ + layoutCreatedEvent: { type: Function, default: (newLayout: any) => { } }, + /** 对应Vue生命周期的beforeMount */ + layoutBeforeMountEvent: { type: Function, default: (newLayout: any) => { } }, + /** 对应Vue生命周期的mounted */ + layoutMountedEvent: { type: Function, default: (newLayout: any) => { } }, + /** 当完成mount中的所有操作时生成的事件 */ + layoutReadyEvent: { type: Function, default: (newLayout: any) => { } }, + /** 更新事件(布局更新或栅格元素的位置重新计算) */ + layoutUpdatedEvent: { type: Function, default: (newLayout: any) => { } }, + + /** 栅栏格中元素移动时的事件 */ + moveEvent: { type: Function, default: (i, newX, newY) => { } }, + /** 栅栏格中元素调整大小时的事件 */ + resizeEvent: { type: Function, default: (i, newH, newW, newHPx, newWPx) => { } }, + /** 栅栏格中元素移动后的事件 */ + movedEvent: { type: Function, default: (i, newX, newY) => { } }, + /** 栅栏格中元素调整大小后的事件 */ + resizedEvent: { type: Function, default: (i, newH, newW, newHPx, newWPx) => { } } +} as Record; + +export type GridLayoutProps = ExtractPropTypes; diff --git a/packages/charts-vue/components/grid-layout/src/gridlayoutwrapper.component.tsx b/packages/charts-vue/components/grid-layout/src/gridlayoutwrapper.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..9071d475fa108b210bd7951a1e3f0ac437e68601 --- /dev/null +++ b/packages/charts-vue/components/grid-layout/src/gridlayoutwrapper.component.tsx @@ -0,0 +1,159 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineComponent, SetupContext, ShallowRef, shallowRef, watch } from 'vue'; +import { GridLayout, GridItem } from "vue3-grid-layout"; +import 'vue3-grid-layout/public/app.css'; +import { gridLayoutProps, GridLayoutProps } from './gridlayout.props'; +import LayoutItem from './composition/type'; + +export default defineComponent({ + name: 'FGridLayoutWrapper', + props: gridLayoutProps, + emits: [ + 'layoutCreated', + 'layoutBeforeMount', + 'layoutMounted', + 'layoutReady', + 'update', + 'itemMove', + 'itemResize', + 'itemMoved', + 'itemResized' + ], + components: { + 'grid-layout': GridLayout, + 'grid-item': GridItem + }, + setup(props: GridLayoutProps, context: SetupContext) { + const model: ShallowRef = shallowRef([...props.layout]); + + const { slots } = context; + + watch( + () => props.layout, + (newLayout) => { + if (newLayout !== props.layout) { + model.value = [...newLayout]; + } + }, + { flush: 'post' } + ); + + function handleLayoutCreated(item: LayoutItem) { + const layoutItem = item as LayoutItem; + context.emit('layoutCreated', layoutItem); + } + + function handleLayoutBeforeMount(item: LayoutItem) { + const layoutItem = item as LayoutItem; + context.emit('layoutBeforeMount', layoutItem); + } + + function handleLayoutMounted(item: LayoutItem) { + const layoutItem = item as LayoutItem; + context.emit('layoutMounted', layoutItem); + } + + function handleLayoutReady(item: LayoutItem) { + const layoutItem = item as LayoutItem; + context.emit('layoutReady', layoutItem); + } + + function handleLayoutUpdatedEvent(item: LayoutItem) { + const layoutItem = item as LayoutItem; + context.emit('update', layoutItem); + } + + function moveEvent(id: any, newX: number, newY: number) { + const item = props.layout.find((item) => item.id === id); + if (item) { + const updatedItem = { ...item, x: newX, y: newY }; + context.emit('itemMove', updatedItem); + } + } + + function resizeEvent(id: any, newX: number, newY: number, newColumnWidth: number, newRowHeight: number) { + const item = props.layout.find((item) => item.id === id); + if (item) { + const updatedItem = { ...item, x: newX, y: newY, w: newColumnWidth, h: newRowHeight }; + context.emit('itemResize', updatedItem); + } + } + + function movedEvent(id: any, newX: number, newY: number) { + const item = props.layout.find((item) => item.id === id); + if (item) { + const updatedItem = { ...item, x: newX, y: newY }; + context.emit('itemMoved', updatedItem); + } + } + + function resizedEvent(id: any, newX: number, newY: number, newColumnWidth: number, newRowHeight: number) { + const item = props.layout.find((item) => item.id === id); + if (item) { + const updatedItem = { ...item, x: newX, y: newY, w: newColumnWidth, h: newRowHeight }; + context.emit('itemResized', updatedItem); + } + } + + return () => ( + + { + model.value.map(item => ( + + { slots.default && slots.default({item}) } + + )) + } + + ); + } +}); diff --git a/packages/charts-vue/components/index.ts b/packages/charts-vue/components/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..824a1ee918bd6d17e54ceebda40151f73e0947aa --- /dev/null +++ b/packages/charts-vue/components/index.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { App } from 'vue'; +import FGridLayoutWrapper from './grid-layout'; +import FChartsLine from './charts-line'; +import FChartsPie from './charts-pie'; +import FChartsScatter from './charts-scatter'; +import FChartsBar from './charts-bar'; +import FChartsRadar from './charts-radar'; +import FChartsFunnel from './charts-funnel'; +import FChartsGraph from './charts-graph'; +import FChartsTreemap from './charts-treemap'; +import FChartsProcessGauge from './charts-process-gauge'; +import FChartsCombined from './charts-combined'; +import '../public/assets/farris-all.css'; +// 导出所有组件,可以按需加载 +export * from './components'; +// 导出设计器部分组件及属性 +// export * from './designer'; + +// 不注册设计时组件 +export default { + install(app: App): void { + app.use(FGridLayoutWrapper) + .use(FChartsLine) + .use(FChartsPie) + .use(FChartsScatter) + .use(FChartsCombined) + .use(FChartsProcessGauge) + .use(FChartsProcessGauge) + .use(FChartsBar) + .use(FChartsRadar) + .use(FChartsFunnel) + .use(FChartsGraph) + .use(FChartsTreemap); + } +}; diff --git a/packages/charts-vue/demos/charts-bar/charts-bar-compare-horizontal.vue b/packages/charts-vue/demos/charts-bar/charts-bar-compare-horizontal.vue new file mode 100644 index 0000000000000000000000000000000000000000..125413645127112b14c44fd9b9a3ac689490b1d6 --- /dev/null +++ b/packages/charts-vue/demos/charts-bar/charts-bar-compare-horizontal.vue @@ -0,0 +1,50 @@ + + diff --git a/packages/charts-vue/demos/charts-bar/charts-bar-group-horizontal.vue b/packages/charts-vue/demos/charts-bar/charts-bar-group-horizontal.vue new file mode 100644 index 0000000000000000000000000000000000000000..e79d3a8314093015bafc2affbb20589d40d88213 --- /dev/null +++ b/packages/charts-vue/demos/charts-bar/charts-bar-group-horizontal.vue @@ -0,0 +1,58 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-bar/charts-bar-group-vertical.vue b/packages/charts-vue/demos/charts-bar/charts-bar-group-vertical.vue new file mode 100644 index 0000000000000000000000000000000000000000..538ec89611252034eb24cf8077902031eb69829a --- /dev/null +++ b/packages/charts-vue/demos/charts-bar/charts-bar-group-vertical.vue @@ -0,0 +1,58 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-bar/charts-bar-horizontal.vue b/packages/charts-vue/demos/charts-bar/charts-bar-horizontal.vue new file mode 100644 index 0000000000000000000000000000000000000000..2c509d67fa0b583ef4817dfcf98c3254eacbbaac --- /dev/null +++ b/packages/charts-vue/demos/charts-bar/charts-bar-horizontal.vue @@ -0,0 +1,46 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-bar/charts-bar-stack-horizontal.vue b/packages/charts-vue/demos/charts-bar/charts-bar-stack-horizontal.vue new file mode 100644 index 0000000000000000000000000000000000000000..561177e0bd92e405e60865b6b86fd94dab4423bd --- /dev/null +++ b/packages/charts-vue/demos/charts-bar/charts-bar-stack-horizontal.vue @@ -0,0 +1,91 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-bar/charts-bar-stack-vertical.vue b/packages/charts-vue/demos/charts-bar/charts-bar-stack-vertical.vue new file mode 100644 index 0000000000000000000000000000000000000000..ce609df15f5f5c3104c33f6e1ed8cafa29cdd995 --- /dev/null +++ b/packages/charts-vue/demos/charts-bar/charts-bar-stack-vertical.vue @@ -0,0 +1,84 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-bar/charts-bar-vertical.vue b/packages/charts-vue/demos/charts-bar/charts-bar-vertical.vue new file mode 100644 index 0000000000000000000000000000000000000000..8e65fc7397c53cc2f031272b9f4bc8e7044ecfe9 --- /dev/null +++ b/packages/charts-vue/demos/charts-bar/charts-bar-vertical.vue @@ -0,0 +1,38 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-combined/charts-combined.vue b/packages/charts-vue/demos/charts-combined/charts-combined.vue new file mode 100644 index 0000000000000000000000000000000000000000..a99c7800bedb171fb3c2d8d666b6d878a05fe993 --- /dev/null +++ b/packages/charts-vue/demos/charts-combined/charts-combined.vue @@ -0,0 +1,24 @@ + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-funnel/charts-funnel.vue b/packages/charts-vue/demos/charts-funnel/charts-funnel.vue new file mode 100644 index 0000000000000000000000000000000000000000..5cd198e4b52461300466aa979e05ea0e40929a8e --- /dev/null +++ b/packages/charts-vue/demos/charts-funnel/charts-funnel.vue @@ -0,0 +1,43 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-graph/charts-graph.vue b/packages/charts-vue/demos/charts-graph/charts-graph.vue new file mode 100644 index 0000000000000000000000000000000000000000..ee836e05c75728a69be05d827420528041c272b3 --- /dev/null +++ b/packages/charts-vue/demos/charts-graph/charts-graph.vue @@ -0,0 +1,100 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-line/charts-line-area.vue b/packages/charts-vue/demos/charts-line/charts-line-area.vue new file mode 100644 index 0000000000000000000000000000000000000000..1debb2876dfde33cf09cbc1a37cf1441ffc56b5b --- /dev/null +++ b/packages/charts-vue/demos/charts-line/charts-line-area.vue @@ -0,0 +1,19 @@ + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-line/charts-line-rangearea.vue b/packages/charts-vue/demos/charts-line/charts-line-rangearea.vue new file mode 100644 index 0000000000000000000000000000000000000000..11f5ea8a1fc633f1b50b9be54493386ae87e65e8 --- /dev/null +++ b/packages/charts-vue/demos/charts-line/charts-line-rangearea.vue @@ -0,0 +1,25 @@ + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-line/charts-line.vue b/packages/charts-vue/demos/charts-line/charts-line.vue new file mode 100644 index 0000000000000000000000000000000000000000..bea36b389c36fbc2d83b99aeb0f7b108696ff703 --- /dev/null +++ b/packages/charts-vue/demos/charts-line/charts-line.vue @@ -0,0 +1,25 @@ + + + diff --git a/packages/charts-vue/demos/charts-pie/charts-pie-ring.vue b/packages/charts-vue/demos/charts-pie/charts-pie-ring.vue new file mode 100644 index 0000000000000000000000000000000000000000..f5ee9b434007a40a82e3488d5fe57237491ec1f4 --- /dev/null +++ b/packages/charts-vue/demos/charts-pie/charts-pie-ring.vue @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-pie/charts-pie-rose.vue b/packages/charts-vue/demos/charts-pie/charts-pie-rose.vue new file mode 100644 index 0000000000000000000000000000000000000000..87ce3a94f172ae3534c0fbdaa6c41e2faa5a6097 --- /dev/null +++ b/packages/charts-vue/demos/charts-pie/charts-pie-rose.vue @@ -0,0 +1,19 @@ + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-pie/charts-pie.vue b/packages/charts-vue/demos/charts-pie/charts-pie.vue new file mode 100644 index 0000000000000000000000000000000000000000..4569560f8f5304d7745d8234975a06e82661ef6c --- /dev/null +++ b/packages/charts-vue/demos/charts-pie/charts-pie.vue @@ -0,0 +1,17 @@ + diff --git a/packages/charts-vue/demos/charts-process-gauge/charts-process-gauge.vue b/packages/charts-vue/demos/charts-process-gauge/charts-process-gauge.vue new file mode 100644 index 0000000000000000000000000000000000000000..a73ced4dc00c8bd2e55758ad72a8cf3aff602e64 --- /dev/null +++ b/packages/charts-vue/demos/charts-process-gauge/charts-process-gauge.vue @@ -0,0 +1,57 @@ + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-radar/charts-radar.vue b/packages/charts-vue/demos/charts-radar/charts-radar.vue new file mode 100644 index 0000000000000000000000000000000000000000..0a2c1ded665fec90d63d0174f1d14c717f4ed282 --- /dev/null +++ b/packages/charts-vue/demos/charts-radar/charts-radar.vue @@ -0,0 +1,47 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-scatter/charts-bubble.vue b/packages/charts-vue/demos/charts-scatter/charts-bubble.vue new file mode 100644 index 0000000000000000000000000000000000000000..55b292c20c31cfe0612385f00011c765d9feeb43 --- /dev/null +++ b/packages/charts-vue/demos/charts-scatter/charts-bubble.vue @@ -0,0 +1,79 @@ + + \ No newline at end of file diff --git a/packages/charts-vue/demos/charts-scatter/charts-scatter.vue b/packages/charts-vue/demos/charts-scatter/charts-scatter.vue new file mode 100644 index 0000000000000000000000000000000000000000..f02e41dc9fcc9fd0266d7508e853efde1db7c301 --- /dev/null +++ b/packages/charts-vue/demos/charts-scatter/charts-scatter.vue @@ -0,0 +1,26 @@ + diff --git a/packages/charts-vue/demos/charts-treemap/charts-treemap.vue b/packages/charts-vue/demos/charts-treemap/charts-treemap.vue new file mode 100644 index 0000000000000000000000000000000000000000..9668b5e805fa9f6fd6820cf0824667ac48c290fa --- /dev/null +++ b/packages/charts-vue/demos/charts-treemap/charts-treemap.vue @@ -0,0 +1,58 @@ + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/grid-layout/grid-layout-charts.vue b/packages/charts-vue/demos/grid-layout/grid-layout-charts.vue new file mode 100644 index 0000000000000000000000000000000000000000..fb9e6ddb923e387161e6d014a54dff586220090a --- /dev/null +++ b/packages/charts-vue/demos/grid-layout/grid-layout-charts.vue @@ -0,0 +1,168 @@ + + + + + \ No newline at end of file diff --git a/packages/charts-vue/demos/grid-layout/grid-layout.vue b/packages/charts-vue/demos/grid-layout/grid-layout.vue new file mode 100644 index 0000000000000000000000000000000000000000..a058d19397e487d5f8fd7cf55f7a20b3d67efe85 --- /dev/null +++ b/packages/charts-vue/demos/grid-layout/grid-layout.vue @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/packages/charts-vue/docs/.vitepress/config.js b/packages/charts-vue/docs/.vitepress/config.js new file mode 100644 index 0000000000000000000000000000000000000000..e66f4a91fa66a3961659bb2dae7e84d3cd4633f1 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/config.js @@ -0,0 +1,6 @@ +import 'esbuild-register' +import config from './config/index' + +const defaultConfig = config.default + +export default config; diff --git a/packages/charts-vue/docs/.vitepress/config/head.ts b/packages/charts-vue/docs/.vitepress/config/head.ts new file mode 100644 index 0000000000000000000000000000000000000000..69ff1cbf0ad35b2da6ddbda8d8782c2051855e7b --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/config/head.ts @@ -0,0 +1,7 @@ +import { HeadConfig } from 'vitepress'; + +const head: HeadConfig[] = [ + ['link', { rel: 'icon', type: 'image/svg+xml', href: '/assets/logo.svg' }], +]; + +export default head; diff --git a/packages/charts-vue/docs/.vitepress/config/index.ts b/packages/charts-vue/docs/.vitepress/config/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb6c0ab9803ced4b08d1b94d07ce96da683ae6b1 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/config/index.ts @@ -0,0 +1,29 @@ +import { defineConfig } from 'vitepress'; +import head from './head'; +import nav from './nav'; +import sidebar from './sidebar'; +import markdown from './markdown'; +import { MarkdownTransform } from '../plugins/markdown-transform'; + +const config = defineConfig({ + base: '/farris-docs', + title: 'Farris Vue', + description: '基于 Farris Design 的前端组件库', + head, + markdown, + themeConfig: { + nav, + sidebar, + logo: { + dark: '/assets/farris_design_dark.png', + light: '/assets/farris_design_light.png' + }, + footer: { + message: '使用 Apache-2.0 开源许可协议', + copyright: '© 版权所有 Copyright 2023 | 浪潮数字企业', + address: '山东省济南市高新区浪潮路1036号' + } + } +}); + +export default config; diff --git a/packages/charts-vue/docs/.vitepress/config/markdown.ts b/packages/charts-vue/docs/.vitepress/config/markdown.ts new file mode 100644 index 0000000000000000000000000000000000000000..24bf3e017e6dc053f92c4bc0b2be74a7e3819233 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/config/markdown.ts @@ -0,0 +1,11 @@ +import type MarkdownIt from 'markdown-it'; +import { farrisMarkdownPlugin } from '../plugins/farris-markdown-plugin'; +import { highlight as highlightFactory } from '../utils/highlight-shiki'; + +const markdown = { + config: async (md: MarkdownIt) => { + const highlight = await highlightFactory(); + md.use(farrisMarkdownPlugin, { cssPreprocessor: 'scss', highlight }); + } +}; +export default markdown; diff --git a/packages/charts-vue/docs/.vitepress/config/nav.ts b/packages/charts-vue/docs/.vitepress/config/nav.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae0283dc2aab26ce10891b1aa74b733366d7fce9 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/config/nav.ts @@ -0,0 +1,3 @@ +const nav = [{ text: '设计原则', link: '/' },{ text: '组件', link: '/guide/quick-start/' }]; + +export default nav; diff --git a/packages/charts-vue/docs/.vitepress/config/sidebar.ts b/packages/charts-vue/docs/.vitepress/config/sidebar.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad143bc3e3fcd65910aab5a1dca6889b74ee3954 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/config/sidebar.ts @@ -0,0 +1,29 @@ +const sidebar = [ + { + text: '介绍', + items: [{ text: '快速开始', link: '/guide/quick-start/' }] + }, + { + text: '布局', + items: [ + { text: 'GridLayout 布局', link: '/components/grid-layout/' } + ] + }, + { + text: '图表', + items: [ + { text: 'Line 折线图', link: '/components/charts-line/' }, + { text: 'Combined 组合图', link: '/components/charts-combined/' }, + { text: 'Pie 饼状图', link: '/components/charts-pie/' }, + { text: 'Scatter 散点图', link: '/components/charts-scatter/' }, + { text: 'Gauge 进度仪表盘', link: '/components/charts-process-gauge/' }, + { text: 'Bar 柱状图', link: '/components/charts-bar/' }, + { text: 'Radar 雷达图', link: '/components/charts-radar/' }, + { text: 'Funnel 漏斗图', link: '/components/charts-funnel/' }, + { text: 'Graph 关系图', link: '/components/charts-graph/' }, + { text: 'Treemap 矩形树图', link: '/components/charts-treemap/' }, + ] + } +]; + +export default sidebar; diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/Layout.vue b/packages/charts-vue/docs/.vitepress/farris-theme/Layout.vue new file mode 100644 index 0000000000000000000000000000000000000000..5ce25db0567a83d8b3f0357dca1913679fd6a9ea --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/Layout.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/NotFound.vue b/packages/charts-vue/docs/.vitepress/farris-theme/NotFound.vue new file mode 100644 index 0000000000000000000000000000000000000000..9c902a9ea659145013c61ee0907244cfbf58b442 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/NotFound.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/FarrisFeatures.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/FarrisFeatures.vue new file mode 100644 index 0000000000000000000000000000000000000000..417459b7ffc02129727b7d8a56477805de6510e7 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/FarrisFeatures.vue @@ -0,0 +1,399 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPAlgoliaSearchBox.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPAlgoliaSearchBox.vue new file mode 100644 index 0000000000000000000000000000000000000000..dce2916aee2e569b71e8cf751e574ea9edc9a7bf --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPAlgoliaSearchBox.vue @@ -0,0 +1,93 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPBackdrop.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPBackdrop.vue new file mode 100644 index 0000000000000000000000000000000000000000..892e38aba84486f25bb1f1d0c0f2380915d6bc42 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPBackdrop.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPButton.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPButton.vue new file mode 100644 index 0000000000000000000000000000000000000000..0ad796e3975f53189d5da69f0bb43a363bf8f72b --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPButton.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPCarbonAds.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPCarbonAds.vue new file mode 100644 index 0000000000000000000000000000000000000000..d1f669f18fd2206c746f5d88eb0bde28ad64420c --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPCarbonAds.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPContent.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPContent.vue new file mode 100644 index 0000000000000000000000000000000000000000..37328ebf5a4a903b9e085f0c7478ec96e1b5a046 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPContent.vue @@ -0,0 +1,276 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDemo.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDemo.vue new file mode 100644 index 0000000000000000000000000000000000000000..e816e46063b0d12acadf12ffe5722e18dc6aa344 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDemo.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDoc.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDoc.vue new file mode 100644 index 0000000000000000000000000000000000000000..0dcb1f5f022c155a9f04519a92ee887e7acf7a86 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDoc.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAside.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAside.vue new file mode 100644 index 0000000000000000000000000000000000000000..4232d1563e829ab1552c68dd0f476efbd8ba35d8 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAside.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideCarbonAds.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideCarbonAds.vue new file mode 100644 index 0000000000000000000000000000000000000000..061a94d8b98f9d0b829480a660233066bb540ee4 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideCarbonAds.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideOutline.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideOutline.vue new file mode 100644 index 0000000000000000000000000000000000000000..dad437816c2ac2e38fcc91ba641b48780902e5cb --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideOutline.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideSponsors.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideSponsors.vue new file mode 100644 index 0000000000000000000000000000000000000000..dc9f8d0e147e2d5da766eb4677fee9547807ca58 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocAsideSponsors.vue @@ -0,0 +1,17 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocFooter.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocFooter.vue new file mode 100644 index 0000000000000000000000000000000000000000..31adc7842b590d956efdf9e2476c576ba838df3c --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocFooter.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocFooterLastUpdated.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocFooterLastUpdated.vue new file mode 100644 index 0000000000000000000000000000000000000000..6843785ca119977e70d5ac29486c410ed33b33d0 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPDocFooterLastUpdated.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFeature.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFeature.vue new file mode 100644 index 0000000000000000000000000000000000000000..7f24f3f351a9bc908b55e428286116be135c4939 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFeature.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFeatures.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFeatures.vue new file mode 100644 index 0000000000000000000000000000000000000000..db49bde43cc3dd6081e3774278bc8e510bf94744 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFeatures.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFlyout.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFlyout.vue new file mode 100644 index 0000000000000000000000000000000000000000..cf36c8f269901a58369f08d14e19a09413acf967 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFlyout.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFooter.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFooter.vue new file mode 100644 index 0000000000000000000000000000000000000000..e31e47c599fee768f538aa51706971aa05a49dc4 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPFooter.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHero.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHero.vue new file mode 100644 index 0000000000000000000000000000000000000000..4d25d078a0730f1298b3920aac976ed01f43da58 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHero.vue @@ -0,0 +1,640 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHome.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHome.vue new file mode 100644 index 0000000000000000000000000000000000000000..dd39b3365b75c0f2038a891b773689d7f98e92a8 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHome.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeFeatures.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeFeatures.vue new file mode 100644 index 0000000000000000000000000000000000000000..80a94332a3ca8290a955a8ad8231541e7b676a3d --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeFeatures.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeHero.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeHero.vue new file mode 100644 index 0000000000000000000000000000000000000000..8895cd28df66a4779ab93a2a9f398b9355c17b4b --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeHero.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeSponsors.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeSponsors.vue new file mode 100644 index 0000000000000000000000000000000000000000..88249522d2837457293b0bcd94bd0853638bd7f3 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPHomeSponsors.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPImage.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPImage.vue new file mode 100644 index 0000000000000000000000000000000000000000..abb9fb7b50b7a0dd5555104ba566cb9d9a70401e --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPImage.vue @@ -0,0 +1,38 @@ + + + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..1271cc350c2f9f7544c151637a6f8305bec18722 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPLink.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPLocalNav.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPLocalNav.vue new file mode 100644 index 0000000000000000000000000000000000000000..ab23eb4354ae1f526e5a6931b61f92f8b6e3ea51 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPLocalNav.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenu.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenu.vue new file mode 100644 index 0000000000000000000000000000000000000000..15249aca44f6ad4d49e01a7287edaacc33ab6806 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenu.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenuGroup.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenuGroup.vue new file mode 100644 index 0000000000000000000000000000000000000000..167874bec0376b8b27fce92965e8fc87d1816cbc --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenuGroup.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenuLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenuLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..a739096c4f7931ae60a47a17610a45680f7e4f34 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPMenuLink.vue @@ -0,0 +1,56 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNav.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNav.vue new file mode 100644 index 0000000000000000000000000000000000000000..f7a890366bb61818043fb34079ae329f3299256c --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNav.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBar.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBar.vue new file mode 100644 index 0000000000000000000000000000000000000000..96a937cf8f7e58e73f95160bb711f9877c70d41a --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBar.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarAppearance.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarAppearance.vue new file mode 100644 index 0000000000000000000000000000000000000000..75121e1cdddff6b2497a4d1a55eebd668bf6589b --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarAppearance.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarExtra.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarExtra.vue new file mode 100644 index 0000000000000000000000000000000000000000..972baba3407602f0a78066d1ef1f2edcff932aa2 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarExtra.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarHamburger.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarHamburger.vue new file mode 100644 index 0000000000000000000000000000000000000000..89a272472b2f8330585f4890a46da7a8053fa5eb --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarHamburger.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenu.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenu.vue new file mode 100644 index 0000000000000000000000000000000000000000..c8fb6df2f44563e5676cfb02359b00058da19bce --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenu.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenuGroup.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenuGroup.vue new file mode 100644 index 0000000000000000000000000000000000000000..d5b86ea62c81aefcaebbfd69fa2274398abc6658 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenuGroup.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenuLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenuLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..663603ac79a92e57b3b58a8011899885d77c2e80 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarMenuLink.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarSearch.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarSearch.vue new file mode 100644 index 0000000000000000000000000000000000000000..e29075492b70af88959c2379ec907a2d96dd0a4a --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarSearch.vue @@ -0,0 +1,287 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarSocialLinks.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarSocialLinks.vue new file mode 100644 index 0000000000000000000000000000000000000000..e5dfd6303b526b3cff66fe97cc8439399fb37cd3 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarSocialLinks.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarTitle.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarTitle.vue new file mode 100644 index 0000000000000000000000000000000000000000..7c633d7d06e638830c181ddc1a51731de975a73f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarTitle.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarTranslations.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarTranslations.vue new file mode 100644 index 0000000000000000000000000000000000000000..fe91597b0a6e086478be6c55ed9f9098240f31b9 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavBarTranslations.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreen.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreen.vue new file mode 100644 index 0000000000000000000000000000000000000000..040030334b154013661503287413d1fece923762 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreen.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenAppearance.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenAppearance.vue new file mode 100644 index 0000000000000000000000000000000000000000..9034ac6b9574bb7bc006ec68ee369b517dbc03c9 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenAppearance.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenu.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenu.vue new file mode 100644 index 0000000000000000000000000000000000000000..d75ef61da895f3460aa39bf31c652b6f38dd3e3b --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenu.vue @@ -0,0 +1,24 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroup.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroup.vue new file mode 100644 index 0000000000000000000000000000000000000000..d66e02c9aca32af7210ef9b6b12a4683d00690fc --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroup.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroupLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroupLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..61f5bd8383ac2ba70a5a2088084e82ae02fa374d --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroupLink.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroupSection.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroupSection.vue new file mode 100644 index 0000000000000000000000000000000000000000..216ddcf607eecc0bf431de40186888abb4899769 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuGroupSection.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..f7ae9800b6fc67c0b044e2829b105cdec579c61e --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenMenuLink.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenSocialLinks.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenSocialLinks.vue new file mode 100644 index 0000000000000000000000000000000000000000..45f4d31845d4113fe26939cd46182edb1f928457 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenSocialLinks.vue @@ -0,0 +1,14 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenTranslations.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenTranslations.vue new file mode 100644 index 0000000000000000000000000000000000000000..7305554ed0ce55b8cb98f22fd6ac2b137d3ca654 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPNavScreenTranslations.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPPage.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPPage.vue new file mode 100644 index 0000000000000000000000000000000000000000..015926b7b586a83a0bd9ddd28fb1308074706c56 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPPage.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebar.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebar.vue new file mode 100644 index 0000000000000000000000000000000000000000..8359dda5f90e5efc09792d9042d2ecc285847d53 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebar.vue @@ -0,0 +1,111 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebarGroup.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebarGroup.vue new file mode 100644 index 0000000000000000000000000000000000000000..0498e320d25c08b0a22bf638293518fec704c0ae --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebarGroup.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebarLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebarLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..f0a9615b7caec7fb423ab4f28752fcfddbe79ed4 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSidebarLink.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSkipLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSkipLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..492e01483b202773b3071dd339683e8e6c453687 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSkipLink.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSocialLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSocialLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..d8bcb725bb16584e2c4b743deaa1a811087c2815 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSocialLink.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSocialLinks.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSocialLinks.vue new file mode 100644 index 0000000000000000000000000000000000000000..19790f0e25fb272008e0b50184e5ba2c36aa1a43 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSocialLinks.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSponsors.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSponsors.vue new file mode 100644 index 0000000000000000000000000000000000000000..770e44aaf7a981cf9ff31a6e7cff2174960bb9c0 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSponsors.vue @@ -0,0 +1,46 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSponsorsGrid.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSponsorsGrid.vue new file mode 100644 index 0000000000000000000000000000000000000000..1ba4351847b58d9913571ff10ab22716a7526ec9 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSponsorsGrid.vue @@ -0,0 +1,50 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSwitch.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSwitch.vue new file mode 100644 index 0000000000000000000000000000000000000000..00d8b9a35ebccbd56ce9b4ae10566f04c23af0d7 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSwitch.vue @@ -0,0 +1,108 @@ + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSwitchAppearance.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSwitchAppearance.vue new file mode 100644 index 0000000000000000000000000000000000000000..70462deef7d5ea4299c488bf741cd0863d9d4c68 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPSwitchAppearance.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamMembers.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamMembers.vue new file mode 100644 index 0000000000000000000000000000000000000000..0f9debdc8dd3a13165f530ad33b2b93e3e7d5cc2 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamMembers.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamMembersItem.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamMembersItem.vue new file mode 100644 index 0000000000000000000000000000000000000000..6a4ba0f87058a8984b514f2fa0abf5f42a1f9c1c --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamMembersItem.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPage.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPage.vue new file mode 100644 index 0000000000000000000000000000000000000000..6af86f2e2972420c1a4b8f762203c78cfa098d6f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPage.vue @@ -0,0 +1,53 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPageSection.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPageSection.vue new file mode 100644 index 0000000000000000000000000000000000000000..d31501400965e95bdd50c08ac232393897c9a40a --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPageSection.vue @@ -0,0 +1,77 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPageTitle.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPageTitle.vue new file mode 100644 index 0000000000000000000000000000000000000000..9751a1886aa572f46acb19427cd5f76deefa6ddf --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/VPTeamPageTitle.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/demo/VPExample.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/demo/VPExample.vue new file mode 100644 index 0000000000000000000000000000000000000000..f7a39c42d7f22effaecc6be4a4cadea2848835a0 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/demo/VPExample.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/demo/VPSourceCode.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/demo/VPSourceCode.vue new file mode 100644 index 0000000000000000000000000000000000000000..6adc50d05049d7919e6ffecd781d5152e7368583 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/demo/VPSourceCode.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignJustify.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignJustify.vue new file mode 100644 index 0000000000000000000000000000000000000000..653dab13e4b8dfa8cea59b5dcbd7e3af5a956713 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignJustify.vue @@ -0,0 +1,8 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignLeft.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignLeft.vue new file mode 100644 index 0000000000000000000000000000000000000000..8dc84eae4ea6d52840697e750edb0852c424da2f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignLeft.vue @@ -0,0 +1,8 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignRight.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignRight.vue new file mode 100644 index 0000000000000000000000000000000000000000..16cbb3c7d08d08b0f07d32e2daf6cb3fb30361f7 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconAlignRight.vue @@ -0,0 +1,8 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconArrowLeft.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconArrowLeft.vue new file mode 100644 index 0000000000000000000000000000000000000000..3f65b866a9e402815809b2f92ce3cd5c97642499 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconArrowLeft.vue @@ -0,0 +1,7 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconArrowRight.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconArrowRight.vue new file mode 100644 index 0000000000000000000000000000000000000000..ed89263f76efe5edcbaad14cf28535fec2c5a32d --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconArrowRight.vue @@ -0,0 +1,7 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronDown.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronDown.vue new file mode 100644 index 0000000000000000000000000000000000000000..d72f4ee832181b9ce88f5512623ba5f5f439ff4f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronDown.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronLeft.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronLeft.vue new file mode 100644 index 0000000000000000000000000000000000000000..013eb7fd9f49c07156db025c4c501cdf81b87608 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronLeft.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronRight.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronRight.vue new file mode 100644 index 0000000000000000000000000000000000000000..cf72418fe5036063117acc825375e865a47ff216 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronRight.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronUp.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronUp.vue new file mode 100644 index 0000000000000000000000000000000000000000..0b6a873e6089f8a72611b1b8eaef8f3af439373d --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconChevronUp.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconEdit.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconEdit.vue new file mode 100644 index 0000000000000000000000000000000000000000..f30a62dd3e8d06ed6942988802864902525c13d0 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconEdit.vue @@ -0,0 +1,6 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconExternalLink.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconExternalLink.vue new file mode 100644 index 0000000000000000000000000000000000000000..724599092f970ca4e7c2f43a9b83c1384bae0a5f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconExternalLink.vue @@ -0,0 +1,13 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconHeart.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconHeart.vue new file mode 100644 index 0000000000000000000000000000000000000000..d408828fec865c31c83fc51d4b10da12f4793776 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconHeart.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconLanguages.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconLanguages.vue new file mode 100644 index 0000000000000000000000000000000000000000..71a728fc04811422a4b4b8963cc0a64b27f925fa --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconLanguages.vue @@ -0,0 +1,9 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMinus.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMinus.vue new file mode 100644 index 0000000000000000000000000000000000000000..e0229195637204b4f537bd942f06045e17263639 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMinus.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMinusSquare.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMinusSquare.vue new file mode 100644 index 0000000000000000000000000000000000000000..266ae3da7483974210768c562268e60a130a89c6 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMinusSquare.vue @@ -0,0 +1,6 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMoon.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMoon.vue new file mode 100644 index 0000000000000000000000000000000000000000..a9b205c61ced57673ad4b33018c319700b775656 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMoon.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMoreHorizontal.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMoreHorizontal.vue new file mode 100644 index 0000000000000000000000000000000000000000..6fa7fca20b2ab9969b85157bb1762e916b9d0a7f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconMoreHorizontal.vue @@ -0,0 +1,7 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconPlus.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconPlus.vue new file mode 100644 index 0000000000000000000000000000000000000000..74d9f6953b2e835978713661f9b55f3fa9f2aeef --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconPlus.vue @@ -0,0 +1,5 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconPlusSquare.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconPlusSquare.vue new file mode 100644 index 0000000000000000000000000000000000000000..88e5b5cf42c3c91a7141cc11e8bd83e189acf0a9 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconPlusSquare.vue @@ -0,0 +1,6 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconSun.vue b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconSun.vue new file mode 100644 index 0000000000000000000000000000000000000000..8ecb25baebcee79f878cbbafdcabba1eede86908 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/components/icons/VPIconSun.vue @@ -0,0 +1,13 @@ + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/aside.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/aside.ts new file mode 100644 index 0000000000000000000000000000000000000000..34b159c81bb53b8c11a424da867f4af1323ec6fb --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/aside.ts @@ -0,0 +1,21 @@ +import { computed } from 'vue' +import { useMediaQuery } from '@vueuse/core' +import { useSidebar } from './sidebar.js' + +export function useAside() { + const { hasSidebar } = useSidebar() + const is960 = useMediaQuery('(min-width: 960px)') + const is1280 = useMediaQuery('(min-width: 1280px)') + + const isAsideEnabled = computed(() => { + if (!is1280.value && !is960.value) { + return false + } + + return hasSidebar.value ? is1280.value : is960.value + }) + + return { + isAsideEnabled + } +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/copy-code.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/copy-code.ts new file mode 100644 index 0000000000000000000000000000000000000000..a705433331fad31e77c9e5857f7eeddc068705c5 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/copy-code.ts @@ -0,0 +1,94 @@ +import { nextTick, watch } from 'vue' +import { inBrowser, useData } from 'vitepress' + +export function useCopyCode() { + const { page } = useData() + + if (inBrowser) + watch( + () => page.value.relativePath, + () => { + nextTick(() => { + document + .querySelectorAll( + '.vp-doc div[class*="language-"] > button.copy' + ) + .forEach(handleElement) + }) + }, + { immediate: true, flush: 'post' } + ) +} + +async function copyToClipboard(text: string) { + try { + return navigator.clipboard.writeText(text) + } catch { + const element = document.createElement('textarea') + const previouslyFocusedElement = document.activeElement + + element.value = text + + // Prevent keyboard from showing on mobile + element.setAttribute('readonly', '') + + element.style.contain = 'strict' + element.style.position = 'absolute' + element.style.left = '-9999px' + element.style.fontSize = '12pt' // Prevent zooming on iOS + + const selection = document.getSelection() + const originalRange = selection + ? selection.rangeCount > 0 && selection.getRangeAt(0) + : null + + document.body.appendChild(element) + element.select() + + // Explicit selection workaround for iOS + element.selectionStart = 0 + element.selectionEnd = text.length + + document.execCommand('copy') + document.body.removeChild(element) + + if (originalRange) { + selection!.removeAllRanges() // originalRange can't be truthy when selection is falsy + selection!.addRange(originalRange) + } + + // Get the focus back on the previously focused element, if any + if (previouslyFocusedElement) { + ;(previouslyFocusedElement as HTMLElement).focus() + } + } +} + +function handleElement(el: HTMLElement) { + el.onclick = () => { + const parent = el.parentElement + const sibling = el.nextElementSibling + ?.nextElementSibling as HTMLPreElement | null + if (!parent || !sibling) { + return + } + + const isShell = /language-(shellscript|shell|bash|sh|zsh)/.test( + parent.classList.toString() + ) + + let { innerText: text = '' } = sibling + + if (isShell) { + text = text.replace(/^ *(\$|>) /gm, '') + } + + copyToClipboard(text).then(() => { + el.classList.add('copied') + setTimeout(() => { + el.classList.remove('copied') + el.blur() + }, 2000) + }) + } +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/edit-link.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/edit-link.ts new file mode 100644 index 0000000000000000000000000000000000000000..e8a6a156e25296e99e94674624c9fa230327d777 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/edit-link.ts @@ -0,0 +1,14 @@ +import { computed } from 'vue' +import { useData } from 'vitepress' + +export function useEditLink() { + const { theme, page } = useData() + + return computed(() => { + const { text = 'Edit this page', pattern } = theme.value.editLink || {} + const { relativePath } = page.value + const url = pattern.replace(/:path/g, relativePath) + + return { url, text } + }) +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/flyout.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/flyout.ts new file mode 100644 index 0000000000000000000000000000000000000000..691c595823532f33443d8ab25389ca110421754a --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/flyout.ts @@ -0,0 +1,58 @@ +import { Ref, ref, watch, readonly, onUnmounted } from 'vue' + +interface UseFlyoutOptions { + el: Ref + onFocus?(): void + onBlur?(): void +} + +export const focusedElement = ref() + +let active = false +let listeners = 0 + +export function useFlyout(options: UseFlyoutOptions) { + const focus = ref(false) + + if (typeof window !== 'undefined') { + !active && activateFocusTracking() + + listeners++ + + const unwatch = watch(focusedElement, (el) => { + if (el === options.el.value || options.el.value?.contains(el!)) { + focus.value = true + options.onFocus?.() + } else { + focus.value = false + options.onBlur?.() + } + }) + + onUnmounted(() => { + unwatch() + + listeners-- + + if (!listeners) { + deactivateFocusTracking() + } + }) + } + + return readonly(focus) +} + +function activateFocusTracking() { + document.addEventListener('focusin', handleFocusIn) + active = true + focusedElement.value = document.activeElement as HTMLElement +} + +function deactivateFocusTracking() { + document.removeEventListener('focusin', handleFocusIn) +} + +function handleFocusIn() { + focusedElement.value = document.activeElement as HTMLElement +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/nav.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/nav.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3cf686b45522b37e79b012516277bf43150818e --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/nav.ts @@ -0,0 +1,69 @@ +import type { DefaultTheme } from 'vitepress/theme' +import { ref, computed, watch } from 'vue' +import { useData, useRoute } from 'vitepress' + +export function useNav() { + const isScreenOpen = ref(false) + + function openScreen() { + isScreenOpen.value = true + window.addEventListener('resize', closeScreenOnTabletWindow) + } + + function closeScreen() { + isScreenOpen.value = false + window.removeEventListener('resize', closeScreenOnTabletWindow) + } + + function toggleScreen() { + isScreenOpen.value ? closeScreen() : openScreen() + } + + /** + * Close screen when the user resizes the window wider than tablet size. + */ + function closeScreenOnTabletWindow() { + window.outerWidth >= 768 && closeScreen() + } + + const route = useRoute() + watch(() => route.path, closeScreen) + + return { + isScreenOpen, + openScreen, + closeScreen, + toggleScreen + } +} + +export function useLanguageLinks() { + const { site, localePath, theme } = useData() + + return computed(() => { + const langs = site.value.langs + const localePaths = Object.keys(langs) + + // one language + if (localePaths.length < 2) { + return null + } + + const route = useRoute() + + // intentionally remove the leading slash because each locale has one + const currentPath = route.path.replace(localePath.value, '') + + const candidates = localePaths.map((localePath) => ({ + text: langs[localePath].label, + link: `${localePath}${currentPath}` + })) + + const selectText = theme.value.selectText || 'Languages' + + return { + text: selectText, + items: candidates + } as DefaultTheme.NavItemWithChildren + }) +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/outline.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/outline.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc308abdab5d4728ff54aaaf918ad1d9c0b5fb16 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/outline.ts @@ -0,0 +1,177 @@ +import { Ref, computed, onMounted, onUpdated, onUnmounted } from 'vue' +import { Header, useData } from 'vitepress' +import { useAside } from '../composables/aside.js' +import { throttleAndDebounce } from '../support/utils.js' + +interface HeaderWithChildren extends Header { + children?: Header[] + hidden?: boolean +} + +interface MenuItemWithLinkAndChildren { + text: string + link: string + children?: MenuItemWithLinkAndChildren[] + hidden?: boolean +} + +// magic number to avoid repeated retrieval +const PAGE_OFFSET = 56 + +export function useOutline() { + const { page } = useData() + + const hasOutline = computed(() => { + return page.value.headers.length > 0 + }) + + return { + hasOutline + } +} + +export function resolveHeaders(headers: Header[]) { + return mapHeaders(groupHeaders(headers)) +} + +function groupHeaders(headers: Header[]): HeaderWithChildren[] { + headers = headers.map((h) => Object.assign({}, h)) + + let lastH2: HeaderWithChildren | undefined + + for (const h of headers) { + if (h.level === 2) { + lastH2 = h + } else if (lastH2 && h.level <= 3) { + ;(lastH2.children || (lastH2.children = [])).push(h) + } + } + + return headers.filter((h) => h.level === 2) +} + +function mapHeaders( + headers: HeaderWithChildren[] +): MenuItemWithLinkAndChildren[] { + return headers.map((header) => ({ + text: header.title, + link: `#${header.slug}`, + children: header.children ? mapHeaders(header.children) : undefined, + hidden: header.hidden + })) +} + +export function useActiveAnchor( + container: Ref, + marker: Ref +) { + const { isAsideEnabled } = useAside() + + const onScroll = throttleAndDebounce(setActiveLink, 100) + + let prevActiveLink: HTMLAnchorElement | null = null + + onMounted(() => { + requestAnimationFrame(setActiveLink) + window.addEventListener('scroll', onScroll) + }) + + onUpdated(() => { + // sidebar update means a route change + activateLink(location.hash) + }) + + onUnmounted(() => { + window.removeEventListener('scroll', onScroll) + }) + + function setActiveLink() { + if (!isAsideEnabled.value) { + return + } + + const links = [].slice.call( + container.value.querySelectorAll('.outline-link') + ) as HTMLAnchorElement[] + + const anchors = [].slice + .call(document.querySelectorAll('.content .header-anchor')) + .filter((anchor: HTMLAnchorElement) => { + return links.some((link) => { + return link.hash === anchor.hash && anchor.offsetParent !== null + }) + }) as HTMLAnchorElement[] + + const scrollY = window.scrollY + const innerHeight = window.innerHeight + const offsetHeight = document.body.offsetHeight + const isBottom = Math.abs(scrollY + innerHeight - offsetHeight) < 1 + + // page bottom - highlight last one + if (anchors.length && isBottom) { + activateLink(anchors[anchors.length - 1].hash) + return + } + + for (let i = 0; i < anchors.length; i++) { + const anchor = anchors[i] + const nextAnchor = anchors[i + 1] + + const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor) + + if (isActive) { + activateLink(hash) + return + } + } + } + + function activateLink(hash: string | null) { + if (prevActiveLink) { + prevActiveLink.classList.remove('active') + } + + if (hash !== null) { + prevActiveLink = container.value.querySelector( + `a[href="${decodeURIComponent(hash)}"]` + ) + } + + const activeLink = prevActiveLink + + if (activeLink) { + activeLink.classList.add('active') + marker.value.style.top = activeLink.offsetTop + 33 + 'px' + marker.value.style.opacity = '1' + } else { + marker.value.style.top = '33px' + marker.value.style.opacity = '0' + } + } +} + +function getAnchorTop(anchor: HTMLAnchorElement): number { + return anchor.parentElement!.offsetTop - PAGE_OFFSET - 15 +} + +function isAnchorActive( + index: number, + anchor: HTMLAnchorElement, + nextAnchor: HTMLAnchorElement | undefined +): [boolean, string | null] { + const scrollTop = window.scrollY + + if (index === 0 && scrollTop === 0) { + return [true, null] + } + + if (scrollTop < getAnchorTop(anchor)) { + return [false, null] + } + + if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) { + return [true, anchor.hash] + } + + return [false, null] +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/prev-next.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/prev-next.ts new file mode 100644 index 0000000000000000000000000000000000000000..2633a617eecfca1b2877713e9247b75b88d150f6 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/prev-next.ts @@ -0,0 +1,26 @@ +import { computed } from 'vue' +import { useData } from 'vitepress' +import { isActive } from '../support/utils.js' +import { getSidebar, getFlatSideBarLinks } from '../support/sidebar.js' + +export function usePrevNext() { + const { page, theme, frontmatter } = useData() + + return computed(() => { + const sidebar = getSidebar(theme.value.sidebar, page.value.relativePath) + const candidates = getFlatSideBarLinks(sidebar) + + const index = candidates.findIndex((link) => { + return isActive(page.value.relativePath, link.link) + }) + + return { + prev: frontmatter.value.prev + ? { ...candidates[index - 1], text: frontmatter.value.prev } + : candidates[index - 1], + next: frontmatter.value.next + ? { ...candidates[index + 1], text: frontmatter.value.next } + : candidates[index + 1] + } + }) +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/scrollIFunc.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/scrollIFunc.ts new file mode 100644 index 0000000000000000000000000000000000000000..0be95f5a49c971e3854a0e829db99e87179de1c5 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/scrollIFunc.ts @@ -0,0 +1,133 @@ +const animateCls = 'inb-animate-slideInUp'; +const topDistance = 120; +/** + * 处理动画 + * @param elements + * @param extendfunc + */ +export function animateFunc(elements, extendfunc,isLargeSize=true) { + var viewHeight = window.innerHeight, + viewTop = document.documentElement.scrollTop, + viewBottom = viewTop + viewHeight; + let wrapper=document.querySelector('.farris-doc-feature-wrapper'); + if(!wrapper) return; + for (var k = 0; k < elements.length; k++) { + let ele = elements[k]; + var elementAnimation = ele.getAttribute('animate') ? ele.getAttribute('animate') : animateCls, + elementHeight = ele.clientHeight, + elementTop = ele.offsetTop+wrapper['offsetTop'], + elementBottom = elementTop + elementHeight; + if (elementBottom >= viewTop && elementTop <= viewBottom) { + ele.style.visibility = 'visible'; + if (!ele.classList.contains(elementAnimation)) { + ele.className = ele.className + ' ' + elementAnimation; + ele.className = ele.className + ' ' + 'inb-animated'; + setTimeout(()=>{ + ele.className = ele.className + ' ' + 'animate-img'; + },300); + } + } else { + ele.style.visibility = 'hidden'; + ele.className = ele.className.replace(elementAnimation, ''); + ele.className = ele.className.replace('inb-animated', ''); + ele.className = ele.className.replace('animate-img', ''); + } + + if (elementTop + 30 < viewHeight * 0.5 + viewTop && elementBottom - 30 > viewHeight * 0.5 + viewTop) { + extendfunc && extendfunc(k,isLargeSize); + } + + // if(elementBottom) + } +} +/** + * 改变标题 + * @param cur + */ +export function changeHeader(cur,sLargeSize=true) { + let headerContainer = document.querySelectorAll('.feature-header'); + if (headerContainer) { + for (let k = 0; k < headerContainer.length; k++) { + let ele = headerContainer[k]; + if (k == cur) { + if (!ele.classList.contains(animateCls)) { + ele.className = ele.className + ' ' + animateCls; + } + } else { + ele.className = ele.className.replace(animateCls, ''); + } + } + } +} +/** + * 中间区域左侧的标题导航 + * @returns + */ +export function changeFeatureCls(isLargeSize=true) { + let featureEL=document.querySelector('.farris-doc-feature'); + let wrapper=document.querySelector('.farris-doc-feature-wrapper'); + if(!featureEL||!wrapper) return; + if(!isLargeSize){ + featureEL.className=featureEL.className.replace('fixed',''); + return; + } + let viewHeight = window.innerHeight; + let viewTop = document.documentElement.scrollTop; + + if (viewTop + topDistance > wrapper['offsetTop'] && wrapper['offsetTop'] + wrapper['clientHeight'] > viewTop + topDistance + viewHeight) { + if(!featureEL.classList.contains('fixed')){ + featureEL.className=featureEL.className+' fixed'; + } + } else { + if(featureEL.classList.contains('fixed')){ + featureEL.className=featureEL.className.replace('fixed',''); + } + } + +} +/** + * 顶部导航 + * @returns + */ +export function changePageHeaderFixedState(){ + let viewTop = document.documentElement.scrollTop; + let navEl=document.querySelector('.VPNav'); + if(!navEl) return; + if(viewTop> navEl['clientHeight']){ + if(!navEl.classList.contains('fixed')){ + navEl.className=navEl.className+' fixed'; + } + }else{ + if(navEl.classList.contains('fixed')){ + navEl.className=navEl.className.replace('fixed',''); + } + + } + +} +/** + * 改变窗口大小 + * @returns + */ +export function resizeWindow() { + var viewSize = { "el": 1600, "xl": 1240, "lg": 1000, "md": 830, "sm": 730,"min":639 }; + var windowWidth = parseInt(document.documentElement.clientWidth+'', 10); + var matchSize = ""; + // 暂时没有其他用处 + if (windowWidth > viewSize['el']) { + matchSize = "ex"; + } else if (windowWidth > viewSize['xl']) { + matchSize = "el"; + } else if (windowWidth > viewSize['lg']) { + matchSize = "xl"; + } else if (windowWidth > viewSize['md']) { + matchSize = "lg"; + } else if (windowWidth > viewSize['sm']) { + matchSize = "md"; + } else if (windowWidth > viewSize['min']) { + matchSize = "sm"; + }else{ + matchSize = "min"; + } + return matchSize; +} \ No newline at end of file diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/sidebar.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/sidebar.ts new file mode 100644 index 0000000000000000000000000000000000000000..414c5558f3b126ded6706232488596f898c5928a --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/sidebar.ts @@ -0,0 +1,88 @@ +import { computed, onMounted, onUnmounted, Ref, ref, watchEffect } from 'vue' +import { useData, useRoute } from 'vitepress' +import { getSidebar } from '../support/sidebar.js' + +export function useSidebar() { + const route = useRoute() + const { theme, frontmatter } = useData() + + const isOpen = ref(false) + + const sidebar = computed(() => { + const sidebarConfig = theme.value.sidebar + const relativePath = route.data.relativePath + return sidebarConfig ? getSidebar(sidebarConfig, relativePath) : [] + }) + + const hasSidebar = computed(() => { + return ( + frontmatter.value.sidebar !== false && + sidebar.value.length > 0 && + frontmatter.value.layout !== 'home' + ) + }) + + const hasAside = computed(() => { + if ( + frontmatter.value.layout !== 'home' && + frontmatter.value.aside === false + ) + return false + + return hasSidebar.value + }) + + function open() { + isOpen.value = true + } + + function close() { + isOpen.value = false + } + + function toggle() { + isOpen.value ? close() : open() + } + + return { + isOpen, + sidebar, + hasSidebar, + hasAside, + open, + close, + toggle + } +} + +/** + * a11y: cache the element that opened the Sidebar (the menu button) then + * focus that button again when Menu is closed with Escape key. + */ +export function useCloseSidebarOnEscape( + isOpen: Ref, + close: () => void +) { + let triggerElement: HTMLButtonElement | undefined + + watchEffect(() => { + triggerElement = isOpen.value + ? (document.activeElement as HTMLButtonElement) + : undefined + }) + + onMounted(() => { + window.addEventListener('keyup', onEscape) + }) + + onUnmounted(() => { + window.removeEventListener('keyup', onEscape) + }) + + function onEscape(e: KeyboardEvent) { + if (e.key === 'Escape' && isOpen.value) { + close() + triggerElement?.focus() + } + } +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/composables/sponsor-grid.ts b/packages/charts-vue/docs/.vitepress/farris-theme/composables/sponsor-grid.ts new file mode 100644 index 0000000000000000000000000000000000000000..e4dc05c82398455ec380bb5430b20b60354a8e9a --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/composables/sponsor-grid.ts @@ -0,0 +1,135 @@ +import { Ref, onMounted, onUnmounted } from 'vue' +import { throttleAndDebounce } from '../support/utils.js' + +export interface GridSetting { + [size: string]: [number, number][] +} + +export type GridSize = 'xmini' | 'mini' | 'small' | 'medium' | 'big' + +export interface UseSponsorsGridOprions { + el: Ref + size?: GridSize +} + +/** + * Defines grid configuration for each sponsor size in touple. + * + * [Screen widh, Column size] + * + * It sets grid size on matching screen size. For example, `[768, 5]` will + * set 5 columns when screen size is bigger or equal to 768px. + * + * Column will set only when item size is bigger than the column size. For + * example, even we define 5 columns, if we only have 1 sponsor yet, we would + * like to show it in 1 column to make it stand out. + */ +const GridSettings: GridSetting = { + xmini: [[0, 2]], + mini: [], + small: [ + [920, 6], + [768, 5], + [640, 4], + [480, 3], + [0, 2] + ], + medium: [ + [960, 5], + [832, 4], + [640, 3], + [480, 2] + ], + big: [ + [832, 3], + [640, 2] + ] +} + +export function useSponsorsGrid({ + el, + size = 'medium' +}: UseSponsorsGridOprions) { + const onResize = throttleAndDebounce(manage, 100) + + onMounted(() => { + manage() + window.addEventListener('resize', onResize) + }) + + onUnmounted(() => { + window.removeEventListener('resize', onResize) + }) + + function manage() { + adjustSlots(el.value!, size) + } +} + +function adjustSlots(el: HTMLElement, size: GridSize) { + const tsize = el.children.length + const asize = el.querySelectorAll('.vp-sponsor-grid-item:not(.empty)').length + + const grid = setGrid(el, size, asize) + + manageSlots(el, grid, tsize, asize) +} + +function setGrid(el: HTMLElement, size: GridSize, items: number) { + const settings = GridSettings[size] + const screen = window.innerWidth + + let grid = 1 + + settings.some(([breakpoint, value]) => { + if (screen >= breakpoint) { + grid = items < value ? items : value + return true + } + }) + + setGridData(el, grid) + + return grid +} + +function setGridData(el: HTMLElement, value: number) { + el.dataset.vpGrid = String(value) +} + +function manageSlots( + el: HTMLElement, + grid: number, + tsize: number, + asize: number +) { + const diff = tsize - asize + const rem = asize % grid + const drem = rem === 0 ? rem : grid - rem + + neutralizeSlots(el, drem - diff) +} + +function neutralizeSlots(el: HTMLElement, count: number) { + if (count === 0) { + return + } + + count > 0 ? addSlots(el, count) : removeSlots(el, count * -1) +} + +function addSlots(el: HTMLElement, count: number) { + for (let i = 0; i < count; i++) { + const slot = document.createElement('div') + + slot.classList.add('vp-sponsor-grid-item', 'empty') + + el.append(slot) + } +} + +function removeSlots(el: HTMLElement, count: number) { + for (let i = 0; i < count; i++) { + el.removeChild(el.lastElementChild!) + } +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/FExample.vue b/packages/charts-vue/docs/.vitepress/farris-theme/example/FExample.vue new file mode 100644 index 0000000000000000000000000000000000000000..93f073984d37024017cf3d99135b0c7b1e6e2aea --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/FExample.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/type.ts b/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/type.ts new file mode 100644 index 0000000000000000000000000000000000000000..4cba9163ed2495534372fbc1572bafcbd3bb879f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/type.ts @@ -0,0 +1,12 @@ +import { ComputedRef, Ref } from "vue"; + +export interface UseCopy { + copyText: ComputedRef; + onCopy: () => Promise; +} + +export interface UseCollapse { + controlText: ComputedRef; + isExpanded: Ref; + onClickControl: () => void; +} \ No newline at end of file diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/use-collapse.ts b/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/use-collapse.ts new file mode 100644 index 0000000000000000000000000000000000000000..445cd6aa6db6ad1941d642960e65f4b028d06263 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/use-collapse.ts @@ -0,0 +1,25 @@ +import { computed, ComputedRef, ref, SetupContext } from 'vue'; +import { UseCollapse } from './type'; + + +export function useCollapse(props: any, setupContext: SetupContext, locale: ComputedRef): UseCollapse { + + const isExpanded = ref(false); + + + + const controlText = computed(() => { + return isExpanded.value ? locale.value['hide-text'] : locale.value['show-text']; + }); + + function onClickControl() { + isExpanded.value = !isExpanded.value; + + }; + + return { controlText, isExpanded, onClickControl }; + +} + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/use-copy.ts b/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/use-copy.ts new file mode 100644 index 0000000000000000000000000000000000000000..d1004f13b2c70c27b626650a99d8e7e5b9434cef --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/composition/use-copy.ts @@ -0,0 +1,24 @@ +import { computed, ComputedRef, ref, Ref, SetupContext } from "vue"; +import { UseCopy } from "./type"; +import message from '../messages/message'; +import clipboardCopy from "../utils/clipboard-copy"; + +export function useCopy(props: any, context: SetupContext, locale: ComputedRef): UseCopy { + + const isShowTip = ref(false); + + const copyText = computed(() => { + return isShowTip.value ? locale.value['copy-success-text'] : locale.value['copy-button-text']; + }); + + async function onCopy() { + try { + await clipboardCopy(decodeURIComponent(props.sourceCode)); + message.info(locale.value['copy-success-text']); + } catch (err) { + message.error(locale.value['copy-success-text']); + } + } + + return { copyText, onCopy }; +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/messages/FMessage.vue b/packages/charts-vue/docs/.vitepress/farris-theme/example/messages/FMessage.vue new file mode 100644 index 0000000000000000000000000000000000000000..275b1218c5a411c09270cb76bf594395df17df55 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/messages/FMessage.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/messages/message.js b/packages/charts-vue/docs/.vitepress/farris-theme/example/messages/message.js new file mode 100644 index 0000000000000000000000000000000000000000..c71688e20769c37587f352031d650e5e8c9e1787 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/messages/message.js @@ -0,0 +1,52 @@ +import { createApp, h } from 'vue' +import Message from './FMessage.vue' + +// newInstance +Message.newInstance = (props = {}) => { + const container = document.createElement('div') + + const app = createApp({ + render() { + return h(Message, { + ...props, + ref: 'messageRef' + }) + } + }) + + const instance = app.mount(container) + const messageRef = instance.$refs.messageRef + document.body.appendChild(container.firstElementChild) + + return { + add(messageProps) { + messageRef.add(messageProps) + }, + remove(name) { + messageRef.remove(name) + } + } +} + +// message +let messageInstance + +function getMessageInstance() { + messageInstance = messageInstance || Message.newInstance() + return messageInstance +} + +function message(content, { duration = 3, type = '' }) { + const instance = getMessageInstance() + + instance.add({ content, duration, type }) +} + +export default { + info(content, options) { + return message(content, { ...options }) + }, + error(content, options) { + return message(content, { ...options, type: 'error' }) + } +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/utils/clipboard-copy.js b/packages/charts-vue/docs/.vitepress/farris-theme/example/utils/clipboard-copy.js new file mode 100644 index 0000000000000000000000000000000000000000..f3672b1ff0f63d526a1c1d9f4f51367e48471f48 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/utils/clipboard-copy.js @@ -0,0 +1,61 @@ +// https://github.com/feross/clipboard-copy +function makeError() { + return new DOMException('The request is not allowed', 'NotAllowedError') +} + +async function copyClipboardApi(text) { + // Use the Async Clipboard API when available. Requires a secure browsing + // context (i.e. HTTPS) + if (!navigator.clipboard) { + throw makeError() + } + return navigator.clipboard.writeText(text) +} + +async function copyExecCommand(text) { + // Put the text to copy into a + const span = document.createElement('span') + span.textContent = text + + // Preserve consecutive spaces and newlines + span.style.whiteSpace = 'pre' + span.style.webkitUserSelect = 'auto' + span.style.userSelect = 'all' + + // Add the to the page + document.body.appendChild(span) + + // Make a selection object representing the range of text selected by the user + const selection = window.getSelection() + const range = window.document.createRange() + selection.removeAllRanges() + range.selectNode(span) + selection.addRange(range) + + // Copy text to the clipboard + let success = false + try { + success = window.document.execCommand('copy') + } finally { + // Cleanup + selection.removeAllRanges() + window.document.body.removeChild(span) + } + + if (!success) throw makeError() +} + +async function clipboardCopy(text) { + try { + await copyClipboardApi(text) + } catch (err) { + // ...Otherwise, use document.execCommand() fallback + try { + await copyExecCommand(text) + } catch (err2) { + throw err2 || err || makeError() + } + } +} + +export default clipboardCopy diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/example/utils/throttle.js b/packages/charts-vue/docs/.vitepress/farris-theme/example/utils/throttle.js new file mode 100644 index 0000000000000000000000000000000000000000..b34989404b8d459a3b4a5c23ec64ef5d07ade9a1 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/example/utils/throttle.js @@ -0,0 +1,45 @@ +/** + * js节流 + * @param method + * @param delay + * @return {Function} + */ +export function throttle(method, delay) { + let timer = null + let begin = new Date() + return function () { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const context = this, + args = arguments + const current = new Date() + const remaining = delay - (current - begin) + clearTimeout(timer) + if (remaining <= 0) { + method.apply(context, args) + begin = new Date() + } else { + timer = setTimeout(function () { + method.apply(context, args) + }, remaining) + } + } +} + +/** + * 函数防抖 + * @param method + * @param delay + * @return {Function} + */ +export function debounce(method, delay) { + let timer = null + return function () { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const context = this, + args = arguments + clearTimeout(timer) + timer = setTimeout(function () { + method.call(context, args) + }, delay) + } +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-cyrillic-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-cyrillic-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f2728758683cd7264fa26d1acb93075505eeeb6a Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-cyrillic-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-cyrillic.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-cyrillic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..de6a128bc11ed2dbc4976e69de1ddb4b240ca6d7 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-cyrillic.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-greek-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-greek-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..701afd3cb0c9d9febc3447de874cb3b432f903bd Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-greek-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-greek.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-greek.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..74125bbc86cd16d42ddc9da4c6d7f1ec0cbf5769 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-greek.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-cyrillic-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-cyrillic-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2a687296748f6b8bc8076cd11bde49cd27e4442b Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-cyrillic-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-cyrillic.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-cyrillic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f64035158d7e4c01654e3f23dcd6e8299928a28c Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-cyrillic.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-greek-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-greek-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..00218960325a4636092b66a46e701c2bf4bb0466 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-greek-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-greek.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-greek.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..71c265f85c9b45b6a3f2df9a0c6aa39547501b98 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-greek.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-latin-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-latin-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..9c1b9440ed419d4a71ba46b0db3951164f9e10df Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-latin-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-latin.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..01fcf20724f915f68a974ef2fb85f86f3039b1d8 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-latin.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-vietnamese.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-vietnamese.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e4f788ee02bb687bc1d5045483ff0d381e7654e0 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-italic-vietnamese.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-latin-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-latin-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2fa148c30c8d34aaca4ad1bd18a853d17fc7f594 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-latin-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-latin.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1a4cd429ecffcaf89a687488e7d06fd41617160d Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-latin.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-cyrillic-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-cyrillic-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..28593ccb8a4d849a746f2b970678fe426cb136e8 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-cyrillic-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-cyrillic.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-cyrillic.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a20adc161f433a7c4e3d92306301b9228bcf9fb4 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-cyrillic.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-greek-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-greek-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e3b0be76db2641253744aa613469eedb118667da Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-greek-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-greek.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-greek.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f790e047daa346583880da2be470431e35aa6054 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-greek.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-latin-ext.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-latin-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..715bd903b9b14d22a056f10e6d13b8d7e0acce57 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-latin-ext.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-latin.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a540b7afe7a46b9ff1412f433cd5180a9a9ab77d Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-latin.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-vietnamese.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-vietnamese.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..5a9f9cb9ca0cd78b6ea2f3e5c9d2838dc8895598 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-roman-vietnamese.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-vietnamese.woff2 b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-vietnamese.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..c2ffbb0913efa81e8bae6d78a803c286fcdeead7 Binary files /dev/null and b/packages/charts-vue/docs/.vitepress/farris-theme/fonts/inter-vietnamese.woff2 differ diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/index.ts b/packages/charts-vue/docs/.vitepress/farris-theme/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..133152d1e2360b84f7e693cafb15c4c24a6a2cdb --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/index.ts @@ -0,0 +1,29 @@ +import './styles/fonts.css'; +import './styles/vars.css'; +import './styles/base.css'; +import './styles/utils.css'; +import './styles/components/custom-block.css'; +import './styles/components/vp-code.css'; +import './styles/components/vp-doc.css'; +import './styles/components/vp-sponsor.css'; +import './styles/lib-override/example-block.css'; + +import { Theme } from 'vitepress'; +import Layout from './Layout.vue'; +import NotFound from './NotFound.vue'; + +export { default as VPHomeHero } from './components/VPHomeHero.vue'; +export { default as VPHomeFeatures } from './components/VPHomeFeatures.vue'; +export { default as VPHomeSponsors } from './components/VPHomeSponsors.vue'; +export { default as VPDocAsideSponsors } from './components/VPDocAsideSponsors.vue'; +export { default as VPTeamPage } from './components/VPTeamPage.vue'; +export { default as VPTeamPageTitle } from './components/VPTeamPageTitle.vue'; +export { default as VPTeamPageSection } from './components/VPTeamPageSection.vue'; +export { default as VPTeamMembers } from './components/VPTeamMembers.vue'; + +const theme: Theme = { + Layout, + NotFound +}; + +export default theme; diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/shared/shared.ts b/packages/charts-vue/docs/.vitepress/farris-theme/shared/shared.ts new file mode 100644 index 0000000000000000000000000000000000000000..580ce1c60926a89d2b5947c8109217ca8a4fe09d --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/shared/shared.ts @@ -0,0 +1,163 @@ +import { + SiteData, + PageData, + LocaleConfig, + HeadConfig +} from '../types/shared' + +export type { + SiteData, + PageData, + HeadConfig, + LocaleConfig, + Header, + DefaultTheme, + PageDataPayload, + CleanUrlsMode, + Awaitable +} from '../types/shared' + +export const EXTERNAL_URL_RE = /^[a-z]+:/i +export const APPEARANCE_KEY = 'vitepress-theme-appearance' + +export const inBrowser = typeof window !== 'undefined' + +export const notFoundPageData: PageData = { + relativePath: '', + title: '404', + description: 'Not Found', + headers: [], + frontmatter: { sidebar: false, layout: 'page' }, + lastUpdated: 0 +} + +function findMatchRoot(route: string, roots: string[]): string | undefined { + // first match to the routes with the most deep level. + roots.sort((a, b) => { + const levelDelta = b.split('/').length - a.split('/').length + if (levelDelta !== 0) { + return levelDelta + } else { + return b.length - a.length + } + }) + + for (const r of roots) { + if (route.startsWith(r)) return r + } +} + +function resolveLocales( + locales: Record, + route: string +): T | undefined { + const localeRoot = findMatchRoot(route, Object.keys(locales)) + return localeRoot ? locales[localeRoot] : undefined +} + +export function createLangDictionary(siteData: { + themeConfig?: Record + locales?: Record +}) { + const { locales } = siteData.themeConfig || {} + const siteLocales = siteData.locales + return locales && siteLocales + ? Object.keys(locales).reduce((langs, path) => { + langs[path] = { + label: locales![path].label, + lang: siteLocales[path].lang + } + return langs + }, {} as Record) + : {} +} + +// this merges the locales data to the main data by the route +export function resolveSiteDataByRoute( + siteData: SiteData, + route: string +): SiteData { + route = cleanRoute(siteData, route) + + const localeData = resolveLocales(siteData.locales || {}, route) + const localeThemeConfig = resolveLocales( + siteData.themeConfig.locales || {}, + route + ) + + // avoid object rest spread since this is going to run in the browser + // and spread is going to result in polyfill code + return Object.assign({}, siteData, localeData, { + themeConfig: Object.assign({}, siteData.themeConfig, localeThemeConfig, { + // clean the locales to reduce the bundle size + locales: {} + }), + lang: (localeData || siteData).lang, + // clean the locales to reduce the bundle size + locales: {}, + langs: createLangDictionary(siteData) + }) +} + +/** + * Create the page title string based on configs. + */ +export function createTitle(siteData: SiteData, pageData: PageData): string { + const title = pageData.title || siteData.title + const template = pageData.titleTemplate ?? siteData.titleTemplate + + if (typeof template === 'string' && template.includes(':title')) { + return template.replace(/:title/g, title) + } + + const templateString = createTitleTemplate(siteData.title, template) + + return `${title}${templateString}` +} + +function createTitleTemplate( + siteTitle: string, + template?: string | boolean +): string { + if (template === false) { + return '' + } + + if (template === true || template === undefined) { + return ` | ${siteTitle}` + } + + if (siteTitle === template) { + return '' + } + + return ` | ${template}` +} + +/** + * Clean up the route by removing the `base` path if it's set in config. + */ +function cleanRoute(siteData: SiteData, route: string): string { + if (!inBrowser) { + return route + } + + const base = siteData.base + const baseWithoutSuffix = base.endsWith('/') ? base.slice(0, -1) : base + + return route.slice(baseWithoutSuffix.length) +} + +function hasTag(head: HeadConfig[], tag: HeadConfig) { + const [tagType, tagAttrs] = tag + if (tagType !== 'meta') return false + const keyAttr = Object.entries(tagAttrs)[0] // First key + if (keyAttr == null) return false + return head.some( + ([type, attrs]) => type === tagType && attrs[keyAttr[0]] === keyAttr[1] + ) +} + +export function mergeHead(prev: HeadConfig[], curr: HeadConfig[]) { + return [...prev.filter((tagAttrs) => !hasTag(curr, tagAttrs)), ...curr] +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/base.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/base.css new file mode 100644 index 0000000000000000000000000000000000000000..7b0868b04b81c96350a763802d3f8a91377432ac --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/base.css @@ -0,0 +1,450 @@ +*, +::before, +::after { + box-sizing: border-box; +} + +html { + line-height: 1.4; + font-size: 16px; + text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} + +html.dark { + color-scheme: dark; +} + +body { + position:relative; + margin: 0; + width: 100%; + min-width: 320px; + min-height: 100vh; + line-height: 24px; + font-family: var(--vp-font-family-base); + font-size: 14px; + font-weight: 400; + color: var(--vp-c-text-1) !important; + background-color: var(--vp-c-bg) !important; + direction: ltr; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +main { + display: block; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + line-height: 24px; + font-size: 16px; + font-weight: 400; + font-family: var(--vp-font-family-title); +} + +p { + margin: 0; +} + +strong, +b { + font-weight: 600; +} + +/** + * Avoid 300ms click delay on touch devices that support the `touch-action` + * CSS property. + * + * In particular, unlike most other browsers, IE11+Edge on Windows 10 on + * touch devices and IE Mobile 10-11 DON'T remove the click delay when + * `` is present. + * However, they DO support removing the click delay via + * `touch-action: manipulation`. + * + * See: + * - http://v4-alpha.getbootstrap.com/content/reboot/#click-delay-optimization-for-touch + * - http://caniuse.com/#feat=css-touch-action + * - http://patrickhlauke.github.io/touch/tests/results/#suppressing-300ms-delay + */ +a, +area, +button, +[role='button'], +input, +label, +select, +summary, +textarea { + touch-action: manipulation; +} + +a { + color: inherit; + text-decoration: inherit; +} + +ol, +ul { + list-style: none; + margin: 0; + padding: 0; +} + +blockquote { + margin: 0; +} + +pre, +code, +kbd, +samp { + font-family: var(--vp-font-family-mono); +} + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + vertical-align: middle; +} + +figure { + margin: 0; +} + +img, +video { + max-width: 100%; + height: auto; +} + +button, +input, +optgroup, +select, +textarea { + border: 0; + padding: 0; + line-height: inherit; + color: inherit; +} + +button { + padding: 0; + font-family: inherit; + background-color: transparent; + background-image: none; +} + +button, +[role='button'] { + cursor: pointer; +} + +button:focus, +button:focus-visible { + outline: 1px dotted; + outline: 4px auto -webkit-focus-ring-color; +} + +button:focus:not(:focus-visible) { + outline: none !important; +} + +input:focus, +textarea:focus, +select:focus { + outline: none; +} + +table { + border-collapse: collapse; +} + +input { + background-color: transparent; +} + +input:-ms-input-placeholder, +textarea:-ms-input-placeholder { + color: var(--vp-c-text-3); +} + +input::-ms-input-placeholder, +textarea::-ms-input-placeholder { + color: var(--vp-c-text-3); +} + +input::placeholder, +textarea::placeholder { + color: var(--vp-c-text-3); +} + +input::-webkit-outer-spin-button, +input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} + +input[type='number'] { + -moz-appearance: textfield; + -webkit-appearance: textfield; +} + +textarea { + resize: vertical; +} + +select { + -webkit-appearance: none; +} + +fieldset { + margin: 0; + padding: 0; +} + +.img-block { + display: block; + margin: 0; + width: 100%; +} + +/*滚动条*/ +::-webkit-scrollbar { + width: 7px; + height: 7px; + background-color: #8e8e8e; +} + +::-webkit-scrollbar-track { + border-radius: 0; + background-color: var(--webkit-scrollbar-track-bg); + border: none; + background-clip: padding-box +} + +::-webkit-scrollbar-thumb { + background-color: var(--webkit-scrollbar-thumb-bg);; + border: none; + background-clip: content-box; + opacity: .6; + border-radius: 4px +} + +::-webkit-scrollbar-thumb:hover { + background: var(--webkit-scrollbar-thumb-hover-bg); +} + + +/*默认蓝色按钮*/ +.farris-doc-btn { + position: relative; + min-width: 152px; + text-align: center; + line-height: 54px; + padding: 0 30px; + font-size: 18px; + color: #ffffff; + background-image: linear-gradient(-51deg, #328bff 0%, #2a87ff 100%); + box-shadow: 0px 4px 10px 0px rgba(69, 144, 255, 0.25); + border-radius: 8px; + display: inline-block; + transition:background-image 0.2s; +} +.farris-doc-btn:hover{ + color:#fff; + background-image: linear-gradient(135deg, #529DFF 0%, #559FFF 100%) ; +} + +.inb-animated { + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both +} + +@-webkit-keyframes slideInUp { + 0% { + -webkit-transform: translate3d(0, 20%, 0); + transform: translate3d(0, 20%, 0); + visibility: visible + } + + to { + -webkit-transform: translateZ(0); + transform: translateZ(0) + } +} + +@keyframes slideInUp { + 0% { + -webkit-transform: translate3d(0, 20%, 0); + transform: translate3d(0, 20%, 0); + visibility: visible + } + + to { + -webkit-transform: translateZ(0); + transform: translateZ(0) + } +} + +.inb-animate-slideInUp { + -webkit-animation-name: slideInUp; + animation-name: slideInUp +} + + +@keyframes animate-left { + 0% { + left: 50%; + top: 50%; + opacity: 0; + } + + 100% { + left: 0%; + top: 15%; + opacity: 1; + } +} + + + +@keyframes animate-top { + 0% { + left: 50%; + top: 50%; + opacity: 0; + } + + 100% { + left: 25%; + top: 0%; + opacity: 1; + } +} + +@keyframes animate-center { + 0% { + top: 50%; + left: 50%; + opacity: 0; + } + + 100% { + top: 30%; + left: 50%; + opacity: 1; + } +} + +@keyframes animate-right { + 0% { + right: 50%; + bottom: 50%; + opacity: 0; + } + + 100% { + right: 0%; + bottom: 50%; + opacity: 1; + } +} + +@keyframes animate-right-bottom { + 0% { + right: 50%; + bottom: 50%; + opacity: 0; + } + + 100% { + right: 15%; + bottom: 0%; + opacity: 1; + } +} + +@keyframes animate-left-bottom { + 0% { + left: 50%; + bottom: 50%; + opacity: 0; + } + + 100% { + left: 15%; + bottom: 0%; + opacity: 1; + } +} + +@keyframes animate-bottom { + 0% { + left: 50%; + bottom: 50%; + opacity: 0; + } + + 100% { + left: 45%; + bottom: 0%; + opacity: 1; + } +} + + +@keyframes changebg { + 0% { + opacity: 0 + } + + to { + opacity: 1 + } +} + +@keyframes bannershowicons { + 0% { + transform: scale(0); + opacity: 0; + } + + to { + transform: scale(1); + opacity: 1; + } +} + +@keyframes bannericonfloat { + 0% { + transform: translateY(10px); + } + + 50% { + transform: translateY(0px); + } + + 100% { + transform: translateY(10px); + } + +} + diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/custom-block.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/custom-block.css new file mode 100644 index 0000000000000000000000000000000000000000..81bc71e400fd88904edfcbf193cbb121f25b9cb3 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/custom-block.css @@ -0,0 +1,90 @@ +.custom-block { + border: 1px solid transparent; + border-radius: 8px; + padding: 16px 16px 8px; + line-height: 24px; + font-size: 14px; + color: var(--vp-c-text-2); +} + +.custom-block.info { + border-color: var(--vp-custom-block-info-border); + color: var(--vp-custom-block-info-text); + background-color: var(--vp-custom-block-info-bg); +} + +.custom-block.info code { + background-color: var(--vp-custom-block-info-code-bg); +} + +.custom-block.tip { + border-color: var(--vp-custom-block-tip-border); + color: var(--vp-custom-block-tip-text); + background-color: var(--vp-custom-block-tip-bg); +} + +.custom-block.tip code { + background-color: var(--vp-custom-block-tip-code-bg); +} + +.custom-block.warning { + border-color: var(--vp-custom-block-warning-border); + color: var(--vp-custom-block-warning-text); + background-color: var(--vp-custom-block-warning-bg); +} + +.custom-block.warning code { + background-color: var(--vp-custom-block-warning-code-bg); +} + +.custom-block.danger { + border-color: var(--vp-custom-block-danger-border); + color: var(--vp-custom-block-danger-text); + background-color: var(--vp-custom-block-danger-bg); +} + +.custom-block.danger code { + background-color: var(--vp-custom-block-danger-code-bg); +} + +.custom-block.details { + border-color: var(--vp-custom-block-details-border); + color: var(--vp-custom-block-details-text); + background-color: var(--vp-custom-block-details-bg); +} + +.custom-block.details code { + background-color: var(--vp-custom-block-details-code-bg); +} + +.custom-block-title { + font-weight: 700; +} + +.custom-block p + p { + margin: 8px 0; +} + +.custom-block.details summary { + margin: 0 0 8px; + font-weight: 700; +} + +.custom-block.details summary + p { + margin: 8px 0; +} + +.custom-block a { + color: inherit; + font-weight: 600; + text-decoration: underline; + transition: opacity 0.25s; +} + +.custom-block a:hover { + opacity: 0.6; +} + +.custom-block code { + font-size: var(--vp-custom-block-code-font-size); +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-code.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-code.css new file mode 100644 index 0000000000000000000000000000000000000000..ee74e5b27d7039095aa5c7f2c9c4a012d6298f14 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-code.css @@ -0,0 +1,7 @@ +.dark .vp-code-light { + display: none; +} + +html:not(.dark) .vp-code-dark { + display: none; +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-doc.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-doc.css new file mode 100644 index 0000000000000000000000000000000000000000..acc5c0c07be2758adb368dc29453048be161f1fe --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-doc.css @@ -0,0 +1,446 @@ +/** + * Headings + * -------------------------------------------------------------------------- */ + +.vp-doc h1, +.vp-doc h2, +.vp-doc h3, +.vp-doc h4, +.vp-doc h5, +.vp-doc h6 { + position: relative; + font-weight: 600; + outline: none; +} + +.vp-doc h1 { + letter-spacing: -0.02em; + line-height: 40px; + font-size: 28px; +} + +.vp-doc h2 { + margin: 48px 0 16px; + border-top: 1px solid var(--vp-c-divider-light); + padding-top: 24px; + letter-spacing: -0.02em; + line-height: 32px; + font-size: 24px; +} + +.vp-doc h3 { + margin: 32px 0 0; + letter-spacing: -0.01em; + line-height: 28px; + font-size: 20px; +} + +.vp-doc .header-anchor { + float: left; + margin-left: -0.87em; + padding-right: 0.23em; + font-weight: 500; + opacity: 0; + transition: color 0.25s, opacity 0.25s; +} + +.vp-doc h1:hover .header-anchor, +.vp-doc h1 .header-anchor:focus, +.vp-doc h2:hover .header-anchor, +.vp-doc h2 .header-anchor:focus, +.vp-doc h3:hover .header-anchor, +.vp-doc h3 .header-anchor:focus, +.vp-doc h4:hover .header-anchor, +.vp-doc h4 .header-anchor:focus, +.vp-doc h5:hover .header-anchor, +.vp-doc h5 .header-anchor:focus, +.vp-doc h6:hover .header-anchor, +.vp-doc h6 .header-anchor:focus { + opacity: 1; +} + +@media (min-width: 768px) { + .vp-doc h1 { + letter-spacing: -0.02em; + line-height: 40px; + font-size: 32px; + } +} + +/** + * Paragraph and inline elements + * -------------------------------------------------------------------------- */ + +.vp-doc p, +.vp-doc summary { + margin: 16px 0; +} + +.vp-doc p { + line-height: 28px; +} + +.vp-doc blockquote { + margin: 16px 0; + border-left: 2px solid var(--vp-c-divider); + padding-left: 16px; + transition: border-color 0.5s; +} + +.vp-doc blockquote > p { + margin: 0; + font-size: 16px; + color: var(--vp-c-text-2); + transition: color 0.5s; +} + +.vp-doc a { + font-weight: 500; + color: var(--vp-c-brand); + text-decoration-style: dotted; + transition: color 0.25s; +} + +.vp-doc a:hover { + color: var(--vp-c-brand-dark); +} + +.vp-doc strong { + font-weight: 600; +} + +/** + * Lists + * -------------------------------------------------------------------------- */ + +/* .vp-doc ul, +.vp-doc ol { + padding-left: 1.25rem; + margin: 16px 0; +} */ + +/* .vp-doc ul { + list-style: disc; +} */ + +/* .vp-doc ol { + list-style: decimal; +} */ + +/* .vp-doc li + li { + margin-top: 8px; +} */ + +/* .vp-doc li > ol, +.vp-doc li > ul { + margin: 8px 0 0; +} */ + +/** + * Table + * -------------------------------------------------------------------------- */ + +.vp-doc table:not(.table) { + display: block; + border-collapse: collapse; + margin: 20px 0; + overflow-x: auto; +} + +.vp-doc table:not(.table) tr { + border-top: 1px solid var(--vp-c-divider); + transition: background-color 0.5s; +} + +.vp-doc table:not(.table) tr:nth-child(2n) { + background-color: var(--vp-c-bg-soft); +} + +.vp-doc table:not(.table) th, +.vp-doc table:not(.table) td { + border: 1px solid var(--vp-c-divider); + padding: 12px 16px; +} + +.vp-doc table:not(.table) th { + font-size: 16px; + font-weight: 600; + background-color: var(--vp-c-white-soft); +} + +.dark .vp-doc th { + background-color: var(--vp-c-black); +} + +/** + * Decorational elements + * -------------------------------------------------------------------------- */ + +.vp-doc hr { + margin: 16px 0; + border: none; + border-top: 1px solid var(--vp-c-divider-light); +} + +/** + * Custom Block + * -------------------------------------------------------------------------- */ + +.vp-doc .custom-block { + margin: 16px 0; +} + +.vp-doc .custom-block p { + margin: 8px 0; + line-height: 24px; +} + +.vp-doc .custom-block p:first-child { + margin: 0; +} + +.vp-doc .custom-block a { + color: inherit; + font-weight: 600; + text-decoration: underline; + transition: opacity 0.25s; +} + +.vp-doc .custom-block a:hover { + opacity: 0.6; +} + +.vp-doc .custom-block code { + font-size: var(--vp-custom-block-code-font-size); + font-weight: 700; + color: inherit; +} + +.vp-doc .custom-block div[class*='language-'] { + margin: 8px 0; +} + +.vp-doc .custom-block div[class*='language-'] code { + font-weight: 400; + background-color: transparent; +} + +/** + * Code + * -------------------------------------------------------------------------- */ + +/* inline code */ +.vp-doc :not(pre, h1, h2, h3, h4, h5, h6) > code { + font-size: var(--vp-code-font-size); +} + +.vp-doc :not(pre) > code { + border-radius: 4px; + padding: 3px 6px; + color: var(--vp-c-text-code); + background-color: var(--vp-c-bg-mute); + transition: color 0.5s, background-color 0.5s; +} + +.vp-doc h1 > code, +.vp-doc h2 > code, +.vp-doc h3 > code { + font-size: 0.9em; +} + +.vp-doc a > code { + color: var(--vp-c-brand); + transition: color 0.25s; +} + +.vp-doc a:hover > code { + color: var(--vp-c-brand-dark); +} + +.vp-doc div[class*='language-'] { + position: relative; + margin: 16px -24px; + background-color: var(--vp-code-block-bg); + overflow-x: auto; + transition: background-color 0.5s; +} + +@media (min-width: 640px) { + .vp-doc div[class*='language-'] { + border-radius: 8px; + margin: 16px 0; + } +} + +@media (max-width: 639px) { + .vp-doc li div[class*='language-'] { + border-radius: 8px 0 0 8px; + } +} + +.vp-doc div[class*='language-'] + div[class*='language-'], +.vp-doc div[class$='-api'] + div[class*='language-'], +.vp-doc div[class*='language-'] + div[class$='-api'] > div[class*='language-'] { + margin-top: -8px; +} + +.vp-doc [class*='language-'] pre, +.vp-doc [class*='language-'] code { + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +.vp-doc [class*='language-'] pre { + position: relative; + z-index: 1; + margin: 0; + padding: 16px 0; + background: transparent; + overflow-x: auto; +} + +.vp-doc [class*='language-'] code { + display: block; + padding: 0 24px; + width: fit-content; + min-width: 100%; + max-height: 560px; + line-height: var(--vp-code-line-height); + font-size: var(--vp-code-font-size); + color: var(--vp-code-block-color); + transition: color 0.5s; +} + +.vp-doc [class*='language-'] code .highlighted { + background-color: var(--vp-code-line-highlight-color); + transition: background-color 0.5s; + margin: 0 -24px; + padding: 0 24px; + width: calc(100% + 2 * 24px); + display: inline-block; +} + +.vp-doc div[class*='language-'].line-numbers-mode { + padding-left: 32px; +} + +.vp-doc .line-numbers-wrapper { + position: absolute; + top: 0; + bottom: 0; + left: 0; + z-index: 3; + border-right: 1px solid var(--vp-c-divider-dark-2); + padding-top: 16px; + width: 32px; + text-align: center; + font-family: var(--vp-font-family-mono); + line-height: var(--vp-code-line-height); + font-size: var(--vp-code-font-size); + color: var(--vp-code-line-number-color); + transition: border-color 0.5s, color 0.5s; +} + +.vp-doc [class*='language-'] > button.copy { + position: absolute; + top: 8px; + right: 8px; + z-index: 3; + display: block; + justify-content: center; + align-items: center; + border-radius: 4px; + width: 40px; + height: 40px; + background-color: var(--vp-code-block-bg); + opacity: 0; + cursor: pointer; + background-image: var(--vp-icon-copy); + background-position: 50%; + background-size: 20px; + background-repeat: no-repeat; + transition: opacity 0.4s; +} + +.vp-doc [class*='language-']:hover > button.copy, +.vp-doc [class*='language-'] > button.copy:focus { + opacity: 1; +} + +.vp-doc [class*='language-'] > button.copy:hover { + background-color: var(--vp-code-copy-code-hover-bg); +} + +.vp-doc [class*='language-'] > button.copy.copied, +.vp-doc [class*='language-'] > button.copy:hover.copied { + border-radius: 0 4px 4px 0; + background-color: var(--vp-code-copy-code-hover-bg); + background-image: var(--vp-icon-copied); +} + +.vp-doc [class*='language-'] > button.copy.copied::before, +.vp-doc [class*='language-'] > button.copy:hover.copied::before { + position: relative; + left: -65px; + display: block; + border-radius: 4px 0 0 4px; + padding-top: 8px; + width: 64px; + height: 40px; + text-align: center; + font-size: 12px; + font-weight: 500; + color: var(--vp-code-copy-code-active-text); + background-color: var(--vp-code-copy-code-hover-bg); + white-space: nowrap; + content: 'Copied'; +} + +.vp-doc [class*='language-'] > span.lang { + position: absolute; + top: 6px; + right: 12px; + z-index: 2; + font-size: 12px; + font-weight: 500; + color: var(--vp-c-text-dark-3); + transition: color 0.4s, opacity 0.4s; +} + +.vp-doc [class*='language-']:hover > button.copy + span.lang, +.vp-doc [class*='language-'] > button.copy:focus + span.lang { + opacity: 0; +} + +/** + * Component: Team + * -------------------------------------------------------------------------- */ + +.vp-doc .VPTeamMembers { + margin-top: 24px; +} + +.vp-doc .VPTeamMembers.small.count-1 .container { + margin: 0 !important; + max-width: calc((100% - 24px) / 2) !important; +} + +.vp-doc .VPTeamMembers.small.count-2 .container, +.vp-doc .VPTeamMembers.small.count-3 .container { + max-width: 100% !important; +} + +.vp-doc .VPTeamMembers.medium.count-1 .container { + margin: 0 !important; + max-width: calc((100% - 24px) / 2) !important; +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-sponsor.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-sponsor.css new file mode 100644 index 0000000000000000000000000000000000000000..32da1de9580d0e12a58f275113b520764c413ae7 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/components/vp-sponsor.css @@ -0,0 +1,155 @@ +/** + * VPSponsors styles are defined as global because a new class gets + * allied in onMounted` hook and we can't use socped style. + */ +.vp-sponsor { + border-radius: 16px; + overflow: hidden; +} + +.vp-sponsor.aside { + border-radius: 12px; +} + +.vp-sponsor-section + .vp-sponsor-section { + margin-top: 4px; +} + +.vp-sponsor-tier { + margin-bottom: 4px; + text-align: center; + letter-spacing: 1px; + line-height: 24px; + width: 100%; + font-weight: 600; + color: var(--vp-c-text-2); + background-color: var(--vp-c-bg-soft); +} + +.vp-sponsor.normal .vp-sponsor-tier { + padding: 13px 0 11px; + font-size: 14px; +} + +.vp-sponsor.aside .vp-sponsor-tier { + padding: 9px 0 7px; + font-size: 12px; +} + +.vp-sponsor-grid + .vp-sponsor-tier { + margin-top: 4px; +} + +.vp-sponsor-grid { + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.vp-sponsor-grid.xmini .vp-sponsor-grid-link { + height: 64px; +} +.vp-sponsor-grid.xmini .vp-sponsor-grid-image { + max-width: 64px; + max-height: 22px; +} + +.vp-sponsor-grid.mini .vp-sponsor-grid-link { + height: 72px; +} +.vp-sponsor-grid.mini .vp-sponsor-grid-image { + max-width: 96px; + max-height: 24px; +} + +.vp-sponsor-grid.small .vp-sponsor-grid-link { + height: 96px; +} +.vp-sponsor-grid.small .vp-sponsor-grid-image { + max-width: 96px; + max-height: 24px; +} + +.vp-sponsor-grid.medium .vp-sponsor-grid-link { + height: 112px; +} +.vp-sponsor-grid.medium .vp-sponsor-grid-image { + max-width: 120px; + max-height: 36px; +} + +.vp-sponsor-grid.big .vp-sponsor-grid-link { + height: 184px; +} +.vp-sponsor-grid.big .vp-sponsor-grid-image { + max-width: 192px; + max-height: 56px; +} + +.vp-sponsor-grid[data-vp-grid='2'] .vp-sponsor-grid-item { + width: calc((100% - 4px) / 2); +} + +.vp-sponsor-grid[data-vp-grid='3'] .vp-sponsor-grid-item { + width: calc((100% - 4px * 2) / 3); +} + +.vp-sponsor-grid[data-vp-grid='4'] .vp-sponsor-grid-item { + width: calc((100% - 4px * 3) / 4); +} + +.vp-sponsor-grid[data-vp-grid='5'] .vp-sponsor-grid-item { + width: calc((100% - 4px * 4) / 5); +} + +.vp-sponsor-grid[data-vp-grid='6'] .vp-sponsor-grid-item { + width: calc((100% - 4px * 5) / 6); +} + +.vp-sponsor-grid-item { + flex-shrink: 0; + width: 100%; + background-color: var(--vp-c-bg-soft); + transition: background-color 0.25s; +} + +.vp-sponsor-grid-item:hover { + background-color: var(--vp-c-bg-mute); +} + +.vp-sponsor-grid-item:hover .vp-sponsor-grid-image { + filter: grayscale(0) invert(0); +} + +.vp-sponsor-grid-item.empty:hover { + background-color: var(--vp-c-bg-soft); +} + +.dark .vp-sponsor-grid-item:hover { + background-color: var(--vp-c-white-soft); +} + +.dark .vp-sponsor-grid-item.empty:hover { + background-color: var(--vp-c-black-mute); +} + +.vp-sponsor-grid-link { + display: flex; +} + +.vp-sponsor-grid-box { + display: flex; + justify-content: center; + align-items: center; + width: 100%; +} + +.vp-sponsor-grid-image { + max-width: 100%; + filter: grayscale(1); + transition: filter 0.25s; +} + +.dark .vp-sponsor-grid-image { + filter: grayscale(1) invert(1); +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/fonts.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/fonts.css new file mode 100644 index 0000000000000000000000000000000000000000..7d18208c88be5cfa272157861a9b66e5ca5b983b --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/fonts.css @@ -0,0 +1,217 @@ +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('../fonts/inter-roman-cyrillic.woff2') format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('../fonts/inter-roman-cyrillic-ext.woff2') format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('../fonts/inter-roman-greek.woff2') format('woff2'); + unicode-range: U+0370-03FF; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('../fonts/inter-roman-greek-ext.woff2') format('woff2'); + unicode-range: U+1F00-1FFF; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('../fonts/inter-roman-latin.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('../fonts/inter-roman-latin-ext.woff2') format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, + U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('../fonts/inter-roman-vietnamese.woff2') format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('../fonts/inter-italic-cyrillic.woff2') format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('../fonts/inter-italic-cyrillic-ext.woff2') format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('../fonts/inter-italic-greek.woff2') format('woff2'); + unicode-range: U+0370-03FF; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('../fonts/inter-italic-greek-ext.woff2') format('woff2'); + unicode-range: U+1F00-1FFF; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('../fonts/inter-italic-latin.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('../fonts/inter-italic-latin-ext.woff2') format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, + U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('../fonts/inter-italic-vietnamese.woff2') format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} + +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('../fonts/inter-cyrillic.woff2') format('woff2'); + unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} + +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('../fonts/inter-cyrillic-ext.woff2') format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, + U+FE2E-FE2F; +} + +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('../fonts/inter-greek.woff2') format('woff2'); + unicode-range: U+0370-03FF; +} + +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('../fonts/inter-greek-ext.woff2') format('woff2'); + unicode-range: U+1F00-1FFF; +} + +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('../fonts/inter-latin.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} + +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('../fonts/inter-latin-ext.woff2') format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, + U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} + +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('../fonts/inter-vietnamese.woff2') format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, + U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/lib-override/example-block.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/lib-override/example-block.css new file mode 100644 index 0000000000000000000000000000000000000000..70d7e4f607be1a5adec1ca0657ab77f01f99f626 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/lib-override/example-block.css @@ -0,0 +1,226 @@ +/********************************************************* +* Tokens +*/ +.namespace { + opacity: .7; +} + +.token.doctype .token.doctype-tag { + color: #569CD6; +} + +.token.doctype .token.name { + color: #9cdcfe; +} + +.token.comment, +.token.prolog { + color: #6a9955; +} + +.token.punctuation, +.language-html .language-css .token.punctuation, +.language-html .language-javascript .token.punctuation { + color: #d4d4d4; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.inserted, +.token.unit { + color: #b5cea8; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.deleted { + color: #ce9178; +} + +.language-css .token.string.url { + text-decoration: underline; +} + +.token.operator, +.token.entity { + color: #d4d4d4; +} + +.token.operator.arrow { + color: #569CD6; +} + +.token.atrule { + color: #ce9178; +} + +.token.atrule .token.rule { + color: #c586c0; +} + +.token.atrule .token.url { + color: #9cdcfe; +} + +.token.atrule .token.url .token.function { + color: #dcdcaa; +} + +.token.atrule .token.url .token.punctuation { + color: #d4d4d4; +} + +.token.keyword { + color: #569CD6; +} + +.token.keyword.module, +.token.keyword.control-flow { + color: #c586c0; +} + +.token.function, +.token.function .token.maybe-class-name { + color: #dcdcaa; +} + +.token.regex { + color: #d16969; +} + +.token.important { + color: #569cd6; +} + +.token.italic { + font-style: italic; +} + +.token.constant { + color: #9cdcfe; +} + +.token.class-name, +.token.maybe-class-name { + color: #4ec9b0; +} + +.token.console { + color: #9cdcfe; +} + +.token.parameter { + color: #9cdcfe; +} + +.token.interpolation { + color: #9cdcfe; +} + +.token.punctuation.interpolation-punctuation { + color: #569cd6; +} + +.token.boolean { + color: #569cd6; +} + +.token.property, +.token.variable, +.token.imports .token.maybe-class-name, +.token.exports .token.maybe-class-name { + color: #9cdcfe; +} + +.token.selector { + color: #d7ba7d; +} + +.token.escape { + color: #d7ba7d; +} + +.token.tag { + color: #569cd6; +} + +.token.tag .token.punctuation { + color: #808080; +} + +.token.cdata { + color: #808080; +} + +.token.attr-name { + color: #9cdcfe; +} + +.token.attr-value, +.token.attr-value .token.punctuation { + color: #ce9178; +} + +.token.attr-value .token.punctuation.attr-equals { + color: #d4d4d4; +} + +.token.entity { + color: #569cd6; +} + +.token.namespace { + color: #4ec9b0; +} +/********************************************************* +* Language Specific +*/ + +pre[class*="language-javascript"], +code[class*="language-javascript"], +pre[class*="language-jsx"], +code[class*="language-jsx"], +pre[class*="language-typescript"], +code[class*="language-typescript"], +pre[class*="language-tsx"], +code[class*="language-tsx"] { + color: #9cdcfe; +} + +pre[class*="language-css"], +code[class*="language-css"] { + color: #ce9178; +} + +pre[class*="language-html"], +code[class*="language-html"] { + color: #d4d4d4; +} + +.language-regex .token.anchor { + color: #dcdcaa; +} + +.language-html .token.punctuation { + color: #808080; +} +/********************************************************* +* Line highlighting +*/ +pre[class*="language-"] > code[class*="language-"] { + position: relative; + z-index: 1; +} + +.line-highlight.line-highlight { + background: #f7ebc6; + box-shadow: inset 5px 0 0 #f7d87c; + z-index: 0; +} \ No newline at end of file diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/lib-override/nprogress.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/lib-override/nprogress.css new file mode 100644 index 0000000000000000000000000000000000000000..330f90ee62db9db7a6a95a4975579afe47b9ba9f --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/lib-override/nprogress.css @@ -0,0 +1,12 @@ +#nprogress .bar { + background: var(--vp-c-brand); +} + +#nprogress .spinner-icon { + border-top-color: var(--vp-c-brand); + border-left-color: var(--vp-c-brand); +} + +#nprogress .peg { + box-shadow: 0 0 10px var(--vp-c-brand), 0 0 5px var(--vp-c-brand); +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/utils.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/utils.css new file mode 100644 index 0000000000000000000000000000000000000000..65c7e55ec30ba4ed830225bc666853477337026e --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/utils.css @@ -0,0 +1,9 @@ +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + white-space: nowrap; + clip: rect(0 0 0 0); + clip-path: inset(50%); + overflow: hidden; +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/styles/vars.css b/packages/charts-vue/docs/.vitepress/farris-theme/styles/vars.css new file mode 100644 index 0000000000000000000000000000000000000000..83208adfde5369662026d97e9bc4866a1bb2b1d1 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/styles/vars.css @@ -0,0 +1,581 @@ +/** + * Colors Base + * + * These are the pure base color presets. Most of the time, you should not be + * using these colors directly in the theme but rather use "Colors Theme" + * instead because those are "Theme (light or dark)" dependant. + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-white: #ffffff; + --vp-c-white-soft: #f9f9f9; + --vp-c-white-mute: #f1f1f1; + + --vp-c-black: #1a1a1a; + --vp-c-black-pure: #000000; + --vp-c-black-soft: #17171A; + --vp-c-black-mute: #2f2f2f; + + --vp-c-gray: #8e8e8e; + --vp-c-gray-light-1: #aeaeae; + --vp-c-gray-light-2: #c7c7c7; + --vp-c-gray-light-3: #d1d1d1; + --vp-c-gray-light-4: #e5e5e5; + --vp-c-gray-light-5: #f2f2f2; + --vp-c-gray-dark-1: #636363; + --vp-c-gray-dark-2: #484848; + --vp-c-gray-dark-3: #3a3a3a; + --vp-c-gray-dark-4: #282828; + --vp-c-gray-dark-5: #202020; + + --vp-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vp-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vp-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vp-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vp-c-text-light-1: var(--vp-c-indigo); + --vp-c-text-light-2: #B4BCCC; + --vp-c-text-light-3: rgba(60, 60, 60, 0.33); + --vp-c-text-light-4: rgba(60, 60, 60, 0.18); + + --vp-c-text-dark-1: rgba(255, 255, 255, 0.87); + --vp-c-text-dark-2: rgba(235, 235, 235, 0.6); + --vp-c-text-dark-3: rgba(235, 235, 235, 0.38); + --vp-c-text-dark-4: rgba(235, 235, 235, 0.18); + + --vp-c-indigo: #213547; + --vp-c-indigo-soft: #476582; + --vp-c-indigo-light: #aac8e4; + --vp-c-indigo-lighter: #c9def1; + --vp-c-indigo-dark: #1d2f3f; + --vp-c-indigo-darker: #14212e; + + --vp-c-green: #42b883; + --vp-c-green-light: #42d392; + --vp-c-green-lighter: #35eb9a; + --vp-c-green-dark: #33a06f; + --vp-c-green-darker: #155f3e; + --vp-c-green-dimm-1: rgba(66, 184, 131, 0.5); + --vp-c-green-dimm-2: rgba(66, 184, 131, 0.25); + --vp-c-green-dimm-3: rgba(66, 184, 131, 0.05); + + --vp-c-yellow: #ffc517; + --vp-c-yellow-light: #fcd253; + --vp-c-yellow-lighter: #fcfc7c; + --vp-c-yellow-dark: #e0ad15; + --vp-c-yellow-darker: #ad850e; + --vp-c-yellow-dimm-1: rgba(255, 197, 23, 0.5); + --vp-c-yellow-dimm-2: rgba(255, 197, 23, 0.25); + --vp-c-yellow-dimm-3: rgba(255, 197, 23, 0.05); + + --vp-c-red: #ed3c50; + --vp-c-red-light: #f54e82; + --vp-c-red-lighter: #fd1d7c; + --vp-c-red-dark: #cd2d3f; + --vp-c-red-darker: #ab2131; + --vp-c-red-dimm-1: rgba(237, 60, 80, 0.5); + --vp-c-red-dimm-2: rgba(237, 60, 80, 0.25); + --vp-c-red-dimm-3: rgba(237, 60, 80, 0.05); + + --vp-c-blue: #3662FF; + --vp-c-blue-light: #488afc; + --vp-c-blue-lighter: #2096fd; + --vp-c-blue-dark: #234de5; + --vp-c-blue-darker: #113acd; + --vp-c-blue-dimm-1: rgba(54, 98, 255, 0.5); + --vp-c-blue-dimm-2: rgba(54, 98, 255, 0.25); + --vp-c-blue-dimm-3: rgba(54, 98, 255, 0.05); + + --character-title-text: #2d2f33; + --character-bg-text: #333; + --feature-text: #5A5E66; + + --footer-content-text-opacity: 0.65; + --footer-title-text-opacity: 0.85; + --footer-bg: #031233; + --footer-gz-bg: #fff; + + --footer-gap:160px; + + --character-distance-outer:15px; + --resource-bg-height:100px; + --nav-bg:#fff; + --nav-box-shadow: 0px 2px 6px 0px rgba(0, 0, 0, 0.15); + + --banner-text: url('/assets/banner-text.png'); + --banner-bg: url('/assets/banner-bg.png'); + + --resource-bg: #2a87ff; + --resource-heaer-bg: url('/assets/bg-resource-header.png'); + + --banner-left-top-001: url('/assets/banner-left-top-001.png'); + --banner-left-top-002: url('/assets/banner-left-top-002.png'); + --banner-left-bottom-001: url('/assets/banner-left-bottom-001.png'); + --banner-left-bottom-002: url('/assets/banner-left-bottom-002.png'); + --banner-right-top-002: url('/assets/banner-right-top-002.png'); + --banner-right-bottom-001: url('/assets/banner-right-bottom-001.png'); + --banner-right-bottom-002: url('/assets/banner-right-bottom-002.png'); + + --resource-bg-001: url('/assets/resource-001.png'); + --resource-bg-002: url('/assets/resource-002.png'); + --resource-bg-003: url('/assets/resource-003.png'); + --resource-bg-004: url('/assets/resource-004.png'); + --feature-main-bg:url('/assets/bg-feature-main.png'); + --feature-group-001-icon: url('/assets/feature-group1-icon.png'); + --feature-group-001-animate-left: url('/assets/feature-group1-003.png'); + --feature-group-001-animate-top: url('/assets/feature-group1-007.png'); + --feature-group-001-animate-right: url('/assets/feature-group1-008.png'); + --feature-group-001-animate-center: url('/assets/feature-group1-006.png'); + --feature-group-001-animate-right-bottom: url('/assets/feature-group1-005.png'); + --feature-group-001-animate-left-bottom: url('/assets/feature-group1-004.png'); + + --feature-group-002-icon: url('/assets/feature-group2-icon.png'); + --feature-group-002-animate-left: url('/assets/feature-group2-003.png'); + --feature-group-002-animate-right: url('/assets/feature-group2-004.png'); + --feature-group-002-animate-bottom: url('/assets/feature-group2-005.png'); + + --feature-group-003-icon: url('/assets/feature-group3-icon-dark.png'); + --feature-group-003-animate-left: url('/assets/feature-group3-007.png'); + --feature-group-003-animate-top: url('/assets/feature-group3-003.png'); + --feature-group-003-animate-right: url('/assets/feature-group3-004.png'); + --feature-group-003-animate-right-bottom: url('/assets/feature-group3-005.png'); + --feature-group-003-animate-left-bottom: url('/assets/feature-group3-006.png'); + + --webkit-scrollbar-track-bg:#fff; + --webkit-scrollbar-thumb-bg:rgba(0, 0, 0, .2); + --webkit-scrollbar-thumb-hover-bg:#CCD1DD; + --nav-link-bg: #DBE9FF; + --character-icon-height:72px; + --resource-item-width:25%; + --flex-direction:row; + --scale-size:1; + +} + +/** + * Colors Theme + * -------------------------------------------------------------------------- */ + +:root { + --vp-c-bg: var(--vp-c-white); + --vp-c-bg-soft: var(--vp-c-white-soft); + --vp-c-bg-mute: var(--vp-c-white-mute); + --vp-c-bg-alt: var(--vp-c-white-soft); + + --vp-c-divider: var(--vp-c-divider-light-1); + --vp-c-divider-light: var(--vp-c-divider-light-2); + + --vp-c-divider-inverse: var(--vp-c-divider-dark-1); + --vp-c-divider-inverse-light: var(--vp-c-divider-dark-2); + + --vp-c-text-1: var(--vp-c-text-light-1); + --vp-c-text-2: var(--vp-c-text-light-2); + --vp-c-text-3: var(--vp-c-text-light-3); + --vp-c-text-4: var(--vp-c-text-light-4); + + --vp-c-text-inverse-1: var(--vp-c-text-dark-1); + --vp-c-text-inverse-2: var(--vp-c-text-dark-2); + --vp-c-text-inverse-3: var(--vp-c-text-dark-3); + --vp-c-text-inverse-4: var(--vp-c-text-dark-4); + + --vp-c-text-code: var(--vp-c-indigo-soft); + + --vp-c-brand: var(--vp-c-blue); + --vp-c-brand-light: var(--vp-c-blue-light); + --vp-c-brand-lighter: var(--vp-c-blue-lighter); + --vp-c-brand-dark: var(--vp-c-blue-dark); + --vp-c-brand-darker: var(--vp-c-blue-darker); + + --vp-c-sponsor: #fd1d7c; +} + +.dark { + --vp-c-bg: var(--vp-c-black-soft); + --vp-c-bg-soft: var(--vp-c-black-mute); + --vp-c-bg-mute: var(--vp-c-gray-dark-3); + --vp-c-bg-alt: var(--vp-c-black); + + --vp-c-divider: var(--vp-c-divider-dark-1); + --vp-c-divider-light: var(--vp-c-divider-dark-2); + + --vp-c-divider-inverse: var(--vp-c-divider-light-1); + --vp-c-divider-inverse-light: var(--vp-c-divider-light-2); + + --vp-c-text-1: var(--vp-c-text-dark-1); + --vp-c-text-2: var(--vp-c-text-dark-2); + --vp-c-text-3: var(--vp-c-text-dark-3); + --vp-c-text-4: var(--vp-c-text-dark-4); + + --vp-c-text-inverse-1: var(--vp-c-text-light-1); + --vp-c-text-inverse-2: var(--vp-c-text-light-2); + --vp-c-text-inverse-3: var(--vp-c-text-light-3); + --vp-c-text-inverse-4: var(--vp-c-text-light-4); + + --vp-c-text-code: var(--vp-c-indigo-lighter); + + --nav-bg:#131318; + --nav-box-shadow: 0px 2px 6px 0px rgba(255, 255, 255, 0.15); + --banner-left-top-001: url('/assets/banner-left-top-001-dark.png'); + --banner-left-top-002: url('/assets/banner-left-top-002-dark.png'); + --banner-left-bottom-001: url('/assets/banner-left-bottom-001-dark.png'); + --banner-left-bottom-002: url('/assets/banner-left-bottom-002-dark.png'); + --banner-right-top-002: url('/assets/banner-right-top-002-dark.png'); + --banner-right-bottom-001: url('/assets/banner-right-bottom-001-dark.png'); + --banner-right-bottom-002: url('/assets/banner-right-bottom-002-dark.png'); + --webkit-scrollbar-track-bg:#19191C; + --webkit-scrollbar-thumb-bg:#38383C; + --webkit-scrollbar-thumb-hover-bg:#68686B; + + --banner-text: url('/assets/banner-text-dark.png'); + --banner-bg: url('/assets/banner-bg-dark.png'); + + --resource-bg: #17171A; + --resource-heaer-bg: url('/assets/bg-resource-header-dark.png'); + + --character-title-text: #fff; + --character-bg-text: #fff; + --feature-text: rgba(255, 255, 255, 0.65); + --resource-bg-001: url('/assets/resource-001-dark.png'); + --resource-bg-002: url('/assets/resource-002-dark.png'); + --resource-bg-003: url('/assets/resource-003-dark.png'); + --resource-bg-004: url('/assets/resource-004-dark.png'); + + --feature-main-bg:url('/assets/bg-feature-main-dark.png'); + --nav-link-bg: rgba(42,135,255,0.15); + --footer-bg: #0D1118; + --feature-group-001-icon: url('/assets/feature-group1-icon-dark.png'); + --feature-group-001-animate-left: url('/assets/feature-group1-003-dark.png'); + --feature-group-001-animate-top: url('/assets/feature-group1-007-dark.png'); + --feature-group-001-animate-right: url('/assets/feature-group1-008-dark.png'); + --feature-group-001-animate-center: url('/assets/feature-group1-006-dark.png'); + --feature-group-001-animate-right-bottom: url('/assets/feature-group1-005-dark.png'); + --feature-group-001-animate-left-bottom: url('/assets/feature-group1-004-dark.png'); + + --feature-group-002-icon: url('/assets/feature-group2-icon-dark.png'); + --feature-group-002-animate-left: url('/assets/feature-group2-003-dark.png'); + --feature-group-002-animate-right: url('/assets/feature-group2-004-dark.png'); + --feature-group-002-animate-bottom: url('/assets/feature-group2-005-dark.png'); + + --feature-group-003-icon: url('/assets/feature-group3-icon-dark.png'); + --feature-group-003-animate-left: url('/assets/feature-group3-007-dark.png'); + --feature-group-003-animate-top: url('/assets/feature-group3-003-dark.png'); + --feature-group-003-animate-right: url('/assets/feature-group3-004-dark.png'); + --feature-group-003-animate-right-bottom: url('/assets/feature-group3-005-dark.png'); + --feature-group-003-animate-left-bottom: url('/assets/feature-group3-006-dark.png'); + +} + +/** + * Typography + * -------------------------------------------------------------------------- */ + +:root { + --vp-font-family-base: 'PingFangSC-Regular', -apple-system, 'Microsoft YaHei', + 'Helvetica Neue', + Helvetica, + Arial, + sans-serif; + --vp-font-family-mono: Menlo, Monaco, Consolas, 'Courier New', monospace; + --vp-font-family-title: 'PingFangSC-Medium', -apple-system, BlinkMacSystemFont, 'Hiragino Sans GB', 'Microsoft YaHei', 'Helvetica Neue', Helvetica, Arial, sans-serif +} + +/** + * Shadows + * -------------------------------------------------------------------------- */ + +:root { + --vp-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06); + --vp-shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07); + --vp-shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08); + --vp-shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12); + --vp-shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16); +} + +/** + * Z-indexes + + * -------------------------------------------------------------------------- */ + +:root { + --vp-z-index-local-nav: 1050; + --vp-z-index-nav: 20; + --vp-z-index-backdrop: 1100; + --vp-z-index-sidebar: 1200; + --vp-z-index-footer: 50; + + --vp-z-index-back-to-top:1000; +} + +/** + * Icons + * -------------------------------------------------------------------------- */ + +:root { + --vp-icon-copy: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2'/%3E%3C/svg%3E"); + --vp-icon-copied: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' height='20' width='20' stroke='rgba(128,128,128,1)' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' d='M9 5H7a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-2M9 5a2 2 0 0 0 2 2h2a2 2 0 0 0 2-2M9 5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2m-6 9 2 2 4-4'/%3E%3C/svg%3E"); +} + +/** + * Layouts + * -------------------------------------------------------------------------- */ + +:root { + --vp-layout-max-width: 1440px; +} + +/** + * Component: Code + * -------------------------------------------------------------------------- */ + +:root { + --vp-code-line-height: 1.7; + --vp-code-font-size: 14px; + + --vp-code-block-color: var(--vp-c-text-dark-1); + --vp-code-block-bg: #292d3e; + + --vp-code-line-highlight-color: rgba(0, 0, 0, 0.5); + --vp-code-line-number-color: var(--vp-c-text-dark-3); + + --vp-code-copy-code-hover-bg: rgba(255, 255, 255, 0.05); + --vp-code-copy-code-active-text: var(--vp-c-text-dark-2); +} + +.dark { + --vp-code-block-bg: var(--vp-c-bg-alt); +} + +/** + * Component: Button + * -------------------------------------------------------------------------- */ + +:root { + --vp-button-brand-border: var(--vp-c-brand-light); + --vp-button-brand-text: var(--vp-c-text-dark-1); + --vp-button-brand-bg: var(--vp-c-brand); + --vp-button-brand-hover-border: var(--vp-c-brand-light); + --vp-button-brand-hover-text: var(--vp-c-text-dark-1); + --vp-button-brand-hover-bg: var(--vp-c-brand-light); + --vp-button-brand-active-border: var(--vp-c-brand-light); + --vp-button-brand-active-text: var(--vp-c-text-dark-1); + --vp-button-brand-active-bg: var(--vp-button-brand-bg); + + --vp-button-alt-border: var(--vp-c-gray-light-3); + --vp-button-alt-text: var(--vp-c-text-light-1); + --vp-button-alt-bg: var(--vp-c-gray-light-5); + --vp-button-alt-hover-border: var(--vp-c-gray-light-3); + --vp-button-alt-hover-text: var(--vp-c-text-light-1); + --vp-button-alt-hover-bg: var(--vp-c-gray-light-4); + --vp-button-alt-active-border: var(--vp-c-gray-light-3); + --vp-button-alt-active-text: var(--vp-c-text-light-1); + --vp-button-alt-active-bg: var(--vp-c-gray-light-3); + + --vp-button-sponsor-border: var(--vp-c-gray-light-3); + --vp-button-sponsor-text: var(--vp-c-text-light-2); + --vp-button-sponsor-bg: transparent; + --vp-button-sponsor-hover-border: var(--vp-c-sponsor); + --vp-button-sponsor-hover-text: var(--vp-c-sponsor); + --vp-button-sponsor-hover-bg: transparent; + --vp-button-sponsor-active-border: var(--vp-c-sponsor); + --vp-button-sponsor-active-text: var(--vp-c-sponsor); + --vp-button-sponsor-active-bg: transparent; +} + +.dark { + --vp-button-brand-border: var(--vp-c-brand-light); + --vp-button-brand-text: var(--vp-c-text-dark-1); + --vp-button-brand-bg: var(--vp-c-brand-dark); + --vp-button-brand-hover-border: var(--vp-c-brand-lighter); + --vp-button-brand-hover-text: var(--vp-c-text-dark-1); + --vp-button-brand-hover-bg: var(--vp-c-brand); + --vp-button-brand-active-border: var(--vp-c-brand-lighter); + --vp-button-brand-active-text: var(--vp-c-text-dark-1); + --vp-button-brand-active-bg: var(--vp-button-brand-bg); + + --vp-button-alt-border: var(--vp-c-gray-dark-2); + --vp-button-alt-text: var(--vp-c-text-dark-1); + --vp-button-alt-bg: var(--vp-c-bg-mute); + --vp-button-alt-hover-border: var(--vp-c-gray-dark-2); + --vp-button-alt-hover-text: var(--vp-c-text-dark-1); + --vp-button-alt-hover-bg: var(--vp-c-gray-dark-2); + --vp-button-alt-active-border: var(--vp-c-gray-dark-2); + --vp-button-alt-active-text: var(--vp-c-text-dark-1); + --vp-button-alt-active-bg: var(--vp-button-alt-bg); + + --vp-button-sponsor-border: var(--vp-c-gray-dark-1); + --vp-button-sponsor-text: var(--vp-c-text-dark-2); + +} + +/** + * Component: Custom Block + * -------------------------------------------------------------------------- */ + +:root { + --vp-custom-block-code-font-size: 13px; + + --vp-custom-block-info-border: var(--vp-c-divider-light); + --vp-custom-block-info-text: var(--vp-c-text-2); + --vp-custom-block-info-bg: var(--vp-c-white-soft); + --vp-custom-block-info-code-bg: var(--vp-c-gray-light-4); + + --vp-custom-block-tip-border: var(--vp-c-blue-dimm-1); + --vp-custom-block-tip-text: var(--vp-c-blue-darker); + --vp-custom-block-tip-bg: var(--vp-c-blue-dimm-3); + --vp-custom-block-tip-code-bg: var(--vp-custom-block-tip-bg); + + --vp-custom-block-warning-border: var(--vp-c-yellow-dimm-1); + --vp-custom-block-warning-text: var(--vp-c-yellow-darker); + --vp-custom-block-warning-bg: var(--vp-c-yellow-dimm-3); + --vp-custom-block-warning-code-bg: var(--vp-custom-block-warning-bg); + + --vp-custom-block-danger-border: var(--vp-c-red-dimm-1); + --vp-custom-block-danger-text: var(--vp-c-red-darker); + --vp-custom-block-danger-bg: var(--vp-c-red-dimm-3); + --vp-custom-block-danger-code-bg: var(--vp-custom-block-danger-bg); + + --vp-custom-block-details-border: var(--vp-custom-block-info-border); + --vp-custom-block-details-text: var(--vp-custom-block-info-text); + --vp-custom-block-details-bg: var(--vp-custom-block-info-bg); + --vp-custom-block-details-code-bg: var(--vp-custom-block-details-bg); +} + +.dark { + --vp-custom-block-info-border: var(--vp-c-divider-light); + --vp-custom-block-info-bg: var(--vp-c-black-mute); + --vp-custom-block-info-code-bg: var(--vp-c-gray-dark-4); + + --vp-custom-block-tip-border: var(--vp-c-blue-dimm-2); + --vp-custom-block-tip-text: var(--vp-c-blue-light); + + --vp-custom-block-warning-border: var(--vp-c-yellow-dimm-2); + --vp-custom-block-warning-text: var(--vp-c-yellow-light); + + --vp-custom-block-danger-border: var(--vp-c-red-dimm-2); + --vp-custom-block-danger-text: var(--vp-c-red-light); +} + +/** + * Component: Nav + * -------------------------------------------------------------------------- */ + +:root { + --vp-nav-height: var(--vp-nav-height-mobile); + --vp-nav-height-mobile: 56px; + --vp-nav-height-desktop: 64px; +} + +@media (min-width: 960px) { + :root { + --vp-nav-height: var(--vp-nav-height-desktop); + } +} + +/** + * Component: Sidebar + * -------------------------------------------------------------------------- */ + +:root { + --vp-sidebar-width: 272px; +} + +/** + * Component: Home + * -------------------------------------------------------------------------- */ + +:root { + --vp-home-hero-name-color: var(--vp-c-brand); + --vp-home-hero-name-background: transparent; + + --vp-home-hero-image-background-image: none; + --vp-home-hero-image-filter: none; +} +@media(max-width:1660px) { +} +@media(max-width:1440px) { + :root{ + --vp-layout-max-width:90%; + } +} +@media(max-width:1240px) { + +} +@media(max-width:1000px) { + :root{ + --resource-item-width:48%; + --footer-gap:60px; + } + +} +@media(max-width:830px) { + :root{ + --character-icon-height:72px; + --flex-direction:column; + } + .farris-doc-banner-wrapper .banner-text{ + width: 80%!important; + margin-left: -40%!important; + background-size:contain; + } + .farris-doc-banner .banner-content>*{ + scale: .7; + } +} +@media(max-width:730px) { + +} +@media(max-width:639px) { + :root{ + --resource-item-width:90%; + --footer-gap:80px; + --scale-size:0.9; + --character-distance-outer:6px; + --resource-bg-height:140px; + } + .banner-content .banner-scene { + animation-iteration-count:0!important; + } + .banner-content .banner-right-top{ + display: none; + } + .farris-doc-feature--content{ + width: 100% !important;; + } + .farris-docs-footer--content{ + display: block !important;; + } + .farris-docs-footer--content .content-intro{ + margin-bottom:20px; + justify-content: space-around; + } + .farris-docs-footer--content .content-gz{ + justify-content: space-around; + } + .content-gz .content-block p{ + margin-bottom:10px!important; + } + .content-gz .content-block img{ + width: 100px !important; + height:100px !important; + } + + .farris-docs-footer--bottom span{ + display: block; + margin:0 auto; + } +} +@media(max-width:420px) { + .farris-doc-character{ + max-width: 100%; + overflow:hidden; + height: 150px; + } + .farris-doc-character--row{ + display: inline-flex; + overflow-x:auto; + padding:10px; + } + .farris-doc-character--row .character-item{ + min-width: 120px; + } +} \ No newline at end of file diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/support/sidebar.ts b/packages/charts-vue/docs/.vitepress/farris-theme/support/sidebar.ts new file mode 100644 index 0000000000000000000000000000000000000000..b55fa1356f237b2dfb6aaa32634a299acf5fb86c --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/support/sidebar.ts @@ -0,0 +1,48 @@ +import type { DefaultTheme } from 'vitepress/theme' +import { ensureStartingSlash } from './utils.js' + +/** + * Get the `Sidebar` from sidebar option. This method will ensure to get correct + * sidebar config from `MultiSideBarConfig` with various path combinations such + * as matching `guide/` and `/guide/`. If no matching config was found, it will + * return empty array. + */ +export function getSidebar( + sidebar: DefaultTheme.Sidebar, + path: string +): DefaultTheme.SidebarGroup[] { + if (Array.isArray(sidebar)) { + return sidebar + } + + path = ensureStartingSlash(path) + + for (const dir in sidebar) { + // make sure the multi sidebar key starts with slash too + if (path.startsWith(ensureStartingSlash(dir))) { + return sidebar[dir] + } + } + + return [] +} + +export function getFlatSideBarLinks(sidebar: DefaultTheme.SidebarGroup[]) { + const links: { text: string; link: string }[] = [] + + function recursivelyExtractLinks(items: DefaultTheme.SidebarItem[]) { + for (const item of items) { + if (item.link) { + links.push({ ...item, link: item.link }) + } + if ('items' in item) { + recursivelyExtractLinks(item.items) + } + } + } + + for (const group of sidebar) { + recursivelyExtractLinks(group.items) + } + return links +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/support/socialIcons.ts b/packages/charts-vue/docs/.vitepress/farris-theme/support/socialIcons.ts new file mode 100644 index 0000000000000000000000000000000000000000..90fb19abfd33f39cbea405480d8287fd5baa1bfb --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/support/socialIcons.ts @@ -0,0 +1,20 @@ +// Used under CC0 1.0 from https://simpleicons.org/ + +export const icons = { + discord: + 'Discord', + facebook: + 'Facebook', + github: + 'GitHub', + instagram: + 'Instagram', + linkedin: + 'LinkedIn', + slack: + 'Slack', + twitter: + 'Twitter', + youtube: + 'YouTube' +} as const diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/support/utils.ts b/packages/charts-vue/docs/.vitepress/farris-theme/support/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..f0a24345aa5e84342286e3029c55875e007aba00 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/support/utils.ts @@ -0,0 +1,89 @@ +import { ref } from 'vue' +import { withBase, useData } from 'vitepress' +import { EXTERNAL_URL_RE } from '../shared/shared' + +export const HASH_RE = /#.*$/ +export const EXT_RE = /(index)?\.(md|html)$/ + +const inBrowser = typeof window !== 'undefined' +const hashRef = ref(inBrowser ? location.hash : '') + +export function isExternal(path: string): boolean { + return EXTERNAL_URL_RE.test(path) +} + +export function throttleAndDebounce(fn: () => void, delay: number): () => void { + let timeout: any + let called = false + + return () => { + if (timeout) { + clearTimeout(timeout) + } + + if (!called) { + fn() + called = true + setTimeout(() => { + called = false + }, delay) + } else { + timeout = setTimeout(fn, delay) + } + } +} + +export function isActive( + currentPath: string, + matchPath?: string, + asRegex: boolean = false +): boolean { + if (matchPath === undefined) { + return false + } + + currentPath = normalize(`/${currentPath}`) + + if (asRegex) { + return new RegExp(matchPath).test(currentPath) + } + + if (normalize(matchPath) !== currentPath) { + return false + } + + const hashMatch = matchPath.match(HASH_RE) + + if (hashMatch) { + return hashRef.value === hashMatch[0] + } + + return true +} + +export function ensureStartingSlash(path: string): string { + return /^\//.test(path) ? path : `/${path}` +} + +export function normalize(path: string): string { + return decodeURI(path).replace(HASH_RE, '').replace(EXT_RE, '') +} + +export function normalizeLink(url: string): string { + if (isExternal(url)) { + return url + } + + const { site } = useData() + const { pathname, search, hash } = new URL(url, 'http://example.com') + + const normalizedPath = + pathname.endsWith('/') || pathname.endsWith('.html') + ? url + : `${pathname.replace( + /(\.md)?$/, + site.value.cleanUrls === 'disabled' ? '.html' : '' + )}${search}${hash}` + + return withBase(normalizedPath) +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/types/default-theme.d.ts b/packages/charts-vue/docs/.vitepress/farris-theme/types/default-theme.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..752a67486ad73b4313b521dd73460b4290bafe54 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/types/default-theme.d.ts @@ -0,0 +1,263 @@ +export namespace DefaultTheme { + export interface Config { + /** + * The logo file of the site. + * + * @example '/logo.svg' + */ + logo?: ThemeableImage + + /** + * Custom site title in navbar. If the value is undefined, + * `config.title` will be used. + */ + siteTitle?: string | false + + /** + * Custom header levels of outline in the aside component. + * + * @default 2 + */ + outline?: number | [number, number] | 'deep' | false + + /** + * Custom outline title in the aside component. + * + * @default 'On this page' + */ + outlineTitle?: string + + /** + * The nav items. + */ + nav?: NavItem[] + + /** + * The sidebar items. + */ + sidebar?: Sidebar + + /** + * Info for the edit link. If it's undefined, the edit link feature will + * be disabled. + */ + editLink?: EditLink + + /** + * Set custom last updated text. + * + * @default 'Last updated' + */ + lastUpdatedText?: string + + /** + * Set custom prev/next labels. + */ + docFooter?: DocFooter + + /** + * The social links to be displayed at the end of the nav bar. Perfect for + * placing links to social services such as GitHub, Twitter, Facebook, etc. + */ + socialLinks?: SocialLink[] + + /** + * The footer configuration. + */ + footer?: Footer + + /** + * Adds locale menu to the nav. This option should be used when you have + * your translated sites outside of the project. + */ + localeLinks?: LocaleLinks + + /** + * The algolia options. Leave it undefined to disable the search feature. + */ + algolia?: AlgoliaSearchOptions + + /** + * The carbon ads options. Leave it undefined to disable the ads feature. + */ + carbonAds?: CarbonAdsOptions + } + + // nav ----------------------------------------------------------------------- + + export type NavItem = NavItemWithLink | NavItemWithChildren + + export type NavItemWithLink = { + text: string + link: string + + /** + * `activeMatch` is expected to be a regex string. We can't use actual + * RegExp object here because it isn't serializable + */ + activeMatch?: string + } + + export type NavItemChildren = { + text?: string + items: NavItemWithLink[] + } + + export interface NavItemWithChildren { + text?: string + items: (NavItemChildren | NavItemWithLink)[] + + /** + * `activeMatch` is expected to be a regex string. We can't use actual + * RegExp object here because it isn't serializable + */ + activeMatch?: string + } + + // image ----------------------------------------------------------------------- + + export type ThemeableImage = Image | { light: Image; dark: Image } + export type Image = string | { src: string; alt?: string } + + // sidebar ------------------------------------------------------------------- + + export type Sidebar = SidebarGroup[] | SidebarMulti + + export interface SidebarMulti { + [path: string]: SidebarGroup[] + } + + export interface SidebarGroup { + text?: string + items: SidebarItem[] + + /** + * If `true`, toggle button is shown. + * + * @default false + */ + collapsible?: boolean + + /** + * If `true`, collapsible group is collapsed by default. + * + * @default false + */ + collapsed?: boolean + } + + export type SidebarItem = + | { text: string; link: string } + | { text: string; link?: string; items: SidebarItem[] } + + // edit link ----------------------------------------------------------------- + + export interface EditLink { + /** + * Pattern for edit link. + * + * @example 'https://github.com/vuejs/vitepress/edit/main/docs/:path' + */ + pattern: string + + /** + * Custom text for edit link. + * + * @default 'Edit this page' + */ + text?: string + } + + // prev-next ----------------------------------------------------------------- + + export interface DocFooter { + /** + * Custom label for previous page button. + * + * @default 'Previous page' + */ + prev?: string + + /** + * Custom label for next page button. + * + * @default 'Next page' + */ + next?: string + } + + // social link --------------------------------------------------------------- + + export interface SocialLink { + icon: SocialLinkIcon + link: string + } + + export type SocialLinkIcon = + | 'discord' + | 'facebook' + | 'github' + | 'instagram' + | 'linkedin' + | 'slack' + | 'twitter' + | 'youtube' + | { svg: string } + + // footer -------------------------------------------------------------------- + + export interface Footer { + message?: string + copyright?: string + address?:string + } + + // team ---------------------------------------------------------------------- + + export interface TeamMember { + avatar: string + name: string + title?: string + org?: string + orgLink?: string + desc?: string + links?: SocialLink[] + sponsor?: string + } + + // locales ------------------------------------------------------------------- + + export interface LocaleLinks { + text: string + items: LocaleLink[] + } + + export interface LocaleLink { + text: string + link: string + } + + // algolia ------------------------------------------------------------------ + + /** + * The Algolia search options. Partially copied from + * `@docsearch/react/dist/esm/DocSearch.d.ts` + */ + export interface AlgoliaSearchOptions { + appId: string + apiKey: string + indexName: string + placeholder?: string + searchParameters?: any + disableUserPersonalization?: boolean + initialQuery?: string + buttonText?: string + } + + // carbon ads ---------------------------------------------------------------- + + export interface CarbonAdsOptions { + code: string + placement: string + } +} diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/types/index.d.ts b/packages/charts-vue/docs/.vitepress/farris-theme/types/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a8d52c89f29f3d244444490f517b071def0b278 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/types/index.d.ts @@ -0,0 +1,3 @@ +export * from './shared.js' +export * from '../dist/client/index.js' +export * from '../dist/node/index.js' diff --git a/packages/charts-vue/docs/.vitepress/farris-theme/types/shared.d.ts b/packages/charts-vue/docs/.vitepress/farris-theme/types/shared.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..f37194dc1c2472a30fd02cae5540507e750fda86 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/farris-theme/types/shared.d.ts @@ -0,0 +1,109 @@ +// types shared between server and client +export type { DefaultTheme } from './default-theme.js' + +export type Awaitable = T | PromiseLike + +export interface PageData { + relativePath: string + title: string + titleTemplate?: string | boolean + description: string + headers: Header[] + frontmatter: Record + lastUpdated?: number +} + +export interface Header { + /** + * The level of the header + * + * `1` to `6` for `

` to `

` + */ + level: number + /** + * The title of the header + */ + title: string + /** + * The slug of the header + * + * Typically the `id` attr of the header anchor + */ + slug: string + /** + * Link of the header + * + * Typically using `#${slug}` as the anchor hash + */ + link: string + /** + * The children of the header + */ + children: Header[] +} + +export type CleanUrlsMode = + | 'disabled' + | 'without-subfolders' + | 'with-subfolders' + +export interface SiteData { + base: string + cleanUrls?: CleanUrlsMode + + /** + * Language of the site as it should be set on the `html` element. + * + * @example `en-US`, `zh-CN` + */ + lang: string + + title: string + titleTemplate?: string | boolean + description: string + head: HeadConfig[] + appearance: boolean + themeConfig: ThemeConfig + scrollOffset: number | string + locales: Record + + /** + * Available locales for the site when it has defined `locales` in its + * `themeConfig`. This object is otherwise empty. Keys are paths like `/` or + * `/zh/`. + */ + langs: Record< + string, + { + /** + * Lang attribute as set on the `` element. + * @example `en-US`, `zh-CN` + */ + lang: string + /** + * Label to display in the language menu. + * @example `English`, `简体中文` + */ + label: string + } + > +} + +export type HeadConfig = + | [string, Record] + | [string, Record, string] + +export interface LocaleConfig { + lang: string + title?: string + titleTemplate?: string | boolean + description?: string + head?: HeadConfig[] + label?: string + selectText?: string +} + +export interface PageDataPayload { + path: string + pageData: PageData +} diff --git a/packages/charts-vue/docs/.vitepress/plugins/farris-markdown-plugin.ts b/packages/charts-vue/docs/.vitepress/plugins/farris-markdown-plugin.ts new file mode 100644 index 0000000000000000000000000000000000000000..2be2ee561a024fa83a4626bc576ccc89b17669af --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/plugins/farris-markdown-plugin.ts @@ -0,0 +1,126 @@ +import { highlight } from './../utils/highlight'; +import path from 'path'; +import fs from 'fs'; +import MarkdownIt from 'markdown-it'; +import mdContainer from 'markdown-it-container'; +import { render } from '../utils/render'; +import type Token from 'markdown-it/lib/token'; +import type Renderer from 'markdown-it/lib/renderer'; + +interface ContainerOpts { + marker?: string | undefined; + validate?(params: string): boolean; + render?(tokens: Token[], index: number, options: any, env: any, self: Renderer): string; +} + +const localMd = MarkdownIt(); + +export const farrisMarkdownPlugin = (md: MarkdownIt, options: any) => { + md.use(mdContainer, 'demo', { + validate(params: string) { + return !!params.trim().match(/^demo\s*(.*)$/); + }, + render(tokens: any[], index: number) { + const isTagOpening = tokens[index].nesting === 1; + const matchDescriptionResult = tokens[index].info.trim().match(/^demo\s*(.*)$/); + const hasDescription = matchDescriptionResult && matchDescriptionResult.length > 1; + if (isTagOpening) { + let sourceCode: string = tokens[index + 1].type === 'fence' ? tokens[index + 1].content : ''; + const startTag = '{'; + const endTag = '}'; + const hasVuePath = sourceCode.trim().indexOf(startTag) > -1 && sourceCode.indexOf(endTag) > -1; + if (hasVuePath) { + const start = sourceCode.indexOf(startTag) + startTag.length; + const end = sourceCode.indexOf(endTag, start); + const vueFilePath = sourceCode.slice(start, end); + sourceCode = fs.readFileSync(path.resolve('', vueFilePath), 'utf-8'); + tokens[index + 1].content = sourceCode; + } + return `${sourceCode ? `` : ''}`; + } else { + return ''; + } + } + }); + const lang = options?.lang || 'vue'; + const defaultRender = md.renderer.rules.fence; + md.renderer.rules.fence = (tokens: any[], index: number, option: any, env: any, self: any) => { + const token = tokens[index]; + const prevToken = tokens[index - 1]; + const isFenceInMDContainer = !!(prevToken && prevToken.nesting === 1 && prevToken.info.trim().match(/^demo\s*(.*)$/)); + const shouldOverrideRender = token.info.trim() === lang && isFenceInMDContainer; + if (shouldOverrideRender) { + const matchDescriptionResult = prevToken.info.trim().match(/^demo\s*(.*)$/); + const hasDescription = matchDescriptionResult && matchDescriptionResult.length > 1; + const description = hasDescription ? matchDescriptionResult[1] : ''; + return ` + ${ + description + ? `` + : '' + } + `; + } + return defaultRender ? defaultRender(tokens, index, options, env, self) : ''; + }; + const originalRender = md.render; + md.render = (src: string, env?: any) => { + let result = originalRender.call(md, src, env); + const startTag = ''; + const hasVueDemo = result.indexOf(startTag) > -1 && result.indexOf(endTag) > -1; + if (hasVueDemo) { + const { template, script, style } = render(result, options); + result = template; + const data = md['__data']; + const hoistedTags = data.hoistedTags || (data.hoistedTags = []); + hoistedTags.push(script); + hoistedTags.push(style); + } + return result; + }; + md.use(mdContainer, 'vdemo', { + validate(params) { + return !!params.trim().match(/^vdemo\s*(.*)$/); + }, + + render(tokens: any[], idx: number, option: any) { + const m = tokens[idx].info.trim().match(/^vdemo\s*(.*)$/); + if (tokens[idx].nesting === 1 /* means the tag is opening */) { + const description = m && m.length > 1 ? m[1] : ''; + const sourceFileToken = tokens[idx + 2]; + let source = ''; + let highlightSourceCode = ''; + // const sourceFile = sourceFileToken.children?.[0].content ?? ''; + const sourceFile: string = tokens[idx + 1].type === 'fence' ? tokens[idx + 1].content : ''; + const startTag = '{'; + const endTag = '}'; + const hasVuePath = sourceFile.trim().indexOf(startTag) > -1 && sourceFile.indexOf(endTag) > -1; + if (hasVuePath) { + const start = sourceFile.indexOf(startTag) + startTag.length; + const end = sourceFile.indexOf(endTag, start); + const vueFilePath = sourceFile.slice(start, end); + source = fs.readFileSync(path.resolve('', vueFilePath), 'utf-8'); + highlightSourceCode = option.highlight(source, lang, ''); + } + + // if (sourceFileToken.type === 'inline') { + // source = fs.readFileSync(path.resolve(docRoot, 'examples', `${sourceFile}.vue`), 'utf-8'); + // } + if (!source) throw new Error(`Incorrect source file: ${sourceFile}`); + + return ``; + } else { + return ''; + } + } + } as ContainerOpts); +}; diff --git a/packages/charts-vue/docs/.vitepress/plugins/markdown-transform.ts b/packages/charts-vue/docs/.vitepress/plugins/markdown-transform.ts new file mode 100644 index 0000000000000000000000000000000000000000..289d40a7ce6fdc6a94fdce2d8ec71722931b90bc --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/plugins/markdown-transform.ts @@ -0,0 +1,54 @@ +import path from 'path'; + +import type { Plugin } from 'vite'; + +type Append = Record<'headers' | 'footers' | 'scriptSetups', string[]>; + +export function MarkdownTransform(): Plugin { + return { + name: 'md-transform', + enforce: 'pre', + async transform(code, id) { + if (id.endsWith('.md') && id.includes('/charts-vue/docs/components')) { + const pathFragements = id.split('/'); + const componentName = pathFragements[pathFragements.length - 2]; + const append: Append = { + headers: [], + footers: [], + scriptSetups: [`const demos = import.meta.globEager('../../../demos/${componentName}/*.vue')`] + }; + // code = transformVpScriptSetup(code, append); + return combineMarkdown(code, [combineScriptSetup(append.scriptSetups), ...append.headers], append.footers); + } + } + }; +} + +const combineScriptSetup = (codes: string[]) => + `\n +`; + +const combineMarkdown = (code: string, headers: string[], footers: string[]) => { + const frontmatterEndsPosition = code.indexOf('---\n\n'); + const frontmatterEnds = frontmatterEndsPosition > -1 ? frontmatterEndsPosition + 4 : 0; + // const firstSubheader = code.search(/\n## \w/); + const firstSubheader = code.search(/\n##/); + const sliceIndex = firstSubheader < 0 ? frontmatterEnds : firstSubheader; + + if (headers.length > 0) code = code.slice(0, sliceIndex) + headers.join('\n') + code.slice(sliceIndex); + code += footers.join('\n'); + + return `${code}\n`; +}; + +const vpScriptSetupRE = /([\s\S]*)<\/vp-script>/; + +const transformVpScriptSetup = (code: string, append: Append) => { + const matches = code.match(vpScriptSetupRE); + if (matches) code = code.replace(matches[0], ''); + const scriptSetup = matches?.[3] ?? ''; + if (scriptSetup) append.scriptSetups.push(scriptSetup); + return code; +}; diff --git a/packages/charts-vue/docs/.vitepress/theme/index.ts b/packages/charts-vue/docs/.vitepress/theme/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b4102e11cdb507cf46f52f96accd57d0a849821 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/theme/index.ts @@ -0,0 +1,15 @@ +import Farris from '../../../components'; +import FarrisTheme from '../farris-theme'; +import { registerComponents } from './register-components.js'; +import { insertBaiduScript } from './insert-baidu-script'; + +// import '../../../public/assets/farris-all.css'; + +export default { + ...FarrisTheme, + enhanceApp({ app }) { + app.use(Farris); + registerComponents(app); + insertBaiduScript(); + } +}; diff --git a/packages/charts-vue/docs/.vitepress/theme/insert-baidu-script.ts b/packages/charts-vue/docs/.vitepress/theme/insert-baidu-script.ts new file mode 100644 index 0000000000000000000000000000000000000000..fdae2d7ff16b71b8f30607c51d689fb4287858a5 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/theme/insert-baidu-script.ts @@ -0,0 +1,15 @@ +export function insertBaiduScript() { + if (typeof document === 'undefined') return; + const baiduScript = document.createElement('script'); + const baiduScriptStr = ` + var _hmt = _hmt || []; + (function() { + var hm = document.createElement("script"); + hm.src = "https://hm.baidu.com/hm.js?d837f6d4ecb051c0c1347ce79f6a831c"; + var s = document.getElementsByTagName("script")[0]; + s.parentNode.insertBefore(hm, s); + })(); + `; + baiduScript.textContent = baiduScriptStr; + document.body.append(baiduScript); +} diff --git a/packages/charts-vue/docs/.vitepress/theme/register-components.js b/packages/charts-vue/docs/.vitepress/theme/register-components.js new file mode 100644 index 0000000000000000000000000000000000000000..f753ce7191ee4f4918044c09aa879b0c47bb4576 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/theme/register-components.js @@ -0,0 +1,13 @@ +// import Demo from 'vitepress-theme-demoblock/components/Demo.vue'; +// import DemoBlock from 'vitepress-theme-demoblock/components/DemoBlock.vue'; +// export function registerComponents(app) { +// app.component('Demo', Demo); +// app.component('DemoBlock', DemoBlock); +// } + +import FExample from '../farris-theme/example/FExample.vue'; +import VPDemo from '../farris-theme/components/VPDemo.vue'; + +export function registerComponents(app) { + app.component('Demo', FExample).component('VPDemo', VPDemo); +} diff --git a/packages/charts-vue/docs/.vitepress/utils/highlight-shiki.ts b/packages/charts-vue/docs/.vitepress/utils/highlight-shiki.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce5ee00397339b2c051b4a6f58d16c05a11fc528 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/utils/highlight-shiki.ts @@ -0,0 +1,73 @@ +import { IThemeRegistration, getHighlighter, HtmlRendererOptions } from 'shiki'; + +export type ThemeOptions = + | IThemeRegistration + | { light: IThemeRegistration; dark: IThemeRegistration } + +/** + * 2 steps: + * + * 1. convert attrs into line numbers: + * {4,7-13,16,23-27,40} -> [4,7,8,9,10,11,12,13,16,23,24,25,26,27,40] + * 2. convert line numbers into line options: + * [{ line: number, classes: string[] }] + */ +const attrsToLines = (attrs: string): HtmlRendererOptions['lineOptions'] => { + const result: number[] = [] + if (!attrs.trim()) { + return [] + } + attrs + .split(',') + .map((v) => v.split('-').map((v) => parseInt(v, 10))) + .forEach(([start, end]) => { + if (start && end) { + result.push( + ...Array.from({ length: end - start + 1 }, (_, i) => start + i) + ) + } else { + result.push(start) + } + }) + return result.map((v) => ({ + line: v, + classes: ['highlighted'] + })) +} + +export async function highlight( + theme: ThemeOptions = 'material-palenight' +): Promise<(str: string, lang: string, attrs: string) => string> { + const hasSingleTheme = typeof theme === 'string' || 'name' in theme + const getThemeName = (themeValue: IThemeRegistration) => + typeof themeValue === 'string' ? themeValue : themeValue.name + + const highlighter = await getHighlighter({ + themes: hasSingleTheme ? [theme] : [theme.dark, theme.light] + }) + const preRE = /^/ + const vueRE = /-vue$/ + + return (str: string, lang: string, attrs: string) => { + const vPre = vueRE.test(lang) ? '' : 'v-pre' + lang = lang.replace(vueRE, '').toLowerCase() + + const lineOptions = attrsToLines(attrs) + + if (hasSingleTheme) { + return highlighter + .codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme) }) + .replace(preRE, `
`)
+    }
+
+    const dark = highlighter
+      .codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme.dark) })
+      .replace(preRE, `
`)
+
+    const light = highlighter
+      .codeToHtml(str, { lang, lineOptions, theme: getThemeName(theme.light) })
+      .replace(preRE, `
`)
+
+    return dark + light
+  }
+}
diff --git a/packages/charts-vue/docs/.vitepress/utils/highlight.ts b/packages/charts-vue/docs/.vitepress/utils/highlight.ts
new file mode 100644
index 0000000000000000000000000000000000000000..229fc5b2046e518482d2a4c053a88963b6e85f34
--- /dev/null
+++ b/packages/charts-vue/docs/.vitepress/utils/highlight.ts
@@ -0,0 +1,55 @@
+// ref https://github.com/vuejs/vitepress/blob/main/src/node/markdown/plugins/highlight.ts
+import chalk from 'chalk';
+import escapeHtml from 'escape-html';
+import prism from 'prismjs';
+
+// prism is listed as actual dep so it's ok to require
+// eslint-disable-next-line @typescript-eslint/no-var-requires
+const loadLanguages = require('prismjs/components/index');
+
+// required to make embedded highlighting work...
+loadLanguages(['markup', 'css', 'javascript','bash','typescript','ts']);
+
+function wrap(code: string, lang: string): string {
+    if (lang === 'text') {
+        code = escapeHtml(code)
+    }
+    return `
${code}
` +} + +export const highlight = (str: string, lang: string) => { + if (!lang) { + return wrap(str, 'text'); + } + lang = lang.toLowerCase(); + const rawLang = lang + if (lang === 'vue' || lang === 'html') { + lang = 'markup'; + } + if (lang === 'md') { + lang = 'markdown'; + } + if (lang === 'ts') { + lang = 'typescript'; + } + if (lang === 'py') { + lang = 'python'; + } + if (!prism.languages[lang]) { + try { + loadLanguages([lang]); + } catch { + // eslint-disable-next-line no-console + console.warn( + chalk.yellow( + `[vitepress] Syntax highlight for language "${lang}" is not supported.` + ) + ); + } + } + if (prism.languages[lang]) { + const code = prism.highlight(str, prism.languages[lang], lang); + return wrap(code, rawLang); + } + return wrap(str, 'text'); +} diff --git a/packages/charts-vue/docs/.vitepress/utils/parser.ts b/packages/charts-vue/docs/.vitepress/utils/parser.ts new file mode 100644 index 0000000000000000000000000000000000000000..6a18f61f35846233ea8f77f602ed60cc3bacb166 --- /dev/null +++ b/packages/charts-vue/docs/.vitepress/utils/parser.ts @@ -0,0 +1,96 @@ +import { compileTemplate, TemplateCompiler, compileScript, parse, SFCTemplateCompileOptions } from '@vue/compiler-sfc'; + +export function stripScript(content: string, id: string) { + const matchScriptResult = content.match(/<(script)(?:.* \bsetup\b)?[^>]*>([\s\S]+)<\/\1>/); + const sourceCode = matchScriptResult && matchScriptResult[0] ? matchScriptResult[0].trim() : ''; + if (sourceCode) { + const { descriptor } = parse(sourceCode); + const result = compileScript(descriptor, { refSugar: true, id }); + return result.content; + } + return sourceCode; +} + +export function stripStyle(content: string) { + const matchStyleResult = content.match(/<(style)[^>]*>([\s\S]+)<\/\1>/) + return matchStyleResult && matchStyleResult[2] ? matchStyleResult[2].trim() : '' +} + +export function stripTemplate(content: string) { + const contentWithoutScriptAndStyle = content.replace(/<(script|style)[\s\S]+<\/\1>/g, '').trim(); + return contentWithoutScriptAndStyle; +} + +function pad(source: string) { + return source + .split(/\r?\n/) + .map(line => ` ${line}`) + .join('\n'); +} + +const templateReplaceRegex = / - - - diff --git a/packages/mobile-ui-vue/demos/button/block.vue b/packages/mobile-ui-vue/demos/button/block.vue index ac1b62ab74951d8f0305fdb27ff6a5721fbb5093..b8cadd0af898c8118e7914ec7e1f37e2f7410628 100644 --- a/packages/mobile-ui-vue/demos/button/block.vue +++ b/packages/mobile-ui-vue/demos/button/block.vue @@ -1,7 +1,3 @@ - - - - diff --git a/packages/mobile-ui-vue/demos/button/color.vue b/packages/mobile-ui-vue/demos/button/color.vue index 954659decfa54001f1d8ddf0e235b09809720d8f..18d1ed76361c1061dfdacb925f208704db5de487 100644 --- a/packages/mobile-ui-vue/demos/button/color.vue +++ b/packages/mobile-ui-vue/demos/button/color.vue @@ -3,7 +3,3 @@ 单色按钮 渐变色按钮 - - - - diff --git a/packages/mobile-ui-vue/demos/button/disabled.vue b/packages/mobile-ui-vue/demos/button/disabled.vue index 6e242fa688c2b692eaa92a1907f697f96bd563d8..6313e98fe33d41ff7b3010dcaa8745fc292520af 100644 --- a/packages/mobile-ui-vue/demos/button/disabled.vue +++ b/packages/mobile-ui-vue/demos/button/disabled.vue @@ -2,7 +2,3 @@ 禁用状态 禁用状态 - - - - diff --git a/packages/mobile-ui-vue/demos/button/icon.vue b/packages/mobile-ui-vue/demos/button/icon.vue index 2f94fe14bc35f3bfd18c4ecc093fcfddc8d94b26..a8c161cde4173fc6831c1d6298c4111406f5e670 100644 --- a/packages/mobile-ui-vue/demos/button/icon.vue +++ b/packages/mobile-ui-vue/demos/button/icon.vue @@ -2,7 +2,3 @@ 按钮 - - - - diff --git a/packages/mobile-ui-vue/demos/button/loading.vue b/packages/mobile-ui-vue/demos/button/loading.vue index 1659578b827af52d90e0da3188b831e1acd58717..eb010eb779f52f75503a6eacc417ca3bb6c7be83 100644 --- a/packages/mobile-ui-vue/demos/button/loading.vue +++ b/packages/mobile-ui-vue/demos/button/loading.vue @@ -3,7 +3,3 @@ - - - - diff --git a/packages/mobile-ui-vue/demos/button/plain.vue b/packages/mobile-ui-vue/demos/button/plain.vue index 200028ea59bddfa68165b058c024e7606a32c2bb..9a82b77133e24bb0602c926ab9948dec65df65bb 100644 --- a/packages/mobile-ui-vue/demos/button/plain.vue +++ b/packages/mobile-ui-vue/demos/button/plain.vue @@ -3,7 +3,3 @@ 线框按钮 线框按钮 - - - - diff --git a/packages/mobile-ui-vue/demos/button/round.vue b/packages/mobile-ui-vue/demos/button/round.vue index c7d8c18833e459455e5e35dcbe378be9a8148627..558edd35c7fe0d58dcad1328b407e4033d0ed020 100644 --- a/packages/mobile-ui-vue/demos/button/round.vue +++ b/packages/mobile-ui-vue/demos/button/round.vue @@ -3,6 +3,3 @@ 圆角按钮 - - - diff --git a/packages/mobile-ui-vue/demos/button/size.vue b/packages/mobile-ui-vue/demos/button/size.vue index b023d231a9105fa89f3c441c9c633719366706cd..3ef91c052595108bbe8ce7bf49759e8998327a69 100644 --- a/packages/mobile-ui-vue/demos/button/size.vue +++ b/packages/mobile-ui-vue/demos/button/size.vue @@ -1,11 +1,6 @@ - - - - diff --git a/packages/mobile-ui-vue/demos/card/base.vue b/packages/mobile-ui-vue/demos/card/base.vue new file mode 100644 index 0000000000000000000000000000000000000000..c9fbc6e741690dcf6ec8bcdf11cf431ea3ad9157 --- /dev/null +++ b/packages/mobile-ui-vue/demos/card/base.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/packages/mobile-ui-vue/demos/card/index.vue b/packages/mobile-ui-vue/demos/card/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..3010a50ba932ec7481ab8f015b9322447be5f72b --- /dev/null +++ b/packages/mobile-ui-vue/demos/card/index.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/packages/mobile-ui-vue/demos/date-picker/basic.vue b/packages/mobile-ui-vue/demos/date-picker/basic.vue index dc31ef18acad40e47bd275598ff70803daa6fc68..86f0e36bf23ab03e42f4d23ee10bbb4719871216 100644 --- a/packages/mobile-ui-vue/demos/date-picker/basic.vue +++ b/packages/mobile-ui-vue/demos/date-picker/basic.vue @@ -3,5 +3,5 @@ diff --git a/packages/mobile-ui-vue/demos/date-picker/formatter.vue b/packages/mobile-ui-vue/demos/date-picker/formatter.vue index 7d37a997b2a36a0673bdcdbabfb0c14e6993b6bc..3a5b6ab87630a9e7053aeb7cbd1a4a098b63224b 100644 --- a/packages/mobile-ui-vue/demos/date-picker/formatter.vue +++ b/packages/mobile-ui-vue/demos/date-picker/formatter.vue @@ -2,7 +2,7 @@ diff --git a/packages/mobile-ui-vue/demos/date-picker/min-max.vue b/packages/mobile-ui-vue/demos/date-picker/min-max.vue index bccee245541c683bf3ce5a63d13e2d58ba1446ba..1786b1943e8efbd57f8308b9cf371dc3041e8a7d 100644 --- a/packages/mobile-ui-vue/demos/date-picker/min-max.vue +++ b/packages/mobile-ui-vue/demos/date-picker/min-max.vue @@ -7,5 +7,5 @@ diff --git a/packages/mobile-ui-vue/demos/date-picker/type.vue b/packages/mobile-ui-vue/demos/date-picker/type.vue index 6a453c35487423e37906276a4ce983a52b007a1c..ea3bd2cf5a9018be325d5bbd08d9b25f4fa103bd 100644 --- a/packages/mobile-ui-vue/demos/date-picker/type.vue +++ b/packages/mobile-ui-vue/demos/date-picker/type.vue @@ -1,7 +1,7 @@ diff --git a/packages/mobile-ui-vue/demos/date-time-picker/basic.vue b/packages/mobile-ui-vue/demos/date-time-picker/basic.vue index 58ba9b637e76f83eedf076ed5f5b9aea4a84b6dc..915b2b0ebcbdad6ac96c161c5611eb806bdf7976 100644 --- a/packages/mobile-ui-vue/demos/date-time-picker/basic.vue +++ b/packages/mobile-ui-vue/demos/date-time-picker/basic.vue @@ -2,5 +2,5 @@ diff --git a/packages/mobile-ui-vue/demos/date-time-picker/formatter.vue b/packages/mobile-ui-vue/demos/date-time-picker/formatter.vue index f0fba9ab5b03909db30715fe43131a066c8c6346..d95f9652094cbcc3350a26bfc1edd370b7c2898d 100644 --- a/packages/mobile-ui-vue/demos/date-time-picker/formatter.vue +++ b/packages/mobile-ui-vue/demos/date-time-picker/formatter.vue @@ -2,7 +2,7 @@ diff --git a/packages/mobile-ui-vue/demos/date-time-picker/max&min.vue b/packages/mobile-ui-vue/demos/date-time-picker/max&min.vue index 9e55e861af69459398cc77b7ab2e7305e77a18ec..2ed704b7d12d47319cc729731f2e6eba475bf247 100644 --- a/packages/mobile-ui-vue/demos/date-time-picker/max&min.vue +++ b/packages/mobile-ui-vue/demos/date-time-picker/max&min.vue @@ -2,7 +2,7 @@ diff --git a/packages/mobile-ui-vue/demos/dialog/avatar.vue b/packages/mobile-ui-vue/demos/dialog/avatar.vue index f370355d8993dd740412698294f7e8377fe23923..85086ba515a70208efa924ca23a5e27455c4d388 100644 --- a/packages/mobile-ui-vue/demos/dialog/avatar.vue +++ b/packages/mobile-ui-vue/demos/dialog/avatar.vue @@ -15,7 +15,7 @@ diff --git a/packages/mobile-ui-vue/demos/form/control-type.vue b/packages/mobile-ui-vue/demos/form/control-type.vue index 35232bfbbfc6b5b9db203a240915d2425a7994be..04da0d31a512bb90f0f98851b0b6c6db678a49d1 100644 --- a/packages/mobile-ui-vue/demos/form/control-type.vue +++ b/packages/mobile-ui-vue/demos/form/control-type.vue @@ -10,14 +10,14 @@ - + > import { onMounted, ref } from 'vue'; -import { Notify } from '@components/notify'; +import { Notify } from '@farris/mobile-ui-vue/notify'; const fmIconDemoRef = ref(null); diff --git a/packages/mobile-ui-vue/demos/input/text-area.vue b/packages/mobile-ui-vue/demos/input/text-area.vue index 4be137c5d50f564490d08de90e1a818a56ce0933..1b4af6f69c17960a9d967431461427ca8c68fb8a 100644 --- a/packages/mobile-ui-vue/demos/input/text-area.vue +++ b/packages/mobile-ui-vue/demos/input/text-area.vue @@ -3,5 +3,5 @@ - + diff --git a/packages/mobile-ui-vue/demos/list-view/index.vue b/packages/mobile-ui-vue/demos/list-view/index.vue index 57b2fb963c56eecdfa4ac2a39a7e443019b70dd4..ae9847676d6481f92dd4fa2b0ed1f1dad69f09ff 100644 --- a/packages/mobile-ui-vue/demos/list-view/index.vue +++ b/packages/mobile-ui-vue/demos/list-view/index.vue @@ -6,12 +6,16 @@ + + + diff --git a/packages/mobile-ui-vue/demos/list-view/swipe-cell.vue b/packages/mobile-ui-vue/demos/list-view/swipe-cell.vue new file mode 100644 index 0000000000000000000000000000000000000000..8bfac2dba6fef2ba997a0fd090263e4816b100d6 --- /dev/null +++ b/packages/mobile-ui-vue/demos/list-view/swipe-cell.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/packages/mobile-ui-vue/demos/loading/index.vue b/packages/mobile-ui-vue/demos/loading/index.vue index 1257fd20ca40e0c392ddcddef2223bac02652402..4ad8eda3e55dd2c4f69dce72331712ffff59da51 100644 --- a/packages/mobile-ui-vue/demos/loading/index.vue +++ b/packages/mobile-ui-vue/demos/loading/index.vue @@ -17,6 +17,9 @@ + + + diff --git a/packages/mobile-ui-vue/demos/loading/service.vue b/packages/mobile-ui-vue/demos/loading/service.vue new file mode 100644 index 0000000000000000000000000000000000000000..1293e20fbaa00befbdec4285f3e3492e08859673 --- /dev/null +++ b/packages/mobile-ui-vue/demos/loading/service.vue @@ -0,0 +1,13 @@ + + diff --git a/packages/mobile-ui-vue/demos/lookup/basic.vue b/packages/mobile-ui-vue/demos/lookup/basic.vue index ef8540ab37b470332dfd2efc0bf2320a98dd7001..94eed4f9e065ecb122f8c8065c99ad48418540c1 100644 --- a/packages/mobile-ui-vue/demos/lookup/basic.vue +++ b/packages/mobile-ui-vue/demos/lookup/basic.vue @@ -9,7 +9,7 @@ diff --git a/packages/mobile-ui-vue/demos/navbar/toolbar.vue b/packages/mobile-ui-vue/demos/navbar/toolbar.vue new file mode 100644 index 0000000000000000000000000000000000000000..41c86e044cd3eb9ac4c7e90a40a8a4547c1e94fd --- /dev/null +++ b/packages/mobile-ui-vue/demos/navbar/toolbar.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/mobile-ui-vue/demos/overlay/base.vue b/packages/mobile-ui-vue/demos/overlay/base.vue index 03296aa31948e5e4da072970d12cb5bbcefae761..502446d9c8076005e5bd62b6ff3dc43171370bb8 100644 --- a/packages/mobile-ui-vue/demos/overlay/base.vue +++ b/packages/mobile-ui-vue/demos/overlay/base.vue @@ -9,7 +9,7 @@ diff --git a/packages/mobile-ui-vue/demos/popup/position.vue b/packages/mobile-ui-vue/demos/popup/position.vue index 44966d393d2bdd667b957d4810056ffb51e8f115..850bbf2408ddea93adc801ec5d48793f6796e835 100644 --- a/packages/mobile-ui-vue/demos/popup/position.vue +++ b/packages/mobile-ui-vue/demos/popup/position.vue @@ -12,7 +12,7 @@ diff --git a/packages/renderer/src/event-handler/data-grid-double-click-row-event-handler.ts b/packages/renderer/src/event-handler/data-grid-double-click-row-event-handler.ts new file mode 100644 index 0000000000000000000000000000000000000000..a01452dc72fefa2d74562c77b67e7252cfb2c87d --- /dev/null +++ b/packages/renderer/src/event-handler/data-grid-double-click-row-event-handler.ts @@ -0,0 +1,49 @@ +import { Injector, Module } from "@farris/devkit-vue"; +import { EventEmitter } from "../common"; +import { FormMetadataService } from "../service"; +import { ViewEvent } from "../types"; + +export class DataGridDoubleClickRowEventHandler{ + constructor(private emitter: EventEmitter, private formMetadataService: FormMetadataService, private module: Module, private injector: Injector) { + } + + bind(): void { + this.emitter.on('doubleClickRow', (payload: ViewEvent) => this.onDoubleClickRow(payload)); + } + + dispose(): void { + this.emitter.on('doubleClickRow', (payload: ViewEvent) => this.onDoubleClickRow(payload)); + } + + private onDoubleClickRow(payload: ViewEvent) { + if (!payload) { + return; + } + const event: ViewEvent = payload; + const { token, type } = event; + if ((type !== 'data-grid' && type !== 'tree-grid') || !token) { + return; + } + const component = this.formMetadataService.getMetadataById(token); + if (!component) { + return; + } + const { id } = component; + if (!id) { + return; + } + const relatedComponent = this.formMetadataService.getRelatedComponent(id); + if (!relatedComponent) { + return; + } + const viewModel = this.module.getViewModel(relatedComponent.id); + if (!viewModel) { + return; + } + // 行切换 + const { payloads } = event; + const item = payloads[1]; + const { id: dataId } = item; + viewModel.entityStore?.changeCurrentEntityByPath(viewModel.bindingPath, dataId); + } +} diff --git a/packages/renderer/src/event-handler/data-grid-selection-change-event-handler.ts b/packages/renderer/src/event-handler/data-grid-selection-change-event-handler.ts index ddef0b3cc705c294490c8a42bf6375046c8d63b4..4a53fceaf220b6514a75ec725f550aef8ac16953 100644 --- a/packages/renderer/src/event-handler/data-grid-selection-change-event-handler.ts +++ b/packages/renderer/src/event-handler/data-grid-selection-change-event-handler.ts @@ -48,6 +48,45 @@ export class DataGridSelectionChangeEventHandler { const items = payloads[0]; const ids = items.map((item: any) => item.id); viewModel.uiStore?.setValue('ids', ids); + + // 单选和多选不启用复选框这2个场景 会切换当前行 + const { schema } = payload as any; + const needChangeCurrentRow = this.needChangeCurrentRow(schema); + if (needChangeCurrentRow) { + const currentRowId = this.getCurrentRowId(ids); + if (currentRowId) { + viewModel.entityStore?.changeCurrentEntityByPath(viewModel.bindingPath, currentRowId); + } + } } + /** + * 是否需要切换当前行 + */ + private needChangeCurrentRow(gridSchema: any) { + const { selection } = gridSchema; + + // 单选模式:需要切换 + if (!selection || !selection.multiSelect) { + return true; + } + + // 多选模式:禁用复选框的场景,需要切换(暂不处理) + // if (selection.multiSelect && !selection.showCheckbox) { + // return true; + // } + + return false; + } + + /** + * 获取最后1条作为选中行 + */ + private getCurrentRowId(ids: string[]) { + if (!Array.isArray(ids) || ids.length === 0) { + return null; + } + + return ids[ids.length - 1]; + } } diff --git a/packages/renderer/src/event-handler/end-edit-cell-event-handler.ts b/packages/renderer/src/event-handler/end-edit-cell-event-handler.ts index 788a842cd84068e073fe9625c880908b42c24991..7f34b50701f2ffaea51cf6bed5c57f5e70989364 100644 --- a/packages/renderer/src/event-handler/end-edit-cell-event-handler.ts +++ b/packages/renderer/src/event-handler/end-edit-cell-event-handler.ts @@ -3,7 +3,7 @@ import { EventEmitter } from "../common"; import { ComponentService, FormMetadataService } from "../service"; import { EventHandler } from "./types"; import { ENTITY_STORE_SUFFIX, ViewEvent } from "../types"; -import { get } from "lodash-es"; +import { cloneDeep, get } from "lodash-es"; import { DataSourceResolver, FieldResolver } from "../resolvers"; export class EndEditCellEventHandler implements EventHandler { @@ -32,7 +32,7 @@ export class EndEditCellEventHandler implements EventHandler { if (!field) { return; } - const { bindingPath, dataSource } = FieldResolver.resolve(this.entitySchema, field) || {}; + const { bindingPath, dataSource, multiLanguage } = FieldResolver.resolve(this.entitySchema, field) || {}; if (!dataSource || !bindingPath) { return; } @@ -57,7 +57,11 @@ export class EndEditCellEventHandler implements EventHandler { fieldPath.forEach((path: string) => { entityPaths.appendNode(new EntityPathNode(EntityPathNodeType.PropName, path)); }); - entityStore?.setValueByPath(entityPaths, newValue); + if (multiLanguage) { + entityStore?.setValueByPath(entityPaths, cloneDeep(newValue)); + } else { + entityStore?.setValueByPath(entityPaths, newValue); + } // 继续处理帮助场景 if (!column || !column.editor) { return; @@ -69,17 +73,20 @@ export class EndEditCellEventHandler implements EventHandler { return; } Object.keys(mappingFields).forEach((lookupField: string) => { - const fieldPath = mappingFields[lookupField]; + const targetField = mappingFields[lookupField]; const value = this.getValue(items, lookupField, separator); - const fieldPaths = fieldPath.split('.'); - const paths = bindingPaths.concat(fieldPaths); - const path = '/' + paths.join('/'); - entityStore?.setValueByPath(path, value); - // update row data - const field = fieldPaths.join('.'); - if (row.data[field]) { - row.data[field].data = value; - } + const targetFields = targetField.split(',').filter((p: any) => p); + targetFields.forEach((field: string) => { + const fieldPaths = field.split('.'); + const paths = bindingPaths.concat(fieldPaths); + const path = '/' + paths.join('/'); + entityStore?.setValueByPath(path, value); + // update row data + const columnField = fieldPaths.join('.'); + if (row.data[columnField]) { + row.data[columnField].data = value; + } + }); }); const datagrid = this.componentService.getComponentById(token); if (datagrid) { diff --git a/packages/renderer/src/event-handler/index.ts b/packages/renderer/src/event-handler/index.ts index 84c7b2f76cf08254ed07608e01d4c16b3c39ec2c..3a1fef748f674b2e24940933c4c426800d56d4f1 100644 --- a/packages/renderer/src/event-handler/index.ts +++ b/packages/renderer/src/event-handler/index.ts @@ -8,4 +8,5 @@ export * from './data-grid-page-index-change-event-handler'; export * from './data-grid-page-size-change-event-handler'; export * from './data-grid-selection-change-event-handler'; export * from './query-solution-condition-change-event-handler'; +export * from './data-grid-double-click-row-event-handler'; export * from './providers'; diff --git a/packages/renderer/src/event-handler/lookup-clear-event-handler.ts b/packages/renderer/src/event-handler/lookup-clear-event-handler.ts index 92962e4cb906ea8014a2a8ceafa53fb5dd9d876b..765a519ae9db2081e4a3c24671f477a2e0adc73f 100644 --- a/packages/renderer/src/event-handler/lookup-clear-event-handler.ts +++ b/packages/renderer/src/event-handler/lookup-clear-event-handler.ts @@ -41,11 +41,14 @@ export class LookupClearEventHandler implements EventHandler { const entityStore = this.module.getEntityStore(entityStoreId); Object.keys(mapFields).forEach((key: string) => { const fieldPath = mapFields[key]; - const value = null; - const fieldPaths = fieldPath.split('.'); - const paths = bindingPaths.concat(fieldPaths); - const path = '/' + paths.join('/'); - entityStore?.setValueByPath(path, value); + const targetFields = fieldPath.split(',').filter((p: string) => p); + targetFields.forEach((field: string) => { + const value = null; + const fieldPaths = field.split('.'); + const paths = bindingPaths.concat(fieldPaths); + const path = '/' + paths.join('/'); + entityStore?.setValueByPath(path, value); + }); }); } diff --git a/packages/renderer/src/event-handler/lookup-data-mapping-event-handler.ts b/packages/renderer/src/event-handler/lookup-data-mapping-event-handler.ts index 6d8183f205bd7fcd537d4d7d7eeb77a4ec35df58..9c878bd854af8e6c19ab0b58731f7e664287131c 100644 --- a/packages/renderer/src/event-handler/lookup-data-mapping-event-handler.ts +++ b/packages/renderer/src/event-handler/lookup-data-mapping-event-handler.ts @@ -44,12 +44,15 @@ export class LookupDataMappingEventHandler implements EventHandler { const entityStoreId = this.formMetadataService.getModuleCode() + ENTITY_STORE_SUFFIX; const entityStore = this.module.getEntityStore(entityStoreId); Object.keys(mapFields).forEach((lookupField: string) => { - const fieldPath = mapFields[lookupField]; - const value = this.getValue(items, lookupField, separator); - const fieldPaths = fieldPath.split('.'); - const paths = bindingPaths.concat(fieldPaths); - const path = '/' + paths.join('/'); - entityStore?.setValueByPath(path, value); + const fieldPath: string = mapFields[lookupField]; + const targetFields = fieldPath.split(',').filter((p) => p); + targetFields.forEach((field: string) => { + const value = this.getValue(items, lookupField, separator); + const fieldPaths = field.split('.'); + const paths = bindingPaths.concat(fieldPaths); + const path = '/' + paths.join('/'); + entityStore?.setValueByPath(path, value); + }); }); } private getValue(items: any[], field: string, separator: string) { diff --git a/packages/renderer/src/event-handler/modal-closed-event-handler.ts b/packages/renderer/src/event-handler/modal-closed-event-handler.ts new file mode 100644 index 0000000000000000000000000000000000000000..8867a15db1403fca1fdbaa4464d5b826dce11e25 --- /dev/null +++ b/packages/renderer/src/event-handler/modal-closed-event-handler.ts @@ -0,0 +1,85 @@ +import { Injector, Module } from "@farris/devkit-vue"; +import { EventEmitter } from "../common"; +import { FormMetadataService } from "../service"; +import { ViewEvent } from "../types"; +import { EventHandler } from "./types"; + +/** + * 弹窗关闭前事件 + */ +export class ModalClosedEventHandler implements EventHandler { + + /** + * 注册事件 + */ + constructor( + private emitter: EventEmitter, + private formMetadataService: FormMetadataService, + private module: Module, + private injector: Injector + ){} + + /** + * 注册事件监听 + */ + bind(): void { + this.emitter.on('closed', (payload: ViewEvent) => this.onClosed(payload)); + } + + /** + * 注销事件监听 + */ + dispose(): void { + this.emitter.on('closed', (payload: ViewEvent) => this.onClosed(payload)); + } + + /** + * 关闭处理 + */ + private onClosed(payload: any){ + // 只处理包含外部表单的弹窗 + const externalContainerSchema = this.getExternalContainerSchema(payload.schema); + if (!externalContainerSchema) { + return; + } + + // 释放模块相关资源 + const modalModuleId = this.getExternalModuleId(externalContainerSchema); + const modalModule = this.module.getDevkit().getModule(modalModuleId); + if (modalModule) { + modalModule.dispose(); + } + } + + /** + * 获取弹窗内部的外部容器 + * @param modalSchema + * @returns + */ + private getExternalContainerSchema(modalSchema: any) { + if (!modalSchema || modalSchema.type !== 'modal' || !Array.isArray(modalSchema.contents)) { + return false; + } + + const { contents } = modalSchema; + const externalContainerSchema = contents.find((childComponent: any) => { + return childComponent.type === 'external-container'; + }); + + return externalContainerSchema; + } + + /** + * 获取外部容器内表单的模块ID + */ + private getExternalModuleId(externalContainerSchema: any): string { + const externalContainerId = externalContainerSchema.id; + const { externalComponent } = externalContainerSchema; + const externalComponentCode = externalComponent.code; + + const externalModuleId = `${externalContainerId}-${externalComponentCode}`; + + return externalModuleId; + } + +} diff --git a/packages/renderer/src/event-handler/model-value-update-event-handler.ts b/packages/renderer/src/event-handler/model-value-update-event-handler.ts index c950d059bd82463abb352692ea21169dcb6f392c..c99fefe41d2915c39edfc3cb66e9ceb1bacf888f 100644 --- a/packages/renderer/src/event-handler/model-value-update-event-handler.ts +++ b/packages/renderer/src/event-handler/model-value-update-event-handler.ts @@ -1,11 +1,10 @@ - - import { EventHandler } from "./types"; import { Injector, Module } from "@farris/devkit-vue"; import { FormMetadataService } from "../service"; import { ViewEvent } from "../types"; import { EventEmitter } from "../common"; import { DataSourceResolver, FieldResolver } from "../resolvers"; +import { cloneDeep } from 'lodash-es'; export class ModelValueUpdateEventHandler implements EventHandler { @@ -24,7 +23,7 @@ export class ModelValueUpdateEventHandler implements EventHandler { if (!field) { return; } - const { dataSource, bindingPath } = field; + const { dataSource, bindingPath, multiLanguage } = field; if (!dataSource || !bindingPath) { console.error(`Invalid field: ${JSON.stringify(field)}`); return; @@ -48,7 +47,11 @@ export class ModelValueUpdateEventHandler implements EventHandler { if (!currentEntity || !currentEntity.idValue) { return; } - viewModel?.entityStore?.setValueByPath(entityPath, value); + if (multiLanguage) { + viewModel?.entityStore?.setValueByPath(entityPath, cloneDeep(value)); + } else { + viewModel?.entityStore?.setValueByPath(entityPath, value); + } } private get entitySchema() { return this.formMetadataService.getEntity(); diff --git a/packages/renderer/src/event-handler/providers.ts b/packages/renderer/src/event-handler/providers.ts index 298fbc9d4b367a8ea41185f61cfa26d2ccd2f30a..ebed75a3b50859a71cf5e74461325712903bd158 100644 --- a/packages/renderer/src/event-handler/providers.ts +++ b/packages/renderer/src/event-handler/providers.ts @@ -11,6 +11,8 @@ import { DataGridPageIndexChangeEventHandler } from "./data-grid-page-index-chan import { DataGridPageSizeChangeEventHandler } from "./data-grid-page-size-change-event-handler"; import { DataGridSelectionChangeEventHandler } from "./data-grid-selection-change-event-handler"; import { QuerySolutionConditionChangeEventHandler } from "./query-solution-condition-change-event-handler"; +import { DataGridDoubleClickRowEventHandler } from './data-grid-double-click-row-event-handler'; +import { ModalClosedEventHandler } from './modal-closed-event-handler'; export const eventHanderProviders: StaticProvider[] = [ { provide: EVENT_HANDLERS_TOKEN, useClass: LookupDataMappingEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, @@ -21,5 +23,7 @@ export const eventHanderProviders: StaticProvider[] = [ { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridPageIndexChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridPageSizeChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridSelectionChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, - { provide: EVENT_HANDLERS_TOKEN, useClass: QuerySolutionConditionChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true } + { provide: EVENT_HANDLERS_TOKEN, useClass: QuerySolutionConditionChangeEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, + { provide: EVENT_HANDLERS_TOKEN, useClass: DataGridDoubleClickRowEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true }, + { provide: EVENT_HANDLERS_TOKEN, useClass: ModalClosedEventHandler, deps: [EventEmitter, FormMetadataService, Module, Injector], multi: true } ]; diff --git a/packages/renderer/src/expression-effectors/expression-required-effector.ts b/packages/renderer/src/expression-effectors/expression-required-effector.ts index 6a53e50b6a070fa8ee64f2bc891381c3d575c356..2742f2f47200af7f9474b5e1b65b593535fab620 100644 --- a/packages/renderer/src/expression-effectors/expression-required-effector.ts +++ b/packages/renderer/src/expression-effectors/expression-required-effector.ts @@ -2,6 +2,7 @@ import { EffectOption, Effector, ENTITY_TEMPLATE, ExpressionObject, FormControlC import { ComponentConfigRegistry } from "../config"; import { FormMetadataService } from "../service"; import { compile, createVNode } from "vue"; +import { FieldResolver } from "../resolvers"; export class ExpressionRequiredEffector implements Effector { constructor( @@ -141,11 +142,14 @@ export class ExpressionRequiredEffector implements Effector { return propertyName && message && message.replace(/\$property/g, propertyName) || undefined; } private buildValidationRules(result: boolean, validationRules: ValidationRule[], expressionObject: ExpressionObject, controlConfig?: FormControlConfig | null): ValidationRule[] { + const entitySchema = this.formMetadataService.getEntity(); + const resolvedField = FieldResolver.resolve(entitySchema, expressionObject.fieldId); validationRules = validationRules.filter((rule) => rule.name !== 'required'); if (result === true) { validationRules?.splice(0, 0, { name: 'required', message: this.formatMessage(expressionObject.message, controlConfig?.displayName) || undefined, + multiLanguage: resolvedField?.multiLanguage }); } return validationRules; @@ -189,7 +193,7 @@ export class ExpressionRequiredEffector implements Effector { } column.headerFormatter = (context: { headerCell: Record; }) => { - const renderFunction = compile(`*{{headerCell.title}}`); + const renderFunction = compile(`*{{headerCell.title}}`); return createVNode({ render: renderFunction, props: Object.keys(context).concat('isRequired') }, { ...context, isRequired }); }; } diff --git a/packages/renderer/src/form-engine/form-engine.ts b/packages/renderer/src/form-engine/form-engine.ts index 8d05cb4aea0e469e21acbb0551b61091ab141a1e..73a45f9c87f5c8325ddc298b4dad747de4be9fc9 100644 --- a/packages/renderer/src/form-engine/form-engine.ts +++ b/packages/renderer/src/form-engine/form-engine.ts @@ -8,6 +8,9 @@ import { Configuration, ConfigurationType } from "../config"; import { DataGridRequiredEffector, FormGroupRequiredEffector } from "../component-effectors"; import { ComponentConfigDependencyResolveService } from "../component-config-dependency-resolver"; +/** + * 表单引擎 + */ export class FormEngine { constructor( private changeObserverRegistry: ChangeObserverRegistry, diff --git a/packages/renderer/src/i18n/global-translate.ts b/packages/renderer/src/i18n/global-translate.ts new file mode 100644 index 0000000000000000000000000000000000000000..d21da7780ce820af0859a42dbd162504f3e20683 --- /dev/null +++ b/packages/renderer/src/i18n/global-translate.ts @@ -0,0 +1,28 @@ +import { Injector } from "@farris/devkit-vue"; +import { MetadataManager } from "../metadata"; +import { ResourceManager } from "./resource-manager"; + +export class GlobalTranslate { + constructor(private injector: Injector) { } + public transform(formMetadataId: string, key: string, defaultValue?: string) { + const metadataManager = this.injector.get(MetadataManager); + const resourceManager = this.injector.get(ResourceManager); + const resources = resourceManager.getResourceCache(formMetadataId); + // const metadata = metadataManager.getMetadataCache(formMetadataId); + // const { resourceMetadatas } = metadata; + // if (!resourceMetadatas || resourceMetadatas.length < 1) { + // return defaultValue; + // } + // const resourceMetadata = resourceMetadatas[0]; + // if(!resourceMetadata){ + // return defaultValue; + // } + // const formMetadata = metadata.form; + // const { type, namespace, code } = formMetadata; + // const prefix = `${namespace}.${code}.${type}.`; + // const messages: any[] = resourceMetadata.content.StringResources; + // const resource = messages.find((resource: Record) => resource.id===`${prefix}${key}`); + // return resource?.value || defaultValue; + return resources && resources[key] || defaultValue; + } +} diff --git a/packages/renderer/src/i18n/index.ts b/packages/renderer/src/i18n/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c64afe4b76f10b5e49f48a2c6e9f780bc70c51b1 --- /dev/null +++ b/packages/renderer/src/i18n/index.ts @@ -0,0 +1,9 @@ +export * from './language-list-manager'; +export * from './language-list-loader'; +export * from './locale-query'; +export * from './global-translate'; +export * from './resource-loader'; +export * from './transformer'; +export * from './resource-manager'; +export * from './translate'; +export * from './providers'; diff --git a/packages/renderer/src/i18n/language-list-loader.ts b/packages/renderer/src/i18n/language-list-loader.ts new file mode 100644 index 0000000000000000000000000000000000000000..083496bae818c862c3a1f36837cad493961b331d --- /dev/null +++ b/packages/renderer/src/i18n/language-list-loader.ts @@ -0,0 +1,16 @@ +import { RuntimeFrameworkService } from "@farris/command-services-vue"; +import { Injector } from "@farris/devkit-vue"; +import { LanguageListManager } from "./language-list-manager"; + +export class LanguageListLoader { + constructor(private injector: Injector, private languageListManager: LanguageListManager) { } + public loadLanguageList(): Promise { + const runtimeFrameworkService = this.injector.get(RuntimeFrameworkService); + this.languageListManager.setLanguageCode(runtimeFrameworkService.languageCode); + return runtimeFrameworkService.languages.then((languages: any) => { + this.languageListManager.setLanguageList(languages); + }).catch((error: any) => { + console.warn(error); + }); + } +} diff --git a/packages/renderer/src/i18n/language-list-manager.ts b/packages/renderer/src/i18n/language-list-manager.ts new file mode 100644 index 0000000000000000000000000000000000000000..da421c1dd134bcf05d2de612a71613d8cbc5310a --- /dev/null +++ b/packages/renderer/src/i18n/language-list-manager.ts @@ -0,0 +1,19 @@ +import { DEFAULT_LOCALE } from "@farris/devkit-vue"; + +export class LanguageListManager { + private languageCode: string = DEFAULT_LOCALE; + private languageList: any[] = []; + public setLanguageCode(languageCode: string) { + this.languageCode = languageCode; + } + public getLanguageCode() { + return this.languageCode; + } + public setLanguageList(languageList: any[]) { + this.languageList = languageList; + } + public getLanguageList() { + return this.languageList; + } + +} diff --git a/packages/renderer/src/i18n/locale-query.ts b/packages/renderer/src/i18n/locale-query.ts new file mode 100644 index 0000000000000000000000000000000000000000..d9e7bf1dde32b260aa1a99e56af29188f94bddfb --- /dev/null +++ b/packages/renderer/src/i18n/locale-query.ts @@ -0,0 +1,9 @@ +import { RuntimeFrameworkService } from "@farris/command-services-vue"; +import { Injector } from "@farris/devkit-vue"; + +export class LocaleQuery { + constructor(private injector: Injector) { } + public get locale() { + return localStorage.getItem('languageCode') || 'zh-CHS'; + } +} diff --git a/packages/renderer/src/i18n/providers.ts b/packages/renderer/src/i18n/providers.ts new file mode 100644 index 0000000000000000000000000000000000000000..e7b831be1c0b67332f5eeb7819228feb04511337 --- /dev/null +++ b/packages/renderer/src/i18n/providers.ts @@ -0,0 +1,22 @@ +import { HttpClient, Injector, StaticProvider, TRANSLATE_TOKEN } from "@farris/devkit-vue"; +import { LanguageListManager } from "./language-list-manager"; +import { LanguageListLoader } from "./language-list-loader"; +import { GlobalTranslate } from "./global-translate"; +import { LocaleQuery } from "./locale-query"; +import { Translate } from "./translate"; +import { ResourceLoader } from "./resource-loader"; +import { ResourceManager } from "./resource-manager"; +import { MetadataManager, ResourceMetadataDataService } from "../metadata"; + +export const i18nProviders: StaticProvider[] = [ + { provide: LanguageListManager, useClass: LanguageListManager, deps: [] }, + { provide: LanguageListLoader, useClass: LanguageListLoader, deps: [Injector, LanguageListManager] }, + { provide: GlobalTranslate, useClass: GlobalTranslate, deps: [Injector] }, + { provide: LocaleQuery, useClass: LocaleQuery, deps: [Injector] }, + { provide: ResourceLoader, useClass: ResourceLoader, deps: [Injector] }, + { provide: ResourceManager, useClass: ResourceManager, deps: [] } +]; + +export const i18nModuleProviders: StaticProvider[] = [ + { provide: TRANSLATE_TOKEN, useClass: Translate, deps: [Injector] }, +]; diff --git a/packages/renderer/src/i18n/resource-loader.ts b/packages/renderer/src/i18n/resource-loader.ts new file mode 100644 index 0000000000000000000000000000000000000000..4946a33f390d77809d10d2ad6ada942fe7bbf6ce --- /dev/null +++ b/packages/renderer/src/i18n/resource-loader.ts @@ -0,0 +1,127 @@ +import { DEFAULT_LOCALE, HttpClient, Injector } from "@farris/devkit-vue"; +import { MetadataManager, ResourceMetadataDataService } from "../metadata"; +import { LanguageListManager } from "./language-list-manager"; +import { Metadata } from "../types"; +import { ResourceManager } from "./resource-manager"; +import { FormMetadataQuery } from "../service"; + +export class ResourceLoader { + private metadataManager: MetadataManager; + private resourceMetadataDataService: ResourceMetadataDataService; + private languageListManager: LanguageListManager; + private httpClient: HttpClient; + private resourceManager: ResourceManager; + constructor(private injector: Injector) { + this.metadataManager = this.injector.get(MetadataManager); + this.resourceMetadataDataService = this.injector.get(ResourceMetadataDataService); + this.languageListManager = this.injector.get(LanguageListManager); + this.httpClient = this.injector.get(HttpClient); + this.resourceManager = this.injector.get(ResourceManager); + } + public async loadByProjectPath(formMetadataId: string, projectPath: string) { + const metadata = this.metadataManager.getMetadataCache(formMetadataId); + const formMetadata = metadata.form; + const resourceMetadataIds = this.resolveResourceMetadataIds(formMetadata); + const resourceMetadataPromise = resourceMetadataIds.map((id: string) => { + return this.loadResourceMetadataByPath(projectPath, id); + }); + await this.loadExternalMetadataByPath(projectPath, formMetadata); + return this.loadAndCacheResources(formMetadataId, resourceMetadataPromise); + } + private async loadByDto(formMetadataId: string, projectPath: string, nameSpace: string, relativePath?: string, fileName?: string) { + const metadata = this.metadataManager.getMetadataCache(formMetadataId); + const formMetadata = metadata.form; + const resourceMetadataIds = this.resolveResourceMetadataIds(formMetadata); + const resourceMetadataPromise = resourceMetadataIds.map((id: string) => { + return this.loadResourceMetadataByDto(projectPath, { id, relativePath, nameSpace, fileName }); + }); + await this.loadExternalMetadataByPath(projectPath, formMetadata); + return this.loadAndCacheResources(formMetadataId, resourceMetadataPromise); + } + private resolveResourceMetadataIds(formMetadata: any) { + return formMetadata.refs && formMetadata.refs.filter((ref: any) => { + return ref.DependentMetadata.Type === "ResourceMetadata"; + }).map((ref: any) => { + const { ID: id } = ref.DependentMetadata; + return id; + }) || []; + } + private loadAndCacheResources(formMetadataId: string, resourceLoader: Promise[]) { + return Promise.all(resourceLoader).then((resourceMetadatas) => { + if (!resourceMetadatas || resourceMetadatas.length < 1) { + return; + } + const resourceMetadata = resourceMetadatas.pop(); + const resources: any[] = resourceMetadata?.content?.StringResources; + if (!resources) { + return; + } + const resourceObject = resources.reduce((result: Record, item: any) => { + const { id, value } = item; + result[id] = value; + return result; + }, {}); + this.resourceManager.setResourceCache(formMetadataId, resourceObject); + }); + } + public async loadByMetadataId(formMetadataId: string) { + const metadata = this.metadataManager.getMetadataCache(formMetadataId); + const formMetadata = metadata.form; + const { relativePath, code } = formMetadata; + const paths = relativePath.split('/').filter((p: any) => p); + const app = paths[0]; + const su = paths[1]; + const projectName = paths[3]; + const basePath = `/apps/${app}/${su}/web/${projectName}/${code}/i18n`.toLowerCase(); + const path = `${basePath}/${this.languageListManager.getLanguageCode()}.json?version=${new Date().valueOf()}`; + await this.loadExternalMetadataById(formMetadata); + return this.httpClient.get(path, {}).then((data: any) => { + this.resourceManager.setResourceCache(formMetadataId, data); + return data; + }).catch(() => { + console.warn('国际化资源加载失败!'); + }); + } + private async loadExternalMetadataByPath(projectPath: string, formMetadata: any) { + const formMetadataQuery = new FormMetadataQuery(formMetadata); + const externalFormInfos = formMetadataQuery.getExternalFormInfos(); + for (const externalFormInfo of externalFormInfos) { + const { id, nameSpace, fileName, relativePath } = externalFormInfo; + await this.loadByDto(id, projectPath, nameSpace, relativePath, fileName); + } + } + private async loadExternalMetadataById(formMetadata: any) { + const formMetadataQuery = new FormMetadataQuery(formMetadata); + const externalFormInfos = formMetadataQuery.getExternalFormInfos(); + + for (const externalFormInfo of externalFormInfos) { + await this.loadByMetadataId(externalFormInfo.id); + } + } + private loadResourceMetadataByPath(projectPath: string, metadataId: string) { + const resourceMetadata = this.resourceMetadataDataService.loadByProjectPath(projectPath, metadataId); + return this.loadCurrentResourceMetadata(projectPath, resourceMetadata); + } + private loadResourceMetadataByDto(projectPath: string, metadataDto: any) { + const resourceMetadata = this.resourceMetadataDataService.loadByDto(projectPath, metadataDto); + return this.loadCurrentResourceMetadata(projectPath, resourceMetadata); + } + private loadCurrentResourceMetadata(projectPath: string, resourceMetadata: any) { + const languageCode = this.languageListManager.getLanguageCode(); + if (languageCode === DEFAULT_LOCALE) { + return resourceMetadata; + } else { + return resourceMetadata.then((metadata: Metadata) => { + const languageList = metadata.content.refs; + if (!languageList || languageList.length < 1) { + return Promise.resolve(null); + } + const ref = languageList.find((item: any) => item.language === languageCode); + if (!ref) { + return Promise.resolve(null); + } + return this.resourceMetadataDataService.loadByProjectPath(projectPath, ref.metadataId); + }); + } + } +} diff --git a/packages/renderer/src/i18n/resource-manager.ts b/packages/renderer/src/i18n/resource-manager.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c577c61d75eddcb629e7d77c155768e65b0482f --- /dev/null +++ b/packages/renderer/src/i18n/resource-manager.ts @@ -0,0 +1,33 @@ +/** + * 资源项管理 + */ +class ResourceManager { + private resourcesCache: Map>; + + /** + * 构造函数 + */ + constructor() { + this.resourcesCache = new Map(); + } + public setResourceCache(formMetadataId: string, resource: Record): void { + this.resourcesCache.set(formMetadataId, resource); + } + public getResourceCache(formMetadataId: string): any { + const metadataCache = this.resourcesCache.get(formMetadataId); + if (!metadataCache) { + return null; + } + return metadataCache; + } + public getResourceCaches() { + const metadataCaches = this.resourcesCache.values(); + const formattedMetadataCaches = metadataCaches.map((metadataCache: any) => { + return metadataCache; + }); + + return formattedMetadataCaches; + } +} + +export { ResourceManager }; diff --git a/packages/renderer/src/i18n/transformer/data-grid-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/data-grid-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..70dc766f5b5d552ebde38a1f25140cdfe3e79a97 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/data-grid-i18n-transformer.ts @@ -0,0 +1,132 @@ +import { isNil } from "lodash-es"; +import { GlobalTranslate } from "../global-translate"; + +export class DataGridI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { columns, id, rowNumber } = this.metadata; + if (!isNil(rowNumber)) { + const { heading } = rowNumber; + if (heading) { + rowNumber.heading = this.translate.transform(this.formMetadataId, `${id}/rowNumber/heading`, heading); + } + } + + if (!columns || !Array.isArray(columns) || columns.length < 1) { + return; + } + columns.forEach((column: Record) => { + const { id: columnId, title } = column; + column.title = this.translate.transform(this.formMetadataId, columnId, title); + this.resolveEnumColumn(column); + this.resolveBooleanFormatter(column); + this.resolveEditorPlaceholder(column); + this.resolveNumberFormatter(column); + this.resolveEnumFormatter(column); + this.resolveSwitchColumn(column); + this.resolveLookupColumn(column); + }); + } + private resolveEnumColumn(column: Record) { + const { dataType, id: columnId, editor } = column; + if (dataType !== 'enum') { + return; + } + if (!editor) { + return; + } + const { data } = editor; + if (!data || data.length < 1) { + return; + } + data.forEach((item: any) => { + item.name = this.translate.transform(this.formMetadataId, `${columnId}/editor/data/${item.value}`, item.name); + }); + } + private resolveEnumFormatter(column: Record) { + const { formatter, id: columnId } = column; + if (!formatter) { + return; + } + const { type } = formatter; + if (type !== 'enum') { + return; + } + const { data } = formatter; + if (data && data.length > 0) { + data.forEach((item: any) => { + item.name = this.translate.transform(this.formMetadataId, `${columnId}/formatter/data/${item.value}`, item.name); + }); + } + } + private resolveBooleanFormatter(column: Record) { + const { formatter, id: columnId } = column; + if (!formatter) { + return; + } + const { type } = formatter; + if (type !== 'boolean') { + return; + } + const { trueText, falseText } = formatter; + if (trueText) { + formatter.trueText = this.translate.transform(this.formMetadataId, `${columnId}/formatter/trueText`, trueText); + } + if (falseText) { + formatter.falseText = this.translate.transform(this.formMetadataId, `${columnId}/formatter/falseText`, falseText); + } + } + private resolveEditorPlaceholder(column: Record) { + const { editor, id: columnId } = column; + if (!editor) { + return; + } + const { placeholder } = editor; + editor.placeholder = this.translate.transform(this.formMetadataId, `${columnId}/placeholder`, placeholder); + } + private resolveNumberFormatter(column: Record) { + const { formatter, id: columnId } = column; + if (!formatter) { + return; + } + const { type } = formatter; + if (type !== 'number') { + return; + } + const { prefix, suffix } = formatter; + if (prefix) { + formatter.prefix = this.translate.transform(this.formMetadataId, `${columnId}/formatter/prefix`, prefix); + } + if (suffix) { + formatter.suffix = this.translate.transform(this.formMetadataId, `${columnId}/formatter/suffix`, suffix); + } + } + private resolveSwitchColumn(column: Record) { + const { editor, id } = column; + if (!editor) { + return; + } + const { type, onLabel, offLabel } = editor; + if (type !== 'switch') { + return; + } + if (onLabel) { + editor.onLabel = this.translate.transform(this.formMetadataId, `${id}/onLabel`, onLabel); + } + if (offLabel) { + editor.offLabel = this.translate.transform(this.formMetadataId, `${id}/offLabel`, offLabel); + } + } + private resolveLookupColumn(column: Record) { + const { editor, id } = column; + if (!editor) { + return; + } + const { type, dialog } = editor; + if (type !== 'lookup' || !dialog) { + return; + } + const { title } = dialog; + dialog.title = this.translate.transform(this.formMetadataId, `${id}/dialog/title`, title); + } +} diff --git a/packages/renderer/src/i18n/transformer/expression-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/expression-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..53b06dbda3e8cad49bb9a1226364a2ee0faa40b1 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/expression-i18n-transformer.ts @@ -0,0 +1,14 @@ +import { GlobalTranslate } from "../global-translate"; + +export class ExpressionI18nTransformer { + constructor(private expression: { rules: { id: string; type: string, message: string; }[]; }, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { rules } = this.expression; + if (!rules || rules.length < 1) { + return; + } + rules.forEach((rule) => { + rule.message = this.translate.transform(this.formMetadataId, `expression/${rule.id}/message`, rule.message); + }); + } +} diff --git a/packages/renderer/src/i18n/transformer/fieldset-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/fieldset-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..956c6d680094a98c0cdb5c3a41bb0f0a6412f961 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/fieldset-i18n-transformer.ts @@ -0,0 +1,9 @@ +import { GlobalTranslate } from "../global-translate"; + +export class FieldsetI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { title, id } = this.metadata; + this.metadata.title = this.translate.transform(this.formMetadataId, id, title); + } +} diff --git a/packages/renderer/src/i18n/transformer/form-group-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/form-group-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..70821d42681b23191a02bf49387923d8a7eefc60 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/form-group-i18n-transformer.ts @@ -0,0 +1,84 @@ +import { GlobalTranslate } from "../global-translate"; + +export class FormGroupI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { editor = null, id, label } = this.metadata; + this.metadata.label = this.translate.transform(this.formMetadataId, id, label); + this.resolveEditorPlaceholder(this.metadata); + this.resolveEditorData(this.metadata); + this.resolveValidationMessage(this.metadata); + this.resolveLookupTitle(this.metadata); + this.resolveSwitchLabel(this.metadata); + this.resolveCheckboxLabel(this.metadata); + } + private resolveEditorData(viewSchema: Record) { + const { editor, id } = viewSchema; + const { data } = editor || {}; + if (!Array.isArray(data) || data.length < 1) { + return; + } + data.forEach((item) => { + const { value, name } = item; + item.name = this.translate.transform(this.formMetadataId, `${id}/editor/data/${value}`, name); + }); + } + private resolveValidationMessage(viewSchema: Record) { + const { editor, id } = viewSchema; + const { formatValidation } = editor || {}; + if (!formatValidation) { + return; + } + const { message } = formatValidation; + formatValidation.message = this.translate.transform(this.formMetadataId, `${id}/formatValidation/message`, message); + } + private resolveEditorPlaceholder(viewSchema: Record) { + const { editor, id } = viewSchema; + if (!editor) { + return; + } + const { placeholder } = editor; + editor.placeholder = this.translate.transform(this.formMetadataId, `${id}/editor/placeholder`, placeholder); + } + private resolveLookupTitle(viewSchema: Record) { + const { editor, id } = viewSchema; + if (!editor) { + return; + } + const { type, dialog } = editor; + if (type !== 'lookup' || !dialog) { + return; + } + const { title } = dialog; + dialog.title = this.translate.transform(this.formMetadataId, `${id}/dialog/title`, title); + } + private resolveSwitchLabel(viewSchema: Record) { + const { editor, id } = viewSchema; + if (!editor) { + return; + } + const { type, onLabel, offLabel } = editor; + if (type !== 'switch') { + return; + } + if (onLabel) { + editor.onLabel = this.translate.transform(this.formMetadataId, `${id}/onLabel`, onLabel); + } + if (offLabel) { + editor.offLabel = this.translate.transform(this.formMetadataId, `${id}/offLabel`, offLabel); + } + } + private resolveCheckboxLabel(viewSchema: Record){ + const { editor, id } = viewSchema; + if (!editor) { + return; + } + const { type, label } = editor; + if (type !== 'check-box') { + return; + } + if (label) { + editor.label = this.translate.transform(this.formMetadataId, `${id}/editor/label`, label); + } + } +} diff --git a/packages/renderer/src/i18n/transformer/index.ts b/packages/renderer/src/i18n/transformer/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..71929593b247ee2e6ba8aca3e04df618e4a50a13 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/index.ts @@ -0,0 +1,14 @@ +export * from './module-i18n-transformer'; +export * from './view-model-i18n-transformer'; +export * from './data-grid-i18n-transformer'; +export * from './form-group-i18n-transformer'; +export * from './page-header-i18n-transformer'; +export * from './query-solution-i18n-transformer'; +export * from './response-toolbar-i18n-transformer'; +export * from './section-i18n-transformer'; +export * from './tab-page-i18n-transformer'; +export * from './tree-grid-i18n-transformer'; +export * from './expression-i18n-transformer'; +export * from './fieldset-i18n-transformer'; +export * from './modal-component-i18n-transformer'; +export * from './lookup-i18n-transformer'; diff --git a/packages/renderer/src/i18n/transformer/lookup-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/lookup-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..ca1c692991f38e5aeb568ba370d66d82b0df9a85 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/lookup-i18n-transformer.ts @@ -0,0 +1,13 @@ +import { GlobalTranslate } from "../global-translate"; + +export class LookupI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { dialog, id } = this.metadata; + if (!dialog) { + return; + } + const { title } = dialog; + dialog.title = this.translate.transform(this.formMetadataId, id, title); + } +} diff --git a/packages/renderer/src/i18n/transformer/modal-component-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/modal-component-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..aeae082ceba5889772faa622f845803a9a9f2b5d --- /dev/null +++ b/packages/renderer/src/i18n/transformer/modal-component-i18n-transformer.ts @@ -0,0 +1,9 @@ +import { GlobalTranslate } from "../global-translate"; + +export class ModalComponentI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { title, id } = this.metadata; + this.metadata.title = this.translate.transform(this.formMetadataId, id, title); + } +} diff --git a/packages/renderer/src/i18n/transformer/module-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/module-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..d1b3e910f92b66d1d2eb029de47ee2e31c9dca3c --- /dev/null +++ b/packages/renderer/src/i18n/transformer/module-i18n-transformer.ts @@ -0,0 +1,97 @@ +import { GlobalTranslate } from "../global-translate"; +import { DataGridI18nTransformer } from "./data-grid-i18n-transformer"; +import { ExpressionI18nTransformer } from "./expression-i18n-transformer"; +import { FieldsetI18nTransformer } from "./fieldset-i18n-transformer"; +import { FormGroupI18nTransformer } from "./form-group-i18n-transformer"; +import { LookupI18nTransformer } from "./lookup-i18n-transformer"; +import { ModalComponentI18nTransformer } from "./modal-component-i18n-transformer"; +import { PageHeaderI18nTransformer } from "./page-header-i18n-transformer"; +import { QuerySolutionI18nTransformer } from "./query-solution-i18n-transformer"; +import { ResponseToolbarI18nTransformer } from "./response-toolbar-i18n-transformer"; +import { SectionI18nTransformer } from "./section-i18n-transformer"; +import { TabPageI18nTransformer } from "./tab-page-i18n-transformer"; +import { TreeGridI18nTransformer } from "./tree-grid-i18n-transformer"; +import { ViewModelI18nTransformer } from "./view-model-i18n-transformer"; + +export class ModuleI18nTransformer { + constructor(private moduleMetadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { viewmodels, components, expressions, externalComponents } = this.moduleMetadata; + this.transformViewModels(viewmodels); + this.transformComponents(components); + this.transformExpression(expressions); + this.transformModalComponents(externalComponents); + } + private transformViewModels(viewModels: any[]) { + if (!viewModels || viewModels.length < 1) { + return; + } + viewModels.forEach((viewModel: Record) => { + const viewModelI18nTransformer = new ViewModelI18nTransformer(viewModel, this.translate, this.formMetadataId); + viewModelI18nTransformer.transform(); + }); + } + private transformComponents(components: any[]) { + if (!components || components.length < 1) { + return; + } + components.forEach((component: Record) => { + const transformer = this.getComponentTransformer(component); + if (transformer) { + transformer.transform(); + } + if (component.contents && Array.isArray(component.contents) && component.contents.length > 0) { + this.transformComponents(component.contents); + } + }); + } + private transformExpression(expressions: any[]) { + if (!expressions || expressions.length < 1) { + return; + } + expressions.forEach((expression) => { + const expressionI18nTransformer = new ExpressionI18nTransformer(expression, this.translate, this.formMetadataId); + expressionI18nTransformer.transform(); + }); + } + private transformModalComponents(modals: any[]) { + if (!modals || modals.length < 1) { + return; + } + modals.forEach((modal: Record) => { + if (modal.type === 'lookup') { + const lookupI18nTransformer = new LookupI18nTransformer(modal, this.translate, this.formMetadataId); + lookupI18nTransformer.transform(); + } else if (modal.type === 'modal') { + const modalI18nTransformer = new ModalComponentI18nTransformer(modal, this.translate, this.formMetadataId); + modalI18nTransformer.transform(); + } + + }); + } + + private getComponentTransformer(metadata: Record) { + const { type } = metadata; + switch (type) { + case 'form-group': + return new FormGroupI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'data-grid': + return new DataGridI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'page-header': + return new PageHeaderI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'query-solution': + return new QuerySolutionI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'response-toolbar': + return new ResponseToolbarI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'section': + return new SectionI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'tab-page': + return new TabPageI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'tree-grid': + return new TreeGridI18nTransformer(metadata, this.translate, this.formMetadataId); + case 'fieldset': + return new FieldsetI18nTransformer(metadata, this.translate, this.formMetadataId); + } + return null; + } +} diff --git a/packages/renderer/src/i18n/transformer/page-header-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/page-header-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..e86a5d55c20a8e1add768195e9d7906fc7b54f0d --- /dev/null +++ b/packages/renderer/src/i18n/transformer/page-header-i18n-transformer.ts @@ -0,0 +1,29 @@ +import { GlobalTranslate } from "../global-translate"; + +export class PageHeaderI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { toolbar, id, visible, title } = this.metadata; + this.metadata.title = this.translate.transform(this.formMetadataId, id, title); + if (!toolbar) { + return; + } + const { buttons, visible: toolbarVisible } = toolbar; + if (!buttons || !Array.isArray(buttons) || buttons.length < 1) { + return; + } + buttons.forEach((button: Record) => { + const { children, text, id: buttonId, tipsText } = button; + button.text = this.translate.transform(this.formMetadataId, buttonId, text); + button.tipsText = this.translate.transform(this.formMetadataId, `${buttonId}/tipsText`, tipsText); + if (!(children && children.length)) { + return; + } + children.forEach((item: Record) => { + const { id: itemId, tipsText, text } = item; + item.text = this.translate.transform(this.formMetadataId, itemId, text); + item.tipsText = this.translate.transform(this.formMetadataId, `${itemId}/tipsText`, tipsText); + }); + }); + } +} diff --git a/packages/renderer/src/i18n/transformer/query-solution-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/query-solution-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..f00e983dc1f8d5ceaba29ea1123b9b5776f68ad2 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/query-solution-i18n-transformer.ts @@ -0,0 +1,32 @@ +import { isNil } from "lodash-es"; +import { GlobalTranslate } from "../global-translate"; + +export class QuerySolutionI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { id, filterText, presetQuerySolutionName, fields } = this.metadata; + if (!isNil(filterText)) { + this.metadata.filterText = this.translate.transform(this.formMetadataId, `${id}/filterText`, filterText); + } + if (!isNil(presetQuerySolutionName)) { + this.metadata.presetQuerySolutionName = this.translate.transform(this.formMetadataId, id, presetQuerySolutionName); + } + if (fields && Array.isArray(fields) && fields.length > 0) { + fields.forEach((field: Record) => { + const { id: fieldId, name, editor } = field; + field.name = this.translate.transform(this.formMetadataId, `${id}/fields/${fieldId}`, name); + if (editor) { + const { placeholder, data } = editor; + if (!isNil(placeholder)) { + editor.placeholder = this.translate.transform(this.formMetadataId, `${id}/fields/${fieldId}/placeholder`, placeholder); + } + if (data && Array.isArray(data)) { + data.forEach((item: Record) => { + item.name = this.translate.transform(this.formMetadataId, `${id}/fields/${fieldId}/editor/data/${item.value}`, item.name); + }); + } + } + }); + } + } +} diff --git a/packages/renderer/src/i18n/transformer/response-toolbar-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/response-toolbar-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..4613df41f2141490b2379b4832fb53976351e27a --- /dev/null +++ b/packages/renderer/src/i18n/transformer/response-toolbar-i18n-transformer.ts @@ -0,0 +1,24 @@ +import { GlobalTranslate } from "../global-translate"; + +export class ResponseToolbarI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { buttons, id } = this.metadata; + if (!buttons || !Array.isArray(buttons) || buttons.length < 1) { + return; + } + buttons.forEach((button: Record) => { + const { children, text, id: buttonId, tipsText } = button; + button.text = this.translate.transform(this.formMetadataId, buttonId, text); + button.tipsText = this.translate.transform(this.formMetadataId, `${buttonId}/tipsText`, tipsText); + if (!(children && children.length)) { + return; + } + children.forEach((item: Record) => { + const { id: itemId, text, tipsText } = item; + item.text = this.translate.transform(this.formMetadataId, itemId, text); + item.tipsText = this.translate.transform(this.formMetadataId, `${itemId}/tipsText`, tipsText); + }); + }); + } +} diff --git a/packages/renderer/src/i18n/transformer/section-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/section-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..653022c3f020f6590b3d4c13e80b8c2a04887aee --- /dev/null +++ b/packages/renderer/src/i18n/transformer/section-i18n-transformer.ts @@ -0,0 +1,28 @@ +import { GlobalTranslate } from "../global-translate"; + +export class SectionI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform(){ + const { toolbar, id, mainTitle, subTitle } = this.metadata; + this.metadata.mainTitle = this.translate.transform(this.formMetadataId, `${id}/mainTitle`, mainTitle); + this.metadata.subTitle = this.translate.transform(this.formMetadataId, `${id}/subTitle`, subTitle); + if (!toolbar) { + return; + } + const { buttons } = toolbar; + if (!buttons || !Array.isArray(buttons) || buttons.length < 1) { + return; + } + buttons.forEach((button: Record) => { + const { children, text, id: buttonId } = button; + button.text = this.translate.transform(this.formMetadataId, buttonId, text); + if (!(children && children.length)) { + return; + } + children.forEach((item: Record) => { + const { id: itemId, text } = item; + item.text = this.translate.transform(this.formMetadataId, itemId, text); + }); + }); + } +} diff --git a/packages/renderer/src/i18n/transformer/tab-page-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/tab-page-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..629e2acd04fe349fe0ce4beb64c694df81c79ee6 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/tab-page-i18n-transformer.ts @@ -0,0 +1,27 @@ +import { GlobalTranslate } from "../global-translate"; + +export class TabPageI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { toolbar, id, title } = this.metadata; + this.metadata.title = this.translate.transform(this.formMetadataId, id, title); + if (!toolbar) { + return; + } + const { buttons } = toolbar; + if (!buttons || !Array.isArray(buttons) || buttons.length < 1) { + return; + } + buttons.forEach((button: Record) => { + const { children, text, id: buttonId } = button; + button.text = this.translate.transform(this.formMetadataId, buttonId, text); + if (!(children && children.length)) { + return; + } + children.forEach((item: Record) => { + const { id: itemId, text } = item; + item.text = this.translate.transform(this.formMetadataId, itemId, text); + }); + }); + } +} diff --git a/packages/renderer/src/i18n/transformer/tree-grid-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/tree-grid-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..adf728c304f5c28e9fa152158025133cd562bce5 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/tree-grid-i18n-transformer.ts @@ -0,0 +1,16 @@ +import { GlobalTranslate } from "../global-translate"; + +export class TreeGridI18nTransformer { + constructor(private metadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { columns, id } = this.metadata; + + if (!columns || !Array.isArray(columns) || columns.length < 1) { + return; + } + columns.forEach((column: Record) => { + const { id: columnId, title } = column; + column.title = this.translate.transform(this.formMetadataId, `${columnId}`, title); + }); + } +} diff --git a/packages/renderer/src/i18n/transformer/view-model-i18n-transformer.ts b/packages/renderer/src/i18n/transformer/view-model-i18n-transformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..5fc9fa0dbf7d84767866866ac06563473ba468a7 --- /dev/null +++ b/packages/renderer/src/i18n/transformer/view-model-i18n-transformer.ts @@ -0,0 +1,9 @@ +import { GlobalTranslate } from "../global-translate"; + +export class ViewModelI18nTransformer { + constructor(private viewModelMetadata: Record, private translate: GlobalTranslate, private formMetadataId: string) { } + public transform() { + const { name, id } = this.viewModelMetadata; + this.viewModelMetadata.name = this.translate.transform(this.formMetadataId, `${id}`, name); + } +} diff --git a/packages/renderer/src/i18n/translate.ts b/packages/renderer/src/i18n/translate.ts new file mode 100644 index 0000000000000000000000000000000000000000..94b3ea2d28d02fd8106621dc8465a3b2f03b1185 --- /dev/null +++ b/packages/renderer/src/i18n/translate.ts @@ -0,0 +1,30 @@ +import { Injector } from "@farris/devkit-vue"; +import { MetadataManager } from "../metadata"; +import { FORM_METADATA_ID_TOKEN } from "../tokens"; +import { ResourceManager } from "./resource-manager"; + +export class Translate { + constructor(private injector: Injector) { } + public transform(key: string, defaultValue?: string) { + const metadataManager = this.injector.get(MetadataManager); + const formMetadataId = this.injector.get(FORM_METADATA_ID_TOKEN); + const resourceManager = this.injector.get(ResourceManager); + const resources = resourceManager.getResourceCache(formMetadataId); + // const metadata = metadataManager.getMetadataCache(formMetadataId); + // const { resourceMetadatas } = metadata; + // if (!resourceMetadatas || resourceMetadatas.length < 1) { + // return defaultValue; + // } + // const resourceMetadata = resourceMetadatas[0]; + // if (!resourceMetadata) { + // return defaultValue; + // } + // const formMetadata = metadata.form; + // const { type, namespace, code } = formMetadata; + // const prefix = `${namespace}.${code}.${type}.`; + // const messages: any[] = resourceMetadata.content.StringResources; + // const resource = messages.find((resource: Record) => resource.id === `${prefix}${key}`); + // return resource?.value || defaultValue; + return resources && resources[key] || defaultValue; + } +} diff --git a/packages/renderer/src/main.ts b/packages/renderer/src/main.ts index 0ff7018b2052b85ad251800881d1139e09c9ef80..e262f58b38e805ab0033cf5704d0fc9d1e3d869b 100644 --- a/packages/renderer/src/main.ts +++ b/packages/renderer/src/main.ts @@ -1,15 +1,30 @@ import { createApp } from 'vue'; import FarrisVue from '@farris/ui-vue'; -import App from './app.vue'; import { createDevkit } from '@farris/devkit-vue'; +import { commandServicesDevkitProviders } from '@farris/command-services-vue'; +import { metadataProviders } from './metadata'; +import { communicationProviders } from './communications'; import router from './router'; +import App from './app.vue'; +import { i18nProviders } from './i18n'; + +const localeId = localStorage.getItem('languageCode') || 'zh-CHS'; const devkit = createDevkit({ - providers: [] + providers: [ + ...commandServicesDevkitProviders, + ...metadataProviders, + ...communicationProviders, + ...i18nProviders + ], + localeId }); const app = createApp(App); app.use(router); app.use(devkit); -app.use(FarrisVue); +app.use(FarrisVue, { + locale: localeId, + uri: '/platform/common/web/@farris/i18n/ui' +}); app.mount('#app'); diff --git a/packages/renderer/src/data-service/common-web-command-metadata-data-service.ts b/packages/renderer/src/metadata/data-service/common-web-command-metadata-data-service.ts similarity index 77% rename from packages/renderer/src/data-service/common-web-command-metadata-data-service.ts rename to packages/renderer/src/metadata/data-service/common-web-command-metadata-data-service.ts index 450e6e6c8a623a52446918c0d5f81afcc1eb2762..d131cc3cedc384ce692518eaed76cbed4effca83 100644 --- a/packages/renderer/src/data-service/common-web-command-metadata-data-service.ts +++ b/packages/renderer/src/metadata/data-service/common-web-command-metadata-data-service.ts @@ -1,6 +1,6 @@ import { MetadataRepository } from "../repository"; -import { Metadata } from "../types"; +import { Metadata } from "../../types"; export class CommonWebCommandMetadataDataService { constructor(private metadataRepository: MetadataRepository) { } @@ -23,9 +23,16 @@ export class CommonWebCommandMetadataDataService { }); } + public loadByDto(currentPath: string, metadataDto: any) { + return this.metadataRepository.loadByDto(currentPath, metadataDto).then((response: any) => { + return this.createMetadata(response); + }); + } + private createMetadata(response: any) { const metadata = { id: response.id, + nameSpace: response.nameSpace, content: JSON.parse(response.content), refs: JSON.parse(response.refs) }; diff --git a/packages/renderer/src/data-service/custom-web-command-metadata-data-service.ts b/packages/renderer/src/metadata/data-service/custom-web-command-metadata-data-service.ts similarity index 82% rename from packages/renderer/src/data-service/custom-web-command-metadata-data-service.ts rename to packages/renderer/src/metadata/data-service/custom-web-command-metadata-data-service.ts index 27e312e032f35ac2a6197e5a7aec36e1ed51f20a..ca5e4807b31ac38a3355eed41496a035ba9d54e4 100644 --- a/packages/renderer/src/data-service/custom-web-command-metadata-data-service.ts +++ b/packages/renderer/src/metadata/data-service/custom-web-command-metadata-data-service.ts @@ -1,5 +1,5 @@ import { MetadataRepository } from "../repository"; -import { Metadata } from "../types"; +import { Metadata } from "../../types"; export class CustomWebCommandMetadataDataService { constructor(private metadataRepository: MetadataRepository) { } @@ -25,6 +25,12 @@ export class CustomWebCommandMetadataDataService { }); } + public loadByDto(currentPath: string, metadataDto: any) { + return this.metadataRepository.loadByDto(currentPath, metadataDto).then((response: any) => { + return this.createMetadata(response); + }); + } + private createMetadata(response: any) { const metadata = { id: response.id, diff --git a/packages/renderer/src/data-service/custom-web-component-metadata-data-service.ts b/packages/renderer/src/metadata/data-service/custom-web-component-metadata-data-service.ts similarity index 73% rename from packages/renderer/src/data-service/custom-web-component-metadata-data-service.ts rename to packages/renderer/src/metadata/data-service/custom-web-component-metadata-data-service.ts index e0fb67bbd475c9bd37c15a3a12a43e004a09de92..14fe5fe6ff6885713394e4f65e00a068637726b0 100644 --- a/packages/renderer/src/data-service/custom-web-component-metadata-data-service.ts +++ b/packages/renderer/src/metadata/data-service/custom-web-component-metadata-data-service.ts @@ -1,13 +1,13 @@ import { MetadataRepository } from "../repository"; -import { Metadata } from "../types"; +import { Metadata } from "../../types"; export class CustomWebComponentMetadataDataService { constructor(private metadataRepository: MetadataRepository) { } public loadByType(metadataPath: string) { - return this.metadataRepository.loadMetadatasByType(metadataPath, ".webcmp").then((metadatas: any[])=>{ - if(metadatas && metadatas.length>0){ - return Promise.all(metadatas.map((metadata: any)=>{ + return this.metadataRepository.loadMetadatasByType(metadataPath, ".webcmp").then((metadatas: any[]) => { + if (metadatas && metadatas.length > 0) { + return Promise.all(metadatas.map((metadata: any) => { return this.loadByFullPath(metadataPath, metadata.id); })); } @@ -31,11 +31,18 @@ export class CustomWebComponentMetadataDataService { }); } + public loadByDto(currentPath: string, metadataDto: any) { + return this.metadataRepository.loadByDto(currentPath, metadataDto).then((response: any) => { + return this.createMetadata(response); + }); + } + private createMetadata(response: any) { const metadata = { id: response.id, content: JSON.parse(response.content), - refs: JSON.parse(response.refs) + refs: JSON.parse(response.refs), + extendProperty: JSON.parse(response.extendProperty), }; return metadata; } diff --git a/packages/renderer/src/data-service/form-metadata-data-service.ts b/packages/renderer/src/metadata/data-service/form-metadata-data-service.ts similarity index 62% rename from packages/renderer/src/data-service/form-metadata-data-service.ts rename to packages/renderer/src/metadata/data-service/form-metadata-data-service.ts index 24f6cab8a878ea5b225b018739619fe646206174..cc197ba762e812ccc990a8560fb25ddea05e9197 100644 --- a/packages/renderer/src/data-service/form-metadata-data-service.ts +++ b/packages/renderer/src/metadata/data-service/form-metadata-data-service.ts @@ -1,6 +1,6 @@ - + import { MetadataRepository } from "../repository"; -import { Metadata } from "../types"; +import { Metadata } from "../../types"; export class FormMetadataDataService { constructor(private repository: MetadataRepository) { } @@ -17,10 +17,17 @@ export class FormMetadataDataService { }); } + public loadByDto(currentPath: string, metadataDto: any) { + return this.repository.loadByDto(currentPath, metadataDto).then((response: any) => { + return this.createMetadata(response); + }); + } + private createMetadata(response: any) { + const { type, code,nameSpace:namespace } = response; const { Contents: content } = JSON.parse(response.content); const refs = JSON.parse(response.refs); - const metadata = { id: response.id, content, refs }; + const metadata = { id: response.id, content, refs, relativePath: response.relativePath, type, code,namespace }; return metadata; } } diff --git a/packages/renderer/src/data-service/index.ts b/packages/renderer/src/metadata/data-service/index.ts similarity index 100% rename from packages/renderer/src/data-service/index.ts rename to packages/renderer/src/metadata/data-service/index.ts diff --git a/packages/renderer/src/data-service/metadata-data-service.ts b/packages/renderer/src/metadata/data-service/metadata-data-service.ts similarity index 48% rename from packages/renderer/src/data-service/metadata-data-service.ts rename to packages/renderer/src/metadata/data-service/metadata-data-service.ts index 5df5ea4936ffab742724542bde22284ada8fec67..b49b22a5e3594b60c8bbd0066f0b5647c90e47cf 100644 --- a/packages/renderer/src/data-service/metadata-data-service.ts +++ b/packages/renderer/src/metadata/data-service/metadata-data-service.ts @@ -3,7 +3,10 @@ import { CustomWebComponentMetadataDataService } from "./custom-web-component-me import { FormMetadataDataService } from './form-metadata-data-service'; import { StateMachineMetadataDataService } from "./state-machine-metadata-data-service"; import { CustomWebCommandMetadataDataService } from "./custom-web-command-metadata-data-service"; -import { FormMetadata, Metadata, StateMachineRefMetadata, WebCommandRefMetadata } from "../types"; +import { FormMetadata, Metadata, StateMachineRefMetadata, WebCommandRefMetadata } from "../../types"; +import { ResourceMetadataDataService } from "./resource-metadata-data-service"; +import { LanguageListManager } from "../../i18n"; +import { DEFAULT_LOCALE } from "@farris/devkit-vue"; export class MetadataDataService { constructor( @@ -11,43 +14,49 @@ export class MetadataDataService { private stateMachineMetadataDataService: StateMachineMetadataDataService, private webCommandMetadataDataService: CommonWebCommandMetadataDataService, private customWebComponentMetadataDataService: CustomWebComponentMetadataDataService, - private customWebCommandMetadataDataService: CustomWebCommandMetadataDataService + private customWebCommandMetadataDataService: CustomWebCommandMetadataDataService, + private resourceMetadataDataService: ResourceMetadataDataService, + private languageListManager: LanguageListManager ) { } + /** + * 根据Path加载元数据 + */ public loadMetadataByPath(projectPath: string, metadataPath: string) { return this.formMetadataDataService.loadByFullPath(metadataPath).then((formMetadata: Metadata) => { const schema = formMetadata.content as FormMetadata; + + // 状态机元数据 const { stateMachines = [] } = schema.module; - // const stateMachineRefSchema = stateMachines; const stateMachineMetadataPromise = stateMachines && stateMachines.length > 0 ? stateMachines.map((stateMachine: StateMachineRefMetadata) => { return this.stateMachineMetadataDataService.loadByProjectPath(projectPath, stateMachine.uri); }) : [Promise.resolve(null)]; - // const stateMachineMetadataPromise = stateMachines ? this.stateMachineMetadataDataService.loadByProjectPath(projectPath, stateMachineRefSchema.uri) : Promise.resolve(null); + + // 命令元数据 const commandsMetadataPromise = schema.module.webcmds.map((webCommand: WebCommandRefMetadata) => { const { id } = webCommand; return this.webCommandMetadataDataService.loadByProjectPath(projectPath, id); }); + + // 自定义构件元数据 const customWebComponentMetadataPromise = this.customWebComponentMetadataDataService.loadByType(projectPath); - // 资源元数据, 暂不加载 - // const resourceMetadatas = formMetadata.refs && formMetadata.refs.filter((ref: any) => { - // return ref.DependentMetadata.Type === "ResourceMetadata"; - // }).map((ref: any) => ref.DependentMetadata.ID) || []; - // const resourceMetadataPromise = resourceMetadatas.map((id: string) => { - // return useResourceMetadata(id, useCache); - // }); - // , Promise.all(resourceMetadataPromise) + const promises = [Promise.all(stateMachineMetadataPromise), Promise.all(commandsMetadataPromise), customWebComponentMetadataPromise]; return Promise.all(promises).then(([stateMachineSchema, commandSchemas, customWebComponents]) => { return { form: formMetadata, stateMachines: stateMachineSchema as Metadata[], commands: commandSchemas as Metadata[], - webComponents: customWebComponents as Metadata[] + webComponents: customWebComponents as Metadata[], + // resourceMetadatas: resourceMetadatas }; }); }); } + /** + * 根据ID加载元数据 + */ public loadMetadataById(metadataId: string) { return this.formMetadataDataService.loadById(metadataId).then((formMetadata: Metadata) => { const schema = formMetadata.content as FormMetadata; @@ -57,13 +66,21 @@ export class MetadataDataService { return this.stateMachineMetadataDataService.loadById(stateMachine.uri); }) : [Promise.resolve(null)]; // const stateMachineMetadataPromise = stateMachines ? this.stateMachineMetadataDataService.loadById(stateMachineRefSchema.uri) : Promise.resolve(null); - + // 资源元数据, 暂不加载 + // const resourceMetadatas = formMetadata.refs && formMetadata.refs.filter((ref: any) => { + // return ref.DependentMetadata.Type === "ResourceMetadata"; + // }).map((ref: any) => ref.DependentMetadata.ID) || []; + // const resourceMetadataPromise = resourceMetadatas.map((id: string) => { + // return this.loadResourceMetadataById(id); + // }); const commandsMetadataPromise = schema.module.webcmds.map((webCommand: WebCommandRefMetadata) => { const { id } = webCommand; return this.webCommandMetadataDataService.loadById(id); }); - const promises = [Promise.all(stateMachineMetadataPromise), Promise.all(commandsMetadataPromise)]; - return Promise.all(promises).then(([stateMachineSchema, commandSchemas]) => { + + const promises = [Promise.all(stateMachineMetadataPromise), Promise.all(commandsMetadataPromise)]; // , Promise.all(resourceMetadataPromise) + + return Promise.all(promises).then(([stateMachineSchema, commandSchemas]) => { // , resourceMetadatas const customWebComponents: string[] = []; if (commandSchemas && commandSchemas.length > 0) { commandSchemas.forEach((commandSchema: Metadata | null) => { @@ -86,6 +103,116 @@ export class MetadataDataService { const customWebComponentMetadataPromise = customWebComponents.length > 0 ? Promise.all(customWebComponents.map((id: string) => { return this.customWebComponentMetadataDataService.loadById(id); })) : Promise.resolve(null); + return customWebComponentMetadataPromise.then((customWebComponents: Metadata[] | null) => { + return { + form: formMetadata, + stateMachines: stateMachineSchema as Metadata[], + commands: commandSchemas as Metadata[], + webComponents: customWebComponents as Metadata[], + // resourceMetadatas + }; + }); + }); + }); + } + private loadResourceMetadataByPath(projectPath: string, metadataId: string) { + const resourceMetadata = this.resourceMetadataDataService.loadByProjectPath(projectPath, metadataId); + const languageCode = this.languageListManager.getLanguageCode(); + if (languageCode === DEFAULT_LOCALE) { + return resourceMetadata; + } else { + return resourceMetadata.then((metadata: Metadata) => { + const languageList = metadata.content.refs; + if (!languageList || languageList.length < 1) { + return Promise.resolve(null); + } + const ref = languageList.find((item: any) => item.language === languageCode); + if (!ref) { + return Promise.resolve(null); + } + return this.resourceMetadataDataService.loadByProjectPath(projectPath, ref.metadataId); + }); + } + } + private loadResourceMetadataById(metadataId: string) { + const resourceMetadata = this.resourceMetadataDataService.loadById(metadataId); + const languageCode = this.languageListManager.getLanguageCode(); + if (languageCode === DEFAULT_LOCALE) { + return resourceMetadata; + } else { + return resourceMetadata.then((metadata: Metadata) => { + const languageList = metadata.content.refs; + if (!languageList || languageList.length < 1) { + return Promise.resolve(null); + } + const ref = languageList.find((item: any) => item.language === languageCode); + if (!ref) { + return Promise.resolve(null); + } + return this.resourceMetadataDataService.loadById(ref.metadataId); + }); + } + } + + /** + * 根据DTO加载元数据 + */ + public loadMetadataByDto(currentPath: any, formMetadataDto: any) { + return this.formMetadataDataService.loadByDto(currentPath, formMetadataDto).then((formMetadata: Metadata) => { + const schema = formMetadata.content as FormMetadata; + + // 状态机 + const { stateMachines } = schema.module; + const stateMachineMetadataPromise = stateMachines && stateMachines.length > 0 ? stateMachines.map((stateMachine: StateMachineRefMetadata) => { + const stateMachineMetadataDto = { + id: stateMachine.uri, + nameSpace: stateMachine.nameSpace + }; + return this.stateMachineMetadataDataService.loadByDto(currentPath, stateMachineMetadataDto); + }) : [Promise.resolve(null)]; + + // 命令构件 + const commandsMetadataPromise = schema.module.webcmds.map((webCommand: WebCommandRefMetadata) => { + const commandMetadataDto = { + id: webCommand.id, + nameSpace: webCommand.nameSpace + }; + return this.webCommandMetadataDataService.loadByDto(currentPath, commandMetadataDto); + }); + + const promises = [Promise.all(stateMachineMetadataPromise), Promise.all(commandsMetadataPromise)]; + + + return Promise.all(promises).then(([stateMachineSchema, commandSchemas]) => { + const customWebComponentDtos: any[] = []; + + if (commandSchemas && commandSchemas.length > 0) { + commandSchemas.forEach((commandSchema: any | null) => { + if (commandSchema && commandSchema.content && commandSchema.content.Extends && commandSchema.content.Extends.IsCommon === false) { + const commands = commandSchema.content.Commands as any[]; + commands.forEach((command: any) => { + const items = command.Items as any[]; + items.forEach((item: any) => { + if (!item.ComponentPath.startsWith('Gsp/')) { + const componentId = item.ComponentId; + const existedCustomWebComponentDto = customWebComponentDtos.find((customWebComponentDto) => { + return customWebComponentDto.id === componentId; + }); + if (!existedCustomWebComponentDto) { + customWebComponentDtos.push({ id: componentId, nameSpace: commandSchema.nameSpace }); + } + } + }); + }); + } + }); + } + + // 自定义Web构件 + const customWebComponentMetadataPromise = customWebComponentDtos.length > 0 ? Promise.all(customWebComponentDtos.map((customWebComponentDto: string) => { + return this.customWebComponentMetadataDataService.loadByDto(currentPath, customWebComponentDto); + })) : Promise.resolve(null); + return customWebComponentMetadataPromise.then((customWebComponents: Metadata[] | null) => { return { form: formMetadata, diff --git a/packages/renderer/src/data-service/providers.ts b/packages/renderer/src/metadata/data-service/providers.ts similarity index 91% rename from packages/renderer/src/data-service/providers.ts rename to packages/renderer/src/metadata/data-service/providers.ts index 9f6de9cfa26417ab8c41311801dd7d170d173247..c036eb76f969aac56ef869791cd3437836da948f 100644 --- a/packages/renderer/src/data-service/providers.ts +++ b/packages/renderer/src/metadata/data-service/providers.ts @@ -1,12 +1,14 @@ import { StaticProvider } from "@farris/devkit-vue"; -import { FormMetadataDataService } from "./form-metadata-data-service"; import { MetadataRepository } from "../repository"; + +import { FormMetadataDataService } from "./form-metadata-data-service"; import { StateMachineMetadataDataService } from "./state-machine-metadata-data-service"; import { CommonWebCommandMetadataDataService } from "./common-web-command-metadata-data-service"; import { ResourceMetadataDataService } from "./resource-metadata-data-service"; import { MetadataDataService } from "./metadata-data-service"; import { CustomWebComponentMetadataDataService } from "./custom-web-component-metadata-data-service"; import { CustomWebCommandMetadataDataService } from "./custom-web-command-metadata-data-service"; +import { LanguageListManager } from "../../i18n"; export const metadataDataServiceProviders: StaticProvider[] = [ { provide: FormMetadataDataService, useClass: FormMetadataDataService, deps: [MetadataRepository] }, @@ -15,5 +17,5 @@ export const metadataDataServiceProviders: StaticProvider[] = [ { provide: ResourceMetadataDataService, useClass: ResourceMetadataDataService, deps: [MetadataRepository] }, { provide: CustomWebComponentMetadataDataService, useClass: CustomWebComponentMetadataDataService, deps: [MetadataRepository] }, { provide: CustomWebCommandMetadataDataService, useClass: CustomWebCommandMetadataDataService, deps: [MetadataRepository] }, - { provide: MetadataDataService, useClass: MetadataDataService, deps: [FormMetadataDataService, StateMachineMetadataDataService, CommonWebCommandMetadataDataService, CustomWebComponentMetadataDataService, CustomWebCommandMetadataDataService] } + { provide: MetadataDataService, useClass: MetadataDataService, deps: [FormMetadataDataService, StateMachineMetadataDataService, CommonWebCommandMetadataDataService, CustomWebComponentMetadataDataService, CustomWebCommandMetadataDataService, ResourceMetadataDataService, LanguageListManager] } ]; diff --git a/packages/renderer/src/metadata/data-service/resource-metadata-data-service.ts b/packages/renderer/src/metadata/data-service/resource-metadata-data-service.ts new file mode 100644 index 0000000000000000000000000000000000000000..98e909a2f62ea5757fa796873c96df975772ef79 --- /dev/null +++ b/packages/renderer/src/metadata/data-service/resource-metadata-data-service.ts @@ -0,0 +1,47 @@ +import { Metadata } from "../../types"; +import { MetadataRepository } from "../repository"; + +export class ResourceMetadataDataService { + constructor(private metadataRepository: MetadataRepository) { } + public loadByType(metadataPath: string) { + return this.metadataRepository.loadMetadatasByType(metadataPath, ".res").then((metadatas: any[]) => { + if (metadatas && metadatas.length > 0) { + return Promise.all(metadatas.map((metadata: any) => { + return this.loadByFullPath(metadataPath, metadata.id); + })); + } + }); + } + public loadByFullPath(metadataPath: string, metadataId: string): Promise { + return this.metadataRepository.relied(metadataId, metadataPath).then((response: any) => { + return this.createMetadata(response); + }); + } + + public loadByProjectPath(projectPath: string, metadataId: string) { + return this.metadataRepository.relied(metadataId, projectPath).then((response: any) => { + return this.createMetadata(response); + }); + } + + public loadById(metadataId: string) { + return this.metadataRepository.retrieve(metadataId).then((response: any) => { + return this.createMetadata(response); + }); + } + public loadByDto(currentPath: string, metadataDto: any) { + return this.metadataRepository.loadByDto(currentPath, metadataDto).then((response: any) => { + return this.createMetadata(response); + }); + } + private createMetadata(response: any) { + const metadata = { + id: response.id, + content: JSON.parse(response.content), + refs: JSON.parse(response.refs), + type: response.type, + namespace: response.nameSpace + }; + return metadata; + } +} diff --git a/packages/renderer/src/data-service/state-machine-metadata-data-service.ts b/packages/renderer/src/metadata/data-service/state-machine-metadata-data-service.ts similarity index 82% rename from packages/renderer/src/data-service/state-machine-metadata-data-service.ts rename to packages/renderer/src/metadata/data-service/state-machine-metadata-data-service.ts index a1df8a868dfa210e8523f686046c65c5e46d0ce8..e1fbead530d60518db667d1405f0332cb5e54e96 100644 --- a/packages/renderer/src/data-service/state-machine-metadata-data-service.ts +++ b/packages/renderer/src/metadata/data-service/state-machine-metadata-data-service.ts @@ -22,6 +22,12 @@ export class StateMachineMetadataDataService { }); } + public loadByDto(currentPath: string, metadataDto: any) { + return this.metadataRepository.loadByDto(currentPath, metadataDto).then((response: any) => { + return this.createMetadata(response); + }); + } + private createMetadata(response: any) { const metadata = { content: JSON.parse(response.content), diff --git a/packages/renderer/src/metadata/index.ts b/packages/renderer/src/metadata/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..38695ad97c151979db96946569b8a803b126d3c6 --- /dev/null +++ b/packages/renderer/src/metadata/index.ts @@ -0,0 +1,5 @@ +export * from './types'; +export * from './data-service'; +export * from './metadata-loader'; +export * from './metadata-manager'; +export * from './providers'; diff --git a/packages/renderer/src/metadata/metadata-loader.ts b/packages/renderer/src/metadata/metadata-loader.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5e63ed4495bec6e8d42e202b870e8d41402cc98 --- /dev/null +++ b/packages/renderer/src/metadata/metadata-loader.ts @@ -0,0 +1,145 @@ +import { MetadataDataService } from './data-service'; +import { MetadataManager } from './metadata-manager'; +import { FormMetadataQuery } from '../service/form-metadata-query'; +import { LocaleQuery } from '../i18n'; + +/** + * 元数据加载器 + */ +class MetadataLoader { + + /** + * 元数据取数服务 + */ + private metadataDataService: MetadataDataService; + + /** + * 元数据管理器 + */ + private metadataManager: MetadataManager; + + /** + * 构造函数 + */ + constructor(metadataDataService: MetadataDataService, metadataManager: MetadataManager, private localeQuery: LocaleQuery) { + this.metadataDataService = metadataDataService; + this.metadataManager = metadataManager; + } + + /** + * 根据路径加载表单元数据 + */ + public async loadMetadataByPath(projectPath: string, formMetadataPath: string): Promise { + const metadatas = await this.metadataDataService.loadMetadataByPath(projectPath, formMetadataPath); + + const formMetadataQuery = new FormMetadataQuery(metadatas.form); + this.normalizeStatemachines(formMetadataQuery, metadatas); + this.normalizeExternalComponents(formMetadataQuery, metadatas); + + this.metadataManager.setMetadatasCache(metadatas.form.id, metadatas); + await this.loadExternalMetadataByPath(formMetadataPath, metadatas.form); + } + + /** + * 根据ID加载元数据 + */ + public async loadMetadataById(formMetadataId: string): Promise { + const metadatas = await this.metadataDataService.loadMetadataById(formMetadataId); + + const formMetadataQuery = new FormMetadataQuery(metadatas.form); + this.normalizeStatemachines(formMetadataQuery, metadatas); + this.normalizeExternalComponents(formMetadataQuery, metadatas); + + this.metadataManager.setMetadatasCache(formMetadataId, metadatas); + await this.loadExternalMetadataById(metadatas.form); + } + + /** + * 根据路径加载表单元数据 + */ + public async loadMetadataByDto(currentPath: string, formMetadataDto: any): Promise { + const metadatas = await this.metadataDataService.loadMetadataByDto(currentPath, formMetadataDto); + + const formMetadataQuery = new FormMetadataQuery(metadatas.form); + this.normalizeStatemachines(formMetadataQuery, metadatas); + this.normalizeExternalComponents(formMetadataQuery, metadatas); + + this.metadataManager.setMetadatasCache(metadatas.form.id, metadatas); + } + + /** + * 根据Path加载外部表单元数据 + */ + public async loadExternalMetadataByPath(formMetadataPath: string, formMetadata: any): Promise { + const formMetadataQuery = new FormMetadataQuery(formMetadata); + const externalFormInfos = formMetadataQuery.getExternalFormInfos(); + + for (const externalFormInfo of externalFormInfos) { + if (externalFormInfo.relativePath) { + const metadataPath = `${externalFormInfo.relativePath}/${externalFormInfo.fileName}`; + await this.loadMetadataByPath(metadataPath, metadataPath); + } else { + const { id, nameSpace } = externalFormInfo; + const metadataDto = { id, nameSpace }; + await this.loadMetadataByDto(formMetadataPath, metadataDto); + } + } + } + + /** + * 根据ID加载外部表单元数据 + */ + public async loadExternalMetadataById(formMetadata: any): Promise { + const formMetadataQuery = new FormMetadataQuery(formMetadata); + const externalFormInfos = formMetadataQuery.getExternalFormInfos(); + + for (const externalFormInfo of externalFormInfos) { + await this.loadMetadataById(externalFormInfo.id); + } + } + /** + * 标准化元数据 + */ + private normalizeStatemachines(formMetadataQuery: FormMetadataQuery, metadatas: any): void { + if (!metadatas) { + return; + } + const formMetadata = metadatas.form?.content; + if (!formMetadata) { + return; + } + + // 获取状态机ID + const stateMachines = formMetadata.module?.stateMachines; + if (!Array.isArray(stateMachines) || stateMachines.length < 1) { + return; + } + const stateMachineId = stateMachines[0].id; + + // 补全状态机ID + const viewModels = formMetadata.module?.viewmodels; + if (!Array.isArray(viewModels) || viewModels.length < 1) { + return; + } + viewModels.forEach((viewModel: any) => { + viewModel.stateMachine = stateMachineId; + }); + } + + /** + * 标准化外部组件 + */ + private normalizeExternalComponents(formMetadataQuery: FormMetadataQuery, metadatas: any): void { + const externalComponentNodes: any[] = formMetadataQuery.getExternalComponents(); + if (!Array.isArray(externalComponentNodes)) { + return; + } + + const rootVmComponent = formMetadataQuery.getRootComponent(); + externalComponentNodes.forEach((externalComponentNode: any) => { + rootVmComponent.contents.push(externalComponentNode); + }); + } +} + +export { MetadataLoader }; diff --git a/packages/renderer/src/metadata/metadata-manager.ts b/packages/renderer/src/metadata/metadata-manager.ts new file mode 100644 index 0000000000000000000000000000000000000000..634c36c238ca981cf68099545e84796374e97811 --- /dev/null +++ b/packages/renderer/src/metadata/metadata-manager.ts @@ -0,0 +1,70 @@ + +/** + * 元数据缓存 + */ +interface MetadataCache { + formId: string; + formCode: string; + formMetadata: any; + allMetadatas: any; +}; + +/** + * 元数据管理 + */ +class MetadataManager { + + /** + * 元数据缓存 + */ + private metadatasCache: Map; + + /** + * 构造函数 + */ + constructor() { + this.metadatasCache = new Map(); + } + + /** + * 设置元数据 + */ + public setMetadatasCache(formMetadataId: string, metadatas: any): void { + const formMetadata = metadatas.form; + const formModule = formMetadata.content.module; + const metadataCache = { + formId: formMetadata.id, + formCode: formModule.code, + formMetadata: formMetadata, + allMetadatas: metadatas + }; + this.metadatasCache.set(formMetadata.id, metadataCache); + } + + /** + * 获取元数据 + */ + public getMetadataCache(formMetadataId: string): any { + const metadataCache = this.metadatasCache.get(formMetadataId); + if (!metadataCache) { + throw new Error(`MetadataCache(formMetadataId=${formMetadataId}) not exist`); + } + + const formattedMetadataCache = metadataCache.allMetadatas; + return formattedMetadataCache; + } + + /** + * 获取全部元数据缓存 + */ + public getMetadataCaches() { + const metadataCaches = this.metadatasCache.values(); + const formattedMetadataCaches = metadataCaches.map((metadataCache: any) => { + return metadataCache.allMetadatas; + }); + + return formattedMetadataCaches; + } +} + +export { MetadataManager }; diff --git a/packages/renderer/src/metadata/providers.ts b/packages/renderer/src/metadata/providers.ts new file mode 100644 index 0000000000000000000000000000000000000000..32adb6095312216e3114bc19d288152a05dae97e --- /dev/null +++ b/packages/renderer/src/metadata/providers.ts @@ -0,0 +1,13 @@ +import { StaticProvider } from "@farris/devkit-vue"; +import { metadataRepositoryProviders } from './repository/index'; +import { metadataDataServiceProviders, MetadataDataService } from "./data-service/index"; +import { MetadataManager } from './metadata-manager'; +import { MetadataLoader } from './metadata-loader'; +import { LocaleQuery } from "../i18n"; + +export const metadataProviders: StaticProvider[] = [ + ...metadataRepositoryProviders, + ...metadataDataServiceProviders, + { provide: MetadataManager, useClass: MetadataManager, deps: [] }, + { provide: MetadataLoader, useClass: MetadataLoader, deps: [MetadataDataService, MetadataManager, LocaleQuery] } +]; diff --git a/packages/renderer/src/repository/index.ts b/packages/renderer/src/metadata/repository/index.ts similarity index 100% rename from packages/renderer/src/repository/index.ts rename to packages/renderer/src/metadata/repository/index.ts diff --git a/packages/renderer/src/repository/metadata-repository.ts b/packages/renderer/src/metadata/repository/metadata-repository.ts similarity index 80% rename from packages/renderer/src/repository/metadata-repository.ts rename to packages/renderer/src/metadata/repository/metadata-repository.ts index bab8adb2437699dffb98fd3f742a73fbdc443fde..5a7dffc82b64ca4dfab7a5631230dcb56e10bd6c 100644 --- a/packages/renderer/src/repository/metadata-repository.ts +++ b/packages/renderer/src/metadata/repository/metadata-repository.ts @@ -16,6 +16,18 @@ export class MetadataRepository { return this.httpClient.get(`/api/dev/main/v1.0/metadatas/load`, { params: httpParams }); } + /** + * 根据DTO加载元数据 + */ + public loadByDto(currentPath: string, metadataDto: any): Promise { + const url = `/api/dev/main/v1.0/mdservice/pickMetadata?currentPath=${currentPath}`; + const body = metadataDto; + const requestConfig: HttpRequestConfig = { headers: { accept: 'application/json' } }; + return this.httpClient.post(url, body, requestConfig).then((result: any) => { + return result.metadata; + }); + } + /** * 根据元数据类型获取表单定义的其他元数据 * @param metadataPath 元数据工程路径 diff --git a/packages/renderer/src/repository/providers.ts b/packages/renderer/src/metadata/repository/providers.ts similarity index 77% rename from packages/renderer/src/repository/providers.ts rename to packages/renderer/src/metadata/repository/providers.ts index 54b12cb874b63b434f99d0536a16ab55a370112d..87a105268f3bad4bd06237f5dcc46687b2411887 100644 --- a/packages/renderer/src/repository/providers.ts +++ b/packages/renderer/src/metadata/repository/providers.ts @@ -1,6 +1,6 @@ import { HttpClient, StaticProvider } from "@farris/devkit-vue"; import { MetadataRepository } from "./metadata-repository"; -export const repositoryProviders: StaticProvider[] = [ +export const metadataRepositoryProviders: StaticProvider[] = [ { provide: MetadataRepository, useClass: MetadataRepository, deps: [HttpClient] } ]; diff --git a/packages/renderer/src/metadata/types.ts b/packages/renderer/src/metadata/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..2acd6ae62b2dd3028f946f851782bf9e90233469 --- /dev/null +++ b/packages/renderer/src/metadata/types.ts @@ -0,0 +1,14 @@ +/** + * 外部表单信息 + */ +export interface ExternalFormInfo { + id: string, + code: string, + name: string, + fileName: string, + bizobjectID: string, + nameSpace: string, + relativePath?: string, + projectName?: string, + externalContainerId: string; +} diff --git a/packages/renderer/src/page.vue b/packages/renderer/src/page.vue new file mode 100644 index 0000000000000000000000000000000000000000..cb6bd74b1c1ff0cb31d5cefbd6d40aad089aa296 --- /dev/null +++ b/packages/renderer/src/page.vue @@ -0,0 +1,105 @@ + + + + diff --git a/packages/renderer/src/preview.vue b/packages/renderer/src/preview.vue index 466b497f82e4369b7764607e2fc7ed57f4034864..0ae4a5df9ef45dd5ecea3a3d70bff5ca34ea146a 100644 --- a/packages/renderer/src/preview.vue +++ b/packages/renderer/src/preview.vue @@ -1,45 +1,13 @@ - - + diff --git a/packages/renderer/src/providers.ts b/packages/renderer/src/providers.ts deleted file mode 100644 index 7df7eb190356168d11130e11fd1baea6060285e8..0000000000000000000000000000000000000000 --- a/packages/renderer/src/providers.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { httpProviders, StaticProvider } from "@farris/devkit-vue"; -import { repositoryProviders } from "./repository"; -import { metadataDataServiceProviders } from "./data-service"; -// import { serviceProviders } from "./service"; - -export const appProviders: StaticProvider[] = [ - ...httpProviders, - ...repositoryProviders, - ...metadataDataServiceProviders -]; diff --git a/packages/renderer/src/render-engine/render-engine-impl.ts b/packages/renderer/src/render-engine/render-engine-impl.ts index 8a55a7876e46edf2fdbb1f53c51720a3cf748c41..652db72be9ee4cc16fa5cde722e1ee4a2739461f 100644 --- a/packages/renderer/src/render-engine/render-engine-impl.ts +++ b/packages/renderer/src/render-engine/render-engine-impl.ts @@ -1,17 +1,24 @@ -import { Injector, RenderEngine } from "@farris/devkit-vue"; +import { Injector, Module, RenderEngine } from "@farris/devkit-vue"; import { Ref } from "vue"; -import { RENDER_TOKEN } from "../tokens"; +import { FORM_METADATA_TOKEN, MODULE_CONFIG_ID_TOKEN, RENDER_TOKEN } from "../tokens"; +import { FieldResolver } from "../resolvers/field-resolver"; +import { DataSourceResolver } from "../resolvers"; +import { ENTITY_STORE_SUFFIX } from "../types"; export class RenderEngineImpl implements RenderEngine { private renderer: Ref; + private module: Module; + private formMetadata: any; constructor(private injector: Injector) { const renderRef = this.injector.get(RENDER_TOKEN); this.renderer = renderRef; + this.module = this.injector.get(Module); + this.formMetadata = this.injector.get(FORM_METADATA_TOKEN); } - public render(componentId: string, type: string,schema: Record) { - const props = this.renderer.value.convertPartialSchemaToProps(type,schema); + public render(componentId: string, type: string, schema: Record) { + const props = this.renderer.value.convertPartialSchemaToProps(type, schema); this.renderer.value.setProps(componentId, props); } @@ -42,4 +49,25 @@ export class RenderEngineImpl implements RenderEngine { public setSchema(componentId: string, schema: Record) { this.renderer.value.setSchema(componentId, schema); } + /** + * 获取组件当前值 + * @param id + * @returns + */ + public getControlValue(id: string){ + return this.renderer.value.getControlValue(id); + } + /** + * 实体仓库 + */ + private get entityStore() { + const configId = this.injector.get(MODULE_CONFIG_ID_TOKEN, undefined); + return this.module.getEntityStore(`${configId}${ENTITY_STORE_SUFFIX}`); + } + /** + * 实体元数据 + */ + private get entityMetadata() { + return this.formMetadata?.module?.entity[0]?.entities[0] || {} as any; + } } diff --git a/packages/renderer/src/renderer.vue b/packages/renderer/src/renderer.vue index 008722c4972119a1ca70d7c58074c0102afb6bca..93b506daaf528a165b5fc1f3b9e2075c805e654f 100644 --- a/packages/renderer/src/renderer.vue +++ b/packages/renderer/src/renderer.vue @@ -1,43 +1,14 @@ + + diff --git a/packages/renderer/src/resolvers/model-value-resolver.ts b/packages/renderer/src/resolvers/model-value-resolver.ts index 17bd319f57937ad45300844a8765a72b43fc9acc..956bbe5d8c52ae529da731a47c9935d9f49bdfcc 100644 --- a/packages/renderer/src/resolvers/model-value-resolver.ts +++ b/packages/renderer/src/resolvers/model-value-resolver.ts @@ -5,43 +5,79 @@ import { FieldResolver } from "./field-resolver"; import { MODULE_CONFIG_ID_TOKEN } from "../tokens"; import { ENTITY_STORE_SUFFIX } from "../types"; +/** + * 模型值解析器 + */ export class ModelValueResolver { + + /** + * 模型值 + * @summary + * key = 组件ID; + * value = 绑定值(字段值或表数据) + */ private modelValue: Record = {}; + /** + * 构造函数 + * @param formMetadataService 表单元数据服务 + * @param module 模块实例 + * @param injector 注入器 + */ constructor(private formMetadataService: FormMetadataService, private module: Module, private injector: Injector) { } + /** + * 解析模型 + */ public resolve() { this.modelValue = {}; const components = this.formMetadataService.getComponents(); if (!components || components.length < 1) { return this.modelValue; } + components.forEach((component) => { this.resolveModelValue(component); }); + return this.modelValue; } + /** + * 解析组件 + * @param viewSchema 组件配置 + * @returns 递归解析组件及下级组件的绑定值 + */ public resolveModelValue(viewSchema: Record) { const { binding, dataSource } = viewSchema; const { id } = viewSchema; + + // 强势别属性名,解析属性值 if (binding) { const value = this.resolveBindingValue(viewSchema); this.modelValue[id] = value; - } else if (dataSource) { + } else if (dataSource && viewSchema.type !== 'lookup') { const value = this.resolveDataSourceValue(viewSchema); this.modelValue[id] = value; } + + // 递归处理下级绑定 if (!viewSchema.contents) { return; } if (typeof viewSchema.contents === 'string') { return; } + viewSchema.contents.forEach((schema: Record) => this.resolveModelValue(schema)); } - public resolveBindingValue(viewSchema: Record) { + /** + * 解析字段绑定 + * @param viewSchema + * @returns 绑定的字段值 + */ + public resolveBindingValue(viewSchema: Record): any | null { const { binding } = viewSchema; const { field } = binding; const resolvedField = FieldResolver.resolve(this.entityMetadata, field); @@ -68,7 +104,12 @@ export class ModelValueResolver { return value === undefined ? null : value; } - public resolveDataSourceValue(viewSchema: Record) { + /** + * 解析集合数据绑定 + * @param viewSchema 组件配置 + * @returns 绑定的集合数据 + */ + public resolveDataSourceValue(viewSchema: Record): any[] | null { const { dataSource } = viewSchema; const resolvedEntity = DataSourceResolver.resolve(this.entityMetadata, dataSource); if (!resolvedEntity) { @@ -81,10 +122,17 @@ export class ModelValueResolver { return value; } + /** + * 实体仓库 + */ private get entityStore() { const configId = this.injector.get(MODULE_CONFIG_ID_TOKEN, undefined); return this.module.getEntityStore(`${configId}${ENTITY_STORE_SUFFIX}`); } + + /** + * 实体元数据 + */ private get entityMetadata() { return this.formMetadataService.getEntity(); } diff --git a/packages/renderer/src/service/form-metadata-query.ts b/packages/renderer/src/service/form-metadata-query.ts index c44be3e813a7209ec4941c9d25baab27f15828e1..4face9a08478d1252a7938c6d7f56a227c1a9fea 100644 --- a/packages/renderer/src/service/form-metadata-query.ts +++ b/packages/renderer/src/service/form-metadata-query.ts @@ -1,3 +1,4 @@ +import { ExternalFormInfo } from '../metadata/index'; import { WrappedEntitySchema, WrappedFieldSchema, EntitySchemaQuery } from './entity-schema-query'; /** @@ -129,6 +130,17 @@ class FormMetadataQuery { return wrappedComponents; } + /** + * 根据类型获取包装组件的集合 + */ + public getWrappedComponentsByType(type: string): WrappedComponent[] { + const allWrappedComponents = this.getWrappedComponents(); + const filteredWrappedComponents = allWrappedComponents.filter((wrappedComponent) => { + return wrappedComponent.component.type === type; + }); + return filteredWrappedComponents; + } + /** * 初始化视图模型Map */ @@ -187,6 +199,76 @@ class FormMetadataQuery { }); } } + + /** + * 获取外部表单信息 + */ + public getExternalFormInfos(): ExternalFormInfo[] { + const wrappedExternalContainers = this.getWrappedComponentsByType("external-container"); + const externalFormInfos: any[] = []; + wrappedExternalContainers.forEach((wrappedExternalContainer) => { + const externalContainer = wrappedExternalContainer.component; + const externalFormInfo = this.buildExternalFormInfo(externalContainer); + if (!externalFormInfo) { + return; + } + externalFormInfos.push(externalFormInfo); + }); + return externalFormInfos; + } + + /** + * 获取外部表单信息 + */ + private buildExternalFormInfo(externalContainer: any): ExternalFormInfo | null { + const { externalComponent } = externalContainer; + if (!externalComponent) { + return null; + } + + const externalFormInfo = { + externalContainerId: externalContainer.id, + ...externalComponent + }; + + return externalFormInfo; + } + + /** + * 获取组件通讯节点集合 + */ + public getCommunications() { + const communicationNodes = this.formModule.communications; + return communicationNodes; + } + + /** + * 获取外部组件集合 + */ + public getExternalComponents(): any[] { + const externalComponentNodes = this.formModule.externalComponents; + return externalComponentNodes; + } + + /** + * 获取外部组件ID + */ + public getExternalComponentById(id: string) { + const externalComponentNodes = this.getExternalComponents(); + const targetExternalComponentNode = externalComponentNodes.find((externalComponentNode) => { + return externalComponentNode === id; + }); + + return targetExternalComponentNode; + } + + /** + * 获取根视图模型组件 + */ + public getRootComponent() { + const vmComponents = this.getVmComponents(); + return vmComponents[0]; + } } export { FormMetadataQuery }; diff --git a/packages/renderer/src/service/form-metadata-service.ts b/packages/renderer/src/service/form-metadata-service.ts index 819c127c08e877ab0082ea624762fa272b74fade..80154ef8acb54510d85ed118e2d06b048cfafdcd 100644 --- a/packages/renderer/src/service/form-metadata-service.ts +++ b/packages/renderer/src/service/form-metadata-service.ts @@ -7,7 +7,6 @@ export class FormMetadataService { const components = this.metadata?.module?.components || []; this.traverseComponent(components); } - public getModuleId(): string { return this.metadata.module.id; } diff --git a/packages/renderer/src/template-transformer/data-grid-column-template-transformer.ts b/packages/renderer/src/template-transformer/data-grid-column-template-transformer.ts index b188d2af91fe3ebdd00e7118a731c8f43d1a5249..d89c305b2cab30efa8b91e3a4898841c3f673459 100644 --- a/packages/renderer/src/template-transformer/data-grid-column-template-transformer.ts +++ b/packages/renderer/src/template-transformer/data-grid-column-template-transformer.ts @@ -1,10 +1,11 @@ import { compile, createVNode } from "vue"; import { TemplateTransformer } from "./template-transformer"; import { FormMetadataService } from "../service"; -import { ViewModel, ViewModelState } from "@farris/devkit-vue"; +import { Injector, ViewModel, ViewModelState } from "@farris/devkit-vue"; +import { TranslateService } from "@farris/command-services-vue"; export class DataGridColumnTemplateTransformer extends TemplateTransformer { - constructor(protected formMetadataService: FormMetadataService, protected viewModel: ViewModel) { + constructor(protected formMetadataService: FormMetadataService, protected viewModel: ViewModel, private injector: Injector) { super(formMetadataService, viewModel); } public transform(schema: Record): void { @@ -20,16 +21,29 @@ export class DataGridColumnTemplateTransformer extends TemplateTransformer { return; } columns.forEach((column: Record) => { - const { columnTemplate } = column; - if (!columnTemplate || typeof columnTemplate !== 'string') { - return; + const { formatter, columnTemplate } = column; + if (columnTemplate && typeof columnTemplate === 'string') { + const compiledTemplate = compile(columnTemplate); + const templateContext = this.buildTempalteContext(viewModel); + const translateService = this.injector.get(TranslateService); + const render = (cell: any, data: any) => { + return createVNode({ render: compiledTemplate, props: ['rowData', 'viewModel', 't'] }, { viewModel: templateContext, rowData: data.raw, t: translateService.transform.bind(translateService) }); + }; + column.columnTemplate = render; } - const compiledTemplate = compile(columnTemplate); - const templateContext = this.buildTempalteContext(viewModel); - const render = (cell: any, data: any) => { - return createVNode({ render: compiledTemplate, props: ['rowData', 'viewModel'] }, { viewModel: templateContext, rowData: data.raw }); - }; - column.columnTemplate = render; + // if (!formatter || formatter.type !== 'custom') { + // return; + // } + // const { customFormat } = formatter; + // if (!customFormat) { + // return; + // } + // const compiledTemplate = compile(customFormat); + // const templateContext = this.buildTempalteContext(viewModel); + // const render = (cell: any, data: any) => { + // return createVNode({ render: compiledTemplate, props: ['rowData', 'viewModel'] }, { viewModel: templateContext, rowData: data.raw }); + // }; + // column.formatter = render; }); } } diff --git a/packages/renderer/src/template-transformer/html-template-component-template-transformer.ts b/packages/renderer/src/template-transformer/html-template-component-template-transformer.ts index 48de01a5497f0471e83a2bafa1c46adc7c44ba05..7d5d565961ba14a01bc0bcd9ebaa6d5230cb68a2 100644 --- a/packages/renderer/src/template-transformer/html-template-component-template-transformer.ts +++ b/packages/renderer/src/template-transformer/html-template-component-template-transformer.ts @@ -1,7 +1,13 @@ import { compile, createVNode } from "vue"; import { TemplateTransformer } from "./template-transformer"; +import { LocaleService, TranslateService } from "@farris/command-services-vue"; +import { Injector, ViewModel, ViewModelState } from "@farris/devkit-vue"; +import { FormMetadataService } from "../service"; export class HtmlTemplateComponentTemplateTransformer extends TemplateTransformer { + constructor(protected formMetadataService: FormMetadataService, protected viewModel: ViewModel) { + super(formMetadataService, viewModel); + } public transform(schema: Record): void { const { id, html, type } = schema; if (type !== 'html-template' || !html) { @@ -13,8 +19,9 @@ export class HtmlTemplateComponentTemplateTransformer extends TemplateTransforme } const compiledTemplate = compile(html); const templateContext = this.buildTempalteContext(viewModel); + const translateService = this.viewModel.getInjector().get(TranslateService); const render = () => { - return createVNode({ render: compiledTemplate, props: ['viewModel'] }, { viewModel: templateContext }); + return createVNode({ render: compiledTemplate, props: ['viewModel', 't'] }, { viewModel: templateContext, t: translateService.transform.bind(translateService) }); }; schema.renderFunction = render; } diff --git a/packages/renderer/src/template-transformer/providers.ts b/packages/renderer/src/template-transformer/providers.ts index 501a58ada5bbe0ddf13e3e6312ee391ba9c8522a..989c0bc1dbc4e2cd55b974e399fa40c593b80842 100644 --- a/packages/renderer/src/template-transformer/providers.ts +++ b/packages/renderer/src/template-transformer/providers.ts @@ -1,4 +1,4 @@ -import { StaticProvider, ViewModel } from "@farris/devkit-vue"; +import { Injector, StaticProvider, ViewModel } from "@farris/devkit-vue"; import { TEMPLATE_TRANSFORMERS_TOKEN } from "./tokens"; import { DataGridColumnTemplateTransformer } from "./data-grid-column-template-transformer"; import { HtmlTemplateComponentTemplateTransformer } from "./html-template-component-template-transformer"; @@ -7,7 +7,7 @@ import { TemplateTransformerRegistry } from "./template-transformer-registry"; import { TemplateTransformService } from "./template-transform-service"; export const htmlTemplateTransformers: StaticProvider[] = [ - { provide: TEMPLATE_TRANSFORMERS_TOKEN, useClass: DataGridColumnTemplateTransformer, deps: [FormMetadataService, ViewModel], multi: true }, + { provide: TEMPLATE_TRANSFORMERS_TOKEN, useClass: DataGridColumnTemplateTransformer, deps: [FormMetadataService, ViewModel, Injector], multi: true }, { provide: TEMPLATE_TRANSFORMERS_TOKEN, useClass: HtmlTemplateComponentTemplateTransformer, deps: [FormMetadataService, ViewModel], multi: true }, { provide: TemplateTransformerRegistry, useClass: TemplateTransformerRegistry, deps: [TEMPLATE_TRANSFORMERS_TOKEN] }, { provide: TemplateTransformService, useClass: TemplateTransformService, deps: [TemplateTransformerRegistry] }, diff --git a/packages/renderer/src/tokens.ts b/packages/renderer/src/tokens.ts index c05e8064321500427a50a8b13f8c74536cb5a006..9701a02f66edff59be458dcf65b1fd896c8e3822 100644 --- a/packages/renderer/src/tokens.ts +++ b/packages/renderer/src/tokens.ts @@ -1,4 +1,4 @@ - import { InjectionToken } from "@farris/devkit-vue"; +import { InjectionToken } from "@farris/devkit-vue"; import { FormMetadata } from "./types"; import { ComponentConfigResolver } from "./component-config-resolver"; import { ComponentConfigDependencyResolver } from "./component-config-dependency-resolver"; @@ -7,6 +7,7 @@ import { ChangeEffectResolver } from "./change-effect-resolver"; import { Ref } from "vue"; import { CallbackHandler } from "./callback-handler"; +export const FORM_METADATA_ID_TOKEN = new InjectionToken('@farris/form_metadata_id'); export const FORM_METADATA_TOKEN = new InjectionToken('@farris/form_metadata'); export const EVENT_HANDLERS_TOKEN = new InjectionToken('@farris/event_handlers_token'); export const MODULE_CONFIG_ID_TOKEN = new InjectionToken('@farris/module_config_id_token'); diff --git a/packages/renderer/src/types.ts b/packages/renderer/src/types.ts index e6867a9e20a9af566ac44194027e4d90dfb74413..837c3ee8418020827128094d3a94abbed271fe96 100644 --- a/packages/renderer/src/types.ts +++ b/packages/renderer/src/types.ts @@ -249,6 +249,10 @@ export interface Metadata { id: string; content: any; refs: any[]; + extendProperty?: any; + code?: string; + type?: string; + namespace?: string; } export enum MetadataType { Component = 'component', diff --git a/packages/renderer/src/validator/expression-validator.ts b/packages/renderer/src/validator/expression-validator.ts index a48afc8c226a77d85839ca60e852d56d7620cfa5..9d5b4884757dbf014f78f0feee73af4a4a67a416 100644 --- a/packages/renderer/src/validator/expression-validator.ts +++ b/packages/renderer/src/validator/expression-validator.ts @@ -1,7 +1,9 @@ import { Entity, ExpressionBindingType, ExpressionEvaluator, ExpressionObject, ExpressionRegistry, ExpressionType, FormControlConfig, ValidationRule, FormValidator, ViewModel, ViewModelState } from "@farris/devkit-vue"; +import { FieldResolver } from "../resolvers"; +import { FormMetadataService } from "../service"; -export class ExpressionValidator implements FormValidator{ - constructor(private viewModel: ViewModel){} +export class ExpressionValidator implements FormValidator { + constructor(private viewModel: ViewModel, private formMetadataService: FormMetadataService) { } validate(entities: Entity[]): void { const expressions = this.getValidationExpressions(); if (!expressions?.length) { @@ -69,12 +71,15 @@ export class ExpressionValidator implements FormValidator{ return viewModel.formStore ? viewModel.formStore.getControlRules(controlName) : viewModel.formArrayStore?.getControlRules(id, controlName); } private buildValidationRules(result: boolean, validationRules: ValidationRule[], expressionObject: ExpressionObject, controlConfig?: FormControlConfig | null): ValidationRule[] { + const entitySchema = this.formMetadataService.getEntity(); + const resolvedField = FieldResolver.resolve(entitySchema, expressionObject.fieldId); if (expressionObject.type === ExpressionType.Required) { validationRules = validationRules.filter((rule) => rule.name !== 'required'); if (result === true) { validationRules?.splice(0, 0, { name: 'required', message: this.formatMessage(expressionObject.message, controlConfig?.displayName) || undefined, + multiLanguage: resolvedField?.multiLanguage }); } return validationRules; diff --git a/packages/renderer/src/validator/providers.ts b/packages/renderer/src/validator/providers.ts index 14a8f8358426ece5e83ab360a0a78a02e4221dc8..0cbe31061fdb4c46414d22c4f3ceae2dc28dee98 100644 --- a/packages/renderer/src/validator/providers.ts +++ b/packages/renderer/src/validator/providers.ts @@ -7,5 +7,5 @@ import { ExpressionValidator } from "./expression-validator"; export const validatorProviders: StaticProvider[] = [ { provide: RESPONSE_REQUIRED_VALIDATOR_TOKEN, useClass: ResponseRequiredValidator, deps: [ComponentConfigRegistry, ComponentConfigDependencyResolveService, FormMetadataService, ConfigResolver, ViewModel] }, - { provide: EXPRESSION_VALIDATOR_TOKEN, useClass: ExpressionValidator, deps: [ViewModel] }, + { provide: EXPRESSION_VALIDATOR_TOKEN, useClass: ExpressionValidator, deps: [ViewModel, FormMetadataService] }, ]; diff --git a/packages/renderer/src/validator/response-required-validator.ts b/packages/renderer/src/validator/response-required-validator.ts index f9c78c8f9f579d1aeeb942568aa3da9af5443710..95f80a41ae9ec0b96f73f8e86ccd916cf9da2bce 100644 --- a/packages/renderer/src/validator/response-required-validator.ts +++ b/packages/renderer/src/validator/response-required-validator.ts @@ -2,6 +2,8 @@ import { Entity, ViewModel, ViewModelState, FormValidator, FormControlConfig } f import { ComponentConfigRegistry, ConfigResolver, Configuration, ConfigurationType, DependencyKind } from "../config"; import { ComponentConfigDependencyResolveService } from "../component-config-dependency-resolver"; import { FormMetadataService } from "../service"; +import { LocaleService } from "@farris/command-services-vue"; +import { FieldResolver } from "../resolvers"; export class ResponseRequiredValidator implements FormValidator { constructor( @@ -69,7 +71,14 @@ export class ResponseRequiredValidator implements FormValidator { if (rules?.some(rule => rule.type === 'required')) { continue; } - + let isMultiLanguage: undefined | boolean = undefined; + if (config.fieldId) { + const resolvedField = FieldResolver.resolve(this.formMetadataService.getEntity(), config.fieldId); + if (resolvedField) { + const { multiLanguage } = resolvedField; + isMultiLanguage = multiLanguage; + } + } const newRules = rules?.filter(rule => rule.type !== 'required') || []; const controlConfig = viewModel.formArrayStore?.getConfigManager().getControlConfig(fieldId); if (!controlConfig) { @@ -77,7 +86,8 @@ export class ResponseRequiredValidator implements FormValidator { } newRules?.splice(0, 0, { name: 'required', - message: this.buildRequiredMessage(controlConfig) + message: this.buildRequiredMessage(controlConfig), + multiLanguage: isMultiLanguage }); viewModel.formArrayStore?.setControlRules( entity.idValue, @@ -88,7 +98,8 @@ export class ResponseRequiredValidator implements FormValidator { } } private buildRequiredMessage(controlConfig: FormControlConfig): string { - return `请输入'${controlConfig.displayName}'`; + const originalMessage = LocaleService.translate('required'); + return originalMessage.replace(/\$property/g, controlConfig.displayName); } private getEntitiesByPaths(entities: Entity[], paths: string[], parentPath: string = ''): Entity[] | null { if (!entities?.length) { diff --git a/packages/ui-binding/lib/compositions/use-data-grid-binding.ts b/packages/ui-binding/lib/compositions/use-data-grid-binding.ts index c7ed4dfe6405b83bc55af36fc7c991d974e9c153..020ae136df7b047fb0f5edfec7ad5473cd21ed72 100644 --- a/packages/ui-binding/lib/compositions/use-data-grid-binding.ts +++ b/packages/ui-binding/lib/compositions/use-data-grid-binding.ts @@ -3,6 +3,7 @@ import { EntityChange, EntityChangeType, Entity, EntitySchema, EntityFieldSchema import { BindingOptions, ElementRef, UseDataGridBinding } from '../types'; import { useElementRef } from './use-element-ref'; import { useEntityPath } from './use-entity-path'; +import { cloneDeep } from 'lodash-es'; export function useDataGridBinding(elementRef: ElementRef, options: BindingOptions): UseDataGridBinding { const componentRef = useElementRef(elementRef); @@ -36,7 +37,8 @@ export function useDataGridBinding(elementRef: ElementRef, options: BindingOptio return; } } - const datas: Record[] | undefined = viewModel.entityStore?.getEntityListByPath(options.entityPath).toJSON(); + const entityData: Record[] | undefined = viewModel.entityStore?.getEntityListByPath(options.entityPath).toJSON(); + const datas = cloneDeep(entityData); const entities = viewModel.entityStore?.getEntityListByPath(options.entityPath).getEntities(); data.value = datas; if (change.type === EntityChangeType.Load) { @@ -62,7 +64,8 @@ export function useDataGridBinding(elementRef: ElementRef, options: BindingOptio scrollToRowByIndex(index); } else if (change.type === EntityChangeType.Append) { const appendEntityChange = change as AppendEntityChange; - const entity = appendEntityChange.entities[0]; + const entities = appendEntityChange.entities; + const entity = entities[entities.length - 1]; const primaryValue = entity.idValue; componentRef.value.updateDataSource(datas); if (primaryValue && datas && datas.length > 0) { @@ -189,12 +192,31 @@ export function useDataGridBinding(elementRef: ElementRef, options: BindingOptio ids = Array.isArray(ids) ? ids : [ids]; viewModel.uiStore?.setValue('ids', currentIds.filter((item) => !ids.includes(item))); } + function getEntitySchema(entityPath?: EntityPath): EntitySchema | undefined { + if (!entityPath) { + return undefined; + } + if(entityPath.getNodes().length<1){ + return viewModel.entityStore?.getEntitySchema(); + } + const fieldSchema = viewModel.entityStore?.getEntitySchema().getFieldSchemaByPath(entityPath) as EntityFieldSchema; + return fieldSchema.entitySchema; + } function updateIdsState(id: string | undefined | null) { if (!id) { viewModel.uiStore?.setValue('ids', []); } else { - // 启用多选后不需要在此处理 + // 启用多选后需要同步 if (multiSelect) { + // 启用多选后使用表格记录的多选记录,还应考虑跨页多选场景,目前不支持 + const entitySchema = getEntitySchema(entityPaths); + if (!entitySchema) { + return; + } + const idKey = entitySchema.getIdKey(); + const items: any[] = componentRef.value.getSelectedItems() || []; + const ids = items.map(item => item[idKey]); + viewModel.uiStore?.setValue('ids', ids); return; } viewModel.uiStore?.setValue('ids', [id]); diff --git a/packages/ui-binding/lib/compositions/use-form-group-binding.ts b/packages/ui-binding/lib/compositions/use-form-group-binding.ts index 80ebc384f5156d5b1424bee5851f6e3e7b7a929a..fc2ba55a9dc85c047668b7bfc1a7fc059cf0486c 100644 --- a/packages/ui-binding/lib/compositions/use-form-group-binding.ts +++ b/packages/ui-binding/lib/compositions/use-form-group-binding.ts @@ -29,11 +29,12 @@ export function useFormGroupBinding(elementRef: ElementRef, options: any, viewSc if (!mappingFields || Object.keys(mappingFields).length < 1 || !idField) { return; } - const dataField = mappingFields[idField]; - if (!dataField) { + const idFields: string = mappingFields[idField]; + if (!idFields) { return; } - + const dataFields = idFields.split(',').filter((p)=>p); + const dataField = dataFields[0]; const entityPaths = toShortPath() || []; const paths = entityPaths.concat(dataField.split('.')); const dataFieldPath = '/' + paths.join('/'); diff --git a/packages/ui-vue/.gitignore b/packages/ui-vue/.gitignore index ca8018c48b20a44abef75a8ca13ce6f2aa0c518a..1e6904186b9062c797be849feb0b3666c9f2cd05 100644 --- a/packages/ui-vue/.gitignore +++ b/packages/ui-vue/.gitignore @@ -29,3 +29,4 @@ package # Vitepress */.vitepress/cache */.vitepress/dist +public/assets/i18n \ No newline at end of file diff --git a/packages/ui-vue/components/avatar/src/avatar.component.tsx b/packages/ui-vue/components/avatar/src/avatar.component.tsx index de22b70850b32fcb25d5f5cc8389b21f26a35cc1..9501e61924025c111b081862a2dc95c999b83819 100644 --- a/packages/ui-vue/components/avatar/src/avatar.component.tsx +++ b/packages/ui-vue/components/avatar/src/avatar.component.tsx @@ -16,12 +16,15 @@ import { defineComponent, computed, ref, SetupContext } from 'vue'; import { avatarProps, AvatarProps } from './avatar.props'; import { useImage } from './composition/use-image'; +import { useI18n } from 'vue-i18n'; + export default defineComponent({ name: 'FAvatar', props: avatarProps, emits: ['change', 'update:modelValue'] as (string[] & ThisType) | undefined, setup(props: AvatarProps, context: SetupContext) { + const { t } = useI18n(); const avatarClass = computed(() => ({ 'f-avatar': true, 'f-avatar-readonly': props.readonly, @@ -45,7 +48,7 @@ export default defineComponent({ return ''; } - function getfiledata() {} + function getfiledata() { } const file = ref(null); @@ -56,7 +59,7 @@ export default defineComponent({
{showLoading && (
-
加载中
+
{t('avatar.loading')}
)} diff --git a/packages/ui-vue/components/avatar/src/locales/designer/en.json b/packages/ui-vue/components/avatar/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..24fef8206de714cedd405714b005d720e95f6acd --- /dev/null +++ b/packages/ui-vue/components/avatar/src/locales/designer/en.json @@ -0,0 +1,3 @@ +{ + "avatar": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/avatar/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/avatar/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..24fef8206de714cedd405714b005d720e95f6acd --- /dev/null +++ b/packages/ui-vue/components/avatar/src/locales/designer/zh-CHS.json @@ -0,0 +1,3 @@ +{ + "avatar": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/avatar/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/avatar/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..24fef8206de714cedd405714b005d720e95f6acd --- /dev/null +++ b/packages/ui-vue/components/avatar/src/locales/designer/zh-CHT.json @@ -0,0 +1,3 @@ +{ + "avatar": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/avatar/src/locales/ui/en.json b/packages/ui-vue/components/avatar/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..721ae0aac6d84721fa70a4a85ad60bbf5860aaa7 --- /dev/null +++ b/packages/ui-vue/components/avatar/src/locales/ui/en.json @@ -0,0 +1,10 @@ +{ + "avatar": { + "imgtitle": "Amend", + "typeError": "Type error", + "sizeError": "Can not be larger than", + "uploadError": "Upload Fail!", + "loadError": "Load error.", + "loading": "Loading" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/avatar/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/avatar/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..bc71b8d1061dad9ff575ac5f95b5eac36ae69299 --- /dev/null +++ b/packages/ui-vue/components/avatar/src/locales/ui/zh-CHS.json @@ -0,0 +1,10 @@ +{ + "avatar": { + "imgtitle": "点击修改", + "typeError": "上传图片类型不正确", + "sizeError": "上传图片不能大于", + "uploadError": "图片上传失败,请重试!", + "loadError": "加载错误", + "loading": "加载中" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/avatar/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/avatar/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..43b40e47cb9f43eacd331146b588c00c43fe5eda --- /dev/null +++ b/packages/ui-vue/components/avatar/src/locales/ui/zh-CHT.json @@ -0,0 +1,10 @@ +{ + "avatar": { + "imgtitle": "點擊修改", + "typeError": "上傳圖片類型不正確", + "sizeError": "上傳圖片不能大於", + "uploadError": "圖片上傳失敗,請重試!", + "loadError": "加載錯誤", + "loading": "加載中" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/avatar/src/property-config/avatar.property-config.ts b/packages/ui-vue/components/avatar/src/property-config/avatar.property-config.ts index df1b4da7ba977d364ccee4fcdf791ba7d0091332..094681fcb859bde1de3fe88e5e09c3956aa2b396 100644 --- a/packages/ui-vue/components/avatar/src/property-config/avatar.property-config.ts +++ b/packages/ui-vue/components/avatar/src/property-config/avatar.property-config.ts @@ -12,6 +12,7 @@ export class AvatarProperty extends InputBaseProperty { "title": "编辑器", "type": "avatar", "$converter": "/converter/property-editor.converter", + "parentPropertyID": "editor", "properties": { "readonly": { "description": "", diff --git a/packages/ui-vue/components/binding-selector/src/binding-selector.component.tsx b/packages/ui-vue/components/binding-selector/src/binding-selector.component.tsx index cdeabe388a8ee3f515de358ceb36135cbfc98f42..898fd1bd04c28baf4235e66e8d623c82821d4f6b 100644 --- a/packages/ui-vue/components/binding-selector/src/binding-selector.component.tsx +++ b/packages/ui-vue/components/binding-selector/src/binding-selector.component.tsx @@ -81,9 +81,10 @@ export default defineComponent({ return false; } updateViewModel(bindingData.value[0], bindingType.value); + clearExpresssionConfig(bindingData.value[0]); const newBindingValue = updateComponentSchema(bindingData.value[0], bindingType.value); setDisplayText(newBindingValue); - clearExpresssionConfig(bindingData.value[0]); + // 触发变更 if (props.onFieldSelected && typeof props.onFieldSelected == 'function') { props.onFieldSelected(newBindingValue, undefined,bindingData.value[0]); diff --git a/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx b/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx index ef974ddd82030e82ce35115428de3c4e5ddd6262..01a6629f40e2b22fccda66a09cc8b3c4077b91db 100644 --- a/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx +++ b/packages/ui-vue/components/binding-selector/src/components/binding-selector-container.component.tsx @@ -10,7 +10,7 @@ import { FNotifyService } from "@farris/ui-vue/components/notify"; export default defineComponent({ name: 'FBindingSelectorContainer', props: bindingSelectorProps, - emits: ['selected', 'bindingTypeChange', 'cancel', 'submit'], + emits: ['selected', 'bindingTypeChange', 'cancel', 'submit', 'skip'], setup(props: BindingSelectorProps, context) { const dataSource = ref(props.data); @@ -78,6 +78,12 @@ export default defineComponent({
; } + function getRealFieldType(visualData: any) { + if (visualData.raw.multiLanguage) { + return 'multiLanguage'; + } + return bindingType.value === 'Form' ? visualData.raw.type?.name : visualData.raw.type; + } // 配置行禁用 const rowOption: Partial = { customRowStatus: (visualData: VisualData) => { @@ -91,7 +97,7 @@ export default defineComponent({ visualData.disabled = true; return visualData; } - const fieldTypeNode = bindingType.value === 'Form' ? visualData.raw.type?.name : visualData.raw.type; + const fieldTypeNode = getRealFieldType(visualData); // 1、由复杂类型字段切换成其他字段类型:新字段适配控件类型即可 // 2、原字段被删除或原本没有绑定信息:新字段适配控件类型即可 @@ -142,6 +148,9 @@ export default defineComponent({ } context.emit('submit', { selectedData: bindingData.value[0], bindingType: bindingType.value }); } + function onSkipBinding() { + context.emit('skip'); + } return () => { return (
@@ -151,6 +160,7 @@ export default defineComponent({ props.showCustomFooter ? : '' } diff --git a/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts b/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts index 54ed88eadc1843e0e51a5d45faeeb472c7844c5f..52e6ff7651fd35ace91aa3e8b71af303175badaa 100644 --- a/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts +++ b/packages/ui-vue/components/binding-selector/src/composition/use-field-selection.ts @@ -2,7 +2,7 @@ import { FNotifyService } from "../../../notify"; import { FormBindingType, SchemaDOMMapping } from "@farris/ui-vue/components/property-panel"; import { merge } from "lodash-es"; import { BindingSelectorProps } from "../binding-selector.props"; -import { FormSchemaEntityField$Type } from "@farris/ui-vue/components/common"; +import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from "@farris/ui-vue/components/common"; export function useFieldSelection(props: BindingSelectorProps) { @@ -55,6 +55,7 @@ export function useFieldSelection(props: BindingSelectorProps) { function updataViewModelField(bindingData: Record) { const currentSelectedField = merge({}, bindingData.rawData, { groupId: null, groupName: null }); const originalBinding = props.editorParams.componentSchema?.binding; + const originalLabel = props.editorParams.componentSchema?.label; const dgViewModel = designViewModelUtils.getDgViewModel(viewModelId); // 删除原始绑定信息 @@ -71,6 +72,9 @@ export function useFieldSelection(props: BindingSelectorProps) { if (props.editorParams.componentSchema?.editor?.type) { dgViewModel.changeField(currentSelectedField.id, { editor: currentSelectedField.editor }); } + if (originalLabel) { + dgViewModel.changeField(currentSelectedField.id, { name: originalLabel }); + } } function updataViewModelVariable(bindingData: Record) { @@ -126,31 +130,44 @@ export function useFieldSelection(props: BindingSelectorProps) { return; } const originalBinding = props.editorParams.componentSchema.binding; - if (originalBinding && originalBinding.field === bindingData.id) { + if (!originalBinding || originalBinding.field === bindingData.id) { return; } - if (formSchemaUtils.expressions && formSchemaUtils.expressions.length) { - const { propertyData } = props.editorParams; - const expFieldIndex = formSchemaUtils.expressions.findIndex(e => e.target === originalBinding.field); + if (formSchemaUtils.getExpressions().length) { + const { componentSchema } = props.editorParams; + const expFieldIndex = formSchemaUtils.getExpressions().findIndex(e => e.target === originalBinding.field); if (expFieldIndex > -1) { - formSchemaUtils.expressions.splice(expFieldIndex, 1); + formSchemaUtils.getExpressions().splice(expFieldIndex, 1); notifyService.warning('切换绑定后,请重新配置表达式'); // 切换绑定后涉及到了表达式的移除,需要触发属性面板的更新 - Object.keys(propertyData).forEach(propertyID => { - if (propertyData[propertyID] && propertyData[propertyID].type === 'Expression') { - propertyData[propertyID].expressionId = ''; + Object.keys(componentSchema).forEach(propertyID => { + if (componentSchema[propertyID] && componentSchema[propertyID].type === 'Expression') { + componentSchema[propertyID].expressionId = ''; + } + + if (componentSchema.editor) { + Object.keys(componentSchema.editor).forEach(editorPropertyID => { + if (componentSchema.editor[editorPropertyID] && componentSchema.editor[editorPropertyID].type === 'Expression') { + componentSchema.editor[editorPropertyID].expressionId = ''; + } + + }); } - // if (propertyData[propertyID] && propertyData[propertyID].type === 'FormRule') { - // // 还原为默认值 - // const schema = this.schemaService.getFieldByID(propertyData.binding.field); - // propertyData[propertyID] = this.domService.getDefaultValueByFiledAndType(propertyID, schema, propertyData, this.formBasicService.envType); - // } }); } } } + /** + * 同步控件的最大长度属性(场景:控件从空绑定至绑定字段后,需要带出字段的长度属性) + */ + function updateComponentMaxLength(bindingData: any) { + const hasMaxLength = [FormSchemaEntityFieldTypeName.Number, FormSchemaEntityFieldTypeName.String, FormSchemaEntityFieldTypeName.Text].includes(bindingData.type.name) && bindingData.type?.length; + if (hasMaxLength && props.editorParams.componentSchema.editor && !Object.prototype.hasOwnProperty.call(props.editorParams.componentSchema.editor, 'maxLength')) { + props.editorParams.componentSchema.editor.maxLength = bindingData.type.length; + } + } /** * 更新控件schema数据 */ @@ -186,6 +203,8 @@ export function useFieldSelection(props: BindingSelectorProps) { props.editorParams.componentSchema.binding.type = bindingType; props.editorParams.componentSchema.path = bindingData.bindingPath; + updateComponentMaxLength(bindingData); + return props.editorParams.componentSchema.binding; } @@ -254,7 +273,7 @@ export function useFieldSelection(props: BindingSelectorProps) { } // 追加多语控件 - // controlFieldMapping.LanguageTextBox = ['multiLanguage']; + controlFieldMapping['language-textbox'] = ['multiLanguage']; return controlFieldMapping[editorType]; } diff --git a/packages/ui-vue/components/button-edit/button-edit.scss b/packages/ui-vue/components/button-edit/button-edit.scss index ddd04c8c735dc28fdaa53146f511f215c5055a7e..0c4202aee64d4768a61e6a8011feb2d92b9dd1d6 100644 --- a/packages/ui-vue/components/button-edit/button-edit.scss +++ b/packages/ui-vue/components/button-edit/button-edit.scss @@ -1,7 +1,10 @@ .f-button-edit { + .farris-tags { + min-height: 24px; + } &:hover { .farris-tags{ - // height: auto; + height: auto; } .more-tags{ visibility: hidden; diff --git a/packages/ui-vue/components/button-edit/src/button-edit.component.tsx b/packages/ui-vue/components/button-edit/src/button-edit.component.tsx index 5c4fc26fbc60df6845eff3bb9ebfb14c20e8fc04..6af4bbd3c4054011ecd1ccb9c82a8e538e432184 100644 --- a/packages/ui-vue/components/button-edit/src/button-edit.component.tsx +++ b/packages/ui-vue/components/button-edit/src/button-edit.component.tsx @@ -43,7 +43,8 @@ export default defineComponent({ 'keydown', 'inputClick', 'input', - 'update:modelValue' + 'update:modelValue', + 'beforeClearValue' ] as (string[] & ThisType) | undefined, setup(props: ButtonEditProps, context: SetupContext) { const buttonEditRef = ref(); @@ -131,6 +132,7 @@ export default defineComponent({ const renderPopupContent = getPopupRender(props, context, popupComposition); const componentInstance = { + displayText, commitValue, elementRef: buttonEditRef, hidePopup, @@ -172,6 +174,7 @@ export default defineComponent({
+ { context.slots.precontent?.() } {renderEditor()} {renderButtonGroup()}
diff --git a/packages/ui-vue/components/button-edit/src/button-edit.props.ts b/packages/ui-vue/components/button-edit/src/button-edit.props.ts index 5c63aabe534ee37f62a8554ac93ae9e4224d9afd..ef9298d8a87ca4f1d468846be45bf3b2cc2ec6ad 100644 --- a/packages/ui-vue/components/button-edit/src/button-edit.props.ts +++ b/packages/ui-vue/components/button-edit/src/button-edit.props.ts @@ -30,7 +30,7 @@ export const buttonEditProps = { /** * 组件标识 */ - id: String, + id: { type: String, default: '' }, /** * 扩展按钮显示内容,这是一段现在扩展按钮中的html标签 */ @@ -92,7 +92,7 @@ export const buttonEditProps = { /** * 输入框最大长度 */ - maxLength: Number, + maxLength: {type: Number}, /** * 输入框Tab键索引 */ diff --git a/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx b/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx index 6bb562017360229f7f1752737ea10676f21ece70..cff05f34464ef15fc1580a8bd698eeacf3111da1 100644 --- a/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx +++ b/packages/ui-vue/components/button-edit/src/components/button-group.component.tsx @@ -1,6 +1,7 @@ import { ref, SetupContext } from "vue"; import { ButtonEditProps } from "../button-edit.props"; import { UseButton, UseClear } from "../composition/types"; +import { debounce } from "lodash-es"; export default function ( props: ButtonEditProps, @@ -12,24 +13,31 @@ export default function ( const { enableClearButton, showClearButton, onClearValue } = useClearComposition; const buttonHandleElement = ref(); + const onClickAppendButton = debounce(($event) => { + onClickButton($event); + }, props.buttonBehavior === 'Modal'? 0: 200); + + const onBeforeClearValue = () => { + context.emit('beforeClearValue'); + }; return { renderButtonGroup: () => { return (
{enableClearButton.value && ( - + )} { context.slots.buttonContent ? {context.slots.buttonContent()} : props.buttonContent ? onClickAppendButton($event)} onMouseenter={onMouseEnterButton} onMouseleave={onMouseLeaveButton} > : null }
diff --git a/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx b/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx index 97c879ab66062653ad07aafc67246fb0729bfcb1..ab4ecdb7432ddd1584613ffbc5a847edbf3b884d 100644 --- a/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx +++ b/packages/ui-vue/components/button-edit/src/components/text-edit.component.tsx @@ -21,8 +21,27 @@ export default function ( } }); + const isComposing = ref(false); + const compositionstart = (event) => { + event.preventDefault(); + isComposing.value = true; + }; + + const compositionend = (event) => { + event.preventDefault(); + isComposing.value = false; + onInput(event); + }; + + function onValueChanged($event) { + if (!isComposing.value) { + onInput($event); + } + } + return () => { return ; }; } diff --git a/packages/ui-vue/components/button-edit/src/composition/use-clear.ts b/packages/ui-vue/components/button-edit/src/composition/use-clear.ts index 90e9470c48a55cb67621f9d5374ae41f4c060f46..e9cfc159d16fdee81c2ab386fe62ae575f671645 100644 --- a/packages/ui-vue/components/button-edit/src/composition/use-clear.ts +++ b/packages/ui-vue/components/button-edit/src/composition/use-clear.ts @@ -21,7 +21,7 @@ export function useClear( props: ButtonEditProps, context: SetupContext, modelValue: Ref, - hasFocusedTextBox: ComputedRef, + hasFocusedTextBox: Ref, displayText: Ref, useTextBoxCompostion: UseTextBox ): UseClear { @@ -44,6 +44,7 @@ export function useClear( $event.stopPropagation(); if (flag1 || flag2) { changeTextBoxValue(''); + displayText.value = ''; toggleClearIcon(!showClearButton.value); context.emit('clear'); } @@ -66,6 +67,7 @@ export function useClear( if (!enableClearButton.value) { return; } + toggleClearIcon(false); } diff --git a/packages/ui-vue/components/button-edit/src/composition/use-popup.ts b/packages/ui-vue/components/button-edit/src/composition/use-popup.ts index 5b4b55456cd44234d08e6b9f77e08e4e2fa26fa1..65ac09838f726defa618ef86f0f6be0c4c26a6cf 100644 --- a/packages/ui-vue/components/button-edit/src/composition/use-popup.ts +++ b/packages/ui-vue/components/button-edit/src/composition/use-popup.ts @@ -26,8 +26,9 @@ export function usePopup( if (window[popoverInstancesListKey]) { const popoverInstances = window[popoverInstancesListKey]; document.querySelectorAll('.f-button-edit,.v-popover').forEach((element) => { - if (popoverInstances.get(element) && element !== buttonEditRef.value) { - popoverInstances.get(element).hide(); + const popoverInstance = popoverInstances.get(element); + if (popoverInstance && element !== buttonEditRef.value && !buttonEditRef.value.closest('.popover-fitcontent')) { + popoverInstance.hide(); } }); } diff --git a/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts b/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts index 91a54ed85e9ffdbc463b49650377c43b11c9482d..2cec3bc9af81865e7ee80ce8d6ba1da7f936949b 100644 --- a/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts +++ b/packages/ui-vue/components/button-edit/src/composition/use-text-box.ts @@ -96,7 +96,7 @@ export function useTextBox( } function onFocusTextBox($event: Event) { - if (!props.disable) { + if (!props.disable&&!props.readonly) { focusState.value = true; if (!isTextBoxReadonly.value) { context.emit('focus', $event); @@ -108,7 +108,7 @@ export function useTextBox( } function onInput($event: Event) { - context.emit('input', ($event.target as HTMLInputElement).value); + context.emit('input', $event); const newValue = ($event.target as HTMLInputElement).value; displayText.value = newValue; if (modelValue.value !== newValue) { diff --git a/packages/ui-vue/components/capsule/src/capsule-item.component.tsx b/packages/ui-vue/components/capsule/src/capsule-item.component.tsx index 6518cf9a1b58baed8d46573fde9cee18a8fd5e29..bfbcd45de93bff3c5c06f722806758b593e8eeb7 100644 --- a/packages/ui-vue/components/capsule/src/capsule-item.component.tsx +++ b/packages/ui-vue/components/capsule/src/capsule-item.component.tsx @@ -24,6 +24,7 @@ export default defineComponent({ }); onMounted(() => { + // eslint-disable-next-line vue/no-ref-as-operand context.emit('mounted', capsuleItemRef, props.value); }); diff --git a/packages/ui-vue/components/checkbox/src/checkbox.props.ts b/packages/ui-vue/components/checkbox/src/checkbox.props.ts index b17e78e6ab2f543fe015bc25bf3ba9b20c1f8a1e..3610db9b1cd2d3d59a5f90b97ee3878e73d88b5a 100644 --- a/packages/ui-vue/components/checkbox/src/checkbox.props.ts +++ b/packages/ui-vue/components/checkbox/src/checkbox.props.ts @@ -42,7 +42,9 @@ export const checkboxProps = { /** 标识是否被选中 */ checked: { type: Boolean, default: false }, /** 显示文本标签 */ - label: { type: String, default: '' } + label: { type: String, default: '' }, + trueValue: { type: [String, Number, Boolean], default: true }, + falseValue: { type: [String, Number, Boolean], default: false } } as Record; export type CheckboxProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts b/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts index b350862fee63c5243061de0c222142ec7f11c2f5..c766bd084a2de343805bf81a57306a8b1d79a226 100644 --- a/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts +++ b/packages/ui-vue/components/checkbox/src/property-config/checkbox.property-config.ts @@ -1,10 +1,12 @@ -import { InputBaseProperty } from "@farris/ui-vue/components/property-panel"; +import { FormSchemaEntityFieldTypeName } from "@farris/ui-vue/components/common"; +import { FormPropertyChangeObject, InputBaseProperty } from "@farris/ui-vue/components/property-panel"; export class CheckBoxProperty extends InputBaseProperty { constructor(componentId: string, designerHostService: any) { super(componentId, designerHostService); } + getEditorProperties(propertyData) { return this.getComponentConfig(propertyData, { "type": "check-box" },{ placeholder: { @@ -17,6 +19,24 @@ export class CheckBoxProperty extends InputBaseProperty { description: "", title: "名称", type: "string" + }, + trueValue: { + description: "选中的值", + title: "选中的值", + type: this.getBindingDataType(), + visible: this.designViewModelField?.type.name !== 'Boolean', + refreshPanelAfterChanged: true, + editor: this.getEditor(), + $converter: this.getBooleanValueConverter() + }, + falseValue: { + description: "未选中的值", + title: "未选中的值", + type: this.getBindingDataType(), + visible: this.designViewModelField?.type.name !== 'Boolean', + refreshPanelAfterChanged: true, + editor: this.getEditor(), + $converter: this.getBooleanValueConverter() } // name: { // description: "控件名称,提交时的唯一标识,可以为空但不能与其他控件重复", @@ -25,4 +45,34 @@ export class CheckBoxProperty extends InputBaseProperty { // } }); } + /** + * 切换绑定属性 + */ + public changeBindingField(propertyData: any, changeObject: FormPropertyChangeObject, parameters?: any): any { + super.changeBindingField(propertyData, changeObject); + + + // 同步trueValue和falseValue属性值(场景:控件从空绑定至绑定日期时间类型字段) + const newBindingField = parameters; + if (propertyData.editor && newBindingField?.type?.name) { + let defaultTrueValue; + let defaultFalseValue; + if (newBindingField.type.name === FormSchemaEntityFieldTypeName.String) { + defaultTrueValue = 'true'; + defaultFalseValue = 'false'; + } + if (newBindingField.type.name === FormSchemaEntityFieldTypeName.Number) { + defaultTrueValue = 1; + defaultFalseValue = 0; + } + if (!Object.prototype.hasOwnProperty.call(propertyData.editor, 'trueValue') && defaultTrueValue !== undefined) { + propertyData.editor.trueValue = defaultTrueValue; + } + if (!Object.prototype.hasOwnProperty.call(propertyData.editor, 'falseValue') && defaultFalseValue !== undefined) { + propertyData.editor.falseValue = defaultFalseValue; + } + + } + + } } diff --git a/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json b/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json index daee0e5d36fdc77ce2cbe89a5a387563fa2ff47c..1a3e4c5a51f2932c9da7f48cf3827c7358b27fa4 100644 --- a/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json +++ b/packages/ui-vue/components/checkbox/src/schema/checkbox.schema.json @@ -80,6 +80,16 @@ "description": "", "type": "string", "default": "" + }, + "trueValue": { + "description": "", + "type": "boolean", + "default": true + }, + "falseValue": { + "description": "", + "type": "boolean", + "default": false } }, "required": [ diff --git a/packages/ui-vue/components/code-editor/src/code-textbox.component.tsx b/packages/ui-vue/components/code-editor/src/code-textbox.component.tsx index a4c4011e73ace8e9503290e6d72f043702eb374a..ebb26d72de66238a4c1e0a02b632a70b34ac8c3d 100644 --- a/packages/ui-vue/components/code-editor/src/code-textbox.component.tsx +++ b/packages/ui-vue/components/code-editor/src/code-textbox.component.tsx @@ -74,8 +74,8 @@ export default defineComponent({ fitContent: false, showHeader: true, showCloseButton: true, - showMaxButton: false, - resizeable: false, + showMaxButton: true, + resizeable: true, draggable: true, buttons: [ { diff --git a/packages/ui-vue/components/collection-property-editor/index.ts b/packages/ui-vue/components/collection-property-editor/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..67f6dcef488ff3135440e5d531f3e8d85d08f010 --- /dev/null +++ b/packages/ui-vue/components/collection-property-editor/index.ts @@ -0,0 +1,29 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { App } from 'vue'; +import FCollectionPropertyEditor from './src/collection-property-editor.component'; +import { propsResolver } from './src/collection-property-editor.props'; + +export default { + install(app: App): void { + app.component(FCollectionPropertyEditor.name as string, FCollectionPropertyEditor); + }, + register(componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record): void { + componentMap['collection-property-editor'] = FCollectionPropertyEditor; + propsResolverMap['collection-property-editor'] = propsResolver; + } +}; diff --git a/packages/ui-vue/components/collection-property-editor/src/collection-property-editor.component.tsx b/packages/ui-vue/components/collection-property-editor/src/collection-property-editor.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..48f3339ee0b538b555f2b2a3730badf5a04defc7 --- /dev/null +++ b/packages/ui-vue/components/collection-property-editor/src/collection-property-editor.component.tsx @@ -0,0 +1,110 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { defineComponent, SetupContext, ref, computed, inject } from 'vue'; +import { FButtonEdit } from '@farris/ui-vue/components/button-edit'; +import { collectionPropertyEditorProps, CollectionPropertyEditorProps } from './collection-property-editor.props'; +import CollectionPropertyContainer from './components/collection-property-container.component'; +import { cloneDeep } from 'lodash-es'; + +export default defineComponent({ + name: 'FCollectionPropertyEditor', + props: collectionPropertyEditorProps, + emits: ['valueChange', 'selectionChange'] as (string[] & ThisType) | undefined, + setup(props: CollectionPropertyEditorProps, context: SetupContext) { + const collectionPropertyContainerRef = ref(); + const useFormCommand = inject('useFormCommand'); + const useFormSchema = inject('useFormSchema'); + + const propertyData = ref(cloneDeep(props.modelValue) || []); + /** 编辑器输入框的显示内容 */ + const displayText = computed(() => { + return `共 ${propertyData.value?.length || 0} 项`; + }); + + /** + * 确定事件 + */ + function onSubmit() { + context.emit('valueChange', propertyData.value); + return true; + } + + /** + * 取消事件 + */ + function onCancel() { + return true; + } + + /** + * 行选择事件 + */ + function onSelectionChange({ selectedData, propertyConfig }) { + context.emit('selectionChange', { selectedData, propertyConfig }); + } + + const modalOptions = { + title: props.modalTitle || '编辑器', + width: 650, + height: 700, + fitContent: false, + showMaxButton: true, + resizeable: true, + draggable: true, + enableEsc: true, + buttons: [ + { + name: 'cancel', + text: '取消', + class: 'btn btn-secondary', + handle: onCancel + }, + { + name: 'accept', + text: '确定', + class: 'btn btn-primary', + handle: onSubmit + } + ] + }; + + return () => { + return +
+ +
+
; + }; + + + } +}); diff --git a/packages/ui-vue/components/collection-property-editor/src/collection-property-editor.props.ts b/packages/ui-vue/components/collection-property-editor/src/collection-property-editor.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..e54fbac2f835c1dfdc257adbe9ba5988d0043df3 --- /dev/null +++ b/packages/ui-vue/components/collection-property-editor/src/collection-property-editor.props.ts @@ -0,0 +1,16 @@ +import { ExtractPropTypes } from "vue"; +import { createPropsResolver } from '../../dynamic-resolver'; +import schema from './schema/collection-property-editor.schema.json'; +import { collectionPropertyContainerProps } from "./components/collection-property-container.props"; + +export const collectionPropertyEditorProps = { + ...collectionPropertyContainerProps, + modalTitle: { + type: String, + default: '编辑器' + }, +} as Record; + + +export type CollectionPropertyEditorProps = ExtractPropTypes; +export const propsResolver = createPropsResolver(collectionPropertyEditorProps, schema); diff --git a/packages/ui-vue/components/collection-property-editor/src/components/collection-property-container.component.tsx b/packages/ui-vue/components/collection-property-editor/src/components/collection-property-container.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6208c9afa931c08f22883f7cd6f6731b642063fa --- /dev/null +++ b/packages/ui-vue/components/collection-property-editor/src/components/collection-property-container.component.tsx @@ -0,0 +1,208 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the 'License'); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an 'AS IS' BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { defineComponent, SetupContext, ref, computed, onMounted, provide } from 'vue'; +import { FPropertyPanel } from '@farris/ui-vue/components/property-panel'; +import { FTreeView } from '@farris/ui-vue/components/tree-view'; +import { DataColumn } from '@farris/ui-vue/components/data-view'; +import { cloneDeep } from 'lodash-es'; +import { CollectionPropertyContainerProps, collectionPropertyContainerProps } from './collection-property-container.props'; + +export default defineComponent({ + name: 'CollectionPropertyContainer', + props: collectionPropertyContainerProps, + emits: ['valueChange', 'selectionChange'] as (string[] & ThisType) | undefined, + setup(props: CollectionPropertyContainerProps, context: SetupContext) { + const treeViewRef = ref(); + const propertyPanelRef = ref(); + const { idField, textField, defaultComponentSchema, isToolbarHidden } = props; + + /** 属性数据 */ + const propertyData = ref(props.modelValue); + /** 属性配置 */ + const propertyConfig = ref(); + const propertyName = ref('collection-property-editor'); + provide('useFormCommand', props.useFormCommand); + provide('useFormSchema', props.useFormSchema); + + /** 当前选中的数据 */ + const selectedData = ref(); + /** 数据是否为空 */ + const isEmptyData = computed(() => { + return !propertyData.value || propertyData.value.length === 0; + }); + + const columns: DataColumn[] = [ + { field: textField, title: '名称', width: 340, resizable: true, dataType: 'string' }, + ]; + + /** + * 获取新的Schema + * @returns + */ + function getNewSchema() { + const newSchema = cloneDeep(defaultComponentSchema); + + const radomNumber = Math.random().toString().slice(2, 6); + newSchema[idField] = newSchema[idField] + '_' + radomNumber; + newSchema[textField] = newSchema[textField] + '_' + radomNumber; + + return newSchema; + } + + /** + * 新增节点 + * @returns + */ + function onAddItem() { + // 1、添加新节点 + const newSchema = getNewSchema(); + propertyData.value.push(newSchema); + treeViewRef.value.updateDataSource(propertyData.value); + + // 2、选中新节点 + treeViewRef.value.selectItemById(newSchema[idField]); + } + + /** + * 选中第一个节点 + */ + function selectFirstNode() { + const firstNode = propertyData.value && propertyData.value.length > 0 ? propertyData.value[0] : null; + if (!firstNode) { + return; + } + treeViewRef.value.selectItemById(firstNode[idField]); + } + + /** + * 删除节点 + * @returns + */ + function onRemoveItem() { + // 1、删除节点 + const indexToDelete = propertyData.value.indexOf(selectedData.value); + propertyData.value.splice(indexToDelete, 1); + treeViewRef.value.updateDataSource(propertyData.value); + + // 2、选中第一个节点 + selectFirstNode(); + } + + /** + * 下移节点 + * @returns + */ + function onMoveDownItem() { + if (!selectedData.value) { + return; + } + + const indexToMove = propertyData.value.indexOf(selectedData.value); + // 1、如果选择了最后一个节点,则不进行移动 + const shouldNotMove = indexToMove === propertyData.value.length - 1; + if (shouldNotMove) { + return; + } + + // 2、下移节点 + propertyData.value[indexToMove] = propertyData.value[indexToMove + 1]; + propertyData.value[indexToMove + 1] = selectedData.value; + treeViewRef.value.updateDataSource(propertyData.value); + } + + /** + * 上移节点 + * @returns + */ + function onMoveUpItem() { + if (!selectedData.value) { + return; + } + // 1、如果选择了第一个节点,则不进行操作 + const indexToMove = propertyData.value.indexOf(selectedData.value); + const shouldNotMove = indexToMove === 0; + if (shouldNotMove) { + return; + } + + // 2、上移节点 + propertyData.value[indexToMove] = propertyData.value[indexToMove - 1]; + propertyData.value[indexToMove - 1] = selectedData.value; + treeViewRef.value.updateDataSource(propertyData.value); + } + + /** + * 左侧树列表选中事件 + * @param selectedDatas + */ + function onSelectionChange(selectedDatas: any[]) { + selectedData.value = selectedDatas[0]; + context.emit('selectionChange', { selectedData, propertyConfig }); + + propertyPanelRef.value.updatePropertyConfig(propertyConfig.value, selectedData.value, true); + } + + /** + * 属性变更事件 + * @param propertyData + */ + function onPropertyChanged() { + treeViewRef.value.updateDataSource(propertyData.value); + + } + + onMounted(() => { + selectFirstNode(); + }); + + return () => { + return ( +
+ +
+
+ +
+ + +
+
+ + ); + }; + } +}); diff --git a/packages/ui-vue/components/collection-property-editor/src/components/collection-property-container.props.ts b/packages/ui-vue/components/collection-property-editor/src/components/collection-property-container.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..59a6e3c9dfe80e0347f86109c8016e06fdc6c6fa --- /dev/null +++ b/packages/ui-vue/components/collection-property-editor/src/components/collection-property-container.props.ts @@ -0,0 +1,35 @@ +import { ExtractPropTypes } from "vue"; + +export const collectionPropertyContainerProps = { + modelValue: { + type: Array, + default: [] + }, + idField: { + type: String, + default: 'id' + }, + textField: { + type: String, + default: '' + }, + defaultComponentSchema: { + type: Object, + default: {} + }, + isToolbarHidden: { + type: Boolean, + default: false + }, + useFormCommand: { + type: Object, + default: {} + }, + useFormSchema: { + type: Object, + default: {} + }, +} as Record; + + +export type CollectionPropertyContainerProps = ExtractPropTypes; diff --git a/packages/mobile-ui-vue/components/date-picker/src/schema/schema.json b/packages/ui-vue/components/collection-property-editor/src/schema/collection-property-editor.schema.json similarity index 43% rename from packages/mobile-ui-vue/components/date-picker/src/schema/schema.json rename to packages/ui-vue/components/collection-property-editor/src/schema/collection-property-editor.schema.json index 1674a23b089950c59763076cc2e5be2e9c0a282e..cc54c526e69af87989f5e8a0edbaa766932867ab 100644 --- a/packages/mobile-ui-vue/components/date-picker/src/schema/schema.json +++ b/packages/ui-vue/components/collection-property-editor/src/schema/collection-property-editor.schema.json @@ -1,18 +1,18 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://farris-design.gitee.io/date-picker.schema.json", - "title": "date-picker", - "description": "A Farris Input Component", + "$id": "https://farris-design.gitee.io/collection-property-editor.schema.json", + "title": "collection-property-editor", + "description": "A Farris Component", "type": "object", "properties": { "id": { - "description": "The unique identifier for a Date Picker", + "description": "The unique identifier for collection-property-editor", "type": "string" }, "type": { - "description": "The type string of Date Picker component", + "description": "The type string of collection-property-editor", "type": "string", - "default": "date-picker" + "default": "collection-property-editor" }, "appearance": { "description": "", @@ -27,52 +27,31 @@ }, "default": {} }, - "binding": { - "description": "", - "type": "object", - "default": {} + "modelValue": { + "type": "array", + "default": [] }, - "title": { - "description": "", + "idField": { "type": "string", - "default": "" - }, - "label": { - "description": "", - "type": "string", - "default": "" + "default": "id" }, - "lableWidth": { - "description": "", - "type": "number" - }, - "placeholder": { - "description": "", + "textField": { "type": "string", "default": "" }, - "visible": { - "description": "", - "type": "boolean", - "default": true - }, - "displayFormat": { - "description": "", - "type": "string", - "default": "YYYY-MM-DD" + "defaultComponentSchema": { + "type": "object", + "default": {} }, - "readonly": { + "modalTitle": { "type": "string", - "default": false + "default": "编辑器" } }, "required": [ "type" ], "ignore": [ - "id", - "appearance", - "binding", - "visible" + "id" ] } \ No newline at end of file diff --git a/packages/ui-vue/components/collection-property-editor/src/schema/schema-mapper.ts b/packages/ui-vue/components/collection-property-editor/src/schema/schema-mapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..97964aee23bbb8b523c7692723ea02db00d4f4c0 --- /dev/null +++ b/packages/ui-vue/components/collection-property-editor/src/schema/schema-mapper.ts @@ -0,0 +1,5 @@ +import { MapperFunction, resolveAppearance } from '../../../dynamic-resolver'; + +export const schemaMapper = new Map([ + ['appearance', resolveAppearance] +]); diff --git a/packages/ui-vue/components/collection-property-editor/src/schema/schema-resolver.ts b/packages/ui-vue/components/collection-property-editor/src/schema/schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..b02bdf93eec9060948f579c53aa81e3963a7d706 --- /dev/null +++ b/packages/ui-vue/components/collection-property-editor/src/schema/schema-resolver.ts @@ -0,0 +1,5 @@ +import { DynamicResolver } from "../../../dynamic-resolver"; + +export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record): Record { + return schema; +} diff --git a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx index c15eff3082e8a9ed143fa5134d671419747b12cc..c7971a8aac661fa380e4867d2af93a8c410ebed4 100644 --- a/packages/ui-vue/components/combo-list/src/combo-list.component.tsx +++ b/packages/ui-vue/components/combo-list/src/combo-list.component.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ import { computed, defineComponent, nextTick, Ref, ref, SetupContext, watch } from 'vue'; +import { useI18n } from 'vue-i18n'; import FButtonEdit from '@farris/ui-vue/components/button-edit'; import { comboListProps, ComboListProps, Option } from './combo-list.props'; import ComboListContainer from './components/list-container.component'; @@ -24,6 +25,7 @@ export default defineComponent({ props: comboListProps, emits: ['clear', 'update:modelValue', 'change', 'input'] as (string[] & ThisType) | undefined, setup(props: ComboListProps, context: SetupContext) { + const { t: getLocaleValue } = useI18n(); const comboListContainerRef = ref(); const comboEditorRef: Ref = ref(); // 只读状态下应该也是禁用,不触发任何事件,如果是只读状态,还会触发click等事件 @@ -34,6 +36,11 @@ export default defineComponent({ let displayTextStore = ''; const { dataSource, displayText, editable, modelValue, getSelectedItemsByDisplayText, getItemsByDisplayText } = useDataSource(props); + const dropdownIcon = ref(props.dropDownIcon); + if (dropdownIcon.value === '') { + dropdownIcon.value = ''; + } + const isMultiSelect = computed(() => props.multiSelect); const comboEditorWidth = computed(() => { @@ -157,8 +164,8 @@ export default defineComponent({ readonly={readonly.value} forcePlaceholder={props.forcePlaceholder} editable={editable.value} - buttonContent={props.dropDownIcon} - placeholder={props.placeholder} + buttonContent={dropdownIcon.value} + placeholder={props.placeholder === '请选择' ? getLocaleValue('comboList.placeholder') : props.placeholder} enableClear={enableClear.value} maxLength={props.maxLength} tabIndex={props.tabIndex} diff --git a/packages/ui-vue/components/combo-list/src/composition/use-data-source.ts b/packages/ui-vue/components/combo-list/src/composition/use-data-source.ts index 72c20b1a994bde4092cffb7fee8f8f9474c29c32..af15caa15be38a5118076fa7dbd4b5c19fca3327 100644 --- a/packages/ui-vue/components/combo-list/src/composition/use-data-source.ts +++ b/packages/ui-vue/components/combo-list/src/composition/use-data-source.ts @@ -1,8 +1,10 @@ import { ref, watch } from "vue"; +import { useI18n } from 'vue-i18n'; import { ComboListProps, Option } from "../combo-list.props"; import { UseDataSource } from "./types"; export function useDataSource(props: ComboListProps): UseDataSource { + const { t: getLocaleValue } = useI18n(); const displayText = ref(''); const modelValue = ref(props.modelValue); const dataSource = ref(props.data || []); @@ -67,7 +69,7 @@ export function useDataSource(props: ComboListProps): UseDataSource { return isFromJson ? response.text() : response.json(); } if(response.status === 405) { - throw new Error('请求方法类型不正确'); + throw new Error(getLocaleValue('comboList.remoteError')); } const error = new Error(response.statusText); throw error; diff --git a/packages/ui-vue/components/combo-list/src/locales/designer/en.json b/packages/ui-vue/components/combo-list/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/combo-list/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/combo-list/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/combo-list/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/combo-list/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/combo-list/src/locales/ui/en.json b/packages/ui-vue/components/combo-list/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..22c7846db3ac992b4474e1ed1b7c81f22ec4fe32 --- /dev/null +++ b/packages/ui-vue/components/combo-list/src/locales/ui/en.json @@ -0,0 +1,6 @@ +{ + "comboList": { + "remoteError": "The request method type is incorrect", + "placeholder": "Please select" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/combo-list/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..3a6cc6f1aa0eafa8d474d64f4306544581c29c60 --- /dev/null +++ b/packages/ui-vue/components/combo-list/src/locales/ui/zh-CHS.json @@ -0,0 +1,6 @@ +{ + "comboList": { + "remoteError": "请求方法类型不正确", + "placeholder": "请选择" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/combo-list/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..eef80d488e23e997c391dec71cdb0953bff7b2a8 --- /dev/null +++ b/packages/ui-vue/components/combo-list/src/locales/ui/zh-CHT.json @@ -0,0 +1,6 @@ +{ + "comboList": { + "remoteError": "請求方法類型不正確", + "placeholder": "請選擇" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/combo-list/src/property-config/combo-list.property-config.ts b/packages/ui-vue/components/combo-list/src/property-config/combo-list.property-config.ts index b4f0647f79db63e44747850ca187b60ba2a92c02..0f926b00a14ca0bf1b90d83a410e69e6b177ab7f 100644 --- a/packages/ui-vue/components/combo-list/src/property-config/combo-list.property-config.ts +++ b/packages/ui-vue/components/combo-list/src/property-config/combo-list.property-config.ts @@ -94,7 +94,7 @@ export class ComboListProperty extends InputBaseProperty { multiSelect: { description: "", title: "启用多选", - visible: fieldType === 'StringType', + visible: !fieldType || fieldType === 'StringType', type: "boolean", refreshPanelAfterChanged: true, }, diff --git a/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx b/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx index 4881cf5a634c0d88be67a75cf6b3e1b16f0dd433..803d5c8b372dd0f22bd55b193cfff2fbb8b9437b 100644 --- a/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx +++ b/packages/ui-vue/components/combo-tree/src/combo-tree.component.tsx @@ -3,18 +3,24 @@ import FButtonEdit from '@farris/ui-vue/components/button-edit'; import { ComboTreeProps, comboTreeProps, Option } from "./combo-tree.props"; import ComboTreeContainer from './components/tree-container.component'; import { useDataSource } from "./composition/use-data-source"; -import { cloneDeep } from "lodash-es"; +import { cloneDeep, debounce } from "lodash-es"; +import { useComboTreeSearch } from "./composition/use-search"; export default defineComponent({ name: 'FComboTree', props: comboTreeProps, - emits: ['clear', 'update:modelValue', 'change'], + emits: ['clear', 'update:modelValue', 'change', 'search'], setup(props: ComboTreeProps, context: SetupContext) { const comboEditorRef: Ref = ref(); const disable = ref(props.disabled); const enableClear = ref(props.enableClear); const enableSearch = ref(props.enableSearch); const readonly = ref(props.readonly); + const searchFields = ref(props.searchFields || [props.textField]); + const originalValue = ref(); + const comboTreeRef = ref(); + + const { dataSource, displayText, editable, modelValue, getSelectedItemsByDisplayText } = useDataSource(props); const isMultiSelect = computed(() => props.multiSelect); @@ -23,6 +29,16 @@ export default defineComponent({ return comboEditorRef.value ? (comboEditorRef.value.elementRef as HTMLElement).getBoundingClientRect().width : 0; }); + const showPopover = computed(() => { + const popoverInstance = comboEditorRef.value?.popoverRef; + if (popoverInstance) { + return popoverInstance.shown; + } + return false; + }); + + const { onValueChange, resetDataSource } = useComboTreeSearch({ comboEditorRef, dataSource, searchFields, originalValue, showPopover }); + function tryHidePopupOnSelect() { const shouldHidePopupOnSelect = !isMultiSelect.value; if (shouldHidePopupOnSelect && comboEditorRef.value) { @@ -31,7 +47,7 @@ export default defineComponent({ } function onSelectionChange(selectedItems: Option[]) { - displayText.value = selectedItems.map((item: Option) => item[props.textField]).join(props.separator); + displayText.value = !props.displayFormatter ? selectedItems.map((item: Option) => item[props.textField]).join(props.separator) : props.displayFormatter(selectedItems); modelValue.value = selectedItems.map((item: Option) => item[props.valueField]).join(props.separator); context.emit('update:modelValue', modelValue.value); context.emit('change', selectedItems, modelValue.value); @@ -40,7 +56,11 @@ export default defineComponent({ function onClear($event: Event) { modelValue.value = ''; + if (showPopover.value) { + comboEditorRef.value?.hidePopup(); + } context.emit('update:modelValue', ''); + context.emit('change', null, modelValue.value); context.emit('clear'); } @@ -60,20 +80,10 @@ export default defineComponent({ } ); - const originalValue = ref(); - const onBeforeOpen = () => { originalValue.value = cloneDeep(dataSource.value); }; - const showPopover = computed(() => { - const popoverInstance = comboEditorRef.value?.popoverRef; - if (popoverInstance) { - return popoverInstance.shown; - } - return false; - }); - return () => { return {showPopover.value && } ; }; diff --git a/packages/ui-vue/components/combo-tree/src/combo-tree.props.ts b/packages/ui-vue/components/combo-tree/src/combo-tree.props.ts index 97937e4e799d53433691037e8f36471f6c0b7a95..e183618316a94bfc758e5543e05090baecb89343 100644 --- a/packages/ui-vue/components/combo-tree/src/combo-tree.props.ts +++ b/packages/ui-vue/components/combo-tree/src/combo-tree.props.ts @@ -210,11 +210,17 @@ export const comboTreeProps = { * 树表展示格式化函数 */ formatter: { type: Function, default: null }, + /** + * 显示文本格式化函数 + */ + displayFormatter: { type: Function, default: null }, editorParams: { type: Object }, repositoryToken: { type: Symbol, default: null }, /** 自定义行状态 */ customRowStatus: { type: Object, default: null }, - minPanelWidth: { type: Number, default: 160 } + minPanelWidth: { type: Number, default: 160 }, + /** 查询字段集合 */ + searchFields: { type: Array, default: ['name'] } } as Record; export type ComboTreeProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx b/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx index 5daf4d1b454a8550f59a2d52f17edcf7adc3245b..6a0325bc057660e7130ad7d2727c1399616ae77d 100644 --- a/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx +++ b/packages/ui-vue/components/combo-tree/src/components/tree-container.component.tsx @@ -1,5 +1,6 @@ -import { SetupContext, computed, defineComponent, inject, onMounted, ref } from "vue"; +import { SetupContext, computed, defineComponent, inject, onMounted, ref, watch } from "vue"; import FTreeView from '@farris/ui-vue/components/tree-view'; +import { FInputGroup } from "@farris/ui-vue/components/input-group"; import { ComboTreeHttpService } from "../combo-tree.props"; import { TreeContainerProps, treeContainerProps } from "./tree-container.props"; @@ -20,6 +21,19 @@ export default defineComponent({ customRowStatus: props.customRowStatus }; + watch(() => props.data, (newValue) => { + dataSource.value = newValue; + treeViewRef.value?.updateDataSource(newValue); + }); + + + const selectOptions = { + enableSelectRow: true, + multiSelect: props.multiSelect, + showCheckbox: props.multiSelect, + multiSelectMode: 'OnCheckAndClick' + }; + let repository: any = null; if (props.repositoryToken) { repository = inject(props.repositoryToken); @@ -47,12 +61,17 @@ export default defineComponent({ styleObject.maxHeight = `${maxHeight.value}px`; styleObject.overflow = 'auto'; } + + if (dataSource.value.length === 0) { + styleObject.height = '200px'; + } + styleObject.position = 'relative'; return styleObject; }); - function onSelectionChange(seletedItems: any[]) { - selections.value = seletedItems.map((item: any) => Object.assign({}, item)); - selectionValues.value = seletedItems.map((item: any) => item[props.idField]); + function onSelectionChange(items: any[]) { + selections.value = [...selections.value, ...items.map((item: any) => Object.assign({}, item))]; + selectionValues.value = selections.value.map((item: any) => item[props.idField]); context.emit('selectionChange', selections.value); } @@ -68,9 +87,20 @@ export default defineComponent({ } }); + context.expose({ treeInstance: treeViewRef }); + return () => { return (
+ {props.multiSelect && props.enableSearch &&
$event.stopPropagation()}> + '} + enableClear={true} + placeholder={'请输入搜索内容'} + onClear={() => context.emit('clearSearch')} + onInput={(value, payload) => props.searchHandler(payload)} + > +
}
diff --git a/packages/ui-vue/components/combo-tree/src/components/tree-container.props.ts b/packages/ui-vue/components/combo-tree/src/components/tree-container.props.ts index 86ac6901b8c47bb51d6020a45bd337d5cfbbc6a6..5a5d9e50f209676f40e415195ece38515de7a37e 100644 --- a/packages/ui-vue/components/combo-tree/src/components/tree-container.props.ts +++ b/packages/ui-vue/components/combo-tree/src/components/tree-container.props.ts @@ -29,6 +29,7 @@ export const treeContainerProps = { maxHeight: {type: Number, default: 350}, repositoryToken: { type: Symbol, default: null }, editorParams: { type: Object}, - customRowStatus: { type: Object, default: null } + customRowStatus: { type: Object, default: null }, + searchHandler: {type: Function, default: () => {} } }; export type TreeContainerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/combo-tree/src/composition/use-data-source.ts b/packages/ui-vue/components/combo-tree/src/composition/use-data-source.ts index 17e0037284dae57956445d6e9f16bd62e1986c3b..4627f49d5012bf81c8f637a91ee1be72b2495d68 100644 --- a/packages/ui-vue/components/combo-tree/src/composition/use-data-source.ts +++ b/packages/ui-vue/components/combo-tree/src/composition/use-data-source.ts @@ -35,7 +35,8 @@ export function useDataSource(props: ComboTreeProps): UseDataSource { } function updateDisplayTextByValue(value: string) { - const matchedValue = getItemsByValue(value).map((item: Option) => item[props.textField]).join(props.separator); + const selectedItems = getItemsByValue(value); + const matchedValue = props.displayFormatter? props.displayFormatter(selectedItems): selectedItems.map((item: Option) => item[props.textField]).join(props.separator); displayText.value = editable.value ? (matchedValue || value) : matchedValue; } diff --git a/packages/ui-vue/components/combo-tree/src/composition/use-search.ts b/packages/ui-vue/components/combo-tree/src/composition/use-search.ts new file mode 100644 index 0000000000000000000000000000000000000000..29f267721d1a494b49e5c68821b116e8c232612b --- /dev/null +++ b/packages/ui-vue/components/combo-tree/src/composition/use-search.ts @@ -0,0 +1,77 @@ +import { cloneDeep, debounce } from "lodash-es"; +import { Ref } from "vue"; + +export function useComboTreeSearch(options: { comboEditorRef: any, dataSource: any, searchFields: Ref, originalValue: any, showPopover: any }) { + + const { comboEditorRef, dataSource, searchFields, originalValue, showPopover } = options; + + function searchTree(node: any, keyword: string): any { + // 先处理子节点(深度优先) + const filteredChildren = (node.children || []) + .map(child => searchTree(child, keyword)) + .filter(child => child !== null) as any[]; + + // 判断当前节点是否匹配 + const isMatch = searchFields.value.some((field: string) => { + return node.data[field]?.toString().toLowerCase().includes(keyword.toLowerCase()); + }); + + // 构造返回条件 + if (isMatch) { + // 当前节点匹配时,保留完整子结构 + return { + data: { ...node.data }, + children: node.children // 注意这里保留原始子节点 + }; + } else if (filteredChildren.length > 0) { + // 子节点有匹配时,返回过滤后的子结构 + return { + data: { ...node.data }, + children: filteredChildren + }; + } + + return null; + } + + function resetDataSource() { + originalValue.value = cloneDeep(dataSource.value); + } + + function searchDataSource(searchValue: string) { + if (!showPopover.value) { + comboEditorRef.value?.showPopup(); + } + + if (!searchValue) { + resetDataSource(); + return; + } + + const searchResult = dataSource.value.map(root => searchTree(root, searchValue)) + .filter(node => node !== null) as any[]; + + originalValue.value = cloneDeep(searchResult); + } + + const handleChangeDebounce = debounce(($event) => { + const searchText = ($event.target as HTMLInputElement)?.value; + searchDataSource(searchText); + }, 200); + + function onValueChange($event: any) { + let searchText = ($event.target as HTMLInputElement)?.value; + if (searchText !== '') { + searchText = searchText.trim(); + } + + if ($event.target['_value'] !== searchText) { + handleChangeDebounce($event); + } + } + + return { + onValueChange, + resetDataSource + }; +} diff --git a/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json b/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json index a98ff0ea73afe28fb9ccbd6fddbccb9d6c581a78..b83f6ad2fdb67db98b9d3113170992c4a163f5f0 100644 --- a/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json +++ b/packages/ui-vue/components/combo-tree/src/schema/combo-tree.schema.json @@ -132,6 +132,30 @@ "description": "", "type": "string", "default": "id" + }, + "multiSelect": { + "description": "", + "type": "boolean", + "default": false + }, + "viewType": { + "description": "", + "type": "string", + "default": "text" + }, + "searchFields": { + "description": "", + "type": "array", + "default": [] + }, + "enableSearch": { + "description": "", + "type": "boolean", + "default": false + }, + "displayFormatter": { + "type": "object", + "default": null } }, "required": [ diff --git a/packages/ui-vue/components/common/directive/area-response.ts b/packages/ui-vue/components/common/directive/area-response.ts index 391acdf1ca985a2568a3d3001878273728511dc2..3bc23444e0cda0a65e18fb2209d7d654f741f457 100644 --- a/packages/ui-vue/components/common/directive/area-response.ts +++ b/packages/ui-vue/components/common/directive/area-response.ts @@ -1,11 +1,11 @@ import { addClass, removeClass } from "../utils/use-class"; const breakPoints = [ - { size: 'sm', width: 576 }, - { size: 'md', width: 768 }, - // { size: 'lg', width: 888 }, 输入控件有此处断点,但没有此处样式 - { size: 'xl', width: 1200 }, - { size: 'el', width: 1690 } + { size: 'sm', width: 576, pattern: /\b(col-|col-sm-)\d+\b/ }, + { size: 'md', width: 768, pattern: /\bcol-md-\d+\b/ }, + { size: 'lg', width: 888, pattern: /\bcol-lg-\d+\b/ }, + { size: 'xl', width: 1200, pattern: /\bcol-xl-\d+\b/ }, + { size: 'el', width: 1690, pattern: /\bcol-el-\d+\b/ } ]; /** @@ -32,9 +32,10 @@ function afterWidthChange(sharedObject, bindElement, newWidth = 0) { } const areaWidth = newWidth ? newWidth : bindElement.getBoundingClientRect().width; const width = parseFloat(areaWidth); - for (let k = 0; k < breakPoints.length; k++) { - if (breakPoints[k]['width'] <= width) { - result.push(breakPoints[k]['size']); + const currentBreakPoints = sharedObject.breakPoints; + for (let k = 0; k < currentBreakPoints.length; k++) { + if (currentBreakPoints[k]['width'] <= width) { + result.push(currentBreakPoints[k]['size']); } } if (sharedObject.className.join(',') !== result.join(',')) { @@ -93,15 +94,67 @@ function supportResponse(sharedObject, bindElement) { } bindObserver(sharedObject, bindElement); } -function updateSharedObject(sharedObject, binding) { + +/** + * 简化计算 + * @param parentElement + * @returns + */ +function hasColClassChildren(parentElement) { + const colClassNameList = [] as any; + breakPoints.map((item) => { + const colClassName = 'col-' + item.size + '-'; + const foundList = parentElement.querySelectorAll('[class*="' + colClassName + '"]'); + if (foundList.length > 0) { + colClassNameList.push({size:item.size,width:item.width}); + } else if (item.size === 'sm') { + const foundItems = parentElement.querySelectorAll('[class*="col-"]'); + const foundSMItem=Array.from(foundItems).some((foundItem) => { + return (foundItem as HTMLElement).className.match(item.pattern); + }); + if(foundSMItem){ + colClassNameList.push({size:item.size,width:item.width}); + } + } + }); + return colClassNameList; +} +function updateSharedObject(sharedObject, binding, bindElement) { if (binding.value && Object.prototype.hasOwnProperty.call(binding.value, 'enable')) { sharedObject.enable = binding.value.enable; } if (binding.value && Object.prototype.hasOwnProperty.call(binding.value, 'autoWidth')) { sharedObject.autoWidth = binding.value.autoWidth; } + sharedObject.breakPoints = hasColClassChildren(bindElement); } +/** + * 整体筛查 + * @param parentElement + * @returns + */ +function hasColNumChildren(parentElement) { + const foundList = parentElement.querySelectorAll('[class*="col-"]'); + const patterns = [] as any; + breakPoints.map((item) => { + patterns.push(Object.assign({}, item, { found: false })); + }); + Array.from(foundList).some((foundItem) => { + const notMatched = patterns.filter(item => !item.found); + notMatched.map((item) => { + if ((foundItem as HTMLElement).className.match(item.pattern)) { + item.found = true; + } + }); + return !notMatched.length + }); + const result = [] as any; + patterns.filter(item => !item.found).map((item) => { + result.push({ size: item.size, width: item.width }); + }); + return result; +} /** * enable:启用 * autoWidth:自动宽度 @@ -116,16 +169,17 @@ const areaResponseDirective = { enable: true, autoWidth: true, threshold: 10, - width: 0 + width: 0, + breakPoints: [] }; - updateSharedObject(vnode.sharedObject, binding); + updateSharedObject(vnode.sharedObject, binding, bindElement); supportResponse(vnode.sharedObject, bindElement); }, // 在绑定元素的父组件 // 及他自己的所有子节点都更新后调用 updated: function (bindElement, binding, vnode, prevVnode) { vnode.sharedObject = prevVnode.sharedObject; - updateSharedObject(vnode.sharedObject, binding); + updateSharedObject(vnode.sharedObject, binding, bindElement); supportResponse(vnode.sharedObject, bindElement); }, // 绑定元素的父组件卸载前调用 diff --git a/packages/ui-vue/components/common/index.ts b/packages/ui-vue/components/common/index.ts index 41507acfefcb127896d9296914c7b7d20abb625e..6cd41b53881ad0704e466f003ff148cc4e60c11f 100644 --- a/packages/ui-vue/components/common/index.ts +++ b/packages/ui-vue/components/common/index.ts @@ -24,6 +24,7 @@ export * from './utils/use-common-utils'; export * from './utils/use-delayed-ref'; export { default as areaResponseDirective } from './directive/area-response'; export * from './utils/type'; +export * from './utils/exclude-properties'; export default { install(app: App): void { diff --git a/packages/ui-vue/components/common/radio-checkbox/use-check.ts b/packages/ui-vue/components/common/radio-checkbox/use-check.ts index ab16d3249fdd60e3caafa55fac305154bd1c8dfc..0b7542813e81d675f5c9c1728f014f00c9cf762e 100644 --- a/packages/ui-vue/components/common/radio-checkbox/use-check.ts +++ b/packages/ui-vue/components/common/radio-checkbox/use-check.ts @@ -19,10 +19,13 @@ export function useCheck( // 展示原生单选框或者复选框 const shouldRenderNative = computed(() => parentProps?.type === 'default' || isUndefined(parentProps?.type)); + const modelValue = ref(props.modelValue); + // 如果是group const checked = computed(() => parentProps ? parentProps.modelValue === props.value || parentProps.modelValue.includes(props.value) : - !!props.checked || !!props.modelValue); + (props.trueValue != null ? props.modelValue === props.trueValue: !!props.modelValue) || !!props.checked + ); // 按钮样式 const buttonClass = computed(() => { @@ -35,7 +38,6 @@ export function useCheck( }); - const modelValue = ref(props.modelValue); const indeterminate = ref(props.indeterminate); watch(() => props.modelValue, (newValue: boolean) => { modelValue.value = newValue; @@ -85,8 +87,8 @@ export function useCheck( } } else { context.emit('update:checked', !checked.value); - context.emit('update:modelValue', !checked.value); - context.emit('changeValue', !checked.value); + context.emit('update:modelValue', !checked.value? props.trueValue: props.falseValu); + context.emit('changeValue', !checked.value? props.trueValue : props.falseValue); context.emit('change', { originalEvent: e, checked: !checked.value }); } }; diff --git a/packages/ui-vue/components/common/text-box/composition/use-text-box.ts b/packages/ui-vue/components/common/text-box/composition/use-text-box.ts index b7b2546f922abce4a4c6d1c423c4517070219a79..520e0156bf060cd0b663da2567ecc12fbb21cf28 100644 --- a/packages/ui-vue/components/common/text-box/composition/use-text-box.ts +++ b/packages/ui-vue/components/common/text-box/composition/use-text-box.ts @@ -31,9 +31,9 @@ export function useTextBox( const updateOn = ref(props.updateOn); const hasClearClass = ref(false); - const canFocus = computed(() => props.editable || !props.readonly); + const canFocus = computed(() => props.editable || !props.readonly && !props.disabled); const editable = computed(() => props.editable && !props.disabled && !props.readonly); - const hasFocused = computed(() => !props.disabled && focusStatus.value); + const hasFocused = computed(() => !props.disabled && !props.readonly && focusStatus.value); const isEmpty = computed(() => modelValue.value === '' || modelValue.value === null || modelValue.value === undefined); const placeholder = computed(() => ((props.disabled || props.readonly) && !props.forcePlaceholder ? '' : props.placeholder)); const readonly = computed(() => props.readonly || !props.editable); @@ -141,7 +141,7 @@ export function useTextBox( } function onInput(payload: Event) { - context.emit('input', (payload.target as HTMLInputElement).value); + context.emit('input', (payload.target as HTMLInputElement).value, payload); const newValue = (payload.target as HTMLInputElement).value; displayText.value = newValue; if (updateOn.value === 'change') { diff --git a/packages/ui-vue/components/common/types.ts b/packages/ui-vue/components/common/types.ts index 0b874782f34ea2ddf7124821d4231333643b51eb..b90534bc11f7e47ebe304cd0cd204260d506c2ff 100644 --- a/packages/ui-vue/components/common/types.ts +++ b/packages/ui-vue/components/common/types.ts @@ -1,4 +1,5 @@ import { ComputedRef, Ref } from "vue"; +import { EffectFunction, SchemaResolverFunction } from "../dynamic-resolver"; export interface TextBoxProps { @@ -135,3 +136,10 @@ export type TimeAgoOptions = { export interface UseTimeAgoFormat { formatTo(date: TimeAgoDate, opts?: TimeAgoOptions): string } + +export interface RegisterContext { + schemaMap: Record; + schemaResolverMap: Record; + propertyConfigSchemaMap: Record; + propertyEffectMap: Record; +} diff --git a/packages/ui-vue/components/common/utils/exclude-properties.ts b/packages/ui-vue/components/common/utils/exclude-properties.ts new file mode 100644 index 0000000000000000000000000000000000000000..231c2bac38f9c297b822ea7cbcb518def778d058 --- /dev/null +++ b/packages/ui-vue/components/common/utils/exclude-properties.ts @@ -0,0 +1,8 @@ +export function excludeProperties( + obj: T, + ...properties: K[]) { + properties.forEach((key: K) => { + delete obj[key]; + }); + return obj; +} \ No newline at end of file diff --git a/packages/ui-vue/components/component/src/component.component.tsx b/packages/ui-vue/components/component/src/component.component.tsx index 079d6a38cff4a4e50d9d3ed76722ab94908ac6cd..4f080d68e86faaa661b5ee36f91220955bdbc04e 100644 --- a/packages/ui-vue/components/component/src/component.component.tsx +++ b/packages/ui-vue/components/component/src/component.component.tsx @@ -1,4 +1,4 @@ -import { SetupContext, defineComponent, onBeforeMount, onMounted } from 'vue'; +import { SetupContext, defineComponent, onBeforeMount, onMounted, ref } from 'vue'; import { ComponentPropsType, componentProps } from './component.props'; export default defineComponent({ @@ -6,14 +6,18 @@ export default defineComponent({ props: componentProps, emits: ['init', 'afterViewInit'], setup(props: ComponentPropsType, context) { + const elementRef = ref(); onBeforeMount(() => { context.emit('init', props.id); }); onMounted(() => { + if (elementRef.value && props.code) { + elementRef.value.setAttribute('scope-'+ props.code.toLowerCase(), ''); + } context.emit('afterViewInit', props.id); }); return () => { - return
{context.slots.default && context.slots.default()}
; + return
{context.slots.default && context.slots.default()}
; }; } }); diff --git a/packages/ui-vue/components/component/src/component.props.ts b/packages/ui-vue/components/component/src/component.props.ts index d681b0fcf664272e269ae89516ce1076abe8db78..d8db90857fa2a03a8e0bc589a8458ef83f6432cd 100644 --- a/packages/ui-vue/components/component/src/component.props.ts +++ b/packages/ui-vue/components/component/src/component.props.ts @@ -10,7 +10,8 @@ export const componentProps = { customClass: { type: String, default: '' }, customStyle: { type: String, default: '' }, componentType: { type: String, default: '' }, - formColumns: { type: Number, default: 4 } + formColumns: { type: Number, default: 4 }, + code: { type: String, default: '' } } as Record; export type ComponentPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/component/src/components/split-form-component.component.tsx b/packages/ui-vue/components/component/src/components/split-form-component.component.tsx index d483614b5fe227f6d8ef7b0f881ca2e41727f3a5..ee544ae2155b0deb5344f094e574d45c8cfdf2ee 100644 --- a/packages/ui-vue/components/component/src/components/split-form-component.component.tsx +++ b/packages/ui-vue/components/component/src/components/split-form-component.component.tsx @@ -68,6 +68,12 @@ export default defineComponent({ {!nodeData.isRemoved && !nodeData.isBindVariable && !nodeData.isComplexField ? {nodeData.bindingField} : ''} ; } + function renderNameCell(row: any) { + const controlData = row.raw?.control; + return <> + {controlData.label ? {controlData.label} : {controlData.id} } + ; + } return () => { return (
@@ -105,8 +111,7 @@ export default defineComponent({ hierarchy={hierarchyOption}> {{ 'cellTemplate': ({ cell, row }) => { - return cell.field === 'bindingPath' ? renderBindingPathCell(cell, row) : - (cell.data != null ? cell.data.toString() : cell.data); + return cell.field === 'bindingPath' ? renderBindingPathCell(cell, row) :renderNameCell(row); } }} diff --git a/packages/ui-vue/components/component/src/designer/use-designer-rules.ts b/packages/ui-vue/components/component/src/designer/use-designer-rules.ts index 2bbc428dfd8a92700210ccc296035887a37744f9..b5b93521355d7a262c74f69fb81ed9e82723c52a 100644 --- a/packages/ui-vue/components/component/src/designer/use-designer-rules.ts +++ b/packages/ui-vue/components/component/src/designer/use-designer-rules.ts @@ -4,6 +4,8 @@ import { UseTemplateDragAndDropRules } from "../../../designer-canvas/src/compos import { ComponentProperty } from "../property-config/component.property-config"; import { useSiblingComponent } from "../composition/use-sibling-component"; import { useSplitFormComponent } from "../composition/use-split-component"; +import { DgControl } from "../../../designer-canvas"; +import { ComponentType } from "../../../designer-outline"; export function useDesignerRules(designItemContext: DesignerItemContext, designerHostService?: DesignerHostService): UseDesignerRules { const dragAndDropRules = new UseTemplateDragAndDropRules(); @@ -159,5 +161,69 @@ export function useDesignerRules(designItemContext: DesignerItemContext, designe return customToolbarConfigs; } - return { canAccepts, checkCanDeleteComponent, checkCanMoveComponent, hideNestedPaddingInDesginerView, getStyles, getPropsConfig, onRemoveComponent, getCustomButtons }; + + function getComponentTitle() { + if (!designerHostService || !designItemContext?.schema) { + return '组件'; + } + switch (designItemContext.schema.componentType) { + case ComponentType.Frame: { + return '根组件'; + } + case ComponentType.dataGrid: { + const { formSchemaUtils } = designerHostService; + const treeGridSchema = formSchemaUtils.selectNode(designItemContext.schema, item => item.type === DgControl['tree-grid'].type); + const title = treeGridSchema ? '树表格' : '表格'; + return `${title}组件`; + } + case ComponentType.attachmentPanel: { + return '附件组件'; + } + case ComponentType.listView: { + return '列表视图组件'; + } + + default: { + return '组件'; + } + } + } + /** + * 配置组件的路径信息,用于事件交互面板显示“已有方法”的事件路径 + */ + function setComponentBasicInfoMap() { + if (designItemContext && designerHostService) { + const { formSchemaUtils } = designerHostService; + + let parentTitle = ''; + let reliedComponentId = ''; + const rootViewModelId = formSchemaUtils.getRootViewModelId(); + const rootComponent = formSchemaUtils.getComponentByViewModelId(rootViewModelId); + const parentSchemaOfComponent = formSchemaUtils.selectNode(rootComponent, item => { + return item.contents && item.contents.find(childItem => childItem.component === designItemContext.schema.id); + }); + // 父级为tab-page + if (parentSchemaOfComponent?.type === DgControl['tab-page']?.type && parentSchemaOfComponent?.contents?.length) { + parentTitle = parentSchemaOfComponent.title || ''; + } + // 父级为section + if (parentSchemaOfComponent?.type === DgControl.section?.type && parentSchemaOfComponent?.showHeader !== false) { + parentTitle = parentSchemaOfComponent.mainTitle || ''; + } + parentTitle = parentTitle ? `${parentTitle} > ` : ''; + reliedComponentId = parentTitle ? parentSchemaOfComponent.id : ''; + + const componentTitle = getComponentTitle(); + formSchemaUtils.getControlBasicInfoMap().set(designItemContext.schema.id, { + componentTitle, + parentPathName: `${parentTitle}${componentTitle}`, + reliedComponentId + }); + } + + } + return { + canAccepts, checkCanDeleteComponent, checkCanMoveComponent, hideNestedPaddingInDesginerView, getStyles, getPropsConfig, onRemoveComponent, getCustomButtons, + setComponentBasicInfoMap + }; } diff --git a/packages/ui-vue/components/component/src/property-config/component.property-config.ts b/packages/ui-vue/components/component/src/property-config/component.property-config.ts index 8865e97f789c3f5c6e98c3512ab7d20975c0c24f..3310e5688e14384ab5d206e23f1711f1caffd7e4 100644 --- a/packages/ui-vue/components/component/src/property-config/component.property-config.ts +++ b/packages/ui-vue/components/component/src/property-config/component.property-config.ts @@ -55,13 +55,8 @@ export class ComponentProperty extends BaseControlProperty { ]; const self = this; const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { - initialData - } - }; + const properties = self.createBaseEventProperty(initialData); + this.propertyConfig.categories['eventsEditor'] = { title: '事件', hideTitle: true, diff --git a/packages/ui-vue/components/components.ts b/packages/ui-vue/components/components.ts index e430b4918726340b464773490bcc8d2b2691fe8a..2fe3f948cd589f6394354dbec10a544900fdb336 100644 --- a/packages/ui-vue/components/components.ts +++ b/packages/ui-vue/components/components.ts @@ -36,7 +36,7 @@ export * from './data-view'; export type { DataColumn, VisualData, VisualDataCell, RowOptions } from './data-view'; export { default as FDrawer } from './drawer'; export type { DrawerProps } from './drawer'; -export { FResponseForm, FResponseForm as FDynamicForm, FDynamicFormGroup, FDynamicFormInput } from './dynamic-form'; +export { FResponseForm, FResponseForm as FDynamicForm, FDynamicFormGroup, FDynamicFormInput } from './dynamic-form'; export type { EditorConfig } from './dynamic-form'; export { default as FFilterBar } from './filter-bar'; export type { FilterBarProps } from './filter-bar'; @@ -53,7 +53,7 @@ export { default as FLayout } from './layout'; export type { LayoutProps } from './layout'; export { default as FLoading, FLoadingService } from './loading'; export type { LoadingProps } from './loading'; -export { default as FModal, F_MODAL_SERVICE_TOKEN, FModalService } from './modal'; +export { default as FModal, FModalService, F_MODAL_SERVICE_TOKEN } from './modal'; export type { ModalProps } from './modal'; export { default as FMessageBox, FMessageBoxService } from './message-box'; export type { MessageBoxProps, MessageType } from './message-box'; @@ -108,3 +108,10 @@ export { default as FSearchBox } from './search-box'; export type { SearchBoxProps } from './search-box'; export { default as FVerifyDetail, FVerifyDetailService } from './verify-detail'; export type { VerifyDetailProps } from './verify-detail'; +export { default as FItemCollectionEditor } from './radio-group/src/designer/item-collection-editor.component'; +export { default as FSchemaSelectorEditor } from './schema-selector/src/schema-selector-editor.component'; +export { default as FPropertyEditor } from './property-editor'; +export { default as MenuLookupContainer } from './menu-lookup/src/components/modal-container.component'; +export { useMenuTreeGridCoordinator } from './menu-lookup/src/composition/use-tree-grid-coordinator'; +export { default as FLookup } from './lookup'; +export { default as Locale, LocaleService} from './locale'; diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/checkbox-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/checkbox-value.ts index 5aea7b36dc21b5fb8f17c8e6bbbbb5c48253d7b9..2e14889700e6eb3fb992de6879896e5b2f7bdd76 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/checkbox-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/checkbox-value.ts @@ -8,13 +8,16 @@ export class CheckBoxValue implements ConditionValue { value: boolean[]; valueType = 'boolean'; + // 编辑器配置 + editiorConfig; - constructor(initialData: { value: any } = { value: [] }) { + constructor(initialData: { value: any } = { value: [] },editor?:any) { const originalBooleanValues = Array.isArray(initialData.value) ? initialData.value : ( typeof initialData.value === 'string' ? initialData.value.split(',') : [] ); const booleanValues = originalBooleanValues.map((originalValue: any) => JSON.parse(originalValue)); this.value = booleanValues; + this.editiorConfig=Object.assign({},editor); } clear(): void { diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/combo-lookup-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/combo-lookup-value.ts index f6ef6ab17db88d52b2dd906d45cf6befa7bfa0a5..8dd679990d14a391ba6bb0a0e807deb2395bb659 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/combo-lookup-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/combo-lookup-value.ts @@ -13,11 +13,14 @@ export class ComboLookupValue implements ConditionValue { valueField: string; valueType = 'text'; - - constructor(initialData: { textValue: string; value: string; valueField: string } = { textValue: '', value: '', valueField: '' }) { - this.textValue = initialData.textValue; - this.value = initialData.value; - this.valueField = initialData.valueField; + // 编辑器配置 + editiorConfig; + + constructor(initialData: { textValue: string; value: string; valueField: string } = { textValue: '', value: '', valueField: '' },editor?:any) { + this.textValue = initialData?.textValue; + this.value = initialData?.value; + this.editiorConfig=Object.assign({},editor); + this.valueField = initialData?.valueField; } clear(): void { diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/date-picker-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/date-picker-value.ts index c95b7bc68dc10090880cfeb76a084c82f407d18f..9b5818158d855963fdcf89d23f57163fc4712518 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/date-picker-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/date-picker-value.ts @@ -1,6 +1,7 @@ import { EditorType } from '@farris/ui-vue/components/dynamic-form'; import { ConditionValue } from "./types"; import { parse, format } from 'date-fns'; +import { useDateFormat } from '@farris/ui-vue/components/common'; type propsDateValue = { count: null | number; @@ -16,23 +17,32 @@ export class DatePickerValue implements ConditionValue { editorType: EditorType = 'date-picker'; - value: string | propsDateValue | undefined; + value: string | Date | undefined; valueType = 'datetime'; - + displayFormat = ''; - - valueFormat = ''; + valueFormat = ''; + // 编辑器配置 + editiorConfig; - constructor(initialData: { value: string | propsDateValue , displayFormat: string, valueFormat:string} = { value: '', displayFormat: '', valueFormat: ''}) { - this.value = initialData.value; - this.displayFormat = initialData.displayFormat; - this.valueFormat = initialData.valueFormat; + constructor(initialData: { value: string | Date, displayFormat: string, valueFormat: string } = { value: '', displayFormat: '', valueFormat: '' }, editor?: any) { + this.value = initialData?.value; + this.editiorConfig = Object.assign({}, editor); + this.displayFormat = initialData?.displayFormat || editor?.displayFormat || ''; + this.valueFormat = initialData?.valueFormat || editor?.valueFormat || ''; } - - setValue(target: { formatted: string | propsDateValue }): void { - this.value = target.formatted; + formatValue() { + const { formatTo, parseToDate } = useDateFormat(); + if (!this.value) { + return ''; + } + const dateObj: any = parseToDate(this.value, this.valueFormat); + return formatTo(dateObj, this.displayFormat); + }; + setValue(value): void { + this.value = value; } getValue() { @@ -40,15 +50,9 @@ export class DatePickerValue implements ConditionValue { } getDisplayText() { - if(typeof(this.value) === 'string' && this.value) { - const parsedDate = parse(this.value, this.valueFormat, new Date()); - const displayTime = format(parsedDate, this.displayFormat); - return displayTime; - } - return this.getValue(); - + return this.formatValue(); } - + isEmpty(): boolean { return !this.value; } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/date-range-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/date-range-value.ts index f0da9812e7922f1a7b0922f3c9250d6641034466..fa944bbfc4b3737ff5d5d1bbf66105f57fbe1107 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/date-range-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/date-range-value.ts @@ -1,6 +1,6 @@ import { EditorType } from '../../../../dynamic-form'; import { ConditionValue } from "./types"; - +import { useDateFormat } from '@farris/ui-vue/components/common'; export class DateRangeValue implements ConditionValue { editorType: EditorType = 'date-range'; @@ -11,37 +11,60 @@ export class DateRangeValue implements ConditionValue { valueType = 'datetime'; - constructor(initialData: { begin: string; end: string } = { begin: '', end: '' }) { - this.begin = initialData.begin; - this.end = initialData.end; + value = ''; + // 编辑器配置 + editiorConfig; + displayFormat = ''; + valueFormat = ''; + + constructor(initialData: { begin: string, end: string, displayFormat: string, valueFormat: string } = { begin: '', end: '', displayFormat: '', valueFormat: '' }, editor:any = {}) { + this.begin = initialData?.begin || ''; + this.end = initialData?.end || ''; + this.editiorConfig = Object.assign({}, editor, { delimiter: '~' }); + this.displayFormat = initialData?.displayFormat || editor?.displayFormat || ''; + this.valueFormat = initialData?.valueFormat || editor?.valueFormat || ''; + this.value = (this.begin === null ? '' : this.begin) + '~' + (this.end === null ? '' : this.end); } clear(): void { this.begin = ''; this.end = ''; + this.value = ''; } - - getValue() { - if (!this.begin || !this.end) { + formatValue(dateValue) { + const { formatTo, parseToDate } = useDateFormat(); + if (!dateValue) { return ''; } - return `${this.begin}~${this.end}`; + const dateObj: any = parseToDate(dateValue, this.valueFormat); + return formatTo(dateObj, this.displayFormat); + }; + getValue() { + return { + begin: this.begin, + end: this.end + }; } - + // TODO getDisplayText() { - return this.getValue(); + if (!this.begin && !this.end) { + return ''; + } + return (this.begin === null ? '' : this.formatValue(this.begin)) + '~' + (this.end === null ? '' : this.formatValue(this.end)); } - setValue(value: { dataRange: string; delimiter: string }): void { - if (value.dataRange) { - this.begin = value.dataRange.split(value.delimiter)[0]; - this.end = value.dataRange.split(value.delimiter)[1]; + setValue(newValue): void { + if (newValue) { + const valueList = newValue.split(this.editiorConfig.delimiter); + this.begin = valueList[0] || ''; + this.end = valueList[1] || ''; + this.value = newValue; } else { this.clear(); } } isEmpty(): boolean { - return !this.begin || !this.end; + return !this.begin && !this.end; } } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/datetime-picker-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/datetime-picker-value.ts index 5bee9fd38b9402cd49e3d02dafe3f64cad8d1fbc..dee7f2b04e59a295be91cdd9a91e9b1f72f14e5d 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/datetime-picker-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/datetime-picker-value.ts @@ -1,48 +1,11 @@ import { EditorType } from '../../../../dynamic-form'; -import { ConditionValue } from "./types"; -import { parse, format } from 'date-fns'; +import { DatePickerValue } from "./types"; -export class DateTimePickerValue implements ConditionValue { +export class DateTimePickerValue extends DatePickerValue { editorType: EditorType = 'datetime-picker'; - value: string | undefined; - - valueType = 'datetime'; - - displayFormat = ''; - - valueFormat = ''; - - constructor(initialData: { value: string , displayFormat: string, valueFormat:string} = { value: '', displayFormat: '', valueFormat: ''}) { - this.value = initialData.value; - this.displayFormat = initialData.displayFormat; - this.valueFormat = initialData.valueFormat; - } - - clear(): void { - this.value = undefined; - } - - getValue() { - return this.value; + constructor(initialData: { value: string , displayFormat: string, valueFormat:string} = { value: '', displayFormat: '', valueFormat: ''},editor?:any) { + super(initialData,editor); } - - getDisplayText() { - if(typeof(this.value) === 'string' && this.value) { - const parsedDate = parse(this.value, this.valueFormat, new Date()); - const displayTime = format(parsedDate, this.displayFormat); - return displayTime; - } - return this.getValue(); - } - - setValue(value: any): void { - this.value = value.formatted; - } - - isEmpty(): boolean { - return !this.value; - } - } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/datetime-range-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/datetime-range-value.ts new file mode 100644 index 0000000000000000000000000000000000000000..f889063a947105317583bbe07793ea8f75fe8316 --- /dev/null +++ b/packages/ui-vue/components/condition/src/composition/condition-value/datetime-range-value.ts @@ -0,0 +1,13 @@ +import { EditorType } from '../../../../dynamic-form'; +import { DateRangeValue } from "./types"; + +export class DateTimeRangeValue extends DateRangeValue { + // 这个属性会在后面进行比对 + editorType: EditorType = 'datetime-range'; + + valueType = 'datetime'; + + constructor(initialData: { begin: string, end: string, displayFormat: string, valueFormat: string } = { begin: '', end: '', displayFormat: '', valueFormat: '' }, editor:any = {}) { + super(initialData,editor); + } +} diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/dropdown-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/dropdown-value.ts index f11c3acb84eafb9040807fb58e11850b97a34938..66e2794ed270c13b546ad07353d91e427ccd4be0 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/dropdown-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/dropdown-value.ts @@ -27,12 +27,19 @@ export class ComboListValue implements ConditionValue { valueType = 'enum'; - valueList:Array<{value: string | number | boolean, name:string}> = []; - - constructor(initialData: { value: any, valueList: any[] } = { value: '', valueList: [] }) { + valueList: Array<{ value: string | number | boolean, name: string }> = []; + // 编辑器配置 + editiorConfig; + constructor(initialData: { value: any, valueList: any[] } = { value: '', valueList: [] }, editor?: any) { // this.value = getEnumValues(initialData); - this.value = initialData.value; - this.valueList = initialData.valueList; + this.value = initialData?.value; + this.editiorConfig=Object.assign({},editor); + if (this.editiorConfig.data && this.editiorConfig.data.length) { + this.valueList = this.editiorConfig.data; + } else { + // 一般赋默认值的时候,不会带着valueList + this.valueList = initialData?.valueList || []; + } } clear(): void { @@ -46,9 +53,9 @@ export class ComboListValue implements ConditionValue { } getDisplayText() { - const valueType = typeof(this.value); + const valueType = typeof (this.value); let dispalyText = ''; - let valueList:string[] = []; + let valueList: string[] = []; switch (valueType) { case 'string': valueList = this.value.split(',') || []; diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/input-group-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/input-group-value.ts index d0dc9c9dae419d761cdbe0ee47bc47bfa4dc478b..06272ff861164e0afb9a20c55f58afc2c37b17f6 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/input-group-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/input-group-value.ts @@ -19,18 +19,21 @@ export class InputGroupValue implements ConditionValue { // 是否是手动输入的值 isInputText: boolean; + // 编辑器配置 + editiorConfig; constructor(initialData: { value: string; displayText: string; displayField: string; isInputText: boolean; - } = { value: '', displayText: '', displayField: '', isInputText: false } + } = { value: '', displayText: '', displayField: '', isInputText: false }, editor?: any ) { - this.value = initialData.value; - this.displayText = initialData.displayText; - this.valueField = initialData.displayField; - this.isInputText = initialData.isInputText; + this.value = initialData?.value; + this.displayText = initialData?.displayText; + this.valueField = initialData?.displayField; + this.isInputText = initialData?.isInputText; + this.editiorConfig = Object.assign({}, editor); } clear(): void { @@ -67,6 +70,6 @@ export class InputGroupValue implements ConditionValue { } isEmpty(): boolean { - return !this.displayText && !this.value.length; + return !this.displayText && (this.value == null || !this.value?.length); } } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/lookup-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/lookup-value.ts index 81b5be5cfab8a03d2dd5603060be5e1084f0d2c3..278bf52230608c10204484ee0d35b04b657e8fe3 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/lookup-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/lookup-value.ts @@ -17,6 +17,8 @@ export class LookupValue implements ConditionValue { // 帮助的值是否为手动输入的任意值,对应帮助的任意输入属性nosearch isInputText: boolean; + // 编辑器配置 + editiorConfig; constructor(initialData: { mapFields: any[]; @@ -24,12 +26,14 @@ export class LookupValue implements ConditionValue { valueField: string; isInputText: boolean; helpId: string; - } = { mapFields: [], value: '', valueField: '', isInputText: false, helpId: '' }) { - this.value = initialData.value; - this.valueField = initialData.valueField; + } = { mapFields: [], value: '', valueField: '', isInputText: false, helpId: '' }, editor?: any) { + this.editiorConfig = Object.assign({}, editor); + this.value = initialData?.value || ''; + this.valueField = initialData?.valueField || editor?.valueField; + // 此处的mapFields被当做是已选数组使用,与组件的mapFields有差别 this.mapFields = initialData.mapFields; - this.isInputText = initialData.isInputText; - this.helpId = initialData.helpId; + this.isInputText = initialData?.isInputText || editor?.isInputText; + this.helpId = initialData?.helpId || editor?.helpId; } clear(): void { diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/month-picker-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/month-picker-value.ts index 671280078bd5e2296d2365b7b3a9e0cc661ada7c..fa6fe45734d6f51d5b0581c3ac18cea5b2821d8e 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/month-picker-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/month-picker-value.ts @@ -1,36 +1,12 @@ import { EditorType } from '../../../../dynamic-form'; -import { ConditionValue } from "./types"; +import { DatePickerValue } from "./types"; -export class MonthPickerValue implements ConditionValue { +export class MonthPickerValue extends DatePickerValue { editorType: EditorType = 'month-picker'; + - value: string | undefined; - - valueType = 'datetime'; - - constructor(initialData: { value: string } = { value: '' }) { - this.value = initialData.value; - } - - setValue(target: { formatted: string | undefined }): void { - this.value = target.formatted; + constructor(initialData: { value: string, displayFormat: string, valueFormat: string } = { value: '', displayFormat: '', valueFormat: '' }, editor?: any) { + super(initialData,editor); } - - getValue() { - return this.value; - } - - getDisplayText() { - return this.getValue(); - } - - clear(): void { - this.value = undefined; - } - - isEmpty(): boolean { - return !this.value; - } - } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/month-range-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/month-range-value.ts index 914ea2aa8867a3e4d1629ef1f833204537b3642f..5ff192060d2342de72cc0655c1b768b6e7053bea 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/month-range-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/month-range-value.ts @@ -1,47 +1,10 @@ import { EditorType } from '../../../../dynamic-form'; -import { ConditionValue } from "./types"; +import { DateRangeValue } from "./types"; -export class MonthRangeValue implements ConditionValue { +export class MonthRangeValue extends DateRangeValue { editorType: EditorType = 'month-range'; - - begin: string; - - end: string; - - valueType = 'datetime'; - - clear(): void { - this.begin = ''; - this.end = ''; - } - - constructor(initialData: { begin: string; end: string } = { begin: '', end: '' }) { - this.begin = initialData.begin; - this.end = initialData.end; - } - - getValue() { - if (!this.begin || !this.end) { - return ''; - } - return `${this.begin}~${this.end}`; - } - - getDisplayText() { - return this.getValue(); - } - - setValue(value: { dataRange: string; delimiter: string }): void { - if (value.dataRange) { - this.begin = value.dataRange.split(value.delimiter)[0]; - this.end = value.dataRange.split(value.delimiter)[1]; - } else { - this.clear(); - } - } - - isEmpty(): boolean { - return !this.begin || !this.end; + constructor(initialData: { begin: string, end: string, displayFormat: string, valueFormat: string } = { begin: '', end: '', displayFormat: '', valueFormat: '' }, editor?: any) { + super(initialData, editor) } } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/number-range-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/number-range-value.ts index a418b4d7248ff78c33827a5335c5397909e4fcb9..49b4d58eeec60933ccb8444b06fc55f1d68612d3 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/number-range-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/number-range-value.ts @@ -10,16 +10,19 @@ export class NumberRangeValue implements ConditionValue { end: number | null; valueType = 'number'; - + // 编辑器配置 + editiorConfig; constructor(initialData: { begin: string | number | null; end: string | number | null; - } = { begin: null, end: null } + } = { begin: null, end: null },editor?:any ) { - this.begin = initialData.begin == null ? null : parseFloat(initialData.begin as string); - this.end = initialData.end == null ? null : parseFloat(initialData.end as string); + + this.editiorConfig=Object.assign({},editor); + this.begin = initialData?.begin == null ? null : parseFloat(initialData.begin as string); + this.end = initialData?.end == null ? null : parseFloat(initialData.end as string); } - + clear(): void { this.begin = null; this.end = null; diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/number-spinner-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/number-spinner-value.ts index 4a99317c916fec056c31066616d4c0feb38d3aad..997d05f2e0343b38ed927f8c0eb1c188eb52c075 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/number-spinner-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/number-spinner-value.ts @@ -8,8 +8,10 @@ export class NumberSpinnerValue implements ConditionValue { value: number | null | undefined; valueType = 'number'; - - constructor(initialData: { value: string } = { value: '' }) { +// 编辑器配置 +editiorConfig; + constructor(initialData: { value: string } = { value: '' },editor?:any) { + this.editiorConfig=Object.assign({},editor); const numberSpinnerValue = parseFloat(initialData.value); this.value = isNaN(numberSpinnerValue) ? null : numberSpinnerValue; } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/radio-group-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/radio-group-value.ts index 09d694bcc83b03fc96acc4f0231e5286d1deff86..b8e30cf36bdf5c8998d5358e2b2e470c12fd762e 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/radio-group-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/radio-group-value.ts @@ -10,10 +10,17 @@ export class RadioGroupValue implements ConditionValue { valueType = 'enum'; valueList:Array<{value: string | number | boolean, name:string}> = []; - - constructor(initialData: { value: any, valueList: any[] } = { value: null, valueList: [] }) { - this.value = initialData.value; - this.valueList = initialData.valueList; +// 编辑器配置 +editiorConfig; + constructor(initialData: { value: any, valueList: any[] } = { value: null, valueList: [] },editor?:any) { + this.editiorConfig=Object.assign({},editor); + this.value = initialData?.value; + if (this.editiorConfig.data && this.editiorConfig.data.length) { + this.valueList = this.editiorConfig.data; + } else { + // 一般赋默认值的时候,不会带着valueList + this.valueList = initialData?.valueList || []; + } } clear(): void { diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/single-year-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/single-year-value.ts index d7b341108981bb23bc3f29bddb10f1a8b42e6ac9..098a7f1f8e5db95cf740bd79f1ef5ae9087e2430 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/single-year-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/single-year-value.ts @@ -1,35 +1,10 @@ import { EditorType } from '../../../../dynamic-form'; -import { ConditionValue } from "./types"; +import { DatePickerValue } from "./types"; -export class YearPickerValue implements ConditionValue { +export class YearPickerValue extends DatePickerValue { editorType: EditorType = 'year-picker'; - - value: string | undefined; - - valueType = 'datetime'; - - constructor(initialData: { value: string } = { value: '' }) { - this.value = initialData.value; - } - - clear(): void { - this.value = undefined; - } - - getValue() { - return this.value; - } - - getDisplayText() { - return this.getValue(); - } - - setValue(value: any): void { - this.value = value.formatted; - } - - isEmpty(): boolean { - return !this.value; + constructor(initialData: { value: string, displayFormat: string, valueFormat: string } = { value: '', displayFormat: '', valueFormat: '' }, editor?: any) { + super(initialData,editor); } } diff --git a/packages/ui-vue/components/condition/src/composition/condition-value/text-value.ts b/packages/ui-vue/components/condition/src/composition/condition-value/text-value.ts index fdd9ac4a19bd8868e805db90ca0fec7d4f859cf1..70c3d494dd00552c414bf0cbdbd9467302473bab 100644 --- a/packages/ui-vue/components/condition/src/composition/condition-value/text-value.ts +++ b/packages/ui-vue/components/condition/src/composition/condition-value/text-value.ts @@ -8,8 +8,9 @@ export class TextValue implements ConditionValue { value: string | undefined; valueType = 'text'; - - constructor(initialData: { value: string } = { value: '' }) { + // 编辑器配置 + editiorConfig; + constructor(initialData: { value: string } = { value: '' },editor?:any) { this.value = initialData.value; } diff --git a/packages/ui-vue/components/condition/src/composition/types.ts b/packages/ui-vue/components/condition/src/composition/types.ts index b85f48f953010cc347dad9a2b85f923484cc564b..e917337bc6f5669fb8188c062ba766a9ac3c056f 100644 --- a/packages/ui-vue/components/condition/src/composition/types.ts +++ b/packages/ui-vue/components/condition/src/composition/types.ts @@ -47,7 +47,7 @@ export interface UseCondition { export interface UseConditionValue { - createConditionValue: (editorType: EditorType, initialValue?: any) => ConditionValue; + createConditionValue: (editorType: EditorType, initialValue?: any,editor?:any) => ConditionValue; } diff --git a/packages/ui-vue/components/condition/src/composition/use-compare.ts b/packages/ui-vue/components/condition/src/composition/use-compare.ts index b1b380af48d893f15a1a02fce0fc4b3bfcebcba6..9eacf3e65db02389c2569f9f24610a8010553814 100644 --- a/packages/ui-vue/components/condition/src/composition/use-compare.ts +++ b/packages/ui-vue/components/condition/src/composition/use-compare.ts @@ -2,6 +2,7 @@ import { SetupContext } from "vue"; import { ConditionProps } from "../condition.props"; import { FieldConfig, Condition } from "../types"; import { UseCompare, UseFieldConfig } from "./types"; +import { useCompareLocale } from "../locale/locale"; enum CompareType { Equal = '0', @@ -16,45 +17,47 @@ enum CompareType { In = '9', NotIn = '10' } - -const CompareTypeName = [ - { - value: '0', - name: '等于', - }, - { - value: '1', - name: '不等于', - }, - { - value: '2', - name: '大于', - }, - { - value: '3', - name: '大于等于', - }, - { - value: '4', - name: '小于', - }, - { - value: '5', - name: '小于等于', - }, - { - value: '6', - name: '包含', - }, - { - value: '7', - name: '开始是', - }, - { - value: '8', - name: '结束是', - } -]; +function getCompareTypeName(){ + const { operatorsLocale } = useCompareLocale(); + return [ + { + value: '0', + name: operatorsLocale.equal, // 等于 + }, + { + value: '1', + name: operatorsLocale.equal,// 不等于 + }, + { + value: '2', + name: operatorsLocale.equal,// 大于 + }, + { + value: '3', + name: operatorsLocale.equal,// 大于等于 + }, + { + value: '4', + name: operatorsLocale.less,// 小于 + }, + { + value: '5', + name: operatorsLocale.lessOrEqual,// 小于等于 + }, + { + value: '6', + name: operatorsLocale.contains,// 包含 + }, + { + value: '7', + name: operatorsLocale.startWith,// 开始是 + }, + { + value: '8', + name: operatorsLocale.endWith,// 结束是 + } + ]; +}; const CompareTypeInEditor = { 'button-edit': ['0', '1', ' 6', '7', '8'], @@ -79,7 +82,7 @@ const CompareTypeInEditor = { export { CompareType, - CompareTypeName, + getCompareTypeName, CompareTypeInEditor }; @@ -92,14 +95,13 @@ export function useCompare( const { fieldMap } = useFieldComposition; function getCompareOperators(condition: Condition): { name: string; value: string }[] { - const field = fieldMap.get(condition.fieldCode) as FieldConfig; if (!field) { return []; } const editorType = field.editor.type; const compareSetInEditor = new Set(CompareTypeInEditor[editorType]); - const result = CompareTypeName.filter((compare: { value: string; name: string }) => compareSetInEditor.has(compare.value)); + const result = getCompareTypeName().filter((compare: { value: string; name: string }) => compareSetInEditor.has(compare.value)); return result; } diff --git a/packages/ui-vue/components/condition/src/composition/use-condition-value.ts b/packages/ui-vue/components/condition/src/composition/use-condition-value.ts index d6afd6effdeee319958b040231d092b656349396..6111ad3e62423c173a5b5861297761f186f0eadf 100644 --- a/packages/ui-vue/components/condition/src/composition/use-condition-value.ts +++ b/packages/ui-vue/components/condition/src/composition/use-condition-value.ts @@ -12,9 +12,11 @@ import { MonthRangeValue } from "./condition-value/month-range-value"; import { NumberRangeValue } from "./condition-value/number-range-value"; import { NumberSpinnerValue } from "./condition-value/number-spinner-value"; import { RadioGroupValue } from "./condition-value/radio-group-value"; +import { YearPickerValue } from './condition-value/single-year-value'; import { TextValue } from "./condition-value/text-value"; import { ConditionValue } from "./condition-value/types"; import { UseConditionValue } from "./types"; +import { DateTimeRangeValue } from "./condition-value/datetime-range-value"; export function useConditionValue(): UseConditionValue { @@ -23,38 +25,40 @@ export function useConditionValue(): UseConditionValue { * @param field 字段配置信息 * @returns 筛选条件的control对象和value对象 */ - function createConditionValue(editorType: EditorType, initialValue?: any): ConditionValue { + function createConditionValue(editorType: EditorType, initialValue?: any, editor?): ConditionValue { switch (editorType) { case 'check-box': - return new CheckBoxValue(initialValue); + return new CheckBoxValue(initialValue,editor); case 'combo-list': - return new ComboListValue(initialValue); + return new ComboListValue(initialValue,editor); case 'combo-lookup': - return new ComboLookupValue(initialValue); + return new ComboLookupValue(initialValue,editor); case 'input-group': - return new InputGroupValue(initialValue); + return new InputGroupValue(initialValue,editor); + case 'year-picker': + return new YearPickerValue(initialValue,editor); case 'date-picker': - return new DatePickerValue(initialValue); + return new DatePickerValue(initialValue,editor); + case 'datetime-range': + return new DateTimeRangeValue(initialValue,editor); case 'date-range': - return new DateRangeValue(initialValue); + return new DateRangeValue(initialValue,editor); case 'datetime-picker': - return new DateTimePickerValue(initialValue); - case 'datetime-range': - return new DateRangeValue(initialValue); + return new DateTimePickerValue(initialValue,editor); case 'lookup': - return new LookupValue(initialValue); + return new LookupValue(initialValue,editor); case 'month-picker': - return new MonthPickerValue(initialValue); + return new MonthPickerValue(initialValue,editor); case 'month-range': - return new MonthRangeValue(initialValue); + return new MonthRangeValue(initialValue,editor); case 'number-range': - return new NumberRangeValue(initialValue); + return new NumberRangeValue(initialValue,editor); case 'number-spinner': - return new NumberSpinnerValue(initialValue); + return new NumberSpinnerValue(initialValue,editor); case 'radio-group': - return new RadioGroupValue(initialValue); + return new RadioGroupValue(initialValue,editor); default: - return new TextValue(initialValue); + return new TextValue(initialValue,editor); } } diff --git a/packages/ui-vue/components/condition/src/composition/use-field-config.ts b/packages/ui-vue/components/condition/src/composition/use-field-config.ts index b7b2bd83ed48666a5362b94170be664d1a7d5a02..8d8ac03b255fec5040c4c685b0c9c841636a27b5 100644 --- a/packages/ui-vue/components/condition/src/composition/use-field-config.ts +++ b/packages/ui-vue/components/condition/src/composition/use-field-config.ts @@ -2,7 +2,7 @@ import { SetupContext, ref } from "vue"; import { FieldConfig, Condition } from "../types"; import { ConditionProps } from "../condition.props"; import { UseFieldConfig } from "./types"; -import { EditorConfig } from "../../../dynamic-form"; +import { EditorConfig, EditorType } from "../../../dynamic-form"; import { useConditionValue } from "./use-condition-value"; export function useFieldConfig(props: ConditionProps, context: SetupContext): UseFieldConfig { @@ -11,21 +11,44 @@ export function useFieldConfig(props: ConditionProps, context: SetupContext): Us const fieldConditions = ref([]); const fieldMap = new Map(); const { createConditionValue } = useConditionValue(); - - function getSingleControlType(fieldConfig: FieldConfig): EditorConfig { - const controlType = fieldConfig.editor ? fieldConfig.editor.type : 'input-group'; + function getRealControlType(controlType):EditorType { + let realType = 'input-group'; switch (controlType) { + case 'year-picker': + case 'month-picker': + case 'month-range': case 'date-range': - fieldConfig.editor.type = 'date-picker'; - break; case 'datetime-range': - fieldConfig.editor.type = 'datetime-picker'; - break; - case 'number-range': - fieldConfig.editor.type = 'number-spinner'; + case 'datetime-picker': + realType = 'date-picker'; break; + default: + realType=controlType; + } + return realType as EditorType; + } + function getSingleControlType(field: FieldConfig): EditorConfig { + const currentType = field.editor ? field.editor.type : 'input-group'; + field.editor.type = getRealControlType(currentType); + if(field.editor.type === 'date-picker'){ + if(currentType.indexOf('range')>-1){ + field.editor.enablePeriod=true; + } + if(currentType.indexOf('datetime')>-1){ + field.editor.showTime = true; + } + if(currentType.indexOf('year')>-1){ + field.editor.selectMode="year"; + } + if(currentType.indexOf('month')>-1){ + field.editor.selectMode="month"; + } + } + if(currentType ==='input-group') { + // 多语言在input-group内处理 + field.editor.placeholder = '请输入'; } - return fieldConfig.editor; + return field.editor; } function convertToSingleControl(configs: FieldConfig[]): FieldConfig[] { @@ -39,7 +62,7 @@ export function useFieldConfig(props: ConditionProps, context: SetupContext): Us function loadFieldConfigs(useRangeEditor = true) { fields.value.reduce((result: Map, field: FieldConfig) => { - if (!useRangeEditor) { + if (useRangeEditor) { field.editor = getSingleControlType(field); } result.set(field.labelCode, field); @@ -51,7 +74,7 @@ export function useFieldConfig(props: ConditionProps, context: SetupContext): Us conditions.forEach((fieldCondition: Condition) => { if (fieldCondition) { const selectedField = fieldMap.get(fieldCondition.fieldCode) as FieldConfig; - selectedField.controlType && (fieldCondition.value = createConditionValue(selectedField.controlType, fieldCondition.value)); + selectedField.controlType && (fieldCondition.value = createConditionValue(selectedField.controlType, fieldCondition.value,selectedField.editor)); } }); return conditions; diff --git a/packages/ui-vue/components/condition/src/condition-fields.component.tsx b/packages/ui-vue/components/condition/src/condition-fields.component.tsx index 46aa858c3b1f9a8c86f2a150bbfd8c4630502446..555cba07607984b08d74c8e02640233efdbad853 100644 --- a/packages/ui-vue/components/condition/src/condition-fields.component.tsx +++ b/packages/ui-vue/components/condition/src/condition-fields.component.tsx @@ -1,4 +1,4 @@ - + /** * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. * @@ -21,6 +21,7 @@ import { FDynamicFormGroup } from '@farris/ui-vue/components/dynamic-form'; import { useFieldConfig } from './composition/use-field-config'; import { useSize } from './composition/use-size'; import { ConditionValue } from './composition/condition-value/types'; +import { useI18n } from 'vue-i18n'; export default defineComponent({ name: 'FConditionFields', @@ -28,13 +29,13 @@ export default defineComponent({ emits: ['valueChange', 'blur', 'focus', 'click', 'input'] as (string[] & ThisType) | undefined, setup(props: ConditionProps, context: SetupContext) { const fieldsElement = ref(); + const { locale } = useI18n(); const key = ref(props.key); - const isControlInline = ref(props.isControlInline); const conditions = ref(props.conditions); const useFieldComposition = useFieldConfig(props, context); const { initialConditionValue, fieldMap, loadFieldConfigs } = useFieldComposition; const useSizeComposition = useSize(props, context, fieldsElement); - const { conditionClass, resizeObserver } = useSizeComposition; + const { conditionClass, resizeObserver } = useSizeComposition; loadFieldConfigs(true); @@ -56,35 +57,38 @@ export default defineComponent({ 'f-utils-flex-row-wrap': true, 'farris-form': true, 'condition-div': true, - 'farris-form-controls-inline': props.isControlInline + 'farris-form-controls-inline': !locale.value || props.isControlInline===true || props.isControlInline === 'auto' && locale.value !== 'en' })); - const conditionItemClass = computed(() => { - // const classArray = ['col-12', 'col-md-6', 'col-xl-3']; - // if (conditions.value.length > 4) { - // classArray.push('col-el-2'); - // } - // return classArray.join(' '); - return 'col-12 col-md-6 col-xl-3 col-el-2 '; - }); - - function onChange(condition:Condition, value:any, editor?, option?:any) { - if(condition.value.editorType === 'combo-list' && option.newValue) { - condition.value.valueList = option.newValue.map(item => { - return {name: item.name, value: item.value}; - }); - } else if(condition.value.editorType === 'radio-group') { - condition.value.valueList = [editor.data.find(item => item.value === value)]; + function onChange(condition: Condition, value: any, editor?, option?: any) { + switch (condition.value.editorType) { + case 'combo-list': + if (option.newValue) { + condition.value.valueList = option.newValue.map(item => { + return { name: item.name, value: item.value }; + }); + } + break; + case 'radio-group': + condition.value.valueList = [editor.data.find(item => item.value === value)]; + break; + case 'year-range': + case 'month-range': + case 'date-range': + case 'datetime-range': + condition.value.setValue(value); + break; + default: } context.emit('valueChange', value, condition); } - + function renderFieldConditions() { return conditions.value.map((condition: Condition) => { const editor = fieldMap.get(condition.fieldCode)?.editor; const id = fieldMap.get(condition.fieldCode)?.id; let needEmitChange = true; - if(condition.value?.editorType === 'lookup' && editor) { + if (condition.value?.editorType === 'lookup' && editor) { editor.idValue = condition.value.mapFields?.map(field => field.id).join(','); editor['onClear'] = () => { condition.value.mapFields = []; @@ -95,7 +99,7 @@ export default defineComponent({ onChange(condition, condition.value.getValue()); }; needEmitChange = false; - } else if(condition.value?.editorType === 'number-range' && editor) { + } else if (condition.value?.editorType === 'number-range' && editor) { editor.beginValue = condition.value.begin; editor['onBeginValueChange'] = (value) => { condition.value.begin = value; @@ -107,10 +111,13 @@ export default defineComponent({ onChange(condition, value); }; needEmitChange = false; - }; - let customClass = editor?.appearance?.class || conditionItemClass.value; + } else if (editor && ['year-range', 'month-range', 'date-range', 'datetime-range'].find(item => item === condition.value?.editorType)) { + editor.beginValue = condition.value.begin; + editor.endValue = condition.value.end; + } + let customClass = editor?.appearance?.class || props.itemClass; editor?.multiLineLabel && (customClass = customClass + ' farris-group-multi-label'); - + return {needEmitChange && onChange(condition, value, editor, option);}}>; + onChange={(value: any, option: any) => { needEmitChange && onChange(condition, value, editor, option); }}>; }); } diff --git a/packages/ui-vue/components/condition/src/condition-fields.design.component.tsx b/packages/ui-vue/components/condition/src/condition-fields.design.component.tsx index dd91b607dcc4af067fd146f96b429ce13ecdb189..dedf543b90fa0ce6c844c7fe9b8e2f62b1d539b2 100644 --- a/packages/ui-vue/components/condition/src/condition-fields.design.component.tsx +++ b/packages/ui-vue/components/condition/src/condition-fields.design.component.tsx @@ -55,17 +55,12 @@ 'f-utils-flex-row-wrap': true, 'farris-form': true, 'condition-div': true - })); - - const conditionItemClass = computed(() => { - return 'col-12 col-md-6 col-xl-3 col-el-2 '; - }); - + })); function renderFieldConditions() { return conditions.value.map((condition: Condition) => { const editor = fieldMap.get(condition.fieldCode)?.editor; editor && (editor.disabled = true); - let customClass = editor?.appearance?.class || conditionItemClass.value; + let customClass = editor?.appearance?.class || props.itemClass; editor?.multiLineLabel && (customClass = customClass + ' farris-group-multi-label'); return unGroup(conditionGroup)}> changeGroupRelation(conditionGroup)}> - {conditionGroup.relation === 1 ? '与' : '或'} + {conditionGroup.relation === 1 ? conditionListLocale.and : conditionListLocale.or}
); @@ -141,14 +142,14 @@ export default defineComponent({
- 添加条件 + {conditionListLocale.add}
- 生成条件组 + {conditionListLocale.create}
- 重置 + {conditionListLocale.reset}
); diff --git a/packages/ui-vue/components/condition/src/condition.props.ts b/packages/ui-vue/components/condition/src/condition.props.ts index 131093fd4637fdf7de0f7d8f0e383a637f31c957..30588bbee1e85ab088a4a0c76641fa7e0e1b32c4 100644 --- a/packages/ui-vue/components/condition/src/condition.props.ts +++ b/packages/ui-vue/components/condition/src/condition.props.ts @@ -20,10 +20,18 @@ export const conditionProps = { conditions: { type: Array, default: [] }, fields: { type: Array, default: [] }, key: { type: String, default: '' }, - /** - * 控间标签同行展示 + /** + * 控间标签同行展示 + */ + isControlInline: { + type: [Boolean, String], default: 'auto', validator: (value) => { + return [true, false, 'auto'].includes(value); + } + }, + /** + * 条件项统一的样式 */ - isControlInline: { type: Boolean, default: true}, + itemClass: { type: Boolean, default: 'col-12 col-md-6 col-xl-3 col-el-2' } }; export type ConditionProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/condition/src/locale/locale.ts b/packages/ui-vue/components/condition/src/locale/locale.ts new file mode 100644 index 0000000000000000000000000000000000000000..64230c688c049b7cf5ae2039bf397ea296105a82 --- /dev/null +++ b/packages/ui-vue/components/condition/src/locale/locale.ts @@ -0,0 +1,36 @@ +import { useI18n } from 'vue-i18n'; +/** + * 此处当做静态处理 + * @param props + * @returns + */ +export function useConditionListLocale() { + const { t } = useI18n(); + const conditionListLocale = { + // 添加条件 + add: t('condition.add'), + // 生成条件组 + create: t('condition.create'), + // 重置 + reset: t('condition.reset'), + and:t('condition.and'), + or:t('condition.or'), + }; + return { conditionListLocale }; +} +export function useCompareLocale() { + const { t } = useI18n(); + const operatorsLocale = { + equal: t('operators.equal'), + notEqual: t('operators.notEqual'), + greater: t('operators.greater'), + greaterOrEqual: t('operators.greaterOrEqual'), + lessOrEqual: t('operators.lessOrEqual'), + less: t('operators.less'), + contains: t('operators.contains'), + startWith: t('operators.startWith'), + endWith: t('operators.endWith'), + }; + return { operatorsLocale }; +} + diff --git a/packages/ui-vue/components/condition/src/locales/designer/en.json b/packages/ui-vue/components/condition/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..dff5ce4ec06a03ce89153241cc2d33d0fd5a99c9 --- /dev/null +++ b/packages/ui-vue/components/condition/src/locales/designer/en.json @@ -0,0 +1,3 @@ +{ + "condition": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/condition/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/condition/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..dff5ce4ec06a03ce89153241cc2d33d0fd5a99c9 --- /dev/null +++ b/packages/ui-vue/components/condition/src/locales/designer/zh-CHS.json @@ -0,0 +1,3 @@ +{ + "condition": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/condition/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/condition/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..dff5ce4ec06a03ce89153241cc2d33d0fd5a99c9 --- /dev/null +++ b/packages/ui-vue/components/condition/src/locales/designer/zh-CHT.json @@ -0,0 +1,3 @@ +{ + "condition": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/condition/src/locales/ui/en.json b/packages/ui-vue/components/condition/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..9c3c07c72ce4e538fe5fd0eae6757814ce5387d2 --- /dev/null +++ b/packages/ui-vue/components/condition/src/locales/ui/en.json @@ -0,0 +1,31 @@ +{ + "condition": { + "add": "Add condition", + "create": "Create condition group", + "reset": "Reset", + "and": "And", + "or": "Or" + }, + "operators": { + "equal": "Equal", + "notEqual": "Not equal", + "greater": "Greater than", + "greaterOrEqual": "Greater than or equal", + "less": "Less than", + "lessOrEqual": "Less than or equal", + "contains": "Contains", + "notContains": "Does not contain", + "like": "Contains", + "notLike": "Does not contain", + "in": "In", + "notIn": "Not in", + "empty": "Is empty", + "notEmpty": "Is not empty", + "null": "Null", + "notNull": "Not null", + "startWith": "Starts with", + "endWith": "Ends with", + "and": "And", + "or": "Or" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/condition/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/condition/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..a190333267bf89c0e61ec2244511e821702ab598 --- /dev/null +++ b/packages/ui-vue/components/condition/src/locales/ui/zh-CHS.json @@ -0,0 +1,31 @@ +{ + "condition": { + "add": "添加条件", + "create": "生成条件组", + "reset": "重置", + "and": "与", + "or": "或" + }, + "operators": { + "equal": "等于", + "notEqual": "不等于", + "greater": "大于", + "greaterOrEqual": "大于等于", + "less": "小于", + "lessOrEqual": "小于等于", + "contains": "包含", + "notContains": "不包含", + "like": "包含", + "notLike": "不包含", + "in": "属于", + "notIn": "不属于", + "empty": "为空", + "notEmpty": "不为空", + "null": "null", + "notNull": "不为null", + "startWith": "开始是", + "endWith": "结束是", + "and": "与", + "or": "或" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/condition/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/condition/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..eca49485ab41e43fab8b0eba2c727454f0347ab5 --- /dev/null +++ b/packages/ui-vue/components/condition/src/locales/ui/zh-CHT.json @@ -0,0 +1,31 @@ +{ + "condition": { + "add": "新增條件", + "create": "建立條件群組", + "reset": "重設", + "and": "且", + "or": "或" + }, + "operators": { + "equal": "等於", + "notEqual": "不等於", + "greater": "大於", + "greaterOrEqual": "大於等於", + "less": "小於", + "lessOrEqual": "小於等於", + "contains": "包含", + "notContains": "不包含", + "like": "包含", + "notLike": "不包含", + "in": "屬於", + "notIn": "不屬於", + "empty": "為空", + "notEmpty": "不為空", + "null": "null", + "notNull": "不為null", + "startWith": "開始於", + "endWith": "結束於", + "and": "且", + "or": "或" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/content-container/src/designer/use-designer-rules.ts b/packages/ui-vue/components/content-container/src/designer/use-designer-rules.ts index c912399ddef6cb93c63ae0b214472c6f079d4940..c6ac285d9bffcbc59a0a7a9e4a5ce80c445b4f00 100644 --- a/packages/ui-vue/components/content-container/src/designer/use-designer-rules.ts +++ b/packages/ui-vue/components/content-container/src/designer/use-designer-rules.ts @@ -40,7 +40,26 @@ export function useDesignerRulesForContentContainer(designItemContext: DesignerI }); return matchedDataGridComponentRef; } - + /** + * 判断当前页面是否已启用筛选条 + */ + function checkFilterBarExisted() { + const formSchemaUtils = designerHostService?.formSchemaUtils; + const matchedDataGridComponentRef = formSchemaUtils.selectNode(schema, (item) => { + if (item.type === 'component-ref') { + const childComponent = formSchemaUtils.getComponentById(item.component); + if (childComponent?.componentType === 'data-grid') { + const filterBar = formSchemaUtils.selectNode(childComponent, childItem => { + return childItem.contents && childItem.contents.find(content => content.type === DgControl['filter-bar'].type); + }); + if (filterBar) { + return true; + } + } + } + }); + return matchedDataGridComponentRef; + } /** * 判断当前容器是否可接收筛选方案 */ @@ -52,8 +71,9 @@ export function useDesignerRulesForContentContainer(designItemContext: DesignerI return false; } if (sourceType === 'control') { + // 要求:当前页面不存在筛选方案、表格不可编辑、当前页面不存在筛选条 const solutionExisted = schema.contents && schema.contents.find(content => content.type === DgControl['section'].type && content.appearance?.class?.includes('f-section-scheme')); - return !solutionExisted && getUnEditableDataGrid(); + return !solutionExisted && getUnEditableDataGrid() && !checkFilterBarExisted(); } if (sourceType === 'move') { return true; diff --git a/packages/ui-vue/components/data-grid/src/components/data/data-area.component.tsx b/packages/ui-vue/components/data-grid/src/components/data/data-area.component.tsx index d788f415e3966aac5f6eb731876cf27a6b24418b..88524f4fd1c0561a0382d059c43fb4d1c7deb235 100644 --- a/packages/ui-vue/components/data-grid/src/components/data/data-area.component.tsx +++ b/packages/ui-vue/components/data-grid/src/components/data/data-area.component.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ import { Fragment, isVNode, Ref, SetupContext, VNodeArrayChildren, Comment, ref, reactive, watch } from 'vue'; -import FTooltip from '@farris/ui-vue/components/tooltip'; import { CellMode, DataColumn, DataViewType, UseCellContent, UseCellPosition, UseColumn, UseDataView, UseEdit, UseFitColumn, UseGroupData, UseNavigation, @@ -89,7 +88,7 @@ export default function ( return { display: 'flex', flexGrow: '1', - overflow:'hidden' + overflow: 'hidden' }; } return { @@ -223,6 +222,13 @@ export default function ( } function renderFormatter(cell: VisualDataCell, row: VisualData) { + if (cell.showTips && typeof cell.column?.formatter !== 'function' && cell.column?.dataType !== 'commands') { + return
+ { + cell.formatter!(cell, row) + } +
; + } return
{ cell.formatter!(cell, row) @@ -252,7 +258,7 @@ export default function (
{cell.getEditor(cell)}
: - cell.formatter ? renderFormatter(cell, row) : renderDefault(cell); + cell.column!.columnTemplate ? cell.column!.columnTemplate(cell, row) : cell.formatter ? renderFormatter(cell, row) : renderDefault(cell); } function renderDataRow( diff --git a/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts b/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts index 8d9a142f4646408c1c932cc89a9c10dfc955e9f7..6aee0053af4cc4e3711ca36a48a30b6e45ccdb11 100644 --- a/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts +++ b/packages/ui-vue/components/data-grid/src/composition/data-grid-component-creator.service.ts @@ -1,5 +1,5 @@ import { DesignerHostService } from '../../../designer-canvas/src/composition/types'; -import { DynamicResolver } from '../../../../components/dynamic-resolver'; +import { DynamicResolver, getSchemaByTypeForDesigner } from '../../../../components/dynamic-resolver'; import { ComponentBuildInfo } from '../../../component/src/composition/inner-component-build-info'; import { ComponentSchema } from '../../../../components/designer-canvas'; import { FormSchemaEntityField$Type, FormSchemaEntityFieldTypeName } from '@farris/ui-vue/components/common'; @@ -111,26 +111,22 @@ export class DataGridComponentCreatorService { const stateMachineRenderState = this.formStateMachineUtils && this.formStateMachineUtils.getRenderStates(); const btnType = DgControl['tab-page'].type === resolvedContainerType ? 'tab-toolbar-item' : 'section-toolbar-item'; - const btns = [{ - id: `button-add-${buildInfo.componentId}`, - type: btnType, - text: '新增', - disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, - appearance: { - class: '' - }, - onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` - }, - { - id: `button-remove-${buildInfo.componentId}`, - type: btnType, - text: '删除', - disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, - appearance: { - class: '' - }, - onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` - }]; + const btnSchema = this.resolver.getSchemaByType(btnType) as ComponentSchema; + const btns = [ + Object.assign({}, btnSchema, { + id: `button-add-${buildInfo.componentId}`, + type: btnType, + text: '新增', + disabled: stateMachineRenderState.find(d => d.id === 'canAddDetail') ? `!viewModel.stateMachine['canAddDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}AddItem1` + }), + Object.assign({}, btnSchema, { + id: `button-remove-${buildInfo.componentId}`, + type: btnType, + text: '删除', + disabled: stateMachineRenderState.find(d => d.id === 'canRemoveDetail') ? `!viewModel.stateMachine['canRemoveDetail']` : false, + onClick: `root-viewModel.${viewModelNode.id}.${commandPrefix}RemoveItem1` + })]; if (!resolvedContainerSchema.toolbar) { resolvedContainerSchema.toolbar = { id: `${resolvedContainerSchema.id}_toolbar`, buttons: [] }; } @@ -143,6 +139,10 @@ export class DataGridComponentCreatorService { resolvedContainerSchema.toolbar.buttons = resolvedContainerSchema.toolbar.buttons.concat(btns); this.appendAddAndDeleteCommands(viewModelNode); + + if (this.useFormCommand) { + this.useFormCommand.checkCommands(); + } } /** * 向视图模型添加新增删除命令 @@ -152,35 +152,39 @@ export class DataGridComponentCreatorService { const addCommandId = useGuid().guid(); const deleteCommandId = useGuid().guid(); const cardControllerId = this.resolveCommandController(); + const addCommand = { + id: addCommandId, + code: `${commandPrefix}AddItem1`, + name: '增加一条子表数据', + params: [], + handlerName: 'AddItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; + const deleteCommand = { + id: deleteCommandId, + code: `${commandPrefix}RemoveItem1`, + name: '删除一条子表数据', + params: [ + { + name: 'id', + shownName: '待删除子表数据的标识', + value: `{DATA~${viewModelNode.bindTo}/id}` + } + ], + handlerName: 'RemoveItem', + cmpId: cardControllerId, + shortcut: {}, + extensions: [] + }; - viewModelNode.commands.push( - { - id: addCommandId, - code: `${commandPrefix}AddItem1`, - name: '增加一条子表数据', - params: [], - handlerName: 'AddItem', - cmpId: cardControllerId, - shortcut: {}, - extensions: [] - }, - { - id: deleteCommandId, - code: `${commandPrefix}RemoveItem1`, - name: '删除一条子表数据', - params: [ - { - name: 'id', - shownName: '待删除子表数据的标识', - value: `{DATA~${viewModelNode.bindTo}/id}` - } - ], - handlerName: 'RemoveItem', - cmpId: cardControllerId, - shortcut: {}, - extensions: [] - } - ); + if (this.designerHostService.designerContext?.appendIdentifyForNewCommand) { + this.designerHostService.designerContext.appendIdentifyForNewCommand(addCommand); + this.designerHostService.designerContext.appendIdentifyForNewCommand(deleteCommand); + } + + viewModelNode.commands.push(addCommand, deleteCommand); // 3、记录构件命令 @@ -221,9 +225,7 @@ export class DataGridComponentCreatorService { name: 'AdvancedListCardController.webcmd', refedHandlers: [] }); - if (this.useFormCommand) { - this.useFormCommand.checkCommands(); - } + return listDicControllerId; @@ -367,6 +369,9 @@ export class DataGridComponentCreatorService { const vmFields: any[] = []; const { selectedFields } = buildInfo; selectedFields?.forEach(field => { + if (field.$type === FormSchemaEntityField$Type.ComplexField) { + return; + } let updateOn = 'blur'; const type = field.type.name; if (type === FormSchemaEntityFieldTypeName.Enum || type === FormSchemaEntityFieldTypeName.Boolean) { diff --git a/packages/ui-vue/components/data-grid/src/data-grid.component.tsx b/packages/ui-vue/components/data-grid/src/data-grid.component.tsx index b5c3eea21af25f6237e47bad87aaaee498dc020a..a13989f836f838d85df559568cbe0b8a05cecbcb 100644 --- a/packages/ui-vue/components/data-grid/src/data-grid.component.tsx +++ b/packages/ui-vue/components/data-grid/src/data-grid.component.tsx @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { DataGridProps, dataGridProps, PaginatonOptions } from './data-grid.props'; -import { computed, defineComponent, onUnmounted, onMounted, ref, nextTick, watch, SetupContext, inject, provide } from 'vue'; +import { computed, defineComponent, onUnmounted, onMounted, ref, nextTick, watch, SetupContext } from 'vue'; import { useResizeObserver } from '@vueuse/core'; +import { DataGridProps, dataGridProps, PaginatonOptions } from './data-grid.props'; import getDataArea from './components/data/data-area.component'; import { DataColumn, DataViewOptions, VisualData, getColumnHeader, getDisableMask, getFilterPanel, getGroupPanel, getHorizontalScrollbar, @@ -31,7 +31,7 @@ import './data-grid.css'; export default defineComponent({ name: 'FDataGrid', props: dataGridProps, - emits: ['changed', 'clickRow', 'doubleClickRow', 'selectionChange', 'unSelectItem', 'enterUpInLastCell', 'pageIndexChanged', 'pageSizeChanged', 'beginEditCell', 'endEditCell'], + emits: ['changed', 'clickRow', 'doubleClickRow', 'selectionChange', 'unSelectItem', 'selectItem', 'enterUpInLastCell', 'pageIndexChanged', 'pageSizeChanged', 'beginEditCell', 'endEditCell'], setup(props: DataGridProps, context) { const preloadCount = 0; const rowHeight = props.rowOption?.height || 28; @@ -207,7 +207,7 @@ export default defineComponent({ const { renderDisableMask } = getDisableMask(); - const { renderDataGridSummery } = getSummary(props as DataViewOptions, dataView, useColumnComposition); + const { renderDataGridSummary } = getSummary(props as DataViewOptions, dataView, useColumnComposition); const { renderHorizontalScrollbar } = getHorizontalScrollbar(props as DataViewOptions, gridContentRef, useVirtualScrollComposition); @@ -454,10 +454,6 @@ export default defineComponent({ useVirtualScrollComposition.scrollToRowByIndex(index); } - function scrollToRowById() { - - } - const dataGridComponentInstance = { activeRowById, addNewDataItem, addNewDataItemAtLast, removeDataItem, removeDataItemById, editDataItem, @@ -509,7 +505,7 @@ export default defineComponent({ {gridContentRef.value && renderHorizontalScrollbar()} {gridContentRef.value && renderVerticalScrollbar()}
- {gridContentRef.value && renderDataGridSummery()} + {gridContentRef.value && renderDataGridSummary()} {shouldRenderPagination.value && renderDataGridPagination()} {renderGridColumnResizeOverlay()} {isDisabled.value && renderDisableMask()} diff --git a/packages/ui-vue/components/data-grid/src/data-grid.css b/packages/ui-vue/components/data-grid/src/data-grid.css index f2a03c1c18604b09e9ca5a22690446ab65cb8866..8ba1e817b769f6c0f80b5a909866af44efb97f94 100644 --- a/packages/ui-vue/components/data-grid/src/data-grid.css +++ b/packages/ui-vue/components/data-grid/src/data-grid.css @@ -20,7 +20,6 @@ align-items: center; pointer-events: none; height: 100%; - /* top: 35px; */ width: 100%; } diff --git a/packages/ui-vue/components/data-grid/src/data-grid.props.ts b/packages/ui-vue/components/data-grid/src/data-grid.props.ts index 17222c12595611b2553f0ff3cc12d0519c48d988..d91d8beb0e05367c8558d4223c1a758c21580ff8 100644 --- a/packages/ui-vue/components/data-grid/src/data-grid.props.ts +++ b/packages/ui-vue/components/data-grid/src/data-grid.props.ts @@ -16,12 +16,12 @@ */ import { ExtractPropTypes, PropType, VNode } from 'vue'; import { EditorConfig } from '@farris/ui-vue/components/dynamic-form'; -import { - CommandOptions, - DataColumn, - HeaderCell, - VisualData, - VisualDataCell +import { + CommandOptions, + DataColumn, + HeaderCell, + VisualData, + VisualDataCell } from '@farris/ui-vue/components/data-view'; import { DataGridColumnCommand, SortType } from './designer/data-grid-column.props'; @@ -75,7 +75,7 @@ export interface DataGridColumn { /** 允许分组,默认为 true */ allowGrouping?: boolean; /** 是否多语字段 */ - isMultilingualField?: boolean; + // isMultilingualField?: boolean; /** 操作列命令 */ commands?: DataGridColumnCommand[]; /** True to allow the column can be filtered. */ @@ -83,12 +83,12 @@ export interface DataGridColumn { filter?: string; showSetting?: boolean; showEllipsis?: boolean; + /** 列模板 */ + columnTemplate?: (cell: VisualDataCell, visualDataRow: VisualData) => VNode; /** inner boolean formatter */ formatter?: ((cell: VisualDataCell, visualDataRow: VisualData) => VNode | string) | object; headerFormatter?: (context: { headerCell: HeaderCell, headerCells: HeaderCell[], columnIndex: number }) => VNode | string; - /** 格式化函数 */ - // format?: (cell: VisualDataCell, visualDataRow: VisualData) => VNode | string; } export interface ColumnGroupItem { diff --git a/packages/ui-vue/components/data-grid/src/designer/data-grid-column.design.component.tsx b/packages/ui-vue/components/data-grid/src/designer/data-grid-column.design.component.tsx index 9902f93a8f375fdd82632db6d164541bc8d590d8..be35bfb4daf2361f269eb0d16ebd44b2c2c2b74f 100644 --- a/packages/ui-vue/components/data-grid/src/designer/data-grid-column.design.component.tsx +++ b/packages/ui-vue/components/data-grid/src/designer/data-grid-column.design.component.tsx @@ -4,6 +4,7 @@ import { DesignerHostService, DesignerItemContext } from '../../../designer-canv import { useDesignerComponent } from '../../../designer-canvas/src/composition/function/use-designer-component'; import { useDesignerRulesForDataGridColumn } from './use-column-rules'; import { useFormBindingResolverDesign } from '../../../dynamic-form/src/composition/form-binding-resolver-design'; +import { DataGridDesignProps } from './data-grid-design.props'; export default defineComponent({ name: 'FDataGridColumnDesign', @@ -13,7 +14,9 @@ export default defineComponent({ const columnRef = ref(); const designerHostService = inject('designer-host-service') as DesignerHostService; const designItemContext = inject('design-item-context') as DesignerItemContext; - const designerRulesComposition = useDesignerRulesForDataGridColumn(designItemContext, designerHostService); + const dataGridContext: { dataGridProps: DataGridDesignProps } = inject('data-grid-context')!; + const designerRulesComposition = useDesignerRulesForDataGridColumn(designItemContext, designerHostService, + dataGridContext); const componentInstance = useDesignerComponent(columnRef, designItemContext, designerRulesComposition); onMounted(() => { columnRef.value.componentInstance = componentInstance; @@ -28,9 +31,12 @@ export default defineComponent({ } const { checkBindingFieldValidation } = useFormBindingResolverDesign(designerHostService, designItemContext, props); + // 外部容器引入的表单,不启用校验 + const externalContainerId = inject('external-container-id'); + const enableValidation = !externalContainerId; return () => { - const isValidBinding = checkBindingFieldValidation(); + const isValidBinding = enableValidation ? checkBindingFieldValidation() : true; const inValidTip = '绑定信息已失效,请手动移除列'; const titleStyle = isValidBinding ? '' : 'color:red'; return <> diff --git a/packages/ui-vue/components/data-grid/src/designer/data-grid-column.props.ts b/packages/ui-vue/components/data-grid/src/designer/data-grid-column.props.ts index 7ac26ee91c1beceefa7bf84d52c31b3b30f46f31..de29dee7dbf560c72bb1a1a0ca81b1134895bd64 100644 --- a/packages/ui-vue/components/data-grid/src/designer/data-grid-column.props.ts +++ b/packages/ui-vue/components/data-grid/src/designer/data-grid-column.props.ts @@ -94,8 +94,6 @@ export const dataGridColumnProps = { /** 列合并原始值 */ origianlColSpan: { type: Number }, index: { type: Number }, - /** 是否多语字段 */ - isMultilingualField: { type: Boolean, default: false }, /** 操作列命令 */ commands: { type: Array, default: [] }, filter: { type: String, default: '' }, diff --git a/packages/ui-vue/components/data-grid/src/designer/data-grid-design.props.ts b/packages/ui-vue/components/data-grid/src/designer/data-grid-design.props.ts index 5ca5469f4e7d21f920909057366640129fba0f2a..8c5b117b0766e25620109e40e0d29200a8bbb200 100644 --- a/packages/ui-vue/components/data-grid/src/designer/data-grid-design.props.ts +++ b/packages/ui-vue/components/data-grid/src/designer/data-grid-design.props.ts @@ -1,10 +1,14 @@ import { ExtractPropTypes } from "vue"; import { dataGridProps } from "../data-grid.props"; +import { excludeProperties } from '@farris/ui-vue/components/common'; -export const dataGridDesignProps = Object.assign({}, dataGridProps, { +export const dataGridDesignProps = excludeProperties(Object.assign({}, dataGridProps, { disabled: {}, editable: {}, componentId: { type: String, default: '' } -}); +}), 'columnOption'); + +// 设计时表格不识别操作列属性 +delete dataGridDesignProps.commandOption; export type DataGridDesignProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/data-grid/src/designer/data-grid.design.component.tsx b/packages/ui-vue/components/data-grid/src/designer/data-grid.design.component.tsx index bda59a8f1537390cf6daec9481b7f54e6df0827d..7dc168bdd721392ab5779744c832caff782b47ee 100644 --- a/packages/ui-vue/components/data-grid/src/designer/data-grid.design.component.tsx +++ b/packages/ui-vue/components/data-grid/src/designer/data-grid.design.component.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ import { DataGridDesignProps, dataGridDesignProps } from './data-grid-design.props'; -import { computed, defineComponent, onMounted, ref, nextTick, watch, SetupContext, inject } from 'vue'; +import { computed, defineComponent, onMounted, ref, nextTick, watch, SetupContext, inject, provide } from 'vue'; import { useResizeObserver } from '@vueuse/core'; import { useDesignerColumn, useDesignerFitColumn, useDesignerVisualData } from '../../../data-view/designer'; import { @@ -46,6 +46,7 @@ export default defineComponent({ dataGridRef.value.componentInstance = componentInstance; }); + provide('data-grid-context', {dataGridProps: props}); context.expose(componentInstance.value); const columns = ref(props.columns); const defaultVisibleCapacity = 20; diff --git a/packages/ui-vue/components/data-grid/src/designer/grid-field-editor.component.tsx b/packages/ui-vue/components/data-grid/src/designer/grid-field-editor.component.tsx index ba26a87a61076006072ccebce76d98f98c7228b0..b995289a5604d1bb91979c68999d71356a13eba4 100644 --- a/packages/ui-vue/components/data-grid/src/designer/grid-field-editor.component.tsx +++ b/packages/ui-vue/components/data-grid/src/designer/grid-field-editor.component.tsx @@ -19,6 +19,7 @@ import FButtonEdit from '../../../button-edit/src/button-edit.component'; import { gridFieldEditorProps, GridFieldEditorProps } from './grid-field-editor.props'; import FTransfer from '../../../transfer/src/transfer.component'; import { FormSchemaEntityField$Type } from '@farris/ui-vue/components/common'; +import { DesignerHostService } from '@farris/ui-vue/components/designer-canvas'; export default defineComponent({ name: 'FGridFieldEditor', @@ -29,7 +30,10 @@ export default defineComponent({ const viewModelId = ref(props.viewModelId); const designViewModelUtils = inject('designViewModelUtils') as any; const formSchemaUtils = inject('useFormSchema') as any; + const formSchemaService = inject('schemaService') as any; const controlCreatorUtils = inject('controlCreatorUtils') as any; + const designerContext = inject('designerContext') as any; + let fieldColumns = props.gridData['columns']; // 控制表格列是否需要有编辑器 const { fieldEditable } = props.gridData; @@ -40,10 +44,25 @@ export default defineComponent({ const dataSource = ref([]); // 打平关联字段后的列数据 let plainColumns = [] as any; + /** + * 检查当前设计器环境是否支持删除字段 + */ + function checkBeforeRemoveItem(removedItem: any, needNotify = true) { + if (designerContext.checkCanDeleteControl) { + const columnNode = plainColumns.find(column => column.data?.id === removedItem.id); + return designerContext.checkCanDeleteControl(columnNode, needNotify); + } + return true; + } const rowOptions = { customRowStatus: (visualData: any) => { const rawData = visualData['raw']; - visualData['disabled'] = rawData.type && rawData.type.fields && rawData.type.fields.length > 0 ? true : visualData['disabled']; + if (rawData.type && rawData.type.fields && rawData.type.fields.length > 0) { + visualData['disabled'] = true; + } + if (!checkBeforeRemoveItem(rawData, false)) { + visualData['disabled'] = true; + } } }; /** @@ -80,21 +99,46 @@ export default defineComponent({ } function convertTreeNodes2PlainObject(nodes: any[], r: any[] = []): any[] { if (nodes) { + // const fieldColumnsIds = fieldColumns.map(column => column.binding.field); nodes.forEach(n => { r.push(n); if (n.children) { convertTreeNodes2PlainObject(n.children, r); } + // 附加定制标识,限制字段的删除 + if (designerContext.appendIdentifyForNewControl) { + const gridColumn = fieldColumns.find(column => column.binding?.field === n.data.id); + if (!gridColumn || gridColumn[designerContext.identifyForNewControl]) { + designerContext.appendIdentifyForNewControl(n); + } + } }); } return r; } + /** + * 合并运行时定制中来自be或vo的字段 + */ + function resolveRtcEntityTreeData() { + if (formSchemaUtils.designerMode === 'PC_RTC' && formSchemaService.rtcAddedTreeNodes.value) { + const entityInfo = formSchemaService.getTableInfoByViewModelId(viewModelId.value); + if (entityInfo?.id) { + const rtcNodesInEntity = formSchemaService.rtcAddedTreeNodes.value[entityInfo.id]; + if (rtcNodesInEntity?.length) { + dataSource.value = dataSource.value.concat(rtcNodesInEntity); + } + } + + } + } /** * 更新dataSource */ function resetDataSource() { dataSource.value = designViewModelUtils['getAllFields2TreeByVMId'](viewModelId.value); + + resolveRtcEntityTreeData(); plainColumns = convertTreeNodes2PlainObject(dataSource.value); } @@ -134,10 +178,20 @@ export default defineComponent({ if (expFieldIndex > -1) { expressions[expFieldIndex].rules?.map(expresssionRule => { // 只有当前控件配置了表达式,才会触发删除 - const propertyInSchema = expresssionRule.type === 'visible' ? gridFieldData[expresssionRule.type] : gridFieldData.editor?.[expresssionRule.type]; - if (propertyInSchema && propertyInSchema.type === 'Expression' && propertyInSchema.expressionId === expresssionRule.id) { - if (!checkExpressionOccupiedRepeated(fieldId, expresssionRule, gridFieldData.id)) { - expresssionRule.needDelete = true; + switch (expresssionRule.type) { + case 'visible': case 'readonly': case 'required': { + const propertyInSchema = expresssionRule.type === 'visible' ? gridFieldData[expresssionRule.type] : gridFieldData.editor?.[expresssionRule.type]; + if (propertyInSchema && propertyInSchema.type === 'Expression' && propertyInSchema.expressionId === expresssionRule.id) { + if (!checkExpressionOccupiedRepeated(fieldId, expresssionRule, gridFieldData.id)) { + expresssionRule.needDelete = true; + } + } + break; + } + default: { + if (!checkExpressionOccupiedRepeated(fieldId, expresssionRule, gridFieldData.id)) { + expresssionRule.needDelete = true; + } } } }); @@ -203,7 +257,6 @@ export default defineComponent({ } function onBeforeOpen() { if (props.getLatestGridData) { - fieldColumns = props.getLatestGridData()['columns']; } else { fieldColumns = props.gridData['columns']; @@ -215,10 +268,10 @@ export default defineComponent({ } const columns = [{ field: 'name', title: '', dataType: 'string', width: '100%', formatter: (cellInfo, totalInfo) => { - return cellInfo.data + ` [${totalInfo.data['path'].data}]`; + return cellInfo.data + ` [${totalInfo.data['bindingField'].data}]`; } }, { - field: 'path', visible: false, title: '', dataType: 'string' + field: 'bindingField', visible: false, title: '', dataType: 'string' }]; onMounted(() => { @@ -242,10 +295,11 @@ export default defineComponent({ columns={columns} displayType={'Tree'} onChange={(datas) => onChangeHandler(datas)} + checkBeforeRemoveItem={checkBeforeRemoveItem} > {{ text: (itemInfo) => { - return
{itemInfo.data['name'].data} [{itemInfo.data['path'].data}]
; + return
{itemInfo.data['name'].data} [{itemInfo.data['bindingField'].data}]
; } }} diff --git a/packages/ui-vue/components/data-grid/src/designer/use-column-rules.ts b/packages/ui-vue/components/data-grid/src/designer/use-column-rules.ts index 4abe19de7abca1c9798a5b3ef37a55b344561183..296daf7fd5922e04c5225185b99be0855a20acb1 100644 --- a/packages/ui-vue/components/data-grid/src/designer/use-column-rules.ts +++ b/packages/ui-vue/components/data-grid/src/designer/use-column-rules.ts @@ -1,11 +1,14 @@ - - + + import { ref } from "vue"; import { DraggingResolveContext, UseDesignerRules } from "../../../designer-canvas/src/composition/types"; import { ComponentSchema, DesignerItemContext } from "../../../designer-canvas/src/types"; import { DataGriColumnProperty } from "../property-config/data-grid-column.property-config"; +import { DgControl } from "../../../designer-canvas/src/composition/dg-control"; +import { DataGridDesignProps } from "./data-grid-design.props"; -export function useDesignerRulesForDataGridColumn(designItemContext: DesignerItemContext, designerHostService): UseDesignerRules { +export function useDesignerRulesForDataGridColumn(designItemContext: DesignerItemContext, + designerHostService, dataGridContext: { dataGridProps: DataGridDesignProps }): UseDesignerRules { const schema = designItemContext.schema as ComponentSchema; /** 组件在拖拽时需要将所属的Component一起拖拽 */ const triggerBelongedComponentToMoveWhenMoved = ref(true); @@ -41,10 +44,38 @@ export function useDesignerRulesForDataGridColumn(designItemContext: DesignerIte // 构造属性配置方法 function getPropsConfig(componentId: string) { const gridColumnProp = new DataGriColumnProperty(componentId, designerHostService); - + const parentSummary = designItemContext?.parent?.schema; + if(parentSummary?.groupFields) { + if(parentSummary?.groupFields.includes(schema.field)) { + schema.enableSummary = true; + } + } return gridColumnProp.getPropertyConfig(schema, designItemContext?.parent?.schema); } + /** + * 配置表格的路径信息,用于事件交互面板显示“已有方法”的事件路径 + */ + function setComponentBasicInfoMap() { + if (!designerHostService || !designItemContext) { + return; + } + const { formSchemaUtils } = designerHostService; + + let parentPath = DgControl['data-grid'].name || '表格'; + const controlBasicInfoMap = formSchemaUtils.getControlBasicInfoMap(); + // 先取父级表格上的路径信息 + const dataGridBasicInfo = controlBasicInfoMap.get(designItemContext.parent?.schema?.id); + if (dataGridBasicInfo && dataGridBasicInfo.parentPathName) { + parentPath = dataGridBasicInfo.parentPathName; + } + // 列的展示名称和路径 + controlBasicInfoMap.set(schema.id, { + showName: schema.title, + parentPathName: `${parentPath} > ${schema.title}`, + reliedComponentId: dataGridBasicInfo.reliedComponentId + }); + } return { canAccepts, checkCanDeleteComponent, @@ -52,7 +83,8 @@ export function useDesignerRulesForDataGridColumn(designItemContext: DesignerIte hideNestedPaddingInDesginerView, triggerBelongedComponentToMoveWhenMoved, triggerBelongedComponentToDeleteWhenDeleted, - getPropsConfig + getPropsConfig, + setComponentBasicInfoMap } as UseDesignerRules; } diff --git a/packages/ui-vue/components/data-grid/src/designer/use-designer-rules.ts b/packages/ui-vue/components/data-grid/src/designer/use-designer-rules.ts index 0f730c481d5b1c34ea5b0beb717115d68df3bc37..f7af4f88054d7a68eadbebd1061c4251f7704cbf 100644 --- a/packages/ui-vue/components/data-grid/src/designer/use-designer-rules.ts +++ b/packages/ui-vue/components/data-grid/src/designer/use-designer-rules.ts @@ -1,5 +1,6 @@ import { ref } from "vue"; import { DraggingResolveContext, DesignerHTMLElement, UseDesignerRules, DesignerHostService } from "../../../designer-canvas/src/composition/types"; +import { DgControl } from "../../../designer-canvas/src/composition/dg-control"; import { ComponentSchema, DesignerItemContext } from "../../../designer-canvas/src/types"; import { DataGridProperty } from "../property-config/data-grid.property-config"; @@ -47,7 +48,42 @@ export function useDesignerRulesForDataGrid(designItemContext: DesignerItemConte return dataGridProp.getPropertyConfig(schema); } + /** + * 配置表格的路径信息,用于事件交互面板显示“已有方法”的事件路径 + */ + function setComponentBasicInfoMap() { + if (designItemContext && designerHostService) { + const belongedComponentId = designItemContext?.componentInstance?.value.belongedComponentId; + let parentTitle = ''; + let reliedComponentId = ''; + const { formSchemaUtils } = designerHostService; + if (belongedComponentId) { + const rootViewModelId = formSchemaUtils.getRootViewModelId(); + const rootComponent = formSchemaUtils.getComponentByViewModelId(rootViewModelId); + const parentSchemaOfComponent = formSchemaUtils.selectNode(rootComponent, item => { + return item.contents && item.contents.find(childItem => childItem.component === belongedComponentId); + }); + // 父级为tab-page + if (parentSchemaOfComponent?.type === DgControl['tab-page']?.type && parentSchemaOfComponent?.contents?.length) { + parentTitle = parentSchemaOfComponent.title || ''; + } + // 父级为section + if (parentSchemaOfComponent?.type === DgControl.section?.type && parentSchemaOfComponent?.showHeader !== false) { + parentTitle = parentSchemaOfComponent.mainTitle || ''; + } + parentTitle = parentTitle ? `${parentTitle} > ` : ''; + reliedComponentId = parentTitle ? parentSchemaOfComponent.id : ''; + } + const dataGridName = DgControl['data-grid'].name; + designerHostService?.formSchemaUtils.getControlBasicInfoMap().set(designItemContext.schema.id, { + componentTitle: dataGridName, + parentPathName: `${parentTitle}${dataGridName}`, + reliedComponentId + }); + } + + } return { canAccepts, checkCanDeleteComponent, @@ -55,7 +91,8 @@ export function useDesignerRulesForDataGrid(designItemContext: DesignerItemConte hideNestedPaddingInDesginerView, triggerBelongedComponentToMoveWhenMoved, triggerBelongedComponentToDeleteWhenDeleted, - getPropsConfig + getPropsConfig, + setComponentBasicInfoMap } as UseDesignerRules; } diff --git a/packages/ui-vue/components/data-grid/src/locales/designer/en.json b/packages/ui-vue/components/data-grid/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/data-grid/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/data-grid/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/data-grid/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/data-grid/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/data-grid/src/locales/ui/en.json b/packages/ui-vue/components/data-grid/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..9a0b353b4183a4348f7fca8a7444f34c607134a1 --- /dev/null +++ b/packages/ui-vue/components/data-grid/src/locales/ui/en.json @@ -0,0 +1,122 @@ +{ + "datagrid": { + "lineNumberTitle": "NO.", + "emptyMessage": "Empty Data", + "pagination": { + "previousLabel": "Prev Page", + "nextLabel": "Next Page", + "message": "Total {1} Items", + "pagelist": { + "firstText": "Display", + "lastText": "items" + } + }, + "filter": { + "title": "Conditions", + "reset": "Reset", + "clear": "Clear", + "clearAll": "Clear All Conditions", + "setting": "Settings", + "nofilter": "[ Empty ]", + "checkAll": "Check All", + "and": "And", + "or": "Or", + "operators": { + "equal": "equal", + "notEqual": "not equal", + "greater": "greater than", + "greaterOrEqual": "greater than or equal", + "less": "less than", + "lessOrEqual": "less than or equal", + "contains": "contains", + "notContains": "not contains", + "like": "contains", + "notLike": "not contains", + "in": "in", + "notIn": "not in", + "empty": "empty", + "notEmpty": "not empty", + "null": "null", + "notNull": "not null" + }, + "more": "More", + "ok": "ok", + "cancel": "cancel", + "sevenDays": "Seven Days", + "oneMonth": "One Month", + "threeMonths": "Three Months", + "sixMonths": "Six Months" + }, + "settings": { + "visible": "Display Columns", + "sortting": "Sortting", + "title": "Column Settings", + "canchoose": "Can choose", + "choosed": "Choosed", + "asc": "ASC", + "desc": "DESC", + "cancelSort": "Cancel sortting", + "ok": "OK", + "cancel": "Cancel", + "reset": "Reset", + "conciseMode": "Concise", + "advancedMode": "Advanced", + "formatSetting": "Column format", + "properties": "Column properties", + "groupping": "Groupping", + "allColumns": "All", + "visibleColumns": "Visible", + "hiddenColumns": "Hidden", + "searchPlaceholder": "Please enter a column name", + "checkall": "Show or hide all", + "headeralign": "Header alignment", + "dataalign": "Data alignment", + "alignLeft": "Left", + "alignCenter": "Center", + "alignRight": "Right", + "summarytype": "Summary type", + "summarytext": "Summary text", + "summaryNone": "None", + "summarySum": "Sum", + "summaryMax": "Max", + "summaryMin": "Min", + "summarCount": "Count", + "summaryAverage": "Average", + "grouppingField": "Groupping field", + "moreGrouppingFieldWarningMessage": "Up to 3 fields are set for grouping", + "grouppingSummary": "Group total", + "addGrouppingFieldTip": "Add groupping field", + "removeGrouppingFieldTip": "Remove groupping field", + "grouppingSummaryType": "Group total type", + "grouppingSummaryText": "Group total text", + "restoreDefaultSettingsText": "Are you sure you want to restore the default settings", + "simple": { + "title": "Show Columns", + "tip": "The selected fields can be displayed in the list. Drag to adjust the display order in the list.", + "count": "show {0} columns" + } + }, + "selectionData": { + "clearAll": "Clear all", + "tooltip": "Click here show list.", + "currentLenth": "{0} items selected." + }, + "groupRow": { + "tips": "Drag columns here to group data.", + "removeColumn": "Remove the group column.", + "clearTip": "Clear all grouped fields.", + "clear": "Empty" + }, + "summary": { + "title": "Current Page Summary" + }, + "loadingMessage": "Loading", + "commandColumn": { + "title": "Operation", + "edit": "Edit", + "remove": "Remove", + "cancel": "Cancel", + "accept": "Accept" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/data-grid/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/data-grid/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..9066c3393e6cb723fab5a5410539f61953c9a8d5 --- /dev/null +++ b/packages/ui-vue/components/data-grid/src/locales/ui/zh-CHS.json @@ -0,0 +1,122 @@ +{ + "datagrid": { + "lineNumberTitle": "序号", + "emptyMessage": "暂无数据", + "pagination": { + "previousLabel": "上一页", + "nextLabel": "下一页", + "message": "共 {1} 条", + "pagelist": { + "firstText": "显示", + "lastText": "条" + } + }, + "filter": { + "title": "筛选", + "reset": "重置", + "clear": "清空", + "clearAll": "清空所有条件", + "setting": "高级设置", + "nofilter": "[ 无 ]", + "checkAll": "全选", + "and": "并且", + "or": "或者", + "operators": { + "equal": "等于", + "notEqual": "不等于", + "greater": "大于", + "greaterOrEqual": "大于等于", + "less": "小于", + "lessOrEqual": "小于等于", + "contains": "包含", + "notContains": "不包含", + "like": "包含", + "notLike": "不包含", + "in": "属于", + "notIn": "不属于", + "empty": "为空", + "notEmpty": "不为空", + "null": "null", + "notNull": "不为null" + }, + "more": "查看更多", + "ok": "确定", + "cancel": "取消", + "sevenDays": "七天", + "oneMonth": "一个月", + "threeMonths": "三个月", + "sixMonths": "半年" + }, + "settings": { + "visible": "显示列", + "sortting": "列排序", + "title": "列配置", + "canchoose": "可选列", + "choosed": "已选列", + "asc": "升序", + "desc": "降序", + "cancelSort": "取消排序", + "ok": "确定", + "cancel": "取消", + "reset": "恢复默认", + "conciseMode": "简洁模式", + "advancedMode": "高级模式", + "formatSetting": "列格式", + "properties": "列属性", + "groupping": "分组", + "allColumns": "所有列", + "visibleColumns": "可见列", + "hiddenColumns": "隐藏列", + "searchPlaceholder": "请输入列名称", + "checkall": "全部显示/隐藏", + "headeralign": "表头对齐", + "dataalign": "数据对齐", + "alignLeft": "左对齐", + "alignCenter": "居中对齐", + "alignRight": "右对齐", + "summarytype": "汇总合计类型", + "summarytext": "汇总合计文本", + "summaryNone": "无", + "summarySum": "求和", + "summaryMax": "最大值", + "summaryMin": "最小值", + "summarCount": "计数", + "summaryAverage": "平均值", + "grouppingField": "分组字段", + "moreGrouppingFieldWarningMessage": "最多设置3个字段进行分组", + "grouppingSummary": "分组合计", + "addGrouppingFieldTip": "添加分组字段", + "removeGrouppingFieldTip": "移除分组字段", + "grouppingSummaryType": "分组合计类型", + "grouppingSummaryText": "分组合计文本", + "restoreDefaultSettingsText": "确认要恢复默认设置吗?", + "simple": { + "title": "显示列", + "tip": "选中的字段可展示到列表中,拖拽可调整在列表中的展示顺序。", + "count": "已显示 {0} 列" + } + }, + "selectionData": { + "clearAll": "清空", + "tooltip": "点击显示已选记录列表", + "currentLenth": "已选择:{0} 条" + }, + "groupRow": { + "tips": "拖动列到这儿可进行数据分组", + "removeColumn": "移除分组列", + "clearTip": "清除所有分组字段", + "clear": "清空" + }, + "summary": { + "title": "当页合计" + }, + "loadingMessage": "正在加载", + "commandColumn": { + "title": "操作", + "edit": "编辑", + "remove": "删除", + "cancel": "取消", + "accept": "确定" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/data-grid/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/data-grid/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..39ecc8c2cbb8ed6ec3c73339addc32943edf80b3 --- /dev/null +++ b/packages/ui-vue/components/data-grid/src/locales/ui/zh-CHT.json @@ -0,0 +1,122 @@ +{ + "datagrid": { + "lineNumberTitle": "序號", + "emptyMessage": "暫無數據", + "pagination": { + "previousLabel": "上一頁", + "nextLabel": "下一頁", + "message": "共 {1} 條", + "pagelist": { + "firstText": "顯示", + "lastText": "條" + } + }, + "filter": { + "title": "過濾條件", + "reset": "重置", + "clear": "清空條件", + "clearAll": "清空所有條件", + "setting": "高級設置", + "nofilter": "[ 無 ]", + "checkAll": "全選", + "and": "並且", + "or": "或者", + "operators": { + "equal": "等於", + "notEqual": "不等於", + "greater": "大於", + "greaterOrEqual": "大於等於", + "less": "小於", + "lessOrEqual": "小於等於", + "contains": "包含", + "notContains": "不包含", + "like": "包含", + "notLike": "不包含", + "in": "屬於", + "notIn": "不屬於", + "empty": "為空", + "notEmpty": "不為空", + "null": "null", + "notNull": "不為null" + }, + "more": "查看更多", + "ok": "確定", + "cancel": "取消", + "sevenDays": "七天", + "oneMonth": "一個月", + "threeMonths": "三個月", + "sixMonths": "半年" + }, + "settings": { + "visible": "顯示列", + "sortting": "列排序", + "title": "列配置", + "canchoose": "可選列", + "choosed": "已選列", + "asc": "升序", + "desc": "降序", + "cancelSort": "取消排序", + "ok": "確定", + "cancel": "取消", + "reset": "恢複默認", + "conciseMode": "簡潔模式", + "advancedMode": "高級模式", + "formatSetting": "列格式", + "properties": "列屬性", + "groupping": "分組", + "allColumns": "所有列", + "visibleColumns": "可見列", + "hiddenColumns": "隱藏列", + "searchPlaceholder": "請輸入列名稱", + "checkall": "全部顯示/隱藏", + "headeralign": "表頭對齊", + "dataalign": "數據對齊", + "alignLeft": "左對齊", + "alignCenter": "居中對齊", + "alignRight": "右對齊", + "summarytype": "匯總合計類型", + "summarytext": "匯總合計文本", + "summaryNone": "無", + "summarySum": "求和", + "summaryMax": "最大值", + "summaryMin": "最小值", + "summarCount": "計數", + "summaryAverage": "平均值", + "grouppingField": "分組字段", + "moreGrouppingFieldWarningMessage": "最多設置3個字段進行分組", + "grouppingSummary": "分組合計", + "addGrouppingFieldTip": "添加分組字段", + "removeGrouppingFieldTip": "移除分組字段", + "grouppingSummaryType": "分組合計類型e", + "grouppingSummaryText": "分組合計文本", + "restoreDefaultSettingsText": "確認要恢複默認設置嗎", + "simple": { + "title": "顯示列", + "tip": "選中的字段可展示到列表中,拖拽可調整在列表中的展示順序。", + "count": "已顯示 {0} 列" + } + }, + "selectionData": { + "clearAll": "清空", + "tooltip": "點擊顯示已選記錄列錶", + "currentLenth": "已選擇:{0} 條" + }, + "groupRow": { + "tips": "拖動列到這兒可進行數據分組", + "removeColumn": "移除分組列", + "clearTip": "清除所有分組字段", + "clear": "清空" + }, + "summary": { + "title": "當頁合計" + }, + "loadingMessage": "正在載入", + "commandColumn": { + "title": "操作", + "edit": "編輯", + "remove": "刪除", + "cancel": "取消", + "accept": "確定" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts b/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts index c436907b71cfe124ae1a7bd132a93dca373e3f01..57423ba2651204fa412e5250437fe6fb02bfb2ef 100644 --- a/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts +++ b/packages/ui-vue/components/data-grid/src/property-config/data-grid-column.property-config.ts @@ -1,4 +1,6 @@ +import { FormSchemaEntityField$Type, FormSchemaEntityFieldType$Type } from '@farris/ui-vue/components/common'; +import { FNotifyService as NotifyService } from "@farris/ui-vue/components/notify"; import { BaseControlProperty } from '../../../property-panel/src/composition/entity/base-property'; import { DgControl } from '../../../designer-canvas/src/composition/dg-control'; import { InputGroupProperty } from '../../../input-group/src/property-config/input-group.property-config'; @@ -14,26 +16,152 @@ import { NumberSpinnerProperty } from '../../../number-spinner/src/property-conf import { RadioGroupProperty } from '../../../radio-group/src/property-config/radio-group.property-config'; import { SwitchProperty } from '../../../switch/src/property-config/switch.property-config'; import { TimePickerProperty } from '../../../time-picker/src/property-config/time-picker.property-config'; +import { LanuageTextBoxPropertyConfig } from '../../../language-textbox/src/property-config/language-textbox.property-config'; import { SchemaDOMMapping } from '../../../property-panel/src/composition/entity/schema-dom-mapping'; -import { FormSchemaEntityField$Type } from '@farris/ui-vue/components/common'; +import { DataGridDesignProps } from '../designer/data-grid-design.props'; export class DataGriColumnProperty extends BaseControlProperty { + private dataGridProps: DataGridDesignProps | null = null; + private notifyService = new NotifyService(); constructor(componentId: string, designerHostService: any) { super(componentId, designerHostService); } getPropertyConfig(propertyData: any, gridData: any) { + this.dataGridProps = gridData; // 基本信息 this.getBasicPropConfig(propertyData); // 外观 - this.getAppearanceProperties(propertyData); + this.getAppearanceProperties(propertyData, gridData); // 行为 - this.propertyConfig.categories['behavior'] = this.getBehaviorConfig(propertyData, 'gridFieldEditor'); + const isVisibleEnum = propertyData.formatter?.type === 'enum' && !propertyData.editor; + const extendProperties = { + formatterEnumData: { + description: "", + title: "数据", + type: "array", + visible: !!isVisibleEnum, + $converter: '/converter/change-formatter-enum.converter', + ...this.getItemCollectionEditor(propertyData, 'value', 'name'), + // 这个属性,标记当属性变更得时候触发重新更新属性 + refreshPanelAfterChanged: true + } + }; + const setPropertyRelates = (changeObject: any, parameters: any) => { + if (!changeObject) { + return; + } + switch (changeObject.propertyID) { + case 'formatterEnumData': { + // 如果可以编辑数据,要同步更新格式化 + if (propertyData.formatter) { + propertyData.formatter.data = [...changeObject.propertyValue]; + } + break; + } + } + }; + this.propertyConfig.categories['behavior'] = this.getBehaviorConfig( + propertyData, + 'gridFieldEditor', + extendProperties, + setPropertyRelates + ); // 编辑器 this.getFieldEditorProperties(propertyData, gridData); - + // 列模板或者列格式化 + if (propertyData.dataType !== 'date' && propertyData.dataType !== 'datetime' && propertyData.formatter?.type !== 'multilingual') { + this.propertyConfig.categories['formatter'] = this.getTemplateProperties(propertyData); + } + // 列事件 + this.getEventPropConfig(propertyData); return this.propertyConfig; } + /** + * 枚举项编辑器 + * @param propertyData + * @param valueField + * @param textField + * @returns + */ + protected getItemCollectionEditor(propertyData, valueField: string, textField: string) { + const realValueField = valueField || 'value'; + const realTextField = textField || 'name'; + return { + editor: { + columns: [ + { field: realValueField, title: '值', dataType: 'string' }, + { field: realTextField, title: '名称', dataType: 'string' }, + ], + type: "item-collection-editor", + valueField: realValueField, + nameField: realTextField, + requiredFields: [realValueField, realTextField], + uniqueFields: [realValueField, realTextField], + readonly: this.checkEnumDataReadonly(propertyData) + } + }; + } + /** + * 判断枚举数据是否只读 + * 1、没有绑定信息或者绑定变量,可以新增、删除、修改 + * 2、绑定类型为字段,且字段为枚举字段,则不可新增、删除、修改枚举值。只能从be修改然后同步到表单上。 + * @param propertyData 下拉框控件属性值 + */ + protected checkEnumDataReadonly(propertyData: any): boolean { + // 没有绑定信息或者绑定变量 + if (!propertyData.binding || propertyData.binding.type !== 'Form') { + return false; + } + if (this.designViewModelField && this.designViewModelField.type && + this.designViewModelField.type.$type === FormSchemaEntityFieldType$Type.EnumType) { + // 低代码、零代码,枚举字段均不可以改 + return true; + } + return false; + } + private getEventPropConfig(propertyData: any) { + const events: { label: string, name: string }[] = [ + { + label: 'onClickLinkCommand', + name: '超链接事件' + } + ]; + const self = this; + const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); + const properties = self.createBaseEventProperty(initialData); + + this.propertyConfig.categories['eventsEditor'] = { + title: '事件', + hideTitle: true, + properties, + // 这个属性,标记当属性变更得时候触发重新更新属性 + refreshPanelAfterChanged: true, + tabId: 'commands', + tabName: '交互', + setPropertyRelates(changeObject: any, data: any) { + const parameters = changeObject.propertyValue; + delete propertyData[self.viewModelId]; + if (parameters) { + parameters.setPropertyRelates = this.setPropertyRelates; // 添加自定义方法后,调用此回调方法,用于处理联动属性 + self.eventsEditorUtils.saveRelatedParameters(propertyData, self.viewModelId, parameters['events'], parameters); + } + // 联动修改排序开关 + propertyData.remoteSort = propertyData.columnSorted ? true : false; + + if (propertyData.onClickLinkCommand && !propertyData.onClickLinkCommand.includes('communication:')) { + // 同步超链接模板 + // 替换自定义模板 + propertyData.columnTemplate = ` + {{rowData.${propertyData.field}}} + `; + // 刷新属性面板 + self.getFormDesignerInstance()?.reloadPropertyPanel(); + } + } + }; + } getBasicPropConfig(propertyData: any) { const self = this; this.propertyConfig.categories['basic'] = { @@ -79,12 +207,13 @@ export class DataGriColumnProperty extends BaseControlProperty { // 刷新实体树 changeObject.needRefreshEntityTree = true; break; - } + }; } } }; } - getAppearanceProperties(propertyData: any) { + getAppearanceProperties(propertyData: any, gridData: any) { + const self = this; this.propertyConfig.categories['appearance'] = { description: '', title: '外观', @@ -94,6 +223,21 @@ export class DataGriColumnProperty extends BaseControlProperty { title: '拖拽改变列宽', type: 'boolean' }, + // filterable: { + // description: '启用过滤', + // title: '启用过滤', + // type: 'boolean' + // }, + width: { + description: '列宽', + title: '列宽', + type: 'number' + }, + showTips: { + description: '启用鼠标悬浮提示', + title: '鼠标悬浮提示', + type: 'boolean' + }, halign: { description: '标题对齐方式', title: '标题对齐方式', @@ -129,6 +273,58 @@ export class DataGriColumnProperty extends BaseControlProperty { { id: 'bottom', name: '底对齐' } ] } + }, + columnTemplate: { + description: '列模板', + title: '列模板', + type: 'string', + refreshPanelAfterChanged: true, + editor: { + type: "code-editor", + language: "html", + } + }, + summaryType: { + description: '合计类型', + title: '合计类型', + visible: !!this.dataGridProps?.summary?.enable && propertyData.dataType === 'number', + type: 'enum', + editor: { + data: [ + { id: 'none', name: '无' }, + { id: 'sum', name: '求和' } + ] + }, + refreshPanelAfterChanged: true + } + }, + setPropertyRelates(changeObject, prop, paramters: any) { + if (!changeObject) { + return; + } + switch (changeObject && changeObject.propertyID) { + case 'width': { + prop.actualWidth = changeObject.propertyValue; + break; + } + case 'summaryType': { + // 更新合计行字段集合 + const { groupFields } = gridData.summary; + if (!groupFields.includes(prop.field) && changeObject.propertyValue === 'sum') { + groupFields.push(prop.field); + } + if (groupFields.includes(prop.field) && changeObject.propertyValue === 'none') { + groupFields.splice(groupFields.indexOf(prop.field), 1); + } + break; + } + case 'columnTemplate': { + // 提示以列模板为主 + if (changeObject.propertyValue) { + self.notifyService.warning({ position: 'top-center', message: '注意:已自定义列模板,【列格式化】【悬浮提示】【数据水平对齐方式】【数据垂直对齐方式】等列属性会失效' }); + } + break; + } } } }; @@ -138,6 +334,199 @@ export class DataGriColumnProperty extends BaseControlProperty { this.getFieldEditorProp(propertyData); } } + + private setFormatterEnumData(propertyData: any) { + if (propertyData.formatter?.data && !propertyData.formatterEnumData) { + propertyData.formatterEnumData = propertyData.formatter?.data; + } + } + + private getTemplateProperties(propertyData: any) { + const self = this; + const hasOwnProps = Object.prototype.hasOwnProperty; + const formatterType = { + enum: [ + { id: 'enum', name: '枚举' }, + // { id: 'custom', name: '自定义模板' }, + { id: 'none', name: '无' } + ], + boolean: [ + { id: 'boolean', name: '布尔' }, + // { id: 'custom', name: '自定义模板' }, + { id: 'none', name: '无' } + ], + number: [ + { id: 'number', name: '数字' }, + // { id: 'custom', name: '自定义模板' }, + { id: 'none', name: '无' } + ], + date: [ + { id: 'date', name: '日期' }, + // { id: 'custom', name: '自定义模板' }, + { id: 'none', name: '无' } + ], + string: [ + { id: 'enum', name: '枚举' }, + // { id: 'boolean', name: '布尔' }, + // { id: 'number', name: '数字' }, + // { id: 'date', name: '日期' }, + // { id: 'custom', name: '自定义模板' }, + { id: 'none', name: '无' } + ] + }; + return { + title: '列格式化', + description: '', + properties: { + type: { + description: '', + title: '类型', + type: 'enum', + $converter: '/converter/change-formatter.converter', + refreshPanelAfterChanged: true, + editor: { + data: formatterType[propertyData.dataType] + } + }, + // data: { + // description: '', + // title: '选项', + // type: 'enum', + // $converter: '/converter/change-formatter.converter', + // visible: propertyData.formatter.type === 'enum', + // // parentPropertyID: 'formatter', + // // refreshPanelAfterChanged: true, + // }, + // customFormat: { + // title: '自定义模板', + // type: 'string', + // visible: !!propertyData.formatter && propertyData.formatter.type === 'custom', + // $converter: '/converter/change-formatter.converter', + // description: '自定义模板', + // refreshPanelAfterChanged: true, + // editor: { + // type: "code-editor", + // language: "html", + // } + // }, + trueText: { + title: '布尔为true时文本', + $converter: '/converter/change-formatter.converter', + type: 'string', + visible: !!propertyData.formatter && propertyData.formatter.type === 'boolean', + refreshPanelAfterChanged: true, + description: '布尔为true时文本' + }, + falseText: { + title: '布尔为false时文本', + $converter: '/converter/change-formatter.converter', + type: 'string', + visible: !!propertyData.formatter && propertyData.formatter.type === 'boolean', + refreshPanelAfterChanged: true, + description: '布尔为false时文本' + }, + prefix: { + title: '前缀', + $converter: '/converter/change-formatter.converter', + type: 'string', + visible: !!propertyData.formatter && propertyData.formatter.type === 'number', + refreshPanelAfterChanged: true, + description: '前缀' + }, + suffix: { + title: '后缀', + $converter: '/converter/change-formatter.converter', + type: 'string', + visible: !!propertyData.formatter && propertyData.formatter.type === 'number', + refreshPanelAfterChanged: true, + description: '后缀' + }, + precision: { + title: '精度', + $converter: '/converter/change-formatter.converter', + type: 'number', + visible: !!propertyData.formatter && propertyData.formatter.type === 'number', + refreshPanelAfterChanged: true, + description: '精度' + }, + decimal: { + title: '小数分隔符', + $converter: '/converter/change-formatter.converter', + type: 'string', + visible: !!propertyData.formatter && propertyData.formatter.type === 'number', + refreshPanelAfterChanged: true, + description: '小数分隔符' + }, + thousand: { + title: '千分位', + $converter: '/converter/change-formatter.converter', + type: 'string', + visible: !!propertyData.formatter && propertyData.formatter.type === 'number', + refreshPanelAfterChanged: true, + description: '千分位' + }, + // dateFormat: { + // title: '日期格式', + // $converter: '/converter/change-formatter.converter', + // type: 'string', + // visible: !!propertyData.formatter && propertyData.formatter.type === 'date', + // refreshPanelAfterChanged: true, + // description: '日期格式' + // } + }, + setPropertyRelates(changeObject, prop, paramters: any) { + if (!changeObject) { + return; + } + switch (changeObject && changeObject.propertyID) { + case 'type': { + // 清空列模板 + if (changeObject.propertyValue !== 'none' && prop.columnTemplate) { + prop.columnTemplate = ''; + // 提示以列格式化为主 + self.notifyService.warning({ position: 'top-center', message: '注意:已设置列格式化,【列模板】被清空且会失效' }); + } + if (changeObject.propertyValue === 'boolean') { + if (!hasOwnProps.call(prop.formatter, 'trueText')) { + prop.formatter.trueText = '是'; + } + if (!hasOwnProps.call(prop.formatter, 'falseText')) { + prop.formatter.falseText = '否'; + } + } + if (changeObject.propertyValue === 'number') { + if (!hasOwnProps.call(prop.formatter, 'prefix')) { + prop.formatter.prefix = ''; + } + if (!hasOwnProps.call(prop.formatter, 'suffix')) { + prop.formatter.suffix = ''; + } + if (!hasOwnProps.call(prop.formatter, 'precision')) { + prop.formatter.precision = 0; + } + if (!hasOwnProps.call(prop.formatter, 'decimal')) { + prop.formatter.decimal = '.'; + } + if (!hasOwnProps.call(prop.formatter, 'thousand')) { + prop.formatter.thousand = ','; + } + } + if (changeObject.propertyValue === 'date') { + if (!hasOwnProps.call(prop.formatter, 'dateFormat')) { + prop.formatter.dateFormat = 'yyyy-MM-dd'; + } + } + if (changeObject.propertyValue === 'enum') { + if (!hasOwnProps.call(prop.formatter, 'data')) { + prop.formatter.data = []; + } + } + break; + } + } + } + }; + } /** * 列编辑器属性 * @param propertyData 列属性值 @@ -184,6 +573,9 @@ export class DataGriColumnProperty extends BaseControlProperty { case 'time-picker': editorTypeConfig = new TimePickerProperty(this.componentId, this.designerHostService); break; + case 'language-textbox': + editorTypeConfig = new LanuageTextBoxPropertyConfig(this.componentId, this.designerHostService); + break; default: editorTypeConfig = new InputGroupProperty(this.componentId, this.designerHostService); } @@ -195,8 +587,6 @@ export class DataGriColumnProperty extends BaseControlProperty { title: '编辑器类型', propertyData: propertyData.editor, tabId: 'gridFieldEditor', - // enableCascade: true, - parentPropertyID: 'editor', tabName: '编辑器', properties: { type: { @@ -204,6 +594,7 @@ export class DataGriColumnProperty extends BaseControlProperty { title: '编辑器类型', type: 'enum', $converter: '/converter/change-editor.converter', + parentPropertyID: 'editor', refreshPanelAfterChanged: true, editor: { type: 'combo-list', @@ -252,6 +643,7 @@ export class DataGriColumnProperty extends BaseControlProperty { tabName: propertyCategory.tabName || tabName, description, $converter, + parentPropertyID: 'editor', propertyData, properties, setPropertyRelates: propertyCategory?.setPropertyRelates @@ -349,7 +741,7 @@ export class DataGriColumnProperty extends BaseControlProperty { let editorTypeList: any = []; if (this.designViewModelField && this.designViewModelField.$type === FormSchemaEntityField$Type.SimpleField) { - editorTypeList = SchemaDOMMapping.getEditorTypesByMDataType(this.designViewModelField.type.name, propertyData.type); + editorTypeList = SchemaDOMMapping.getEditorTypesByMDataType(this.designViewModelField.type.name, this.designViewModelField.multiLanguage, propertyData.type); } return { canChangeControlType, editorTypeList }; } @@ -364,4 +756,4 @@ export class DataGriColumnProperty extends BaseControlProperty { this.controlCreatorUtils.setGridFieldFormatter(propertyData.dataType, propertyData, schemaField); } -} +} \ No newline at end of file diff --git a/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts b/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts index f6f97ad75c88f3c5975afda0d11475455fa3c74b..73174f1fd467353a1cf5a6ffeb1739357abec65a 100644 --- a/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts +++ b/packages/ui-vue/components/data-grid/src/property-config/data-grid.property-config.ts @@ -15,7 +15,13 @@ export class DataGridProperty extends BaseControlProperty { // 外观 this.getAppearanceProperties(propertyData); + // 操作列 + this.propertyConfig.categories['command'] = this.getCommandColumnProperties(propertyData); + // 填充列宽,仅支持平分列宽 + this.propertyConfig.categories['column'] = this.getColumnOptionProperties(propertyData); + // 合计行 + this.propertyConfig.categories['summary'] = this.getSummaryProperties(propertyData); // 事件 this.getEventPropConfig(propertyData); @@ -61,6 +67,103 @@ export class DataGridProperty extends BaseControlProperty { }; } + private getColumnOptionProperties(propertyData: any) { + return { + title: '填充列宽', + description: '', + properties: { + fitColumns: { + title: '启用', + type: 'boolean', + description: '启用填充列宽', + $converter: '/converter/column-option.converter', + refreshPanelAfterChanged: true + }, + // fitMode: { + // description: '', + // title: '填充模式', + // type: 'enum', + // visible: !!propertyData.column?.fitColumns, + // $converter: '/converter/column-option.converter', + // editor: { + // data: [ + // { id: 'average', name: '平分' }, + // { id: 'expand', name: '等比' } + // ] + // } + // }, + }, + setPropertyRelates(changeObject: PropertyChangeObject, data: any) { + switch (changeObject && changeObject.propertyID) { + case 'fitColumns': { + data.fit = changeObject.propertyValue; + // 列的拖拽列宽设置为false + if (data.columns) { + if (changeObject.propertyValue) { + data.columns.forEach((column: any) => { + column.resizable = false; + }); + } + } + break; + } + } + } + }; + } + private getCommandColumnProperties(propertyData: any) { + return { + title: '操作列', + description: '', + properties: { + enable: { + title: '启用', + type: 'boolean', + $converter: '/converter/column-command.converter', + description: '启用操作列', + refreshPanelAfterChanged: true + }, + // commands: { + // description: '', + // title: '命令', + // type: 'enum', + // editor: { + // data: [ + // { id: 'edit', name: '编辑' }, + // { id: 'remove', name: '删除' } + // ] + // } + // }, + // formatter: { + // title: '操作列模板', + // type: 'string', + // visible: false, + // description: '自定义操作列模板', + // refreshPanelAfterChanged: true, + // editor: { + // type: "code-editor", + // language: "html", + // } + // } + } + }; + } + + private getSummaryProperties(propertyData: any) { + return { + title: '合计行', + description: '', + properties: { + enable: { + title: '启用', + type: 'boolean', + description: '启用合计行', + $converter: '/converter/summary.converter', + refreshPanelAfterChanged: true + } + } + }; + } /** * 将schema实体表组装成树 */ @@ -93,6 +196,11 @@ export class DataGridProperty extends BaseControlProperty { this.propertyConfig.categories['appearance'] = { title: "外观", properties: { + class: { + title: 'class样式', + type: 'string', + description: '组件的CSS样式' + }, columns: { title: "列设置", description: "列设置", @@ -117,11 +225,11 @@ export class DataGridProperty extends BaseControlProperty { // type: "boolean", // description: "是否显示边框" // }, - // showSetting:{ - // title: "显示设置按钮", - // type: "boolean", - // description: "是否显示设置按钮" - // } + showSetting: { + title: "显示设置按钮", + type: "boolean", + description: "是否显示设置按钮" + } // useBlankWhenDataIsEmpty: { // title: '空数据表格显示空白行', // description: '表格没有数据时是否显示空白行', @@ -150,6 +258,7 @@ export class DataGridProperty extends BaseControlProperty { this.propertyConfig.categories['selection'] = { title: '多选配置', $converter: '/converter/grid-selection.converter', + parentPropertyID: 'editor', properties: { multiSelect: { title: '启用多选', @@ -183,6 +292,7 @@ export class DataGridProperty extends BaseControlProperty { this.propertyConfig.categories['rowNumber'] = { title: '行号配置', $converter: '/converter/row-number.converter', + parentPropertyID: 'rowNumber', properties: { enable: { title: '显示行号', @@ -204,6 +314,7 @@ export class DataGridProperty extends BaseControlProperty { this.propertyConfig.categories['pagination'] = { title: '分页', $converter: '/converter/pagination.converter', + parentPropertyID: 'pagination', properties: { enable: { title: '启用分页', @@ -238,6 +349,7 @@ export class DataGridProperty extends BaseControlProperty { }; } + private getEventPropConfig(propertyData: any) { const events = [ { @@ -247,6 +359,10 @@ export class DataGridProperty extends BaseControlProperty { { "label": "onSelectionChange", "name": "行切换事件" + }, + { + "label": "onDoubleClickRow", + "name": "行双击事件" } ]; if (propertyData.pagination?.enable !== false) { @@ -260,15 +376,21 @@ export class DataGridProperty extends BaseControlProperty { "name": "分页条数变化事件" }); } + if (propertyData.command?.enable) { + // 如果没有设置,默认是按照true处理的 + events.push({ + "label": "onClickEditCommand", + "name": "操作列编辑事件" + }, + { + "label": "onClickDeleteCommand", + "name": "操作列删除事件" + }); + } const self = this; const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { - initialData - } - }; + const properties = self.createBaseEventProperty(initialData); + this.propertyConfig.categories['eventsEditor'] = { title: '事件', hideTitle: true, @@ -286,6 +408,12 @@ export class DataGridProperty extends BaseControlProperty { } // 联动修改排序开关 propertyData.remoteSort = propertyData.columnSorted ? true : false; + // 同步操作列命令 + if (propertyData.command) { + propertyData.command.onClickEditCommand = propertyData.onClickEditCommand; + propertyData.command.onClickDeleteCommand = propertyData.onClickDeleteCommand; + } + } }; } diff --git a/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json b/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json index ec078ae4b3da1ac1a85839c71cbbe6447176cb8c..5117df01c787fbf54de27595b1e181feef81e05d 100644 --- a/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json +++ b/packages/ui-vue/components/data-grid/src/schema/data-grid-column.schema.json @@ -44,7 +44,7 @@ "filterable": { "description": "", "type": "boolean", - "default": true + "default": false }, "fixed": { "description": "", @@ -143,10 +143,42 @@ "description": "", "type": "number", "default": 120 + }, + "formatter": { + "description":"列格式化", + "type": "object", + "default": { + "type":"none", + "customFormat": "", + "trueText": "是", + "falseText": "否", + "dateFormat": "yyyy-MM-dd" + } + }, + "columnTemplate": { + "description":"列模板", + "type": "string", + "default": "" + }, + "summaryType": { + "description": "合计类型", + "type": "enum", + "default": "none" + }, + "formatterEnumData": { + "description": "", + "type": "array" + }, + "multiLanguage": { + "description": "是否启用多语", + "type": "boolean" } }, "required": [ "id", "type" - ] + ], + "events": { + "onClickLinkCommand": "超链接事件" + } } \ No newline at end of file diff --git a/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json b/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json index 464446227699a2dc6790a7ec6b3a7993ec05485e..f76f755d4b73925977ef476f68c5a2c4661abbb6 100644 --- a/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json +++ b/packages/ui-vue/components/data-grid/src/schema/data-grid.schema.json @@ -154,7 +154,23 @@ }, "commands": { "type": "array", - "default": [] + "default": [ + { + "text": "编辑", + "type": "primary", + "command": "edit" + }, + { + "text": "删除", + "type": "danger", + "command": "remove" + } + ] + }, + "formatter": { + "description": "自定义操作列模板", + "type": "string", + "default": "" }, "commandColumnWidth": { "description": "", @@ -798,6 +814,9 @@ "onClickRow": "行点击事件", "onSelectionChange": "行切换事件", "onPageIndexChanged": "切换页码事件", - "onPageSizeChanged": "分页条数变化事件" + "onPageSizeChanged": "分页条数变化事件", + "onDoubleClickRow": "行双击事件", + "onClickEditCommand": "操作列编辑事件", + "onClickDeleteCommand": "操作列删除事件" } } \ No newline at end of file diff --git a/packages/ui-vue/components/data-grid/src/schema/schema-mapper.ts b/packages/ui-vue/components/data-grid/src/schema/schema-mapper.ts index cd9697fa1b699607adb3b48c48815c3a8071185d..92bdcd4547bd4612d1e4df44b7698429503046a8 100644 --- a/packages/ui-vue/components/data-grid/src/schema/schema-mapper.ts +++ b/packages/ui-vue/components/data-grid/src/schema/schema-mapper.ts @@ -4,7 +4,7 @@ import { resolveGridColumns } from './column-resolver'; export const schemaMapper = new Map([ ['appearance', resolveAppearance], ['column', 'columnOption'], - // ['fields', resolveGridColumns] + ['command', 'commandOption'] ]); export const columnSchemaMapper = new Map([]); diff --git a/packages/ui-vue/components/data-view/components/column-filter/column-filter-container.component.tsx b/packages/ui-vue/components/data-view/components/column-filter/column-filter-container.component.tsx index 079aa5708fa0d14405c81c40da3e23e91aaa912d..7a789bdbb9f6b7a5b14561e4c54d5a166bd071ce 100644 --- a/packages/ui-vue/components/data-view/components/column-filter/column-filter-container.component.tsx +++ b/packages/ui-vue/components/data-view/components/column-filter/column-filter-container.component.tsx @@ -15,6 +15,8 @@ */ import { CapsuleItem } from '@farris/ui-vue/components/capsule'; +import { Tag } from '@farris/ui-vue/components/tags'; +import { useI18n } from 'vue-i18n'; import { HeaderCell, HeaderCellStatus, @@ -26,7 +28,6 @@ import { UseVirtualScroll, UseFilter } from '../../composition/types'; -import { Tag } from '@farris/ui-vue/components/tags'; export default function ( useColumnComposition: UseColumn, @@ -37,10 +38,13 @@ export default function ( useSorterComposition: UseSort, useVirtualScrollComposition: UseVirtualScroll ) { + const { t: getLocaleValue } = useI18n(); const items: CapsuleItem[] = [ - { name: '升序', value: 'asc', icon: 'f-icon f-icon-col-ascendingorder' }, - { name: '无', value: 'none' }, - { name: '降序', value: 'desc', icon: 'f-icon f-icon-col-descendingorder' } + { name: getLocaleValue('datagrid.settings.asc'), + value: 'asc', icon: 'f-icon f-icon-col-ascendingorder' }, + { name: getLocaleValue('datagrid.settings.summaryNone'), value: 'none' }, + { name: getLocaleValue('datagrid.settings.desc'), value: 'desc', + icon: 'f-icon f-icon-col-descendingorder' } ]; function clearSortStatus(headerCell: HeaderCell) { @@ -141,7 +145,7 @@ export default function (
-
筛选
+
{getLocaleValue('datagrid.filter.title')}
{useColumnFilterComposition.getFilterEditor(headerCell)}
onConfirm(payload, headerCell)}> - 确定 + {getLocaleValue('datagrid.filter.ok')} onCancel(payload, headerCell)}> - 取消 + {getLocaleValue('datagrid.filter.cancel')}
diff --git a/packages/ui-vue/components/data-view/components/column-filter/date-filter-editor.component.tsx b/packages/ui-vue/components/data-view/components/column-filter/date-filter-editor.component.tsx index 57809aaf4d4ddc708928f282ee7d88c61cc3329f..20f9feaa950e317f36e0735b987d70a39fb1d1d8 100644 --- a/packages/ui-vue/components/data-view/components/column-filter/date-filter-editor.component.tsx +++ b/packages/ui-vue/components/data-view/components/column-filter/date-filter-editor.component.tsx @@ -1,20 +1,5 @@ import { ref } from "vue"; import { HeaderCell } from "../../composition/types"; - -const tags = ref([ - { - name: '七天', selectable: true - }, - { - name: '一个月', selectable: true - }, - { - name: '三个月', selectable: true - }, - { - name: '半年', selectable: true - } -]); export default function (headerCell: HeaderCell) { const status = ref(false); const filterValue = ref(headerCell.column?.filter || ''); diff --git a/packages/ui-vue/components/data-view/components/column-format/column-format.component.tsx b/packages/ui-vue/components/data-view/components/column-format/column-format.component.tsx index 50e05071588f976aa0b1e5d4bd55c31522a4bda6..9c64aea98a3a62cb2dfc7d09928a5fa8bff031e0 100644 --- a/packages/ui-vue/components/data-view/components/column-format/column-format.component.tsx +++ b/packages/ui-vue/components/data-view/components/column-format/column-format.component.tsx @@ -1,15 +1,17 @@ import { resolveField } from '@farris/ui-vue/components/common'; import { - DataColumn, + ColumnTemplateType, DateTimeFormatOptions, InnerFormatter, - VisualData + VisualData, + VisualDataCell } from '../../composition/types'; import getEnumColumn from './enum.component'; import getBooleanColumn from './boolean.component'; import getDateColumn from './date.component'; import getNumberColumn from './number.component'; import getImageColumn from './image.component'; +import getMultilingualColumn from './multilingual.component'; export default function () { @@ -18,47 +20,55 @@ export default function () { const { renderNumberColumn } = getNumberColumn(); const { renderDateColumn, renderDateTimeColumn } = getDateColumn(); const { renderImageColumn } = getImageColumn(); + const { renderMultilingualColumn } = getMultilingualColumn(); // format none function renderNone(value: any) { + if(typeof value === 'boolean') { + return value.toString(); + } return value; } // format custom - function renderCustom(value: any, options: InnerFormatter) { + function renderCustom(value: any, options: InnerFormatter, row: VisualData) { return options.customFormat ? - new Function(`return ${options.customFormat}`)() : + // new Function(`return ${options.customFormat}`)() : + options.customFormat(value, row.raw): renderNone(value); } function renderFormatColumn( - column: DataColumn, + cell: VisualDataCell, visualDataRow: VisualData, formatter: InnerFormatter ) { - const value = resolveField(visualDataRow.raw, column.field); + const { column } = cell; + const value = resolveField(visualDataRow.raw, column!.field); const options = (formatter.options || formatter) as InnerFormatter; // no formatter - if (formatter.type === 'none') { + if (formatter.type === ColumnTemplateType.NONE) { return renderNone(value); } // custom formatter - if (formatter.type === 'custom') { - return renderCustom(value, formatter); + if (formatter.type === ColumnTemplateType.CUSTOM) { + return renderCustom(value, formatter,visualDataRow); } // formatter - if (formatter.type === 'enum') { + if (formatter.type === ColumnTemplateType.ENUM) { return renderEnumColumn(value, options); - } else if (formatter.type === 'number') { + } else if (formatter.type === ColumnTemplateType.NUMBER) { return renderNumberColumn(value, options); - } else if (formatter.type === 'boolean') { + } else if (formatter.type === ColumnTemplateType.BOOLEAN) { return renderText(value, options); - } else if (formatter.type === 'date') { + } else if (formatter.type === ColumnTemplateType.DATE) { return renderDateColumn(value, options); - } else if (formatter.type === 'datetime') { - return renderDateTimeColumn(value, formatter as DateTimeFormatOptions); - } else if (formatter.type === 'image') { + } else if (formatter.type === ColumnTemplateType.DATE_TIME) { + return renderDateTimeColumn(value, formatter as unknown as DateTimeFormatOptions); + } else if (formatter.type === ColumnTemplateType.IMAGE) { return renderImageColumn(value, options); + } else if(formatter.type === ColumnTemplateType.MULTI_LINGUAL) { + return renderMultilingualColumn(value, options); } // default no formmater return renderNone(value); diff --git a/packages/ui-vue/components/data-view/components/column-format/multilingual.component.tsx b/packages/ui-vue/components/data-view/components/column-format/multilingual.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c7fbbd6c9fc2ee7fbe62f62a05eab39bea9dc552 --- /dev/null +++ b/packages/ui-vue/components/data-view/components/column-format/multilingual.component.tsx @@ -0,0 +1,12 @@ +import { InnerFormatter } from '../../composition/types'; +import { useI18n } from 'vue-i18n'; +export default function () { + const { locale } = useI18n(); + + function renderMultilingualColumn(value: any, options: InnerFormatter) { + // 当前语言环境下的值 + return value?.[locale.value || 'zh-CHS'] || ''; + } + + return { renderMultilingualColumn }; +} diff --git a/packages/ui-vue/components/data-view/components/column-setting/column-setting-icon.component.tsx b/packages/ui-vue/components/data-view/components/column-setting/column-setting-icon.component.tsx index 85ea75548736cf54837b235fc5cd1c5c2db066ab..561477f17868feadcff75595bed73aa4e42ae01f 100644 --- a/packages/ui-vue/components/data-view/components/column-setting/column-setting-icon.component.tsx +++ b/packages/ui-vue/components/data-view/components/column-setting/column-setting-icon.component.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { useI18n } from 'vue-i18n'; import { DataViewOptions, UseColumn, UseDataView, UseFilter, UseFitColumn, UseSort, UseVirtualScroll } from '../../composition/types'; import { F_MODAL_SERVICE_TOKEN, FModalService } from '@farris/ui-vue/components/modal'; import getDataGridSettingsRender from './column-setting.component'; @@ -30,6 +31,7 @@ export default function ( useVirtualScrollComposition: UseVirtualScroll ) { const modalService = inject(F_MODAL_SERVICE_TOKEN) as any; + const vueI18nComposition = useI18n(); function onClickColumnHandler(payload: MouseEvent) { const { openSettingPanel @@ -43,7 +45,8 @@ export default function ( useFitColumnComposition, useSorterComposition, useVirtualScrollComposition, - modalService + modalService, + vueI18nComposition ); openSettingPanel(); } diff --git a/packages/ui-vue/components/data-view/components/column-setting/column-setting.component.tsx b/packages/ui-vue/components/data-view/components/column-setting/column-setting.component.tsx index 5ce7f9b2e594acfbc543d9555c6f44f319690e1f..473b726bfa1bd11d1853961aa0fb186aab266ce0 100644 --- a/packages/ui-vue/components/data-view/components/column-setting/column-setting.component.tsx +++ b/packages/ui-vue/components/data-view/components/column-setting/column-setting.component.tsx @@ -1,6 +1,6 @@ +import { cloneDeep } from 'lodash-es'; import FTransfer from '@farris/ui-vue/components/transfer'; import FTabs, { FTabPage } from '@farris/ui-vue/components/tabs'; -// import FTabPage from '../../../tabs/src/components/tab-page.component'; import { FOrder, OrderedItem, SortType } from '@farris/ui-vue/components/order'; import FConditionList, { Condition, FieldConfig } from '@farris/ui-vue/components/condition'; import { App, Ref, computed, inject, nextTick, ref } from 'vue'; @@ -10,7 +10,7 @@ import { } from '../../composition/types'; import { ConditionValue } from '../../../condition/src/composition/condition-value/types'; import { EditorConfig } from '@farris/ui-vue/components/dynamic-form'; - +type VisibleColumn = { id: string; name: string }; export default function ( props: DataViewOptions, gridContentRef: Ref, @@ -21,7 +21,8 @@ export default function ( useFitColumnComposition: UseFitColumn, useSorterComposition: UseSort, useVirtualScrollComposition: UseVirtualScroll, - modalService: any + modalService: any, + vueI18nComposition: any ) { const identifyField = 'id'; const conditionListRef = ref(); @@ -38,15 +39,19 @@ export default function ( const { fitHorizontalScroll: resetHorizontalScrollPosition } = useVirtualScrollComposition; const orderedResult = ref([]); const shouldApplyOrderedResult = computed(() => !!orderedResult.value.length); + let originVisibleColumns: VisibleColumn[] = []; const dataSoruce = computed(() => { - return columnContext.value.primaryColumns.map((dataGridColumn: DataColumn) => ({ - id: dataGridColumn.field, - name: dataGridColumn.title - })); + return columnContext.value.primaryColumns + .filter((column: DataColumn) => column.dataType !== 'commands') + .map((dataGridColumn: DataColumn) => ({ + id: dataGridColumn.field, + name: dataGridColumn.title + })); }); const visibleColumns = computed(() => { return columnContext.value.primaryColumns + .filter((column: DataColumn) => column.dataType !== 'commands') .filter((dataGridColumn: DataColumn) => dataGridColumn.visible) .map((visibleColumn: DataColumn) => ({ id: visibleColumn.field, @@ -75,21 +80,20 @@ export default function ( } as FieldConfig)); }); - function onCloseSettingPanel() { - modalInstance?.destroy(); - } function resetVisibleColumns() { const dataGridColumnMap = new Map(); - columnContext.value.primaryColumns.reduce((columnMap: Map, column: DataColumn) => { - column.visible = false; - columnMap.set(column.field, column); - return columnMap; - }, dataGridColumnMap); + columnContext.value.primaryColumns + .filter((column: DataColumn) => column.dataType !== 'commands') + .reduce((columnMap: Map, column: DataColumn) => { + column.visible = false; + columnMap.set(column.field, column); + return columnMap; + }, dataGridColumnMap); return dataGridColumnMap; } - function applyVisibleStatus(visibleStatus: { id: string; name: string }[], dataGridColumnMap: Map) { + function applyVisibleStatus(visibleStatus: VisibleColumn[], dataGridColumnMap: Map) { const latestOrderedVisibleColumns = visibleStatus.map(({ id: field }) => { const column = dataGridColumnMap.get(field) as DataColumn; column.visible = true; @@ -99,10 +103,19 @@ export default function ( return latestOrderedVisibleColumns; } - function onVisibleColumnsChange(visibleStatus: { id: string; name: string }[]) { + function onVisibleColumnsChange(visibleStatus: VisibleColumn[]) { + const commandColumn = columnContext.value.primaryColumns.find((column: DataColumn) => column.dataType === 'commands'); const dataGridColumnMap = resetVisibleColumns(); const latestOrderedVisibleColumns = applyVisibleStatus(visibleStatus, dataGridColumnMap); - columnContext.value.primaryColumns = [...latestOrderedVisibleColumns, ...Array.from(dataGridColumnMap.values())]; + if (commandColumn) { + columnContext.value.primaryColumns = [ + ...latestOrderedVisibleColumns, + ...Array.from(dataGridColumnMap.values()), + commandColumn + ] + } else { + columnContext.value.primaryColumns = [...latestOrderedVisibleColumns, ...Array.from(dataGridColumnMap.values())]; + } resetSettingIconPosition(); resetVisibleColumnPosition(); nextTick(() => { @@ -114,6 +127,11 @@ export default function ( orderedResult.value = ordereditems; } + function onCloseSettingPanel() { + onVisibleColumnsChange(originVisibleColumns); + modalInstance?.destroy(); + } + function renderSettingsPanel(app: App) { return (
@@ -121,13 +139,16 @@ export default function ( {{ headerPrefix: () => ( ), default: () => [ - +
{context.slots.empty && context.slots.empty() || '暂无数据'} + transform:translateY(-50%);user-select:none"> {getLocaleValue('datagrid.emptyMessage')}
; } diff --git a/packages/ui-vue/components/data-view/components/editors/commands.component.tsx b/packages/ui-vue/components/data-view/components/editors/commands.component.tsx index 24eecfa8bf39aaa7de2ef43d471144a6ebc3c705..a83a86de90409b5df281e1dc975aa632b4944865 100644 --- a/packages/ui-vue/components/data-view/components/editors/commands.component.tsx +++ b/packages/ui-vue/components/data-view/components/editors/commands.component.tsx @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { DataColumn, DataColumnCommand, VisualData, VisualDataStatus } from '../../composition/types'; +import { DataColumn, DataColumnCommand, DataViewOptions, VisualData, VisualDataCell, VisualDataStatus } from '../../composition/types'; import FButton from '@farris/ui-vue/components/button'; -export default function () { - - function shouldShowCurrentCommandButton(command: DataColumnCommand,visualDataRow: VisualData) { +import { useI18n } from 'vue-i18n'; +export default function (props: DataViewOptions) { + const { t: getLocaleValue } = useI18n(); + function shouldShowCurrentCommandButton(command: DataColumnCommand, visualDataRow: VisualData) { switch (command.command) { case 'edit': case 'remove': @@ -30,28 +31,59 @@ export default function () { } } - function excuteCommand(command: DataColumnCommand, payload: MouseEvent, visualDataRow: VisualData) { - command.onClick(payload, visualDataRow.dataIndex, visualDataRow); + function excuteCommand(command: DataColumnCommand, payload: MouseEvent, visualDataRow: VisualData, cell: VisualDataCell) { + command.onClick?.(payload, visualDataRow.dataIndex, visualDataRow); switch (command.command) { case 'edit': visualDataRow.status === VisualDataStatus.editing; + // 编辑回调事件 + props.commandOption?.onClickEditCommand?.(cell, visualDataRow); break; case 'accept': case 'cancel': visualDataRow.status === VisualDataStatus.initial; break; + case 'remove': + // 删除回调事件 + props.commandOption?.onClickDeleteCommand?.(cell, visualDataRow); + break; + } + } + + function getCommandLcales(commandText: string) { + if(commandText === '编辑') { + return getLocaleValue('datagrid.commandColumn.edit'); + } + if(commandText === '确定') { + return getLocaleValue('datagrid.commandColumn.accept'); + } + if(commandText === '取消') { + return getLocaleValue('datagrid.commandColumn.cancel'); + } + if(commandText === '删除') { + return getLocaleValue('datagrid.commandColumn.remove'); } + return commandText; } - function renderCommandColumn(column: DataColumn, visualDataRow: VisualData) { - return column.commands && column.commands.map((command: DataColumnCommand) => { - return shouldShowCurrentCommandButton(command,visualDataRow) && excuteCommand(command, payload, visualDataRow)} - style={{'margin-bottom':'3px'}} - > {command.text} ; - }); + function renderCommandColumn(cell: VisualDataCell, visualDataRow: VisualData) { + const { column } = cell; + return column!.commands && column!.commands.map((command: DataColumnCommand, index: number) => { + if(!index) { + return shouldShowCurrentCommandButton(command, visualDataRow) && excuteCommand(command, payload, visualDataRow, cell)} + style={{ 'margin-bottom': '3px' }} + > {getCommandLcales(command.text)} ; + } + return shouldShowCurrentCommandButton(command, visualDataRow) && excuteCommand(command, payload, visualDataRow, cell)} + style={{ 'margin-bottom': '3px' }} + > {getCommandLcales(command.text)} ; + }); } return { renderCommandColumn }; diff --git a/packages/ui-vue/components/data-view/components/row/hierarchy-row.component.tsx b/packages/ui-vue/components/data-view/components/row/hierarchy-row.component.tsx index f35fc8da161b91c97871583cecc25656e170af39..47217748faeabbab42d8d11239ef508f26ebf8d1 100644 --- a/packages/ui-vue/components/data-view/components/row/hierarchy-row.component.tsx +++ b/packages/ui-vue/components/data-view/components/row/hierarchy-row.component.tsx @@ -1,6 +1,9 @@ import { Fragment, Ref, SetupContext, VNodeArrayChildren, isVNode, ref } from "vue"; import FTooltip from '@farris/ui-vue/components/tooltip'; -import { CellMode, ColumnContext, DataColumn, DataViewOptions, UseDataView, UseEdit, UseHierarchy, UseRow, UseSelectHierarchyItem, UseSelection, UseVirtualScroll, UseVisualData, UseVisualDataBound, VisualData, VisualDataCell } from "../../composition/types"; +import { useCommonUtils } from '@farris/ui-vue/components/common'; +import { CellMode, ColumnContext, DataColumn, DataViewOptions, UseDataView, UseEdit, UseHierarchy, UseRow, + UseSelectHierarchyItem, UseSelection, UseVirtualScroll, UseVisualData, UseVisualDataBound, VisualData, + VisualDataCell } from "../../composition/types"; import { useCellPosition } from "../../composition/appearance/use-cell-position"; import { useHierarchyStyle } from "../../composition/appearance/use-hierarchy-style"; import { useTreeNodeIcon } from "../../composition/hierarchy/use-tree-node-icon"; @@ -23,6 +26,7 @@ export default function ( useVisualDataComposition: UseVisualData, useVisualDataBoundComposition: UseVisualDataBound ) { + const { isUndefined } = useCommonUtils(); const { onClickCell } = useEditComposition; const { gridCellClass, gridHierarchyCellClass, gridRowClass, onClickRow, onMouseoverRow, onMouseoutRow } = useRowComposition; const { enableMultiSelect, select, unSelect, selectItem, updateSelectAllStatus } = useSelectionCompostion; @@ -44,7 +48,7 @@ export default function ( const { treeNodeIconsClass } = useTreeNodeIcon(props as DataViewOptions, treeNodeIconsData, hasChildrenField); function treeNodeIconClass(visualTreeNode: VisualData, visualTreeNodeCell: VisualDataCell) { - const hasChildren = visualTreeNode.raw[hasChildrenField.value]; + const hasChildren = visualTreeNode.raw[hasChildrenField.value]; const classObject = { 'fv-tree-node-toggle': hasChildren && visualTreeNode.collapse, 'fv-tree-node-toggle-minus': hasChildren && !visualTreeNode.collapse @@ -66,6 +70,13 @@ export default function ( // ); // } + // 异步加载子级数据 + function loadDataAsync(visualData: VisualData) { + if(visualData.collapse && props.loadData) { + return props.loadData(visualData); + } + return Promise.resolve(); + } function ensureValidVNode(slot: VNodeArrayChildren | null) { return (slot || []).some(child => { if (!isVNode(child)) { @@ -84,7 +95,10 @@ export default function ( }; function onClickToggleIcon(payload: MouseEvent, visualData: VisualData) { payload.stopPropagation(); - toggleTreeNode(visualData); + const loadData = loadDataAsync(visualData); + loadData.then(() => { + toggleTreeNode(visualData); + }); } function onDblclick(payload: MouseEvent) { diff --git a/packages/ui-vue/components/data-view/components/summary/data-grid-summary.component.tsx b/packages/ui-vue/components/data-view/components/summary/data-grid-summary.component.tsx index 147da21fe26714d1c69976df70feb68f6fc84f58..41954ebc6c0e7b34058d117419b2493158a1e1d9 100644 --- a/packages/ui-vue/components/data-view/components/summary/data-grid-summary.component.tsx +++ b/packages/ui-vue/components/data-view/components/summary/data-grid-summary.component.tsx @@ -14,9 +14,11 @@ * limitations under the License. */ import { computed, ref } from 'vue'; +import { useI18n } from 'vue-i18n'; import { DataColumn, DataViewOptions, UseColumn, UseDataView } from '../../composition/types'; export default function (props: DataViewOptions, dataView: UseDataView, useColumnComposition: UseColumn) { + const { t: getLocaleValue } = useI18n(); const { columnContext } = useColumnComposition; const summaryOptions = ref(props.summary); @@ -26,12 +28,14 @@ export default function (props: DataViewOptions, dataView: UseDataView, useColum return options && options.enable && options.groupFields && options.groupFields.length > 0; }); - function renderDataGridSummery() { + function renderDataGridSummary() { return ( shouldShowSummary.value && (
- 当页合计 + + {getLocaleValue('datagrid.summary.title')} +
{columnContext.value.summaryColumns.map((column: DataColumn) => { return ( @@ -48,5 +52,5 @@ export default function (props: DataViewOptions, dataView: UseDataView, useColum ); } - return { renderDataGridSummery }; + return { renderDataGridSummary }; } diff --git a/packages/ui-vue/components/data-view/composition/appearance/use-hierarchy-style.ts b/packages/ui-vue/components/data-view/composition/appearance/use-hierarchy-style.ts index 237427662d1788148a2d577bda24932b809d8073..01f994c6202198f0859053edf500aba16407e64b 100644 --- a/packages/ui-vue/components/data-view/composition/appearance/use-hierarchy-style.ts +++ b/packages/ui-vue/components/data-view/composition/appearance/use-hierarchy-style.ts @@ -1,4 +1,4 @@ - + import { Ref, ref } from "vue"; import { DataViewOptions, DictTreeItem, UseHierarchy, VisualData } from "../types"; import { isUndefined } from "lodash-es"; @@ -117,6 +117,9 @@ export function useHierarchyStyle( : { display: 'none' }; + if (!hasChildren && isTopLevelNode) { + styleObject.paddingLeft = `calc(1.18rem)`; + } if (!hasChildren && !isTopLevelNode) { styleObject.paddingLeft = `calc(${paddingLeft} + 1.25rem)`; } diff --git a/packages/ui-vue/components/data-view/composition/column/use-command-column.ts b/packages/ui-vue/components/data-view/composition/column/use-command-column.ts index d61a8b5aec1bcace38a1187545400737c6a6c3c5..193873b844fdd2a35eb264348dcf39cf94619e8b 100644 --- a/packages/ui-vue/components/data-view/composition/column/use-command-column.ts +++ b/packages/ui-vue/components/data-view/composition/column/use-command-column.ts @@ -14,12 +14,14 @@ * limitations under the License. */ import { Ref, ref } from 'vue'; -import { DataColumn, DataViewOptions, UseCommandColumn } from '../types'; +import { useI18n } from 'vue-i18n'; +import { DataColumn, DataViewOptions, UseCommandColumn, VisualData, VisualDataCell } from '../types'; export function useCommandColumn(props: DataViewOptions): UseCommandColumn { - const defaultColumnWidth = 120; - const enableCommands = ref(props.commandOption.enable || false); - const commands = ref(props.commandOption.commands || []); + const { t: getLocaleValue } = useI18n(); + const defaultColumnWidth = 150; + const enableCommands = ref(props.commandOption?.enable || false); + const commands = ref(props.commandOption?.commands || []); function applyCommands(columns: Ref) { if (enableCommands.value) { @@ -27,13 +29,16 @@ export function useCommandColumn(props: DataViewOptions): UseCommandColumn { if (!hasCommandColumn) { const commandColumn = { field: '__commands__', - title: '操作', + title: getLocaleValue('datagrid.commandColumn.title'), width: defaultColumnWidth, fixed: 'right', dataType: 'commands', commands: commands.value, + resizable: false, halign: props.commandOption?.halign || 'left', - visible: true + visible: true, + formatter: props.commandOption.formatter? (cell: VisualDataCell, row: VisualData) => + props.commandOption.formatter(cell, row): null } as DataColumn; columns.value.push(commandColumn as DataColumn); } diff --git a/packages/ui-vue/components/data-view/composition/column/use-fit-column.ts b/packages/ui-vue/components/data-view/composition/column/use-fit-column.ts index d5a69f1de7ad69323c2b74c7679a72b8ab25d726..83bd8b6defee28b58d27989dd78ffd5fd726ccc3 100644 --- a/packages/ui-vue/components/data-view/composition/column/use-fit-column.ts +++ b/packages/ui-vue/components/data-view/composition/column/use-fit-column.ts @@ -35,7 +35,7 @@ export function useFitColumn( const showRowNumer = computed(() => props.rowNumber?.enable || false); // todo 树表的多选不在此开启,因此也不应该包含siderbar里面的checkbox宽度,后续应该统一 const showRowCheckbox = computed(() => !props.hierarchy && - (props.selection?.multiSelect || props.selection?.showCheckbox || false)); + (props.selection?.multiSelect || props.selection?.showCheckbox || false)); const sidebarColumnWidth = computed(() => 0 + (showRowNumer.value ? props.rowNumber?.width || 32 : 0) + (showRowCheckbox.value ? defaultCheckboxWidth : 0) ); @@ -100,13 +100,30 @@ export function useFitColumn( } function calculateColumnsSizeByAverage(context: Ref, viewPortWidth: number) { - const columnWidth = viewPortWidth / (context.value.primaryColumns.filter((column: DataColumn) => column.visible).length || 1); - context.value.primaryColumns - .filter((column: DataColumn) => column.visible) - .forEach((visibleColumn: DataColumn) => { - visibleColumn.actualWidth = columnWidth; - context.value.primaryColumnsWidth += visibleColumn.actualWidth; - }); + const commandColumn = context.value.primaryColumns.find((column: DataColumn) => column.dataType === 'commands'); + if (!commandColumn) { + const columnWidth = viewPortWidth / (context.value.primaryColumns.filter((column: DataColumn) => column.visible).length || 1); + context.value.primaryColumns + .filter((column: DataColumn) => column.visible) + .forEach((visibleColumn: DataColumn) => { + visibleColumn.actualWidth = columnWidth; + context.value.primaryColumnsWidth += visibleColumn.actualWidth; + }); + } else { + // 如果存在操作列,暂时设置操作列宽度(默认150px)固定,其他列平分宽度 + const columnWidth = (viewPortWidth - 150) / ( + context.value.primaryColumns + .filter((column: DataColumn) => column.dataType !== 'commands') + .filter((column: DataColumn) => column.visible).length || 1); + context.value.primaryColumns + .filter((column: DataColumn) => column.dataType !== 'commands') + .filter((column: DataColumn) => column.visible) + .forEach((visibleColumn: DataColumn) => { + visibleColumn.actualWidth = columnWidth; + context.value.primaryColumnsWidth += visibleColumn.actualWidth; + }); + context.value.primaryColumnsWidth += 150; + } } function calculateColumnsSizeByExpand(context: Ref, viewPortWidth: number) { diff --git a/packages/ui-vue/components/data-view/composition/data/use-data-view.ts b/packages/ui-vue/components/data-view/composition/data/use-data-view.ts index a4a57a8177546c440689abf5f5da94db936bc861..a1f6bd0a79fefd92524f5dad44a80b7360d70b1e 100644 --- a/packages/ui-vue/components/data-view/composition/data/use-data-view.ts +++ b/packages/ui-vue/components/data-view/composition/data/use-data-view.ts @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { computed, ref } from 'vue'; import { cloneDeep } from 'lodash-es'; +import { resolveField, useNumberFormat } from '@farris/ui-vue/components/common'; import { DataViewFilter, DataViewOptions, DataViewSorter, DataViewType, UseDataView, UseFilter, UseHierarchy, UseIdentify, VisualData } from '../types'; -import { computed, ref, watch } from 'vue'; -import { useFilter } from '../filter/use-filter'; import { useGroupData } from './use-group-data'; import { useHierarchyData } from './use-hierarchy-data'; import { FNotifyService } from '../../../notify'; @@ -31,6 +31,7 @@ export function useDataView( useHierarchyCompostion: UseHierarchy, useIdentifyCompostion: UseIdentify ): UseDataView { + const { toNumber } = useNumberFormat(); const collapseMap = new Map(); const filterMap = new Map(); const groupFilterMap = new Map(); @@ -42,19 +43,17 @@ export function useDataView( const { generateGroupData, groupFields, shouldGroupingData } = groupComposition; const originalData = ref(props.data); const hierarchyDataComposition = useHierarchyData(props, originalData, useHierarchyCompostion, useIdentifyCompostion); - const { collapseTo: collpaseToLayer, expandTo: expandToLayer, generateHierarchyData, hasRealChildren, insertSibling, insertChild, isVisibleInTree, + const { collapseTo: collpaseToLayer, expandTo: expandToLayer, generateHierarchyData, hasRealChildren, insertSibling, insertChild, + insertChildren, isVisibleInTree, shouldStratifyData, toggleChildrenVisibiltyByCollapseStatus, trimmedOriginalData, checkVisibleInHierarchy } = hierarchyDataComposition; const { idField, reviseIdentifyField } = useIdentifyCompostion; const getNewDataItem = ref(props.newDataItem); const totalData = ref(props.data || []); const rawView = ref(props.data); - const { flatTreeData } = useTreeData(props); - function flattenTreeData(data: any) { - return flatTreeData(data) as any; - } - if (props.hierarchy && props.data && props.data[0]?.children) { + const { flatTreeData, isNestedTree } = useTreeData(props); + if (props.hierarchy && isNestedTree(props.data)) { // 树表打平数据 - const flattenedData = flattenTreeData(props.data); + const flattenedData = flatTreeData(props.data); originalData.value = flattenedData; totalData.value = flattenedData; rawView.value = flattenedData; @@ -187,7 +186,7 @@ export function useDataView( } groupSummaryFields.value.forEach((summaryField: string) => { const summaryFieldValue = summaries.get(summaryField) || 0; - summaries.set(summaryField, summaryFieldValue + dataItem[summaryField]); + summaries.set(summaryField, summaryFieldValue + resolveField(dataItem, summaryField)); }); dataViewItems.push(dataItem); } @@ -195,6 +194,10 @@ export function useDataView( if (shouldStratifyData.value) { generateHierarchyData(dataViewItems); } + groupSummaryFields.value.forEach((summaryField: string) => { + const summaryFieldValue = summaries.get(summaryField) || 0; + summaries.set(summaryField, toNumber(summaryFieldValue.toFixed(2))); + }); rawView.value = [...dataViewItems]; return dataViewItems; } @@ -358,8 +361,8 @@ export function useDataView( function load(newData: Record[]) { // jumphere, it need to remove the dependency of children property at first row, it's awful. - const shouldFlattenData = props.hierarchy && newData && newData.length && newData[0].children; - const loadingData = shouldFlattenData ? flattenTreeData(newData) : newData; + const shouldFlattenData = props.hierarchy && isNestedTree(newData); + const loadingData = shouldFlattenData ? flatTreeData(newData) : newData; originalData.value = loadingData; updateDataView(); } @@ -415,6 +418,17 @@ export function useDataView( updateDataView(); } + function insertNewChildDataItems(newDataItems: any[], targetIndex = 0) { + const canInsertHierarchyData = targetIndex > -1 && props.hierarchy; + if (!canInsertHierarchyData) { + return false; + } + const targetDataItem = originalData.value[targetIndex]; + removeFoldFilterOf(targetDataItem); + insertChildren(targetIndex, newDataItems, originalData); + updateDataView(); + } + function removeDataItem(dataIndex: number) { const targetIndex = dataIndex ? dataIndex - 1 : 0; originalData.value.splice(targetIndex, 1); @@ -488,6 +502,24 @@ export function useDataView( }); return selectionItems.filter((item: any) => item != null); } + + function updateSummary() { + const groupSummaryFields = props.summary.groupFields; + summaries = groupSummaryFields.reduce((sumaries: Map, summaryField: string) => { + sumaries.set(summaryField, 0); + return sumaries; + }, summaries); + for (const dataItem of totalData.value) { + groupSummaryFields.forEach((summaryField: string) => { + const summaryFieldValue = summaries.get(summaryField) || 0; + summaries.set(summaryField, summaryFieldValue + resolveField(dataItem, summaryField)); + }); + } + groupSummaryFields.forEach((summaryField: string) => { + const summaryFieldValue = summaries.get(summaryField) || 0; + summaries.set(summaryField, toNumber(summaryFieldValue.toFixed(2))); + }); + } return { addFilter, addNewDataItem, @@ -506,6 +538,7 @@ export function useDataView( getSelectionItems, hasRealChildren, insertNewChildDataItem, + insertNewChildDataItems, insertNewDataItem, isVisibleInTree, load, @@ -523,6 +556,7 @@ export function useDataView( setSorters, sorters, summaries, + updateSummary, toggleChildrenVisibiltyByCollapseStatus, totalItems, unFold, diff --git a/packages/ui-vue/components/data-view/composition/data/use-hierarchy-data.ts b/packages/ui-vue/components/data-view/composition/data/use-hierarchy-data.ts index d75782b916b6f0e17f65aa0263638e0cb9d405de..4ecaa087005f727f0a5e3105ddb317d4a2a251ce 100644 --- a/packages/ui-vue/components/data-view/composition/data/use-hierarchy-data.ts +++ b/packages/ui-vue/components/data-view/composition/data/use-hierarchy-data.ts @@ -1,5 +1,6 @@ import { Ref, ref } from "vue"; import { DataViewOptions, HierarchyGraphNode, HierarchyOptions, UseHierarchy, UseHierarchyData, UseIdentify } from "../types"; +import { useTreeData } from "./use-tree-data"; export function useHierarchyData( props: DataViewOptions, @@ -14,6 +15,7 @@ export function useHierarchyData( const hierarchyGraph = new Map(); const parentIdAndChildrenMap = new Map(); const rawDataItemMap = new Map(); + const { flatTreeData, isNestedTree } = useTreeData(props); function generateParentIdAndChildrenMap(rawData: any[], rawDataItemMap: Map) { parentIdAndChildrenMap.clear(); @@ -256,6 +258,7 @@ export function useHierarchyData( rawDataItem.__fv_descendant_index__ = [...childrenIndex]; rawDataItem.__fv_parents__ = new Map; if (hasLazyChildren(rawDataItem)) { + // 初始化懒加载节点,默认收折状态 rawDataItem[collapseField.value] = true; } }); @@ -327,6 +330,29 @@ export function useHierarchyData( } } + /** 添加子节点, + * 修改子节点父级属性 + * 添加子节点到原始数据 + */ + function insertChildren(targetIndex: number, newDataItems: any[], originalData: Ref) { + const currentDataItem = originalData.value[targetIndex]; + const currentDataItemId = currentDataItem[idField.value]; + const currentTreeNode = hierarchyGraph.get(currentDataItemId) as HierarchyGraphNode; + if (currentTreeNode) { + const currentIndexInOriginalData = originalData.value.findIndex((item: any) => + item[idField.value] === currentDataItemId); + const indexToInsert = currentIndexInOriginalData + 1; + let children = newDataItems; + if (isNestedTree(newDataItems)) { + children = flatTreeData(newDataItems); + } + children.forEach((newDataItem: any) => { + newDataItem[parentIdField.value] = currentDataItemId; + }); + originalData.value.splice(indexToInsert, 0, ...children); + } + } + /** 删除树节点后,将剩余节点排序,重新赋id及parentId */ function trimmedOriginalData(itemToTrim: any, originalData: any[]) { const dataItemIdToTrim = itemToTrim[idField.value]; @@ -355,6 +381,7 @@ export function useHierarchyData( hasRealChildren, insertSibling, insertChild, + insertChildren, isVisibleInTree, shouldStratifyData, toggleChildrenVisibiltyByCollapseStatus, diff --git a/packages/ui-vue/components/data-view/composition/data/use-loading.ts b/packages/ui-vue/components/data-view/composition/data/use-loading.ts index 52c5ab0bec4815a98b9972e888de0df964774514..11ef270773443540334d28f156c0c95a7bd05d06 100644 --- a/packages/ui-vue/components/data-view/composition/data/use-loading.ts +++ b/packages/ui-vue/components/data-view/composition/data/use-loading.ts @@ -1,11 +1,14 @@ import { computed, inject, Ref, ref, watch } from 'vue'; +import { useI18n } from 'vue-i18n'; import { FLoadingService } from '../../../loading'; -import { DataGridProps, dataGridProps } from '../../../data-grid/src/data-grid.props'; +import { DataGridProps } from '../../../data-grid/src/data-grid.props'; export function useLoading(props: DataGridProps, gridRef: Ref) { + const { t: getLocaleValue } = useI18n(); const LoadingService: FLoadingService | any = inject('FLoadingService'); const showLoading = computed(() => typeof props.loading === 'object' ? props.loading.show : props.loading); - const loadingMessage = computed(() => typeof props.loading === 'object' ? props.loading.message : '正在加载...'); + const loadingMessage = computed(() => typeof props.loading === 'object' ? + props.loading.message : `${getLocaleValue('datagrid.loadingMessage')}...`); let loadingInstance; function renderLoading() { const config: any = { diff --git a/packages/ui-vue/components/data-view/composition/data/use-tree-data.ts b/packages/ui-vue/components/data-view/composition/data/use-tree-data.ts index 5a09cda5123b7a449428953ebadd6da2a636d1bf..ac797d3c894ef7b1340156b462128a03c5f6ba8b 100644 --- a/packages/ui-vue/components/data-view/composition/data/use-tree-data.ts +++ b/packages/ui-vue/components/data-view/composition/data/use-tree-data.ts @@ -10,9 +10,11 @@ export function useTreeData(props: DataViewOptions) { node.id = node.id || node.data[props.idField]; node[parentField] = parentId; node[layerField] = layer; - node[hasChildrenField] = false; + node[hasChildrenField] = node[hasChildrenField] === undefined ? + false : + node[hasChildrenField]; // 兼容内部treeNode 合并node和node.data属性 - if(node.data){ + if (node.data) { // 偶发此处数据为null导致报错 Object.keys(node.data).forEach((key: string) => { const nodeKeys = Object.keys(node); @@ -31,5 +33,12 @@ export function useTreeData(props: DataViewOptions) { return flattenedData; }; - return { flatTreeData }; + /** nested tree structure */ + const isNestedTree = (data: Record): boolean => { + return data.some((dataItem: any) => { + return Array.isArray(dataItem.children); + }); + }; + + return { flatTreeData, isNestedTree }; } diff --git a/packages/ui-vue/components/data-view/composition/hierarchy/use-select-hierarchy-item.ts b/packages/ui-vue/components/data-view/composition/hierarchy/use-select-hierarchy-item.ts index 1b34d493b8736519b715c8ecab7698bc66bd1b2c..c3a9bf1fd75027b74abd2d6f427a5f93e610c04e 100644 --- a/packages/ui-vue/components/data-view/composition/hierarchy/use-select-hierarchy-item.ts +++ b/packages/ui-vue/components/data-view/composition/hierarchy/use-select-hierarchy-item.ts @@ -367,6 +367,8 @@ export function useSelectHierarchyItem( context.emit('selectionChange', selectedItems); if (!visualData.checked) { context.emit('unSelectItem', visualData); + } else { + context.emit('selectItem', visualData); } } diff --git a/packages/ui-vue/components/data-view/composition/hierarchy/use-toggle-hierarchy-item.ts b/packages/ui-vue/components/data-view/composition/hierarchy/use-toggle-hierarchy-item.ts index 23a0d38d60cf45c53feb9145f2732c73a9ad8725..3697c68a291efe76c8ccc2e7eaf949780a22062d 100644 --- a/packages/ui-vue/components/data-view/composition/hierarchy/use-toggle-hierarchy-item.ts +++ b/packages/ui-vue/components/data-view/composition/hierarchy/use-toggle-hierarchy-item.ts @@ -98,8 +98,10 @@ export function useToggleHierarchyItem( reCalculateVisualDataRows(); // 更新滚动条位置 updateVirticalScroll(); + if (!currentDataItem[collapseField.value]) { + context.emit('expandNode', { row: visualTreeNode }); + } } - context.emit('expandNode', { row: visualTreeNode }); } return { toggleTreeNode }; diff --git a/packages/ui-vue/components/data-view/composition/types.ts b/packages/ui-vue/components/data-view/composition/types.ts index fe5a59884a7c4c69cef255c2c4bc206f19c1dc5e..78af8792ed3c5485fc30cae6aee2cb3147aa1cc8 100644 --- a/packages/ui-vue/components/data-view/composition/types.ts +++ b/packages/ui-vue/components/data-view/composition/types.ts @@ -55,8 +55,6 @@ export interface DataColumn { id?: string; /** 列索引 */ index?: number; - /** 是否多语字段 */ - isMultilingualField?: boolean; /** 横向坐标 */ left?: number; /** 列合并原始值 */ @@ -99,7 +97,8 @@ export interface DataColumn { format?: (cell: VisualDataCell, visualDataRow: VisualData) => VNode | string; binding?: any; draggable?: boolean; - headerFormatter?: (context: { headerCell: HeaderCell, headerCells: HeaderCell[], columnIndex: number }) => VNode| string; + headerFormatter?: (context: { headerCell: HeaderCell, headerCells: HeaderCell[], columnIndex: number }) => VNode | string; + columnTemplate?: (cell: VisualDataCell, visualDataRow: VisualData) => VNode; } export interface DataFilter { @@ -396,6 +395,12 @@ export interface CommandOptions { commands: ColumnCommand[]; /** 标题水平位置 */ halign: 'left' | 'center' | 'right'; + /** 自定义操作列模板,如果设置了列模板,commands属性失效 */ + formatter: (cell: VisualDataCell, visualDataRow: VisualData) => VNode | string; + /** 编辑回调事件 */ + onClickEditCommand: (cell: VisualDataCell, visualDataRow: VisualData) => void; + /** 删除回调事件 */ + onClickDeleteCommand: (cell: VisualDataCell, visualDataRow: VisualData) => void; } export interface ColumnCommand { @@ -517,6 +522,7 @@ export interface DataViewOptions { /** 虚拟化渲染数据 */ virtualized: boolean; keepSelectingOnClick: boolean; + loadData: (treeNode: VisualData) => Promise; /** 自动高度 */ autoHeight: boolean; @@ -558,6 +564,8 @@ export interface UseDataView { insertNewChildDataItem: (targetIndex?: number) => void; + insertNewChildDataItems: (dataItems: any[], targetIndex?: number) => void; + insertNewDataItem: (targetIndex?: number) => void; isVisibleInTree: (rawDataItem: any) => boolean; @@ -603,6 +611,7 @@ export interface UseDataView { visibleDataItems: ComputedRef; shouldGroupingData: ComputedRef; + updateSummary: () => void; } export type DataViewType = UseDataView['dataView']; @@ -657,7 +666,7 @@ export interface UseEdit { onClickCell: (payload: MouseEvent, cell: VisualDataCell, row: VisualData, column: DataColumn) => any; - onClickOutOfCell:(payload: MouseEvent | KeyboardEvent) => void; + onClickOutOfCell: (payload: MouseEvent | KeyboardEvent) => void; onClickCellByKeyboard: (payload: MouseEvent, cell: VisualDataCell, row: VisualData, column: DataColumn) => any; onMousedownCell: (payload: MouseEvent) => any; @@ -1082,6 +1091,8 @@ export interface UseHierarchyData { insertChild: (targetIndex: number, newDataItem: any, originalData: Ref) => void; + insertChildren: (targetIndex: number, newDataItems: any[], originalData: Ref) => void; + isVisibleInTree: (rawDataItem: any) => boolean; shouldStratifyData: Ref; @@ -1239,7 +1250,21 @@ export type LookupFormatOptions = NumberFormatOptions | BooleanFormatOptions | EnumFormatOptions | ImageFormatOptions; - +export enum ColumnTemplateType { + DATE = 'date', + DATE_TIME = 'datetime', + NUMBER = 'number', + ENUM = 'enum', + IMAGE = 'image', + BOOLEAN = 'boolean', + BOOLEAN2 = 'boolean2', + TIMEAGO = 'timeago', + NONE = 'none', + CUSTOM = 'custom', + MULTI_LINGUAL = 'multilingual' +} +// type ColumnTemplateType = 'date' | 'datetime' | 'number' | 'enum' +// | 'image' | 'boolean' | 'boolean2' | 'timeago' | 'none' | 'custom' | 'multilingual'; export interface InnerFormatter { data?: any; customFormat?: any; @@ -1255,8 +1280,7 @@ export interface InnerFormatter { height?: number; valueField?: string; textField?: string; - type?: 'date' | 'datetime' | 'number' | 'enum' - | 'image' | 'boolean' | 'boolean2' | 'timeago' | 'none' | 'custom'; + type?: ColumnTemplateType; options?: LookupFormatOptions; } diff --git a/packages/ui-vue/components/data-view/composition/use-edit.tsx b/packages/ui-vue/components/data-view/composition/use-edit.tsx index 5bbb5469239383b3128f779e4d8faec5ad68c71b..6e24149e6752d1a181aeda79489c516c87ad173e 100644 --- a/packages/ui-vue/components/data-view/composition/use-edit.tsx +++ b/packages/ui-vue/components/data-view/composition/use-edit.tsx @@ -291,7 +291,7 @@ export function useEdit( editor.updateOn = 'change'; } if (editor.type === 'number-spinner') { - // editor.keyboard = false; + editor.updateOn = 'change'; } if (editor.type === 'textarea') { editor.lineBreak = 'alt enter'; diff --git a/packages/ui-vue/components/data-view/composition/use-selection.ts b/packages/ui-vue/components/data-view/composition/use-selection.ts index dceede186322d50ceaf3933dcf5bf237a19179ac..18a92dc11e2d9921492dadbaa080ba3f305ea495 100644 --- a/packages/ui-vue/components/data-view/composition/use-selection.ts +++ b/packages/ui-vue/components/data-view/composition/use-selection.ts @@ -131,6 +131,10 @@ export function useSelection( context.emit('unSelectItem', visibleData); } + function emitSelectItem(visibleData: VisualData) { + context.emit('selectItem', visibleData); + } + function resetSelection() { const indeterminateVisualDataIdList = visibleDatas.value @@ -299,6 +303,8 @@ export function useSelection( if (!visualData.checked) { // 取消选中 emitUnSelectItem(visualData); + } else { + emitSelectItem(visualData); } } @@ -352,7 +358,7 @@ export function useSelection( }); if (visibleItemToBeSelected.length || dataItemToBeSelected.length) { // todo: should not clear,should append selected values - clearSelection(); + // clearSelection(); // if single select row, active current row if (isSingleSelect.value) { currentSelectedDataId.value = dataItemIds[0]; diff --git a/packages/ui-vue/components/data-view/composition/use-sidebar.ts b/packages/ui-vue/components/data-view/composition/use-sidebar.ts index a5f7391ee59cbaed559c3ba5c298a57d087e8c0f..d18087cb4be62e66677f29a0f0f8bd2f17d8df84 100644 --- a/packages/ui-vue/components/data-view/composition/use-sidebar.ts +++ b/packages/ui-vue/components/data-view/composition/use-sidebar.ts @@ -14,9 +14,11 @@ * limitations under the License. */ import { computed, ref, watch } from 'vue'; +import { useI18n } from 'vue-i18n'; import { DataViewOptions, RowNumberOptions, UseSelection, UseSidebar, VisualData } from './types'; export function useSidebar(props: DataViewOptions, useSelectionCompostion: UseSelection): UseSidebar { + const { t: getLocaleValue } = useI18n(); const timeStamp = String(Date.now()); // const defaultCheckboxWidth = 24; const defaultCheckboxWidth = 50; @@ -31,7 +33,8 @@ export function useSidebar(props: DataViewOptions, useSelectionCompostion: UseSe const rowNumberWidth = ref(showRowNumer.value ? props.rowNumber?.width ?? 32 : 0); const checkboxWidth = ref(showSidebarCheckBox.value ? defaultCheckboxWidth : 0); - const sidebarTitle = computed(() => showRowNumer.value ? props.rowNumber?.heading ?? '序号' : ''); + // const sidebarTitle = computed(() => showRowNumer.value ? props.rowNumber?.heading ?? '序号' : '');getLocaleValue('datagrid.lineNumberTitle') + const sidebarTitle = computed(() => showRowNumer.value ? props.rowNumber?.heading === '序号' ? getLocaleValue('datagrid.lineNumberTitle') : props.rowNumber?.heading : ''); const sidebarWidth = computed(() => { return ((showSidebarCheckBox.value && !props.hierarchy) ? Number(checkboxWidth.value) : 0) + (showRowNumer.value ? Number(rowNumberWidth.value) : 0); diff --git a/packages/ui-vue/components/data-view/composition/visualization/use-virtual-scroll.ts b/packages/ui-vue/components/data-view/composition/visualization/use-virtual-scroll.ts index 2da2cefbb1488c43e7698da8773b8e15140bd66e..eb56485c8729387715494e3aa3ef3e45f37c56af 100644 --- a/packages/ui-vue/components/data-view/composition/visualization/use-virtual-scroll.ts +++ b/packages/ui-vue/components/data-view/composition/visualization/use-virtual-scroll.ts @@ -48,7 +48,7 @@ export function useVirtualScroll( const minThumbHeight = 24; const zoomFactorOfThumbScope = 1.5; - let onDragThumbHandler: DebouncedFunc<(...args: any[]) => any> | null = null; + let onDragThumbHandler: DebouncedFunc<(...args: any[]) => any> | null | any = null; function getLatestGridViewHeight(): number { const visibleDataViewItems = visibleDataItems.value; @@ -473,10 +473,10 @@ export function useVirtualScroll( scrollThumbContainer.value = scrollThumbParent; } - const draggingHandler = thumbType === 'horizontal' ? onDragHorizontalScroll : onDragVerticalScroll; + const draggingHandler = thumbType === 'horizontal' ? onDragHorizontalScroll : throttle(onDragVerticalScroll, 50); if (!onDragThumbHandler) { // 节流,解决纵向滚动条卡顿问题 - onDragThumbHandler = throttle(draggingHandler, 50); + onDragThumbHandler = draggingHandler; } if (thumbType === 'vertical') { onDraggingStartY.value = $event.pageY; diff --git a/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-cell.ts b/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-cell.ts index 5eaf99b1c2bff558cd2b77f276fbc95056f416b9..4d140b1b461169ae724510195a6c61fdaa20e3d7 100644 --- a/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-cell.ts +++ b/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-cell.ts @@ -1,4 +1,4 @@ -import { ref, SetupContext, VNode, Comment } from "vue"; +import { ref } from "vue"; import { CellMode, DataColumn, DataViewOptions, UseCellEditor, UseVisualDataBound, UseVisualDataCell, VisualData, @@ -7,19 +7,19 @@ import { import getCommandColumn from '../../components/editors/commands.component'; import getFormatColumn from '../../components/column-format/column-format.component'; -import { resolveField, setFieldValue } from "@farris/ui-vue/components/common"; -import { isUndefined } from "lodash"; +import { resolveField, setFieldValue, useCommonUtils } from "@farris/ui-vue/components/common"; export function useVisualDataCell( props: DataViewOptions, useCellEditorComposition: UseCellEditor, useVisualDataBoundComposition: UseVisualDataBound ): UseVisualDataCell { + const { isUndefined } = useCommonUtils(); const mergingCell = ref(props.mergeCell); const rowOption = ref(props.rowOption); const autoRowHeight = rowOption.value?.wrapContent || false; const { updateVisualInfomation } = useVisualDataBoundComposition; - const { renderCommandColumn } = getCommandColumn(); + const { renderCommandColumn } = getCommandColumn(props); const { renderFormatColumn } = getFormatColumn(); function createCellByField(targetField: string, mode: CellMode, index: number, dataItem: any, parent: VisualData, colSpan = 1) { const targetCell: VisualDataCell = { @@ -67,14 +67,17 @@ export function useVisualDataCell( if (column.dataType === 'commands') { targetCell.formatter = (cell: VisualDataCell, visualDataRow: VisualData) => { // 此处应该渲染操作列组件而不是编辑组件 - return renderCommandColumn(column, visualDataRow); + if (column.formatter) { + return column.formatter(targetCell, visualDataRow); + } + return renderCommandColumn(targetCell, visualDataRow); }; } else { if (column.formatter) { targetCell.formatter = (cell: VisualDataCell, visualDataRow: VisualData) => { return typeof column.formatter === 'function' ? column.formatter(cell, visualDataRow) : - renderFormatColumn(column, visualDataRow, column.formatter); + renderFormatColumn(targetCell, visualDataRow, column.formatter); }; } } @@ -112,7 +115,8 @@ export function useVisualDataCell( if (column.showEllipsis !== undefined) { targetCell.showEllipsis = column.showEllipsis; } - targetCell.showTips = column.showTips; + // targetCell.showTips = column.showTips; + targetCell.showTips = isUndefined(column.showTips) ? true : column.showTips; targetCell.column = column; targetCell.align = column.align || 'left'; targetCell.valign = column.valign || 'middle'; @@ -134,7 +138,7 @@ export function useVisualDataCell( // 所有单元格编辑状态默认取决于editable, // 如果进一步控制单元格的编辑状态,应该配置编辑器前事件beforeBeginEdit // 对于editor的readonly,不应该是单元格的职责 - const cellMode = !props.editable? CellMode.readonly : CellMode.editable; + const cellMode = !props.editable ? CellMode.readonly : CellMode.editable; const targetCell: VisualDataCell = createCellByField(column.field, cellMode, index, dataItem, parent); updateCellByColumn(targetCell, column, preVisualData, dataItem); return targetCell; diff --git a/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-row.ts b/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-row.ts index 2fe09578118fe899dafce4127eb190bbcc1592ac..8d823891a6fb45190b6ed44d4031e6de34462b5a 100644 --- a/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-row.ts +++ b/packages/ui-vue/components/data-view/composition/visualization/use-visual-data-row.ts @@ -73,6 +73,7 @@ export function useVisualDataRow( status: VisualDataStatus.initial, updateCell: (newDataItem: any, field: string) => { updateCellData(newDataItem, dataItem, currentRow, field); + dataView.updateSummary(); }, updateCells: (newDataItem: any, fields: string[]) => { fields.forEach(field => { diff --git a/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx b/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx index 9346b013b81afbf117f29f7360a3574521f1e90b..aa516b6fa978c348b882dc205d83017802ec7a22 100644 --- a/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/calendar-navbar/calendar-navbar.component.tsx @@ -22,169 +22,183 @@ export default defineComponent({ props: calendarNavbarProps, emits: ['clickMonth', 'clickYear', 'prePage', 'preRecord', 'nextRecord', 'nextPage'] as (string[] & ThisType) | undefined, setup(props: CalendarNavbarProps, context: SetupContext) { - return () => { - const ariaLabelPrevMonth = ref(props.ariaLabelPrevMonth); - const ariaLabelNextMonth = ref(props.ariaLabelNextMonth); - const dateFormat = ref(props.dateFormat); - const disablePrePageButton = ref(props.disablePrePage); - const disablePreRecordButton = ref(props.disablePreRecord); - const disableNextRecordButton = ref(props.disableNextRecord); - const disableNextPageButton = ref(props.disableNextPage); - const activeMonth = ref(props.activeMonth as ActiveMonth); - const years = ref(props.years); - const selectingMonth = ref(props.selectingMonth); - const selectingYear = ref(props.selectingYear); - const selectMode = ref(props.selectMode); - - const yearSelector = ref(true); - const monthSelector = ref(true); - - watch( - () => props.activeMonth, - () => { - activeMonth.value = { - month: props.activeMonth?.month, - year: props.activeMonth?.year, - displayTextOfMonth: props.activeMonth?.displayTextOfMonth, - displayTextOfYear: props.activeMonth?.displayTextOfMonth - }; - } - ); - - const navbarClass = computed(() => { - const classObject = { - 'f-datepicker-header': true, - monthYearSelBarBorder: selectingMonth.value || selectingYear.value - } as Record; - return classObject; - }); - - const prePageButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disablePrePageButton.value - } as Record; - return classObject; - }); - - const shouldShowNavRecordButton = computed(() => { - return !selectingMonth.value && !selectingYear.value; - }); - - const preRecordButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disablePreRecordButton.value - } as Record; - return classObject; - }); - - const nextRecordButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disableNextRecordButton.value - } as Record; - return classObject; - }); - - const nextPageButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-header-btn-disabled': disableNextPageButton.value - } as Record; - return classObject; - }); - - const navbarSelectYearButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-yearLabel': yearSelector.value, - 'f-datepicker-labelBtnNotEdit': !yearSelector.value - } as Record; - return classObject; - }); - - const navbarSelectMonthButtonClass = computed(() => { - const classObject = { - 'f-datepicker-header-btn': true, - 'f-datepicker-monthLabel': monthSelector.value, - 'f-datepicker-labelBtnNotEdit': !monthSelector.value - } as Record; - return classObject; - }); - - const formateType = computed(() => { - const yIndex = dateFormat.value ? dateFormat.value.indexOf('yyyy') : 0; - const mIndex = dateFormat.value ? dateFormat.value.indexOf('MM') : 0; - return yIndex > mIndex ? `${'MM'}-${'yyyy'}` : `${'yyyy'}-${'MM'}`; - }); - - function navigateToPreviousPage($event: MouseEvent) { - $event.stopPropagation(); - context.emit('prePage'); - } - - function navigateToPreviousRecord($event: MouseEvent) { - $event.stopPropagation(); - context.emit('preRecord'); - } - - function onClearActiveYear($event: MouseEvent) { - $event.stopPropagation(); - context.emit('clickYear'); - } - - function onClickActiveMonth($event: MouseEvent) { - $event.stopPropagation(); - context.emit('clickMonth'); - } - - function navigateToNextRecord($event: MouseEvent) { - $event.stopPropagation(); - context.emit('nextRecord'); - } - function navigateToNextPage($event: MouseEvent) { - $event.stopPropagation(); - context.emit('nextPage'); + const ariaLabelPrevMonth = ref(props.ariaLabelPrevMonth); + const ariaLabelNextMonth = ref(props.ariaLabelNextMonth); + const dateFormat = ref(props.dateFormat); + const disablePrePageButton = ref(props.disablePrePage); + const disablePreRecordButton = ref(props.disablePreRecord); + const disableNextRecordButton = ref(props.disableNextRecord); + const disableNextPageButton = ref(props.disableNextPage); + const activeMonth = ref(props.activeMonth as ActiveMonth); + const years = ref(props.years); + const selectingMonth = ref(props.selectingMonth); + const selectingYear = ref(props.selectingYear); + const selectMode = ref(props.selectMode); + + const yearSelector = ref(true); + const monthSelector = ref(true); + + + watch(() => props.selectingMonth, (newValue, oldValue) => { + selectingMonth.value = newValue; + }); + + watch(() => props.selectingYear, (newValue, oldValue) => { + selectingYear.value = newValue; + }); + + watch(() => props.years, (newValue, oldValue) => { + years.value = newValue; + }); + + watch( + () => props.activeMonth, + () => { + activeMonth.value = { + month: props.activeMonth?.month, + year: props.activeMonth?.year, + displayTextOfMonth: props.activeMonth?.displayTextOfMonth, + displayTextOfYear: props.activeMonth?.displayTextOfYear + }; } + ); + + const navbarClass = computed(() => { + const classObject = { + 'f-datepicker-header': true, + monthYearSelBarBorder: selectingMonth.value || selectingYear.value + } as Record; + return classObject; + }); + + const prePageButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disablePrePageButton.value + } as Record; + return classObject; + }); + + const shouldShowNavRecordButton = computed(() => { + return !selectingMonth.value && !selectingYear.value; + }); + + const preRecordButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disablePreRecordButton.value + } as Record; + return classObject; + }); + + const nextRecordButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disableNextRecordButton.value + } as Record; + return classObject; + }); + + const nextPageButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-header-btn-disabled': disableNextPageButton.value + } as Record; + return classObject; + }); + + const navbarSelectYearButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-yearLabel': yearSelector.value, + 'f-datepicker-labelBtnNotEdit': !yearSelector.value + } as Record; + return classObject; + }); + + const navbarSelectMonthButtonClass = computed(() => { + const classObject = { + 'f-datepicker-header-btn': true, + 'f-datepicker-monthLabel': monthSelector.value, + 'f-datepicker-labelBtnNotEdit': !monthSelector.value + } as Record; + return classObject; + }); + + const formateType = computed(() => { + const yIndex = dateFormat.value ? dateFormat.value.indexOf('yyyy') : 0; + const mIndex = dateFormat.value ? dateFormat.value.indexOf('MM') : 0; + return yIndex > mIndex ? `${'MM'}-${'yyyy'}` : `${'yyyy'}-${'MM'}`; + }); + + function navigateToPreviousPage($event: MouseEvent) { + $event.stopPropagation(); + context.emit('prePage'); + } + + function navigateToPreviousRecord($event: MouseEvent) { + $event.stopPropagation(); + context.emit('preRecord'); + } + + function onClearActiveYear($event: MouseEvent) { + $event.stopPropagation(); + context.emit('clickYear'); + } + + function onClickActiveMonth($event: MouseEvent) { + $event.stopPropagation(); + context.emit('clickMonth'); + } + + function navigateToNextRecord($event: MouseEvent) { + $event.stopPropagation(); + context.emit('nextRecord'); + } + + function navigateToNextPage($event: MouseEvent) { + $event.stopPropagation(); + context.emit('nextPage'); + } + + function renderSelectYearButton() { + return ( + + ); + } - function renderSelectYearButton() { - return ( + function renderSelectMonthButton() { + return ( + !selectingYear.value && + selectMode.value !== 'month' && ( - ); - } - - function renderSelectMonthButton() { - return ( - !selectingYear.value && - selectMode.value !== 'month' && ( - - ) - ); - } + ) + ); + } + return () => { return (
diff --git a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx index 6e1c6d22e5c27689e2cb5e7ccbe9623e58b8add5..7a8875ca972284634867a64a6decf686fd28f0bc 100644 --- a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.component.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { computed, defineComponent, ref, SetupContext, watch } from "vue"; +import { computed, defineComponent, ref, SetupContext, watch, watchEffect } from "vue"; import { DatePickerCalendarProps, datePickerCalendarProps } from "./calendar.props"; import { DateObject, MonthTag, Period, SelectMode } from "../../types/common"; import { CalendarWeekItem, CalenderDayItem } from "../../types/calendar"; @@ -30,31 +30,35 @@ export default defineComponent({ const enableKeyboadNavigate = ref(props.enableKeyboadNavigate); const enableMarkCurrent = ref(props.enableMarkCurrent); const enablePeriod = ref(props.enablePeriod); - const firstDayOfTheWeek = ref(props.firstDayOfTheWeek); const selected = ref(props.selected); const selectedPeriod = ref(props.selectedPeriod); const selectedWeek = ref(props.selectedWeek); const selectMode = ref(props.selectMode as SelectMode); const showWeekNumber = ref(props.showWeekNumber); - const weekTitle = ref(props.weekTitle); - - const shouldShowWeekTitle = computed(() => { - return showWeekNumber.value && firstDayOfTheWeek.value === 'Mon'; - }); - + const weekTitle = ref(props.weekTitle || '周'); + watch(() => props.dates, () => { dates.value = props.dates; }); + watch(() => props.selected, () => { selected.value = props.selected; }); - + watch(() => props.enablePeriod, (newValue, oldValue) => { if (newValue !== oldValue) { enablePeriod.value = newValue; } }); + watch(() => props.selectedPeriod, (newValue) => { + selectedPeriod.value = newValue; + }); + + watch(() => props.selectedWeek, (newValue) => { + selectedWeek.value = newValue; + }); + const { equal, inPeriod, isInitializedDate, equalOrEarlier, isPoint } = useCompare(); const { getKeyCodeFromEvent } = useEvent(); @@ -69,16 +73,29 @@ export default defineComponent({ return classObject; }; - const shouldShowWeekNumber = computed(() => { - return showWeekNumber.value && firstDayOfTheWeek.value === 'Mon'; - }); - + function isDateInPeriod(day: DateObject) { return inPeriod(day, selectedPeriod.value); } function isDateRangeBeginOrEndSame(day: DateObject) { - return !!selectedPeriod.value && isPoint(selectedPeriod.value, day); + if (!selectedPeriod.value) { + return false; + } + const selectedDateRange = { + from: { + year: selectedPeriod.value.from.year, + month: selectedPeriod.value.from.month, + day: selectedPeriod.value.from.day + }, + to: { + year: selectedPeriod.value.to.year, + month: selectedPeriod.value.to.month, + day: selectedPeriod.value.to.day + } + }; + + return !!selectedPeriod.value && isPoint(selectedDateRange, day); } function isDateSame(day: DateObject) { @@ -90,11 +107,8 @@ export default defineComponent({ const dayContainerClass = (currentDay: CalenderDayItem, weekIndex: number, dayIndex: number) => { const showDateRange = selectMode.value !== 'week' && currentDay.tag === MonthTag.current && - ( - ( - enablePeriod.value && isDateInPeriod(currentDay.date) && !isDateRangeBeginOrEndSame(currentDay.date) - ) || currentDay.range - ); + ((enablePeriod.value && isDateInPeriod(currentDay.date) && !isDateRangeBeginOrEndSame(currentDay.date)) || currentDay.range); + const notInCurrentMonth = currentDay.tag === MonthTag.previous || currentDay.tag === MonthTag.next; const classObject = { 'f-datepicker-range': showDateRange, @@ -109,10 +123,7 @@ export default defineComponent({ const dayClass = (currentDay: CalenderDayItem) => { const isSelected = selectMode.value !== 'week' && currentDay.tag === MonthTag.current && - ((!enablePeriod.value && - isDateSame(currentDay.date)) || - (enablePeriod.value && - isDateRangeBeginOrEndSame(currentDay.date))); + ((!enablePeriod.value && isDateSame(currentDay.date)) || (enablePeriod.value && isDateRangeBeginOrEndSame(currentDay.date))); const shouldMarkCurrentDay = currentDay.isCurrent && enableMarkCurrent.value; const shouldHight = currentDay.highlight && @@ -163,13 +174,14 @@ export default defineComponent({ function onMouseEnter(target: CalenderDayItem) { if (selectedPeriod.value && isInitializedDate(selectedPeriod.value.from) && - !isInitializedDate(selectedPeriod.value.to) + (!isInitializedDate(selectedPeriod.value.to) || JSON.stringify(selectedPeriod.value.from) === JSON.stringify(selectedPeriod.value.to)) ) { + const { from } = selectedPeriod.value; dates.value.forEach((week: CalendarWeekItem) => { week.days.forEach((item: CalenderDayItem) => { - item.range = !!selectedPeriod.value && ( - (equalOrEarlier(selectedPeriod.value.from, item.date) && equalOrEarlier(item.date, target.date)) || - (equalOrEarlier(item.date, selectedPeriod.value.from) && equalOrEarlier(target.date, item.date)) + item.range = ( + (equalOrEarlier(from, item.date) && equalOrEarlier(item.date, target.date)) || + (equalOrEarlier(item.date, from) && equalOrEarlier(target.date, item.date)) ); }); }); @@ -193,7 +205,7 @@ export default defineComponent({ - {shouldShowWeekTitle.value && + {showWeekNumber.value && @@ -216,7 +228,7 @@ export default defineComponent({ return ( onClickWeek(payload, week)}> { - shouldShowWeekNumber.value && ( + showWeekNumber.value && ( @@ -227,7 +239,6 @@ export default defineComponent({ return ( diff --git a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts index e27a940ec86421169a073b4f7b62e652db548df9..acac33ce993cf1a7175a5160bac350f97458ff47 100644 --- a/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/calendar/calendar.props.ts @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; import { CalendarWeekItem } from '../../types/calendar'; -import { weekDays } from '../../types/common'; +import { FirstDayOfTheWeek } from '../../types/common'; export const datePickerCalendarProps = { dates: { Type: Array, default: [] }, - daysInWeek: { Type: Array, default: weekDays }, + daysInWeek: { Type: Array, default: [] }, enableKeyboadNavigate: { Type: Boolean, default: true }, enableMarkCurrent: { Type: Boolean, default: true }, enablePeriod: { Type: Boolean, default: false }, - firstDayOfTheWeek: { Type: Boolean, default: 'Sun.' }, + firstDayOfTheWeek: { Type: String as PropType, default: FirstDayOfTheWeek.Sunday }, selected: { Type: Object, default: null }, selectedPeriod: { Type: Object, default: null }, selectedWeek: { Type: Object, default: null }, diff --git a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx index ebdad418a1d5b5c6befa4774d6c7a819b8755f3a..b0dd4f932acb48ca74d204e02ba485de88b25826 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.component.tsx @@ -25,24 +25,29 @@ import YearView from '../year/year.component'; import { useDisableDate } from "../../composition/use-disable-date"; import { UseDisableDate } from "../../composition/types"; import useCalendar from "../../composition/use-calendar"; -import { DateObject } from "../../types/common"; +import { DateObject, Period, WeekDays } from "../../types/common"; import { useDate } from "../../composition/use-date"; -import { ActiveMonth } from "../../types/month"; +import { ActiveMonth, MonthViewItem } from "../../types/month"; import { useMonth } from "../../composition/use-month"; import { useYear } from "../../composition/use-year"; import { useDateFormat, formatTime, getTimeObject } from '@farris/ui-vue/components/common'; -import { CalenderDayItem } from "../../types/calendar"; import { useDisableMonth } from "../../composition/use-disable-month"; +import { CalendarWeekItem, CalenderDayItem } from "../../types/calendar"; +import { useNormalizeDate } from "../../composition/use-normalize-date"; +import { useCompare } from "../../composition/use-compare"; +import { useWeek } from "../../composition/use-week"; +import { useNumber } from "../../composition/use-number"; +import { useQuickSelector } from "../../composition/use-quick-selector"; export default defineComponent({ name: 'FDateView', props: datePickerContainerProps, emits: ['datePicked', 'confirm'] as (string[] & ThisType) | undefined, setup(props: DatePickerContainerProps, context: SetupContext) { - const timePicker = ref(); const timeValue = ref(''); + const timeRangeValue = ref(''); /** 模式 */ const displayMode = ref(props.mode); const top = ref(props.top); @@ -60,12 +65,11 @@ export default defineComponent({ /** 存储格式 */ const valueFormat = ref(props.valueFormat); - const secondaryYears = ref([]); const selectingMonth = ref(false); - const selectingSecondaryMonth = ref(''); + const selectingSecondaryMonth = ref(false); const selectingYear = ref(false); - const selectingSecondaryYear = ref(''); - const selectTime = ref(); + const selectingSecondaryYear = ref(false); + const selectingTime = ref(false); const disablePrePage = ref(false); const disablePreRecord = ref(false); const disableNextRecord = ref(false); @@ -74,10 +78,8 @@ export default defineComponent({ const disableSecondaryPreRecord = ref(false); const disableSecondaryNextRecord = ref(false); const disableSecondaryNextPage = ref(false); - /** 二级日期 */ - const secondaryDates = ref(props.secondaryDates); /** 每日 */ - const daysInWeek = ref(props.daysInWeek); + const weekDays = ref([]); /** 是否允许键盘定位 */ const enableKeyboadNavigate = ref(props.enableKeyboadNavigate); /** 是否启用标记当前 */ @@ -94,12 +96,9 @@ export default defineComponent({ const dateValue = ref(props.value); /** 选择周 */ const selectedWeek = ref(props.selectedWeek); - /** 选择期限 */ - const selectedPeriod = ref(props.selectedPeriod); /** 每周标题 */ const weekTitle = ref(props.weekTitle); - /** 二级月份 */ - const secondaryMonths = ref(props.secondaryMonths); + /** 选择的月份 */ const selectedMonth = ref(props.selectedMonth); /** 最早年限 */ @@ -119,6 +118,30 @@ export default defineComponent({ /** 禁用周末 */ const disableWeekends = ref(props.disableWeekends); + const { quickSelectorList } = useQuickSelector(props, context); + + const {getToday, getDateObject, getTimeValue, getEndTimeValue, emptyDate, convertDateToDateObject } = useDate(); + const { setNewDateRange, getActiveMonth, getTimeStr, getMonthAndYear } = useNormalizeDate(props); + const selectedPeriod = ref({ from: { ...emptyDate() }, to: { ...emptyDate() } }); + + + function getWeekDays() { + const { weekDayLabels } = props.locales; + const dayLabels: any = []; + const firstDayIndex = WeekDays.indexOf(firstDayOfTheWeek.value); + if (firstDayIndex !== -1) { + let idx: number = firstDayIndex; + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < WeekDays.length; i++) { + dayLabels.push(weekDayLabels[WeekDays[idx]]); + idx = WeekDays[idx] === 'Sat' ? 0 : idx + 1; + } + } + return dayLabels; + } + + weekDays.value = getWeekDays(); + const refDisableDate: UseDisableDate = useDisableDate( minYear.value, maxYear.value, @@ -134,18 +157,30 @@ export default defineComponent({ const { isMonthDisabledByDisableSince, isMonthDisabledByDisableUntil } = useDisableMonth(); const { generateCalendar } = useCalendar(refDisableDate); - const { getToday, getDateObject } = useDate(); const { generateMonths, getNextMonth, getPreviousMonth, daysInMonth } = useMonth(); + const { getNowWeekTime } = useWeek(); + const { getWeekNumber } = useNumber(); + + const { generateYears } = useYear(); + const { equalOrEarlier, isInitializedDate, isDateEarlier } = useCompare(); const today = getToday(); const selectedDateObj = computed(() => { const todayClone = cloneDeep(today); - const parsedDate = dateValue.value && parseToDate(dateValue.value, valueFormat.value); + const parsedDate = dateValue.value && !enablePeriod.value ? parseToDate(dateValue.value, valueFormat.value) : null; return parsedDate ? getDateObject(formatTo(parsedDate, 'yyyy-MM-dd'), 'yyyy-MM-dd') : todayClone; }); - selectedDate.value = selectedDateObj.value; + + if (dateValue.value && !enablePeriod.value) { + const parsedDate = parseToDate(dateValue.value, valueFormat.value); + if (parsedDate) { + selectedDate.value = getDateObject(formatTo(parsedDate, 'yyyy-MM-dd'), 'yyyy-MM-dd'); + } + } + + // selectedDate.value = selectedDateObj.value; const activeMonth = ref({ year: selectedDateObj.value.year || 1, @@ -155,16 +190,19 @@ export default defineComponent({ }); selectedMonth.value = { - year: selectedDateObj.value.year ? selectedDateObj.value.year : undefined, - month: selectedDateObj.value.month ? selectedDateObj.value.month : undefined, + year: selectedDate.value?.year, + month: selectedDate.value?.month, }; - const initSecondaryYear = today.month === 12 ? (today.year || 1) + 1 : today.year; - const initSecondaryMonth = (today.month || 1) < 12 ? (today.month || 1) + 1 : 1; + + const { endMonth } = getMonthAndYear(); + + const initSecondaryYear = endMonth.year; + const initSecondaryMonth = endMonth.month; const secondaryActiveMonth = ref({ year: initSecondaryYear, month: initSecondaryMonth, - displayTextOfMonth: nameOfMonths.value[today.month || '1'], + displayTextOfMonth: nameOfMonths.value[initSecondaryMonth], displayTextOfYear: `${initSecondaryYear}` }); @@ -182,6 +220,12 @@ export default defineComponent({ return weekItems; }); + const currentDates = ref(dates.value); + + watch(() => dates.value, (newValue, oldValue) => { + currentDates.value = newValue; + }); + const secondaryRealDates = computed(() => { return generateCalendar( secondaryActiveMonth.value.month, @@ -195,6 +239,10 @@ export default defineComponent({ showWeekNumber.value ); }); + const secondaryDates = ref(secondaryRealDates.value); + watch(() => secondaryRealDates.value, (newValue, oldValue) => { + secondaryDates.value = newValue; + }); const months = computed(() => { const monthViewItems = generateMonths( @@ -205,6 +253,11 @@ export default defineComponent({ ); return monthViewItems; }); + const currentMonths = ref(months.value); + watch(() => months.value, (newValue, oldValue) => { + currentMonths.value = newValue; + }); + const years = computed(() => { const yearViewItems = generateYears( @@ -217,14 +270,39 @@ export default defineComponent({ ); return yearViewItems; }); + const currentYears = ref(years.value); + watch(() => years.value, (newValue, oldValue) => { + currentYears.value = newValue; + }); - const containerClass = computed(() => { - const classObject = { - 'f-datepicker-container': true - } as Record; - const className = `container-position-${position.value}`; - classObject[className] = true; - return classObject; + + const secondYears = computed(() => { + return generateYears( + secondaryActiveMonth.value.year, + { year: secondaryActiveMonth.value.year, month: secondaryActiveMonth.value.month }, + minYear.value, + maxYear.value, + disableSince.value, + disableUntil.value + ); + }); + const secondaryYears = ref(secondYears.value); + watch(() => secondYears.value, (newValue, oldValue) => { + secondaryYears.value = newValue; + }); + + const secondMonths = computed(() => { + const monthViewItems = generateMonths( + nameOfMonths.value, + { year: secondaryActiveMonth.value.year, month: secondaryActiveMonth.value.month }, + disableSince.value, + disableUntil.value + ); + return monthViewItems; + }); + const secondaryMonths = ref(secondMonths.value); + watch(() => secondMonths.value, (newValue, oldValue) => { + secondaryMonths.value = newValue; }); watch(() => props.enablePeriod, (newValue, oldValue) => { @@ -233,13 +311,13 @@ export default defineComponent({ } }); - function getWidth() { return enablePeriod.value ? '' : props.showTime ? '487px' : '287px'; }; + function getWidth() { return enablePeriod.value && selectMode.value !== 'week' ? 575 : props.showTime && selectMode.value !== 'week' ? 487 : 287; } const containerStyle = computed(() => { const styleObject = { 'top': `${top.value}px`, 'left': `${left.value}px`, - 'width': getWidth(), + 'width': `${getWidth() + (props.enableQuickSelect ? 90: 0)}px`, // 'position': displayMode.value === 'Embedded' ? 'relative' : 'absolute', 'position': 'relative', 'z-index': displayMode.value === 'Embedded' ? 0 : 9999, @@ -254,65 +332,155 @@ export default defineComponent({ $event.stopPropagation(); } - function navigateToPreviousPage($event: any, inPeriod: boolean) { - const previousYear = activeMonth.value.year - (selectingYear.value ? 10 : 1); - const previous = { - year: previousYear, - month: activeMonth.value.month, - displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], - displayTextOfYear: `${previousYear}` - }; + function navigateToPreviousYear($event: any, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + const previousYear = activeMonth.value.year - (selectingYear.value ? 10 : 1); + const previous = { + year: previousYear, + month: activeMonth.value.month, + displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], + displayTextOfYear: `${previousYear}` + }; + activeMonth.value = previous; + } else { + const previousYear = secondaryActiveMonth.value.year - (selectingSecondaryYear.value ? 10 : 1); + const previous = { + year: previousYear, + month: secondaryActiveMonth.value.month, + displayTextOfMonth: nameOfMonths.value[secondaryActiveMonth.value.month || '1'], + displayTextOfYear: `${previousYear}` + }; + secondaryActiveMonth.value = previous; + } + } - activeMonth.value = previous; + function navigateToPreviousMonth($event: any, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + const previousMonthDate = getPreviousMonth(activeMonth.value.month, activeMonth.value.year); + const previous = { + year: previousMonthDate.year || 1, + month: previousMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[previousMonthDate.month || '1'], + displayTextOfYear: `${previousMonthDate.year}` + }; + activeMonth.value = previous; + } else { + const previousMonthDate = getPreviousMonth(secondaryActiveMonth.value.month, secondaryActiveMonth.value.year); + const previous = { + year: previousMonthDate.year || 1, + month: previousMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[previousMonthDate.month || '1'], + displayTextOfYear: `${previousMonthDate.year}` + }; + secondaryActiveMonth.value = previous; + } } - function navigateToPreviousMonth($event: any, inPeriod: boolean) { - const previousMonthDate = getPreviousMonth(activeMonth.value.month, activeMonth.value.year); - const previous = { - year: previousMonthDate.year || 1, - month: previousMonthDate.month || 1, - displayTextOfMonth: nameOfMonths.value[previousMonthDate.month || '1'], - displayTextOfYear: `${previousMonthDate.year}` - }; + function navigateToNextMonth($event: any, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + const nextMonthDate = getNextMonth(activeMonth.value.month, activeMonth.value.year); + const next = { + year: nextMonthDate.year || 1, + month: nextMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[nextMonthDate.month || '1'], + displayTextOfYear: `${nextMonthDate.year}` + }; + activeMonth.value = next; + } else { + const nextMonthDate = getNextMonth(secondaryActiveMonth.value.month, secondaryActiveMonth.value.year); + const next = { + year: nextMonthDate.year || 1, + month: nextMonthDate.month || 1, + displayTextOfMonth: nameOfMonths.value[nextMonthDate.month || '1'], + displayTextOfYear: `${nextMonthDate.year}` + }; + secondaryActiveMonth.value = next; + } + } - activeMonth.value = previous; + function navigateToNextYear($event: any, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + let nextYear = activeMonth.value.year + 1; + if (selectingYear.value) { + nextYear = years.value[3][0].year + 2; + } + const next = { + year: nextYear, + month: activeMonth.value.month, + displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], + displayTextOfYear: `${nextYear}` + }; + activeMonth.value = next; + } else { + let nextYear = secondaryActiveMonth.value.year + 1; + if (selectingSecondaryYear.value) { + nextYear = secondYears.value[3][0].year + 2; + } + const next = { + year: nextYear, + month: secondaryActiveMonth.value.month, + displayTextOfMonth: nameOfMonths.value[secondaryActiveMonth.value.month || '1'], + displayTextOfYear: `${nextYear}` + }; + secondaryActiveMonth.value = next; + } } - function navigateToNextMonth($event: any, inPeriod: boolean) { - const nextMonthDate = getNextMonth(activeMonth.value.month, activeMonth.value.year); - const next = { - year: nextMonthDate.year || 1, - month: nextMonthDate.month || 1, - displayTextOfMonth: nameOfMonths.value[nextMonthDate.month || '1'], - displayTextOfYear: `${nextMonthDate.year}` - }; + function navigateToMonthView(isSecondCalendar: boolean) { + selectingTime.value = false; + if (!isSecondCalendar) { + selectingMonth.value = !selectingMonth.value; + selectingYear.value = false; + } else { + selectingSecondaryMonth.value = !selectingSecondaryMonth.value; + selectingSecondaryYear.value = false; + } + } - activeMonth.value = next; + function navigateToYearView(isSecondCalendar: boolean) { + selectingTime.value = false; + if (!isSecondCalendar) { + selectingYear.value = selectMode.value === 'year' ? true : !selectingYear.value; + if (selectMode.value === 'month') { + selectingMonth.value = !selectingMonth.value; + } else { + selectingMonth.value = false; + } + } else { + selectingSecondaryYear.value = selectMode.value === 'year' ? true : !selectingSecondaryYear.value; + if (selectMode.value === 'month') { + selectingSecondaryMonth.value = !selectingSecondaryMonth.value; + } else { + selectingSecondaryMonth.value = false; + } + } } - function navigateToNextPage($event: any, inPeriod: boolean) { - let nextYear = activeMonth.value.year + 1; - if (selectingYear.value) { - nextYear = years.value[3][0].year + 2; + function navigateToTimeView() { + if (selectedPeriod.value) { + const { from, to } = selectedPeriod.value; + const beginIsInitializedDate = isInitializedDate(from); + const endIsInitializedDate = isInitializedDate(to); + + const now = new Date(); + if (!beginIsInitializedDate || !endIsInitializedDate) { + if (!beginIsInitializedDate) { + selectedPeriod.value.from = { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() }; + } + + selectedPeriod.value.to = { ...selectedPeriod.value.from }; + timeValue.value = getTimeStr(now); + timeRangeValue.value = getTimeStr(now); + } } - const next = { - year: nextYear, - month: activeMonth.value.month, - displayTextOfMonth: nameOfMonths.value[activeMonth.value.month || '1'], - displayTextOfYear: `${nextYear}` - }; - activeMonth.value = next; - } - function navigateToMonthView(inPeriod: boolean) { - selectingMonth.value = !selectingMonth.value; + selectingTime.value = !selectingTime.value; + selectingMonth.value = false; selectingYear.value = false; - } - function navigateToYearView(inPeriod: boolean) { - selectingMonth.value = selectMode.value === 'month' ? !selectingMonth.value : false; - selectingYear.value = selectMode.value === 'year' ? true : !selectingYear.value; + selectingSecondaryYear.value = false; + selectingSecondaryMonth.value = false; } @@ -324,7 +492,7 @@ export default defineComponent({ 'disable-pre-record': disablePreRecord.value, 'disable-next-record': disableNextRecord.value, 'disable-next-page': disableNextPage.value, - 'years': years.value, + 'years': currentYears.value, 'selecting-month': selectingMonth.value, 'selecting-year': selectingYear.value, 'select-mode': selectMode.value @@ -346,23 +514,27 @@ export default defineComponent({ }; }); - function renderNavBar(inPeriod: boolean, calendarNavBarProps: any) { + function renderNavBar(calendarNavBarProps: any, isSecondCalendar: boolean) { return ( navigateToPreviousPage($event, inPeriod)} - onPreRecord={($event: any) => navigateToPreviousMonth($event, inPeriod)} - onNextRecord={($event: any) => navigateToNextMonth($event, inPeriod)} - onNextPage={($event: any) => navigateToNextPage($event, inPeriod)} - onClickMonth={($event: any) => navigateToMonthView(inPeriod)} - onClickYear={($event: any) => navigateToYearView(inPeriod)} + onPrePage={($event: any) => navigateToPreviousYear($event, isSecondCalendar)} + onPreRecord={($event: any) => navigateToPreviousMonth($event, isSecondCalendar)} + onNextRecord={($event: any) => navigateToNextMonth($event, isSecondCalendar)} + onNextPage={($event: any) => navigateToNextYear($event, isSecondCalendar)} + onClickMonth={($event: any) => navigateToMonthView(isSecondCalendar)} + onClickYear={($event: any) => navigateToYearView(isSecondCalendar)} > ); } const shouldShowCalendarView = computed(() => { - return (!selectingMonth.value && !selectingYear.value && !selectTime.value) || - (enablePeriod.value && selectMode.value !== 'week' && !selectingSecondaryMonth.value - && !selectingSecondaryYear.value && !selectTime.value); + return (isSecondCalendar: boolean) => { + if (isSecondCalendar) { + return selectMode.value !== 'week' && !selectingSecondaryMonth.value && !selectingSecondaryYear.value && !selectingTime.value; + } else { + return !selectingMonth.value && !selectingYear.value && !selectingTime.value; + } + }; }); const shouldShowMonthView = computed(() => { @@ -373,26 +545,26 @@ export default defineComponent({ return selectingYear.value && !selectingMonth.value; }); - watchEffect(() => { - if (shouldShowCalendarView.value) { - activeMonth.value = { - year: selectedMonth.value.year || 1, - month: selectedMonth.value.month || 1, - displayTextOfMonth: nameOfMonths.value[selectedMonth.value.month || '1'], - displayTextOfYear: `${selectedMonth.value.year}` - }; - } - }); + // watchEffect(() => { + // if (shouldShowCalendarView.value) { + // activeMonth.value = { + // year: selectedMonth.value.year || 1, + // month: selectedMonth.value.month || 1, + // displayTextOfMonth: nameOfMonths.value[selectedMonth.value.month || '1'], + // displayTextOfYear: `${selectedMonth.value.year}` + // }; + // } + // }); const primaryCalendarProps = computed(() => { return { - dates: dates.value, - daysInWeek: daysInWeek.value, + dates: currentDates.value, + daysInWeek: weekDays.value, enableKeyboadNavigate: enableKeyboadNavigate.value, enableMarkCurrent: enableMarkCurrent.value, enablePeriod: enablePeriod.value, firstDayOfTheWeek: firstDayOfTheWeek.value, - selected: selectedDate.value, + selected: selectedDate.value || null, selectedPeriod: selectedPeriod.value, selectedWeek: selectedWeek.value, selectMode: selectMode.value, @@ -403,8 +575,8 @@ export default defineComponent({ const secondaryCalendarProps = computed(() => { return { - dates: secondaryRealDates.value, - daysInWeek: daysInWeek.value, + dates: secondaryDates.value, + daysInWeek: weekDays.value, enableKeyboadNavigate: enableKeyboadNavigate.value, enableMarkCurrent: enableMarkCurrent.value, enablePeriod: enablePeriod.value, @@ -417,28 +589,33 @@ export default defineComponent({ }; }); - function onClickDay($event: { event: Event, dayItem: CalenderDayItem }, isPeriod: boolean, type: string) { + function onClickDay($event: { event: Event, dayItem: CalenderDayItem }, isSecondCalendar: boolean) { const { event, dayItem } = $event; const currentDate = dayItem.date; if (dayItem.tag === 1) { - navigateToPreviousMonth(event, isPeriod); + navigateToPreviousMonth(event, isSecondCalendar); } else if (dayItem.tag === 3) { - navigateToNextMonth(event, isPeriod); + navigateToNextMonth(event, isSecondCalendar); } - if (!props.showTime) { - if (type === 'start') { - selectedDate.value = currentDate; - } else { - selectedSecondDate.value = currentDate; + // 区间 + if (enablePeriod.value) { + const { from, to, emit } = setNewDateRange(selectedPeriod.value, currentDate); + selectedPeriod.value = { from, to }; + if (!props.showTime && emit) { + context.emit('datePicked', { startDate: from, endDate: to }); } - context.emit('datePicked', currentDate); } else { - const { year, month, day } = currentDate; - selectedDate.value.year = year; - selectedDate.value.month = month; - selectedDate.value.day = day; + if (!props.showTime) { + context.emit('datePicked', currentDate); + } else { + const { year, month, day } = currentDate; + selectedDate.value = Object.assign(selectedDate.value || {}, { year, month, day }); + // selectedDate.value.year = year; + // selectedDate.value.month = month; + // selectedDate.value.day = day; + } } const { year, month } = currentDate; @@ -449,33 +626,90 @@ export default defineComponent({ } - function onClickWeek($event: any) { } + function onClickWeek(weekData: CalendarWeekItem) { + const showTime = valueFormat.value.toLowerCase().indexOf('hh:mm') > -1; + selectedWeek.value = { numberInTheYear: weekData.numberInTheYear, year: weekData.year }; + + if (showTime) { + weekData.days[0].date.hour = 0; + weekData.days[0].date.minute = 0; + weekData.days[0].date.second = 0; + + weekData.days[6].date.hour = 23; + weekData.days[6].date.minute = 59; + weekData.days[6].date.second = 59; + } + + setNewDateRange(selectedPeriod.value, weekData.days[0].date); + const { from, to, emit } = setNewDateRange(selectedPeriod.value, weekData.days[6].date); + selectedPeriod.value = { from, to }; + if (emit) { + context.emit('datePicked', { startDate: from, endDate: to }); + } + } function onKeyDownCalendar($event: any) { } - function onMouseEnterCalendar($event: any, isPeriod: boolean) { } + function onMouseEnterCalendar(date: DateObject, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const w of dates.value) { + for (const day of w.days) { + day.range = + (equalOrEarlier(selectedPeriod.value.from, day.date) && equalOrEarlier(day.date, date)) || + (equalOrEarlier(day.date, selectedPeriod.value.from) && equalOrEarlier(date, day.date)); + } + } - function onMouseLeaveCalendar($event: any, isPeriod: boolean) { } + currentDates.value = cloneDeep(dates.value); + } else { + for (const w of secondaryRealDates.value) { + for (const day of w.days) { + day.range = + (equalOrEarlier(selectedPeriod.value.from, day.date) && equalOrEarlier(day.date, date)) || + (equalOrEarlier(day.date, selectedPeriod.value.from) && equalOrEarlier(date, day.date)); + } + } + secondaryDates.value = cloneDeep(secondaryRealDates.value); + } + } + + function onMouseLeaveCalendar($event: any, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const w of dates.value) { + for (const day of w.days) { + day.range = false; + } + } + currentDates.value = cloneDeep(dates.value); + } else { + for (const w of secondaryRealDates.value) { + for (const day of w.days) { + day.range = false; + } + } + secondaryDates.value = cloneDeep(secondaryRealDates.value); + } + } - function renderCalender(inPeriod: boolean, calendarProps: any, type = 'start') { - return shouldShowCalendarView.value && - onClickDay(payload, inPeriod, type)} - onClickWeek={(payload: any) => onClickWeek(payload)} - onKeyDown={(payload: any) => onKeyDownCalendar(payload)} - onMouseEnter={(payload: any) => onMouseEnterCalendar(payload, inPeriod)} - onMouseLeave={(payload: any) => onMouseLeaveCalendar(payload, inPeriod)} - >; + function renderCalender(calendarProps: any, isSecondCalendar: boolean) { + return onClickDay(payload, isSecondCalendar)} + onClickWeek={(payload: any) => onClickWeek(payload)} + onKeyDown={(payload: any) => onKeyDownCalendar(payload)} + onMouseEnter={(payload: any) => onMouseEnterCalendar(payload, isSecondCalendar)} + onMouseLeave={(payload: any) => onMouseLeaveCalendar(payload, isSecondCalendar)} + >; }; const monthProps = computed(() => { return { - months: months.value, + months: currentMonths.value, enableMarkCurrent: enableMarkCurrent.value, enableKeyboadNavigate: enableKeyboadNavigate.value, enablePeriod: enablePeriod.value, selected: selectedMonth.value, - selectedPeriod: selectedPeriod.value + selectedPeriod: selectedPeriod.value, + selectMode: selectMode.value }; }); @@ -490,36 +724,100 @@ export default defineComponent({ }; }); - function onClickMonth(currentMonth: DateObject, isPeriod: boolean) { - selectingMonth.value = false; - selectingYear.value = false; - selectedMonth.value = currentMonth; - activeMonth.value = { - month: currentMonth.month || 1, - displayTextOfMonth: nameOfMonths.value[currentMonth.month || '1'], - year: currentMonth.year || 1, - displayTextOfYear: `${currentMonth.year}` - }; + const selectRangeMonth = (currentMonth: DateObject) => { + const { from: begin, to: end } = selectedPeriod.value; + const isBeginMonthInitialized: boolean = isInitializedDate(begin); + const isEndMonthInitialized: boolean = isInitializedDate(end); + if (isBeginMonthInitialized && isEndMonthInitialized) { + // both already selected - set begin date and reset end date + selectedPeriod.value.from = { year: currentMonth.year, month: currentMonth.month }; + selectedPeriod.value.to = emptyDate(); + } else if (!isBeginMonthInitialized) { + selectedPeriod.value.from = { year: currentMonth.year, month: currentMonth.month }; + } else { + const firstDateEarlier: boolean = isDateEarlier({ year: currentMonth.year, month: currentMonth.month }, begin); + if (firstDateEarlier) { + const _date = selectedPeriod.value.from; + selectedPeriod.value.to = { ..._date }; + selectedPeriod.value.from = { year: currentMonth.year, month: currentMonth.month }; + } else { + selectedPeriod.value.to = { year: currentMonth.year, month: currentMonth.month }; + } - if (selectMode.value === 'month') { - const selectedMonth = cloneDeep(currentMonth); - selectedMonth.day = 1; - context.emit('datePicked', selectedMonth); + context.emit('datePicked', { startDate: selectedPeriod.value.from, endDate: selectedPeriod.value.to }); + } + }; + + function onClickMonth(currentMonth: DateObject, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + activeMonth.value = { + month: currentMonth.month || 1, + displayTextOfMonth: nameOfMonths.value[currentMonth.month || '1'], + year: currentMonth.year || 1, + displayTextOfYear: `${currentMonth.year}` + }; + if (selectMode.value === 'month') { + if (!enablePeriod.value) { + context.emit('datePicked', currentMonth); + } else { + selectRangeMonth(currentMonth); + } + } else { + selectingMonth.value = false; + selectingYear.value = false; + selectedMonth.value = currentMonth; + } + } else { + secondaryActiveMonth.value = { + month: currentMonth.month || 1, + displayTextOfMonth: nameOfMonths.value[currentMonth.month || '1'], + year: currentMonth.year || 1, + displayTextOfYear: `${currentMonth.year}` + }; + + if (selectMode.value === 'month') { + selectRangeMonth(currentMonth); + } else { + selectingSecondaryMonth.value = false; + selectingSecondaryYear.value = false; + } } } function onKeyDownMonthView($event: any) { } - function onMouseEnterMonthView($event: any, isPeriod: boolean) { } + function onMouseEnterMonthView(cell: MonthViewItem, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const item of months.value) { + for (const month of item) { + month.range = + (equalOrEarlier(selectedPeriod.value.from, month.date) && equalOrEarlier(month.date, cell.date)) || + (equalOrEarlier(month.date, selectedPeriod.value.from) && equalOrEarlier(cell.date, month.date)); + } + } + currentMonths.value = cloneDeep(months.value); + } else { + for (const item of secondMonths.value) { + for (const month of item) { + month.range = + (equalOrEarlier(selectedPeriod.value.from, month.date) && equalOrEarlier(month.date, cell.date)) || + (equalOrEarlier(month.date, selectedPeriod.value.from) && equalOrEarlier(cell.date, month.date)); + } + } + + secondaryMonths.value = cloneDeep(secondMonths.value); + } + } - function onMouseLeaveMonthView($event: any, isPeriod: boolean) { } + function onMouseLeaveMonthView($event: MonthViewItem, isSecondCalendar: boolean) { + } - function renderMonth(inPeriod: boolean, monthProps: any) { + function renderMonth(monthProps: any, isSecondCalendar: boolean) { return onClickMonth(payload, inPeriod)} + onClick={(payload: any) => onClickMonth(payload, isSecondCalendar)} onKeyDownMonthView={(payload: any) => onKeyDownMonthView(payload)} - onMouseEnterMonthView={(payload: any) => onMouseEnterMonthView(payload, inPeriod)} - onMouseLeaveMonthView={(payload: any) => onMouseLeaveMonthView(payload, inPeriod)} + onMouseEnter={(payload: any) => onMouseEnterMonthView(payload, isSecondCalendar)} + onMouseLeave={(payload: any) => onMouseLeaveMonthView(payload, isSecondCalendar)} >; } @@ -530,7 +828,8 @@ export default defineComponent({ enableMarkCurrent: enableMarkCurrent.value, enablePeriod: enablePeriod.value, selected: selectedDate.value, - selectedPeriod: selectedPeriod.value + selectedPeriod: selectedPeriod.value, + selectMode: selectMode.value }; }); @@ -545,42 +844,120 @@ export default defineComponent({ }; }); - function onClickYear(currentYear: DateObject, isPeriod: boolean) { - selectingMonth.value = true; - selectingYear.value = false; - activeMonth.value = { - month: activeMonth.value.month, - displayTextOfMonth: activeMonth.value.displayTextOfMonth, - year: currentYear.year || 1, - displayTextOfYear: `${currentYear.year}` - }; + function onClickYear(currentYear: DateObject, isSecondCalendar: boolean) { + if (!isSecondCalendar) { + activeMonth.value = { + month: activeMonth.value.month, + displayTextOfMonth: activeMonth.value.displayTextOfMonth, + year: currentYear.year || 1, + displayTextOfYear: `${currentYear.year}` + }; - if (selectMode.value === 'year') { - const selectedYear = cloneDeep(currentYear); - selectedYear.month = 1; - selectedYear.day = 1; - context.emit('datePicked', selectedYear); + if (selectMode.value === 'year') { + if (!enablePeriod.value) { + context.emit('datePicked', currentYear); + } else { + selectRangeMonth(currentYear); + } + } else { + selectingMonth.value = true; + selectingYear.value = false; + } + + } else { + secondaryActiveMonth.value = { + month: secondaryActiveMonth.value.month || 1, + displayTextOfMonth: secondaryActiveMonth.value.displayTextOfMonth, + year: currentYear.year || 1, + displayTextOfYear: `${currentYear.year}` + }; + + if (selectMode.value === 'year') { + selectRangeMonth(currentYear); + } else { + selectingSecondaryMonth.value = true; + selectingSecondaryYear.value = false; + } } + } function onKeyDownYearView($event: any) { } - function onMouseEnterYearView($event: any, isPeriod: boolean) { } + function onMouseEnterYearView(cell: any, isSecondCalendar: boolean) { + if (isSecondCalendar) { + for (const item of years.value) { + for (const year of item) { + year.range = + (equalOrEarlier(selectedPeriod.value.from, year.date) && + equalOrEarlier(year.date, cell.date)) || + (equalOrEarlier(year.date, selectedPeriod.value.from) && + equalOrEarlier(cell.date, year.date)); + } + } + currentYears.value = cloneDeep(years.value); + } else { + for (const item of secondYears.value) { + for (const year of item) { + year.range = + (equalOrEarlier(selectedPeriod.value.from, year.date) && + equalOrEarlier(year.date, cell.date)) || + (equalOrEarlier(year.date, selectedPeriod.value.from) && + equalOrEarlier(cell.date, year.date)); + } + } + secondaryYears.value = cloneDeep(secondYears.value); + } + } - function onMouseLeaveYearView($event: any, isPeriod: boolean) { } + function onMouseLeaveYearView($event: any, isSecondCalendar: boolean) { } + + const updateRangeDate = () => { + + const { from, to } = selectedPeriod.value; + + const isBeginEmptyDate = !isInitializedDate(from); + const isEndEmptyDate = !isInitializedDate(to); + + let begin = {...from}; + let end = {...to}; + + + if (isBeginEmptyDate || isEndEmptyDate) { + const now = new Date(); + if (isBeginEmptyDate) { + begin = { year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() }; + } + + end = { ...begin }; + } - function onConfirm() { if (props.showTime) { - context.emit('confirm', selectedDate.value); + const { hour, minute, second } = getTimeValue(timeValue.value, true); + begin = { ...begin, ...{ hour, minute, second } }; + + const { hour: endHour, minute: endMinute, second: endSecond } = getEndTimeValue(timeRangeValue.value); + end = { ...end, ...{ hour: endHour, minute: endMinute, second: endSecond } }; + } + + return { begin, end }; + }; + + function onConfirm() { + // 区间 + if (enablePeriod.value && selectedPeriod.value) { + const { begin, end } = updateRangeDate(); + selectedPeriod.value= { from: begin, to: end }; + context.emit('datePicked', { startDate: begin, endDate: end }); } else { - const resultDate = { startDate: {}, endDate: {} }; - if (selectedDate.value) { - resultDate.startDate = selectedDate.value; - } - if (selectedSecondDate.value) { - resultDate.endDate = selectedSecondDate.value; + if (props.showTime) { + const { hour, minute, second } = getTimeValue(timeValue.value, true); + if (!isInitializedDate(selectedDate.value)) { + selectedDate.value = {...today}; + } + selectedDate.value = { ...selectedDate.value, ...{ hour, minute, second } }; } - context.emit('confirm', resultDate); + context.emit('confirm', selectedDate.value); } } @@ -603,8 +980,14 @@ export default defineComponent({ displayTextOfMonth: nameOfMonths.value[today.month || '1'], displayTextOfYear: `${today.year}` }; - selectedDate.value = cloneDeep(today); + selectedDate.value = { year: today.year, month: today.month }; selectedMonth.value = { year: today.year, month: today.month }; + + onClickMonth(selectedDate.value, false); + + if (selectMode.value === 'month') { + context.emit('datePicked', selectedMonth.value); + } } function navigateToCurrentYear($event: MouseEvent) { @@ -614,23 +997,40 @@ export default defineComponent({ displayTextOfMonth: nameOfMonths.value[today.month || '1'], displayTextOfYear: `${today.year}` }; - selectedDate.value = cloneDeep(today); + selectedDate.value = { year: today.year }; + + onClickYear(selectedDate.value, false); + + if (selectMode.value === 'year') { + context.emit('datePicked', selectedDate.value); + } } const shouldShowSecondCalendar = computed(() => { return enablePeriod.value && selectMode.value !== 'week'; }); - const showConfirmButton = computed(() => (shouldShowCalendarView.value && shouldShowSecondCalendar.value) || props.showTime); - function renderYear(inPeriod: boolean, yearProps: any) { + const containerClass = computed(() => { + const classObject = { + 'flex-row': true, + 'f-datepicker-container': true, + 'f-daterange-select-timerange': shouldShowSecondCalendar.value && selectingTime.value + } as Record; + const className = `container-position-${position.value}`; + classObject[className] = true; + return classObject; + }); + const showConfirmButton = computed(() => props.showTime && selectMode.value !== 'week'); + + function renderYear(yearProps: any, isSecondCalendar: boolean) { return onClickYear(payload, inPeriod)} + onClick={(payload: any) => onClickYear(payload, isSecondCalendar)} onKeyDownYearView={(payload: any) => onKeyDownYearView(payload)} - onClickPreRecord={(payload: any) => navigateToPreviousMonth(payload, inPeriod)} - onClickNextRecord={(payload: any) => navigateToNextMonth(payload, inPeriod)} - onMouseEnterYearView={(payload: any) => onMouseEnterYearView(payload, inPeriod)} - onMouseLeaveYearView={(payload: any) => onMouseLeaveYearView(payload, inPeriod)} + onClickPreRecord={(payload: any) => navigateToPreviousMonth(payload, isSecondCalendar)} + onClickNextRecord={(payload: any) => navigateToNextMonth(payload, isSecondCalendar)} + onMouseEnter={(payload: any) => onMouseEnterYearView(payload, isSecondCalendar)} + onMouseLeave={(payload: any) => onMouseLeaveYearView(payload, isSecondCalendar)} >; } @@ -645,15 +1045,60 @@ export default defineComponent({ selectedDate.value.second = convertedDate?.getSeconds() || 0; } } else { - const { hour, minute, second } = selectedDate.value; + const { hour, minute, second } = selectedDate.value || today; timeValue.value = `${hour || '0'}:${minute || 0}:${second || 0}`; timeValue.value = formatTime(timeValue.value); } } onMounted(() => { - if (props.showTime) { - formatDate(); + if (enablePeriod.value) { + const { dateRange, beginDateActiveMonth, endDateActiveMonth, beginTime, endTime, selectedWeekInfo } = getActiveMonth(dateValue.value); + selectedPeriod.value = dateRange; + if (beginDateActiveMonth) { + activeMonth.value = beginDateActiveMonth; + } + if (endDateActiveMonth) { + secondaryActiveMonth.value = endDateActiveMonth; + } + + if (beginTime) { + timeValue.value = beginTime; + } + + if (endTime) { + timeRangeValue.value = endTime; + } + + if (selectedWeekInfo) { + selectedWeek.value = selectedWeekInfo; + } + } else { + if (props.showTime) { + const dateAndTimeFormatList = valueFormat.value.split(' '); + const timeFormat = dateAndTimeFormatList[1] || 'HH:mm:ss'; + timeValue.value = formatTo(dateValue.value, timeFormat); + formatDate(); + } + } + + if (selectMode.value === 'month') { + navigateToMonthView(false); + if (enablePeriod.value) { + navigateToMonthView(true); + } + } else { + if (selectMode.value === 'year') { + navigateToYearView(false); + if (enablePeriod.value) { + const secondYear = secondaryActiveMonth.value.year; + if (currentYears.value.length && secondYear < currentYears.value[3][2].year && secondYear > currentYears.value[0][0].year) { + secondaryActiveMonth.value.year = secondYear + 10; + } + + navigateToYearView(true); + } + } } if (selectMode.value === 'month') { @@ -665,23 +1110,58 @@ export default defineComponent({ } }); - const onValueChangeHandler = (textValue: TimeValueText) => { - timeValue.value = textValue.text; - const { hour, minute, second } = getTimeObject(timeValue.value); - if (selectedDate.value) { - selectedDate.value.hour = hour || 0; - selectedDate.value.minute = minute || 0; - selectedDate.value.second = second || 0; + const onValueChangeHandler = (textValue: TimeValueText, isSecondCalendar = false) => { + const dateAndTimeFormatList = valueFormat.value.split(' '); + const timeFormat = dateAndTimeFormatList[1] || 'HH:mm:ss'; + const convertedDate = convertToDate(textValue.text, timeFormat); + const hour = convertedDate?.getHours() || 0; + const minutes = convertedDate?.getMinutes() || 0; + const seconds = convertedDate?.getSeconds() || 0; + + if (!isSecondCalendar) { + timeValue.value = textValue.text; + if (enablePeriod.value && selectedPeriod.value) { + selectedPeriod.value.from.hour = hour; + selectedPeriod.value.from.minute = minutes; + selectedPeriod.value.from.second = seconds; + } else { + if (selectedDate.value) { + selectedDate.value.hour = hour; + selectedDate.value.minute = minutes; + selectedDate.value.second = seconds; + } + } + + } else { + timeRangeValue.value = textValue.text; + if (enablePeriod.value && selectedPeriod.value) { + selectedPeriod.value.to.hour = hour; + selectedPeriod.value.to.minute = minutes; + selectedPeriod.value.to.second = seconds; + } } }; + function renderTimePicker(isSecondCalendar: boolean) { + return onValueChangeHandler(timeValue, isSecondCalendar)} + >; + } + const btnTodayStyle = computed(() => { - return (date: any) => { - const todayIsDisable = date.day == null || refDisableDate.isDisabledDate(props.showTime ? date : { year: date.year, month: date.month, day: date.day }); - return { + return (date: any, isButton = false) => { + const todayIsDisable = date?.day == null || refDisableDate.isDisabledDate(props.showTime ? date : { year: date.year, month: date.month, day: date.day }); + const styles = { "pointer-events": todayIsDisable ? "none" : "auto", - opacity: todayIsDisable ? 0.3 : 1 + opacity: todayIsDisable ? 0.3 : 1, }; + + return isButton? {}: { + ...styles, + "font-size": "15px" + } }; }); @@ -694,7 +1174,8 @@ export default defineComponent({ return { "pointer-events": disable ? "none" : "auto", - opacity: disable ? 0.3 : 1 + opacity: disable ? 0.3 : 1, + "font-size": "15px" }; }); @@ -703,7 +1184,87 @@ export default defineComponent({ today.year >= minYear.value && today.year <= maxYear.value : false; return { "pointer-events": canSelected ? "auto" : "none", - opacity: canSelected ? 1 : 0.3 + opacity: canSelected ? 1 : 0.3, + "font-size": "15px" + }; + }); + + const canConfirm = computed(() => { + if (enablePeriod.value) { + if (props.showTime) { + const { begin, end } = updateRangeDate(); + const canClick = equalOrEarlier(begin, end); + + return { + "pointer-events": canClick ? "auto" : "none", + opacity: canClick ? 1 : 0.3, + }; + } + // return true; + } else { + const { hour, minute, second } = getTimeValue(timeValue.value, true); + const currentDateValue = { ...selectedDate.value, ...{ hour, minute, second } }; + return btnTodayStyle.value(currentDateValue, true); + + } + }); + + function selectCurrentWeek() { + const { from: begin, to: end } = getNowWeekTime(new Date(), firstDayOfTheWeek.value); + const year = new Date().getFullYear(); + const weekNumber = getWeekNumber(begin); + const showTime = valueFormat.value.toLowerCase().indexOf('hh:mm') > -1; + selectedWeek.value = { numberInTheYear: weekNumber, year }; + + if (showTime) { + begin['hour'] = 0; + begin['minute'] = 0; + begin['second'] = 0; + + end['hour'] = 23; + end['minute'] = 59; + end['second'] = 59; + } + + setNewDateRange(selectedPeriod.value, begin); + const { from, to, emit } = setNewDateRange(selectedPeriod.value, end); + selectedPeriod.value = { from, to }; + if (emit) { + context.emit('datePicked', { startDate: from, endDate: to }); + } + } + + const currentLabels = props.locales.buttons.current; + const toggleDateAndTimeLabel = computed(() => { + return selectingTime.value ? props.locales.buttons.selectDate : props.locales.buttons.selectTime; + }); + + function onQuickSelected(item: any, $event) { + const { value } = item; + if (enablePeriod.value) { + selectedPeriod.value = { + from: convertDateToDateObject(value[0]), + to: convertDateToDateObject(value[1]) + }; + } else { + selectedDate.value = { ...convertDateToDateObject(value) }; + } + + onConfirm(); + } + + const renderQuickSelector = () => { + const items = enablePeriod.value? quickSelectorList.range : quickSelectorList.single; + return
    + {items.map((item, index) => { + return
  • onQuickSelected(item, $event)}>{item.label}
  • ; + })} +
; + }; + + const quickSelectContainerStyles = computed(() => { + return { + "max-height": showConfirmButton.value || !enablePeriod.value? '339px' : '294px' }; }); @@ -713,20 +1274,19 @@ export default defineComponent({ onKeyup={(payload: KeyboardEvent) => onCloseSelector(payload)} onClick={(payload: MouseEvent) => onClickContainer(payload)} > + {props.enableQuickSelect && selectMode.value === 'day' &&
+ {renderQuickSelector()} +
} + +
- {renderNavBar(false, primaryCalendarNavBarProps.value)} - {renderCalender(false, primaryCalendarProps.value)} - {selectingMonth.value && renderMonth(false, monthProps.value)} - {selectingYear.value && renderYear(false, yearProps.value)} - + {renderNavBar(primaryCalendarNavBarProps.value, false)} + {shouldShowCalendarView.value(false) && renderCalender(primaryCalendarProps.value, false)} + {selectingMonth.value && renderMonth(monthProps.value, false)} + {selectingYear.value && renderYear(yearProps.value, false)} + {selectingTime.value && renderTimePicker(false)}
- {props.showTime &&
{timeValue.value}
-
} { - shouldShowSecondCalendar.value && -
- {renderNavBar(true, secondaryCalendarNavBarProps.value)} - {renderCalender(true, secondaryCalendarProps.value, 'end')} - {selectingSecondaryMonth.value && renderMonth(true, secondMonthProps.value)} - {selectingSecondaryYear.value && renderYear(true, secondYearProps.value)} + shouldShowSecondCalendar.value && selectMode.value !== 'week' && +
+ {renderNavBar(secondaryCalendarNavBarProps.value, true)} + {shouldShowCalendarView.value(true) && renderCalender(secondaryCalendarProps.value, true)} + {selectingSecondaryMonth.value && renderMonth(secondMonthProps.value, true)} + {selectingSecondaryYear.value && renderYear(secondYearProps.value, true)} + {selectingTime.value && renderTimePicker(true)}
} { showConfirmButton.value &&
- - 确定 - + {!enablePeriod.value && shouldShowCalendarView.value(false) && } + {!enablePeriod.value && shouldShowMonthView.value && } + {!enablePeriod.value && shouldShowYearView.value && } + {enablePeriod.value && selectMode.value === 'day' &&
+
+ } + {props.locales.buttons.commit}
+ } + {!enablePeriod.value && !showConfirmButton.value &&
+ {shouldShowCalendarView.value(false) && } + {shouldShowMonthView.value && } + {shouldShowYearView.value && } +
} + {enablePeriod.value && selectMode.value === 'week' &&
+ {shouldShowMonthView.value && } + {shouldShowYearView.value && } + {!shouldShowMonthView.value && !shouldShowYearView.value && } +
} +
); }; diff --git a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts index 7e9205a40d21e96c948cec776b0e24dfbd83c24f..1813a0bcb889e37ef9fc8f9495cf6130d32e28f5 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/date-picker-container/date-picker-container.props.ts @@ -17,17 +17,18 @@ import { ExtractPropTypes, PropType } from 'vue'; import { createPropsResolver } from '@farris/ui-vue/components/dynamic-resolver'; import { CalendarWeekItem } from '../../types/calendar'; -import { DateObject, Period, SelectMode, weekDays } from '../../types/common'; +import { DateObject, FirstDayOfTheWeek, Period, SelectMode, WeekDays } from '../../types/common'; import { defaultNameOfMonths, MonthViewItem } from '../../types/month'; import { YearViewItem } from '../../types/year'; import { schemaMapper } from '../../schema/schema-mapper'; import { schemaResolver } from '../../schema/schema-resolver'; import dateViewSchema from '../../schema/date-view.schema.json'; import propertyConfig from '../../property-config/date-view.property-config.json'; - +import { DatePickerLocale } from '../../composition/use-locales'; export type DisplayMode = 'Embedded' | 'Popup'; export const datePickerContainerProps = { + locales: { type: Object as PropType, default: {}}, /** */ top: { type: Number, default: 0 }, /** */ @@ -39,7 +40,6 @@ export const datePickerContainerProps = { dateFormat: { type: String, default: 'yyyy-MM-dd' }, valueFormat: { type: String, default: 'yyyy-MM-dd' }, dates: { type: Array, default: [] }, - daysInWeek: { type: Array, default: weekDays }, /** 禁用日期 */ disableDates: { Type: Array, default: [] }, /** 禁用范围 */ @@ -57,7 +57,7 @@ export const datePickerContainerProps = { /** 是否启用标记当前 */ enableMarkCurrent: { type: Boolean, default: true }, /** 每周第一天 */ - firstDayOfTheWeek: { type: String, default: 'Sun.' }, + firstDayOfTheWeek: { type: String as PropType, default: FirstDayOfTheWeek.Sunday }, /** 高亮日期 */ highlightDates: { Type: Array, default: [] }, /** 是否高亮周六 */ @@ -84,8 +84,7 @@ export const datePickerContainerProps = { selectedSecondDate: { type: Object, default: null }, /** 选择的月份 */ selectedMonth: { type: Object, default: null }, - /** 选择期限 */ - selectedPeriod: { type: Object, default: null }, + /** 选择周 */ selectedWeek: { type: Object, default: null }, /** 选择方式 */ @@ -93,13 +92,15 @@ export const datePickerContainerProps = { /** 显示第几周 */ showWeekNumber: { type: Boolean, default: false }, /** 每周标题 */ - weekTitle: { type: String, default: 'Week' }, + weekTitle: { type: String, default: '周' }, /** 年份 */ years: { Type: Array>, default: [[]] }, /** 日期时间值 */ value: { type: String, default: null }, /** 是否展示时分秒 */ - showTime: {type: Boolean, default: false} + showTime: {type: Boolean, default: false}, + /** 启用快捷选择 */ + enableQuickSelect: {type: Boolean, default: false} } as Record; export type DatePickerContainerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx index 60aac6437000302a5e321d1d94d3681f584b7316..9bc57e4a2d09fcc9ba8ccf060b8be5442d7e740c 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.component.tsx @@ -1,17 +1,17 @@ -import { computed, defineComponent, onMounted, ref, Ref } from "vue"; +import { computed, defineComponent, onMounted, ref, Ref, watch } from "vue"; import { FInputGroup } from "@farris/ui-vue/components/input-group"; import FPopover from '@farris/ui-vue/components/popover'; import { useDateFormat } from "@farris/ui-vue/components/common"; import { DateRangeProps, dateRangeProps } from "./date-range.props"; import FDatePickerContainer from '../date-picker-container/date-picker-container.component'; import { DateObject } from "../../types/common"; +import { useDate } from "../../composition/use-date"; export default defineComponent({ name: 'FDateRange', props: dateRangeProps, - emits: ['update:modelValue', 'confirm'] as (string[] & ThisType) | undefined, + emits: ['update:modelValue', 'confirm', 'clear'] as (string[] & ThisType) | undefined, setup(props: DateRangeProps, context) { - const modelValue = ref(props.modelValue); /** 最晚年限 */ const maxYear = ref(props.maxYear); /** 最早年限 */ @@ -30,32 +30,76 @@ export default defineComponent({ const showWeekNumber = ref(props.showWeekNumber); /** 显示方式 */ const selectMode = ref(props.selectMode); - /** 是否显示时间 */ - // const displayTime = ref(props.displayTime); /** 禁用周末 */ const disableWeekends = ref(props.disableWeekends); /** 禁用特定日 */ const disableWeekdays = ref(props.disableWeekdays); /** 自...禁用 */ - const disableSince = ref([props.disableSince]); + const disableSince = ref(props.disableSince); /** 禁用至该范围 */ - const disableUntil = ref([props.disableUntil]); + const disableUntil = ref(props.disableUntil); /** 展示日期范围 */ const shouldPopupContent = ref(false); // const popoverRef = ref(); const dateRef = ref(); - const { formatTo } = useDateFormat(); + const showClearButton = ref(false); - const startDisplayDate = computed(() => formatTo(modelValue.value.split('~')?.[0], props.displayFormat)); - const endDisplayDate = computed(() => formatTo(modelValue.value.split('~')?.[1], props.displayFormat)); + const { formatTo, parseToDate } = useDateFormat(); + const newBeginValue = ref(props.beginValue); + const newEndValue = ref(props.endValue); + const setBeginAndEndValueFromModelValue = (dateValue) => { + const splitValue = dateValue.split('~'); + newBeginValue.value = splitValue?.[0] || ''; + newEndValue.value = splitValue?.[1] || ''; + }; + /** + * 可能只有beginValue或者endValue的值,modelValue没有设置值 + * @returns + */ + function getInitModelValue() { + if (props.modelValue) { + setBeginAndEndValueFromModelValue(props.modelValue); + return props.modelValue; + } + if (props.beginValue || props.endValue) { + return props.beginValue + '~' + props.endValue; + } + return ''; + } + const modelValue = ref(getInitModelValue()); + function getDisplayText(dateText) { + if (dateText) { + const formatedValue = parseToDate(dateText, props.valueFormat); + return formatedValue ? formatTo(formatedValue, props.displayFormat) : ''; + } + return ''; + } + const startDisplayDate = computed(() => getDisplayText(newBeginValue.value)); + const endDisplayDate = computed(() => getDisplayText(newEndValue.value)); + const rangeTitle = computed(() => startDisplayDate.value + '~' + endDisplayDate.value); + + watch(() => props.beginValue, (newValue) => { + newBeginValue.value = newValue; + } + ); + watch(() => props.endValue, (newValue) => { + newEndValue.value = newValue; + }); + watch(() => props.modelValue, (newVlaue) => { + modelValue.value = props.modelValue; + }); + watch(modelValue, (newValue) => { + setBeginAndEndValueFromModelValue(newValue); + }); const buttonEditClass = computed(() => { const classObject = { 'f-button-edit': true, 'f-cmp-inputgroup': true, - 'f-button-edit-nowrap': true + 'f-button-edit-nowrap': true, + 'f-cmp-datepicker': true } as Record; return classObject; }); @@ -64,19 +108,32 @@ export default defineComponent({ const classObject = { 'f-cmp-inputgroup': true, 'input-group': true, - // 'f-state-disable': disabled.value, + 'f-state-disable': props.disabled, 'f-state-editable': true, - // 'f-state-readonly': readonly.value, + 'align-items-center': true, + 'f-state-readonly': props.readonly // 'f-state-focus': hasFocused.value }; return classObject; }); + const clearButtonStyles = computed(() => { + const styles: Record = { width: '22px', height: '22px' }; + if (!showClearButton.value) { + styles.visibility = 'hidden'; + } + return styles; + }); + const showDateRangePanel = () => { popoverRef.value?.show(dateRef.value); }; const onClickButton = () => { + if (props.readonly || props.disabled) { + // input-group会始终触发click事件 + return; + } shouldPopupContent.value = !shouldPopupContent.value; setTimeout(() => { if (shouldPopupContent.value) { @@ -95,8 +152,28 @@ export default defineComponent({ const onConfirm = (dateValue: any) => { const { startDate, endDate } = dateValue; - const startDateValueString = `${startDate.year}-${startDate.month}-${startDate.day}`; - const endDateValueString = `${endDate.year}-${endDate.month}-${endDate.day}`; + let startDateValueString = `${startDate.year}-${startDate.month}-${startDate.day}`; + let endDateValueString = `${endDate.year}-${endDate.month}-${endDate.day}`; + + if (props.showTime) { + const { hour, minute, second } = startDate; + startDateValueString = `${startDateValueString} ${hour || 0}:${minute || 0}:${second || 0}`; + + const { hour: endHour, minute: endMinute, second: endSecond } = endDate; + endDateValueString = `${endDateValueString} ${endHour || 0}:${endMinute || 0}:${endSecond || 0}`; + } + + if (selectMode.value === 'month') { + startDateValueString = `${startDate.year}-${startDate.month}`; + endDateValueString = `${endDate.year}-${endDate.month}`; + } + + if (selectMode.value === 'year') { + startDateValueString = `${startDate.year}`; + endDateValueString = `${endDate.year}`; + } + + const formatStartDate = formatTo(startDateValueString, props.valueFormat); const formatEndDate = formatTo(endDateValueString, props.valueFormat); const resultDate = formatStartDate + '~' + formatEndDate; @@ -107,29 +184,61 @@ export default defineComponent({ }; const onDatePicked = (dateValue: any) => { + onConfirm(dateValue); + }; + const onClear = () => { + modelValue.value = ''; + closeCalendarPanel(); + context.emit('update:modeValue', ''); + context.emit('clear'); }; + onMounted(() => { + nameOfMonths.value = props.locales.monthLabels; + }); + + function onMouseEnter() { + showClearButton.value = !!startDisplayDate.value || !!endDisplayDate.value; + } + + function onMouseLeave() { + showClearButton.value = false; + } + return () => { return <>
-
- - - +
+
+ + + +
+ {props.enableClear && + } '} onClick={onClickButton} - // onMouseenter={onMouseEnterButton} onMouseleave={onMouseLeaveButton} >
@@ -137,15 +246,12 @@ export default defineComponent({ {shouldPopupContent.value && - {context.slots.default?.()} onDatePicked(dateValue)} onConfirm={(dateValue: string) => onConfirm(dateValue)} diff --git a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts index 00cd73951452a56f7be01ce276aeb99e35d8ac76..7cee8c7625721ace7f5231f21b5e88335408adc8 100644 --- a/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/date-range/date-range.props.ts @@ -1,22 +1,10 @@ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; import { datePickerProps } from '../../..'; +import { DatePickerLocale } from '../../composition/use-locales'; export const dateRangeProps = { ...datePickerProps, - /** 显示第二个日期输入框 */ - showEndDate: { type: Boolean, default: true }, - /** 显示中间图标 */ - showMiddleIcon: { type: Boolean, default: true }, - /** 开始日期 */ - startDateValue: { type: String }, - /** 结束日期 */ - endDateValue: { type: String }, - /** 绑定值 */ - modelValue: { type: String, default: '' }, - /** 展示日期范围面板 */ - showPeriod: { type: Boolean, default: true }, - /** 启用删除 */ - enableClear: {type: Boolean, default: true}, + locales: { type: Object as PropType, default: {}}, } as Record; export type DateRangeProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx b/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx index 19a27991eab06ecd7ef6a3a3784f432c6e7ab6eb..bd5c469ada2dee745e51f9ca04e6c1f22af9ff3d 100644 --- a/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/month/month.component.tsx @@ -41,6 +41,10 @@ export default defineComponent({ selectedMonth.value = props.selected; }); + watch(() => props.selectedPeriod, (newValue) => { + selectedMonthPeriod.value = newValue; + }); + function onClick($event: Event, target: MonthViewItem) { $event.stopPropagation(); @@ -99,8 +103,8 @@ export default defineComponent({ function isMonthRangeBeginOrEndSame(month: DateObject) { return !!selectedMonthPeriod.value && ( - equal({ year: selectedMonthPeriod.value.from.month }, month) || - equal({ year: selectedMonthPeriod.value.to.month }, month) + equal({ year: selectedMonthPeriod.value.from.year, month: selectedMonthPeriod.value.from.month }, month) || + equal({ year: selectedMonthPeriod.value.to.year, month: selectedMonthPeriod.value.to.month }, month) ); } @@ -112,7 +116,7 @@ export default defineComponent({ const classObject = { 'f-datepicker-month-cell': true, 'f-datepicker-current': monthViewItem.isCurrent && enableMarkCurrent.value, - 'f-datepicker-selected': (!enablePeriod.value && isMonthSame(monthViewItem.date)) || + 'f-datepicker-selected': ((!enablePeriod.value || props.selectMode === 'week') && isMonthSame(monthViewItem.date)) || (enablePeriod.value && isMonthRangeBeginOrEndSame(monthViewItem.date)), 'f-datepicker-disabled': monthViewItem.disable, 'f-datepicker-range': isMonthInPeriod(monthViewItem.date) || monthViewItem.range @@ -133,11 +137,10 @@ export default defineComponent({ row.map((monthViewItem: MonthViewItem, index: number) => { return (
diff --git a/packages/ui-vue/components/date-picker/src/components/month/month.props.ts b/packages/ui-vue/components/date-picker/src/components/month/month.props.ts index f9a937ce8b7b9aa03a87addda91219eb3b2846c4..881c31e5c135c11f12a7d943ebad47ed8a0fdffb 100644 --- a/packages/ui-vue/components/date-picker/src/components/month/month.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/month/month.props.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; import { MonthViewItem } from '../../types/month'; +import { SelectMode } from '../../types/common'; export const monthProps = { months: { Type: Array>, default: [[]] }, @@ -22,7 +23,8 @@ export const monthProps = { enableKeyboadNavigate: { Type: Boolean, default: true }, enablePeriod: { Type: Boolean, default: false }, selected: { Type: Object, default: null }, - selectedPeriod: { Type: Object, default: null } + selectedPeriod: { Type: Object, default: null }, + selectMode: { Type: String as PropType, default: 'day' }, }; export type MonthProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx b/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx index 8a804dcee09df08db205507a870fc559c54de231..902d828e16f00c5a4f638c6bf5434b9071e89c1a 100644 --- a/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx +++ b/packages/ui-vue/components/date-picker/src/components/year/year.component.tsx @@ -70,7 +70,7 @@ export default defineComponent({ const classObject = { 'f-datepicker-year-cell': true, 'f-datepicker-current': target.isCurrent && enableMarkCurrent.value, - 'f-datepicker-selected': (!selectedYearPeriod.value && isSelectedYear(target.date)) || + 'f-datepicker-selected': ((!enablePeriod.value || props.selectMode === 'week') && isSelectedYear(target.date)) || (enablePeriod.value && hasSameYearInPeriod(target.date)), 'f-datepicker-disabled': target.disable, 'f-datepicker-range': isYearInPeriod(target.date) || target.range @@ -135,13 +135,12 @@ export default defineComponent({ {row.map((yearViewItem: YearViewItem, index: number) => { return ( ); diff --git a/packages/ui-vue/components/date-picker/src/components/year/year.props.ts b/packages/ui-vue/components/date-picker/src/components/year/year.props.ts index 5c34704e5d3149748e91792a1297468ac5f4b12d..d848706d7d4d31a3d8298bab1ad6fad49b9a7dce 100644 --- a/packages/ui-vue/components/date-picker/src/components/year/year.props.ts +++ b/packages/ui-vue/components/date-picker/src/components/year/year.props.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ExtractPropTypes } from 'vue'; +import { ExtractPropTypes, PropType } from 'vue'; import { YearViewItem } from '../../types/year'; +import { SelectMode } from '../../types/common'; export const yearProps = { years: { Type: Array>, default: [[]] }, @@ -22,7 +23,8 @@ export const yearProps = { enableMarkCurrent: { Type: Boolean, default: true }, enablePeriod: { Type: Boolean, default: false }, selected: { Type: Object, default: null }, - selectedPeriod: { Type: Object, default: null } + selectedPeriod: { Type: Object, default: null }, + selectMode: { Type: String as PropType, default: 'day' }, }; export type YearPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/composition/types.ts b/packages/ui-vue/components/date-picker/src/composition/types.ts index 60043b9b1a69c4eb3c6d41fceef4f482b551b9cb..a7a413540fc7cb7fa940adf4b40efe247c31fe98 100644 --- a/packages/ui-vue/components/date-picker/src/composition/types.ts +++ b/packages/ui-vue/components/date-picker/src/composition/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { CalendarWeekItem } from "../types/calendar"; -import { DateObject, Period, MarkedDates, MarkStatus } from "../types/common"; +import { DateObject, Period, MarkedDates, MarkStatus, DataRangePicked, FirstDayOfTheWeek } from "../types/common"; import { DateModel } from "../types/date-model"; import { ActiveMonth, MonthViewItem, NameOfMonths } from "../types/month"; import { YearViewItem } from "../types/year"; @@ -52,12 +52,16 @@ export interface UseDate { getNearDate: (now: DateObject, min: DateObject, max: DateObject) => DateObject; - getToday(): DateObject; + getToday(): Required; getDateObject(dateString: string, dateFormat: string): DateObject; getMinDate(value: Date | string | null | undefined): DateObject; getMaxDate(value: Date | string | null | undefined): DateObject; + + getTimeValue(timeValue: string, isDateRange?: boolean) : { hour: number, minute: number, second: number }; + getEndTimeValue(timeValue: string) : { hour: number, minute: number, second: number }; + convertDateToDateObject(date: Date): DateObject; } export interface UseNumber { @@ -73,6 +77,11 @@ export interface UseNumber { export interface UseNormalizeDate { normalizeDate: (dateString: string, useValueFormat: boolean) => DateObject; + normalizeDateRange:(dateRangeString: string) => Period; + setNewDateRange: (period: Period, newDate: DateObject) => DataRangePicked; + getActiveMonth: (dateValue: string) => { dateRange: Period, beginDateActiveMonth?: any, endDateActiveMonth?:any, beginTime?: any, endTime?: any, selectedWeekInfo?: any }; + getTimeStr:(date: Date) => string; + getMonthAndYear:() => { startMonth: {month: number, year: number }, endMonth: {month: number, year: number }}; } export interface UseCompare { @@ -124,7 +133,7 @@ export interface UseMark { } export interface UseWeek { - getNowWeekTime: (date: Date) => Period; + getNowWeekTime: (date: Date, firstDayOfWeek: FirstDayOfTheWeek) => Period; } export interface UseDisableTime { diff --git a/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts b/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts index 52843559350ed831b54a118e2aa6d3b205e24ecb..a6014d3f29e7f0596bff563494a7134dc8ee1266 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-calendar.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { DateObject, MarkedDates, MarkStatus, MonthTag, weekDays } from '../types/common'; +import { DateObject, FirstDayOfTheWeek, MarkedDates, MarkStatus, MonthTag, WeekDays } from '../types/common'; import { CalendarWeekItem, CalenderDayItem } from "../types/calendar"; import { useDate } from './use-date'; import { useMark } from './use-mark'; @@ -30,7 +30,7 @@ export default function useCalendar({ isDisabledDate }: UseDisableDate): UseCale function getSundayIndex(firstDayOfWeek: string): number { // Index of Sunday day - const dayIdx = weekDays.indexOf(firstDayOfWeek); + const dayIdx = WeekDays.indexOf(firstDayOfWeek); return dayIdx > 0 ? 7 - dayIdx : 0; } @@ -123,7 +123,9 @@ export default function useCalendar({ isDisabledDate }: UseDisableDate): UseCale day++; } } - const numberInTheYear: number = showWeekNumber && firstDayOfWeek === 'Mon.' ? getWeekNumber(days[0].date) : 0; + + const dateIndex = firstDayOfWeek === FirstDayOfTheWeek.Monday ? 0 : 6; + const numberInTheYear: number = getWeekNumber(days[dateIndex].date); dates.push({ days, numberInTheYear, year }); } return dates; diff --git a/packages/ui-vue/components/date-picker/src/composition/use-date.ts b/packages/ui-vue/components/date-picker/src/composition/use-date.ts index b8ca85ef15e2778e96ee44c1f32207327ed2aa07..b7f26d145d3270858a8d98184754f3b6f2953c5d 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-date.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-date.ts @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { useDateFormat } from '@farris/ui-vue/components/common'; import { isValid } from "date-fns"; -import { DateObject, weekDays } from "../types/common"; +import { DateObject, WeekDays } from "../types/common"; import { UseDate } from "./types"; export function useDate(): UseDate { - + const { formatTo, parseToDate } = useDateFormat(); function emptyDate(): DateObject { return { year: 0, month: 0, day: 0 }; } @@ -76,7 +77,7 @@ export function useDate(): UseDate { } function getWeekdayIndex(weekday: string): number { - return weekDays.indexOf(weekday); + return WeekDays.indexOf(weekday); } function getTimeInMilliseconds(date: DateObject): number { @@ -97,7 +98,7 @@ export function useDate(): UseDate { return max; } - function getToday(): DateObject { + function getToday(): Required { const date: Date = new Date(); return { year: date.getFullYear(), @@ -158,6 +159,16 @@ export function useDate(): UseDate { return dateObject; } + function convertDateToDateObject(date: Date): DateObject { + return { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate(), + hour: date.getHours(), + minute: date.getMinutes(), + second: date.getSeconds() + }; + } function getMinOrMaxDateObject(value: Date | string | null | undefined, isMin = true): DateObject { const MIN_DATE = new Date(1900, 1, 1, 0, 0, 0); @@ -166,12 +177,12 @@ export function useDate(): UseDate { const DEFAULT_DATE = isMin ? MIN_DATE : MAX_DATE; // 使用类型守卫替代 switch-case - let date: Date; + let date: any; if (value instanceof Date) { date = value; } else if (typeof value === 'string') { - date = value.trim() ? new Date(value) : DEFAULT_DATE; + date = value.trim() ? parseToDate(value, 'yyyy-MM-dd HH:mm:ss') : DEFAULT_DATE; } else { date = DEFAULT_DATE; } @@ -199,8 +210,52 @@ export function useDate(): UseDate { return getMinOrMaxDateObject(value, false); } + function getTimeValue(timeValue: string, isDateRange = false): { hour: any, minute: any, second: any } { + if (timeValue) { + const timeArr = timeValue.replace('时', ':').replace('分', ':').replace('秒', '').split(':'); + if (timeArr.length >= 2) { + return { + hour: parseInt(timeArr[0], 10), + minute: parseInt(timeArr[1]), + second: parseInt(timeArr[2] || '0') || 0 + }; + } + } else { + const nowDate = new Date(); + const hour = nowDate.getHours(); + const minute = nowDate.getMinutes(); + const second = nowDate.getSeconds(); + + if (isDateRange) { + return { hour: 0, minute: 0, second: 0 }; + } else { + return { hour, minute, second }; + } + } + + return { hour: 0, minute: 0, second: 0 }; + } + + function getEndTimeValue(timeValue: string): { hour: any, minute: any, second: any } { + if (timeValue) { + const timeArr_range = timeValue.replace('时', ':').replace('分', ':').replace('秒', '').split(':'); + if (timeArr_range.length >= 2) { + return { + hour: timeArr_range[0], + minute: timeArr_range[1], + second: timeArr_range[2] ? timeArr_range[2] : 0 + }; + } + } + + const hour = 23; + const minute = 59; + const second = 59; + return { hour, minute, second }; + } + return { emptyDate, getDate, getDate2, getDayNumber, getEpocTime, getNearDate, getWeekdayIndex, getTimeInMilliseconds, getToday, getDateObject, getMinDate, - getMaxDate + getMaxDate, getTimeValue, getEndTimeValue, convertDateToDateObject }; } diff --git a/packages/ui-vue/components/date-picker/src/composition/use-disable-date.ts b/packages/ui-vue/components/date-picker/src/composition/use-disable-date.ts index 6410775bf9457d784fd1884a4a6e10ad1508dc9f..63e619cbcefc25ed3442bbfe517e244da4e9555f 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-disable-date.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-disable-date.ts @@ -23,8 +23,8 @@ import { useNumber } from "./use-number"; export function useDisableDate( minYear: number, maxYear: number, - disableSince: DateObject, - disableUntil: DateObject, + disableSince: DateObject, // 最小日期 + disableUntil: DateObject, // 最大日期 disableDates: DateObject[], disablePeriod: Period[], disableWeekends: boolean, diff --git a/packages/ui-vue/components/date-picker/src/composition/use-locales.ts b/packages/ui-vue/components/date-picker/src/composition/use-locales.ts new file mode 100644 index 0000000000000000000000000000000000000000..9bb9117568b7535ddd2e26f64df65dde30e86703 --- /dev/null +++ b/packages/ui-vue/components/date-picker/src/composition/use-locales.ts @@ -0,0 +1,118 @@ +import { DatePickerProps } from "../date-picker.props"; +import { useI18n } from 'vue-i18n'; + +export interface DatePickerLocale { + weekTitle: string; + weekDayLabels: Record; + monthLabels: Record; + placeholder: { + default: string; + begin: string; + end: string; + }, + buttons: { + selectDate: string; + selectTime: string; + commit: string; + current: { + week: string; + year: string; + month: string; + today: string; + } + }, + quickSelectors: Record; +} + + +export function useDatePickerLocale(props: DatePickerProps): DatePickerLocale { + const { t: getLocaleValue, messages } = useI18n(); + function getValue(localeKey, propertyValue?: string, defaultValue?: string): any { + if (propertyValue === defaultValue) { + return getLocaleValue(localeKey); + } + return propertyValue || ''; + } + + + const weekTitle = getValue('datepicker.weekTitle', props.weekTitle, '周'); + const weekDayLabels = { + "Sun": getValue('datepicker.dayLabels.Sun'), + "Mon": getValue('datepicker.dayLabels.Mon'), + "Tue": getValue('datepicker.dayLabels.Tue'), + "Wed": getValue('datepicker.dayLabels.Wed'), + "Thu": getValue('datepicker.dayLabels.Thu'), + "Fri": getValue('datepicker.dayLabels.Fri'), + "Sat": getValue('datepicker.dayLabels.Sat') + }; + const monthLabels = { + "1": getValue('datepicker.monthLabels.1'), + "2": getValue('datepicker.monthLabels.2'), + "3": getValue('datepicker.monthLabels.3'), + "4": getValue('datepicker.monthLabels.4'), + "5": getValue('datepicker.monthLabels.5'), + "6": getValue('datepicker.monthLabels.6'), + "7": getValue('datepicker.monthLabels.7'), + "8": getValue('datepicker.monthLabels.8'), + "9": getValue('datepicker.monthLabels.9'), + "10": getValue('datepicker.monthLabels.10'), + "11": getValue('datepicker.monthLabels.11'), + "12": getValue('datepicker.monthLabels.12') + }; + + + const placeholder = { + default: getValue('datepicker.placeholder', props.placeholder, '请选择日期'), + begin: getValue('datepicker.range.begin', props.beginPlaceholder, '请选择开始日期'), + end: getValue('datepicker.range.end', props.endPlaceholder, '请选择结束日期') + }; + + const buttons = { + selectDate: getValue('datepicker.dateBtnText'), + selectTime: getValue('datepicker.timeBtnText'), + commit: getValue('datepicker.commitBtnText'), + current: { + week: getValue('datepicker.current.week') || '本周', + year: getValue('datepicker.current.year') || '今年', + month: getValue('datepicker.current.month') || '本月', + today: getValue('datepicker.current.today') || '今天' + } + }; + + const quickSelectors = { + today: getValue('datepicker.quickSelectors.today'), + yesterday: getValue('datepicker.quickSelectors.yesterday'), + tomorrow: getValue('datepicker.quickSelectors.tomorrow'), + thisMonday: getValue('datepicker.quickSelectors.thisMonday'), + nextMonday: getValue('datepicker.quickSelectors.nextMonday'), + firstDayOfMonth: getValue('datepicker.quickSelectors.firstDayOfMonth'), + lastDayOfMonth: getValue('datepicker.quickSelectors.lastDayOfMonth'), + firstDayOfLastMonth: getValue('datepicker.quickSelectors.firstDayOfLastMonth'), + lastDayOfLastMonth: getValue('datepicker.quickSelectors.lastDayOfLastMonth'), + firstDayOfYear: getValue('datepicker.quickSelectors.firstDayOfYear'), + lastDayOfYear: getValue('datepicker.quickSelectors.lastDayOfYear'), + firstDayOfQuarter: getValue('datepicker.quickSelectors.firstDayOfQuarter'), + lastDayOfQuarter: getValue('datepicker.quickSelectors.lastDayOfQuarter'), + thisWeek: getValue('datepicker.quickSelectors.thisWeek'), + lastWeek: getValue('datepicker.quickSelectors.lastWeek'), + nextWeek: getValue('datepicker.quickSelectors.nextWeek'), + past7Days: getValue('datepicker.quickSelectors.past7Days'), + past30Days: getValue('datepicker.quickSelectors.past30Days'), + thisMonth: getValue('datepicker.quickSelectors.thisMonth'), + lastMonth: getValue('datepicker.quickSelectors.lastMonth'), + thisQuarter: getValue('datepicker.quickSelectors.thisQuarter'), + lastQuarter: getValue('datepicker.quickSelectors.lastQuarter'), + thisYear: getValue('datepicker.quickSelectors.thisYear'), + lastYear: getValue('datepicker.quickSelectors.lastYear'), + recent3Months: getValue('datepicker.quickSelectors.recent3Months') + }; + + return { + weekTitle, + weekDayLabels, + monthLabels, + placeholder, + buttons, + quickSelectors + }; +} diff --git a/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts b/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts index 2be38ae892bd650fd60b5e939fcbbcb07a6d085f..98c77850f5398eb63614a00feabc267e4e7c96bb 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-normalize-date.ts @@ -15,20 +15,22 @@ */ import { ref } from 'vue'; import { DatePickerProps } from "../date-picker.props"; -import { DateObject, Period } from "../types/common"; +import { DataRangePicked, DateObject, FirstDayOfTheWeek, Period } from "../types/common"; import { DateFormatInfo, UseNormalizeDate } from "./types"; import { useDateFormat } from "./use-date-format"; import { useNumber } from "./use-number"; import { useCompare } from "./use-compare"; import { NameOfMonths } from "../types/month"; import { useDisableDate } from "./use-disable-date"; +import { useDate } from "./use-date"; export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { - const { displayFormat, minYear, maxYear, selectMode, valueFormat, displayTime, periodDelimiter } = props; - const { getDateValue } = useDateFormat(); - const { getNumberByValue, getMonthNumberByMonthName } = useNumber(); - const { equalOrEarlier, isInitializedDate } = useCompare(); + const { displayFormat, minYear, maxYear, selectMode, valueFormat, showTime, periodDelimiter } = props; + const { emptyDate, getToday } = useDate(); + const { getDateValue, preZero } = useDateFormat(); + const { getNumberByValue, getMonthNumberByMonthName, getWeekNumber } = useNumber(); + const { equalOrEarlier, isInitializedDate, isDateEarlier } = useCompare(); const { isDisabledDate } = useDisableDate( props.minYear, props.maxYear, @@ -43,8 +45,36 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { const nameOfMonths = ref(props.nameOfMonths); const daysInMonth: Array = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + function getMonthAndYear() { + const today = getToday(); + + switch (selectMode) { + case 'month': + return { + startMonth: { month: 0, year: today.year }, + endMonth: { month: 0, year: today.year + 1 } + }; + case 'year': + return { + startMonth: { month: 0, year: today.year }, + endMonth: { month: 0, year: today.year + 10 } + }; + default: { + const startMonth = { month: today.month, year: today.year }; + let endMonth = { month: today.month + 1, year: today.year }; + if (today.month >= 12) { + endMonth = { month: 1, year: today.year + 1 }; + } + + return { startMonth, endMonth }; + } + } + } + + function normalizeInputDate(dateString: string, useValueFormat = false) { - let shouldDisplayTime = displayTime; + let shouldDisplayTime = showTime; let actualFormat = displayFormat; let targetDateString = dateString; if (useValueFormat) { @@ -53,7 +83,7 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { if (targetDateString[targetDateString.length - 1] === '-' || targetDateString[targetDateString.length - 1] === ':') { targetDateString = targetDateString.substring(0, targetDateString.length - 1); } - if (!displayTime && valueFormat.toLocaleLowerCase().indexOf('hh:mm') > -1) { + if (!showTime && valueFormat.toLocaleLowerCase().indexOf('hh:mm') > -1) { shouldDisplayTime = true; } } @@ -141,7 +171,7 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { if (year < minYear || year > maxYear || month < 1 || month > 12) { return normalizedDate; } - const date: DateObject = displayTime ? { year, month, day, hour, minute, second } : { year, month, day }; + const date: DateObject = showTime ? { year, month, day, hour, minute, second } : { year, month, day }; if (isDisabledDate(date)) { return normalizedDate; @@ -174,20 +204,148 @@ export function useNormalizeDate(props: DatePickerProps): UseNormalizeDate { to: { year: 0, month: 0, day: 0 } }; if (dateRangeStr && dateRangeStr.length) { - const dates: Array = dateRangeStr.split(periodDelimiter); + const dates: Array = dateRangeStr.split(periodDelimiter || '~'); if (dates && dates.length === 2) { const [fromDate, toDate] = dates; - const from: DateObject = normalizeDate(fromDate, true); - if (isInitializedDate(from)) { - const to: DateObject = normalizeDate(toDate, true); - if (isInitializedDate(to) && equalOrEarlier(from, to)) { - period = { from, to }; - } + let from: any = fromDate? normalizeDate(fromDate, true): null; + let to: any = toDate? normalizeDate(toDate, true): null; + + if (!from && to) { + from = {...to}; + } + if (!to && from) { + to = {...from}; } + + if (!from && !to) { + return period; + } + + period = { from, to }; + // if (isInitializedDate(from)) { + // if (isInitializedDate(to) && equalOrEarlier(from, to)) { + // period = { from, to }; + // } + // } } } return period; } - return { normalizeDate }; + function setNewDateRange(period: Period, date: DateObject): DataRangePicked { + const { from: begin, to: end } = period; + const isBeginDateInitialized: boolean = isInitializedDate(begin); + const isEndDateInitialized: boolean = isInitializedDate(end) && JSON.stringify(begin) !== JSON.stringify(end); + + if (isBeginDateInitialized && isEndDateInitialized) { + period.from = date; + period.to = emptyDate(); + } else if (!isBeginDateInitialized) { + period.from = date; + + if (!isEndDateInitialized) { + period.to = date; + } + } else { + const firstDateEarlier: boolean = isDateEarlier(date, begin); + if (firstDateEarlier) { + const _date = begin; + period.to = _date; + period.from = date; + } else { + period.to = date; + } + + return { from: period.from, to: period.to, emit: true }; + } + return { from: period.from, to: period.to }; + } + + function getTimeStr(date: Date): string { + const hour = preZero(date.getHours()); + const minute = preZero(date.getMinutes()); + const second = preZero(date.getSeconds()); + return `${hour}:${minute}:${second}`; + } + + function getActiveMonth(dateRangeValue: string) { + const dateRange = normalizeDateRange(dateRangeValue); + const beginDate = dateRange.from; + const endDate = dateRange.to; + + if (isInitializedDate(beginDate) && isInitializedDate(endDate)) { + + const hour = preZero(beginDate.hour || 0); + const minute = preZero(beginDate.minute || 0); + const second = preZero(beginDate.second || 0); + const beginTimeValue = `${hour}:${minute}:${second}`; + const _hour = preZero(endDate.hour || 0); + const _minute = preZero(endDate.minute || 0); + const _second = preZero(endDate.second || 0); + const endTimeValue = `${_hour}:${_minute}:${_second}`; + + const beginDateActiveMonth = { + year: beginDate.year || 1, + month: beginDate.month || 1, + displayTextOfMonth: nameOfMonths.value[beginDate.month || '1'], + displayTextOfYear: `${beginDate.year}` + }; + + const endDateActiveMonth = { + year: endDate.year || 1, + month: endDate.month || 1, + displayTextOfMonth: nameOfMonths.value[endDate.month || '1'], + displayTextOfYear: `${endDate.year}` + }; + + if(beginDate.month === endDate.month && beginDate.year === endDate.year) { + if (!dateRangeValue) { + if (endDateActiveMonth.month >= 12) { + endDateActiveMonth.month = 1; + endDateActiveMonth.year = endDateActiveMonth.year + 1; + } else { + endDateActiveMonth.month = endDateActiveMonth.month + 1; + if (selectMode === 'month') { + endDateActiveMonth.year = endDateActiveMonth.year + 1; + } + endDateActiveMonth.displayTextOfMonth = nameOfMonths.value[endDateActiveMonth.month]; + } + } else { + const rangeVal = dateRangeValue.split('~'); + const beginVal = rangeVal[0]; + const endVal = rangeVal[1]; + if (!beginVal && endVal) { + if (beginDateActiveMonth.month === 1) { + beginDateActiveMonth.year = beginDateActiveMonth.year - 1; + beginDateActiveMonth.month = 12; + } else { + beginDateActiveMonth.month = beginDateActiveMonth.month - 1; + } + beginDateActiveMonth.displayTextOfMonth = nameOfMonths.value[beginDateActiveMonth.month]; + } else { + if (endDateActiveMonth.month === 12) { + endDateActiveMonth.year = endDateActiveMonth.year + 1; + endDateActiveMonth.month = 1; + } else { + endDateActiveMonth.month = endDateActiveMonth.month + 1; + } + endDateActiveMonth.displayTextOfMonth = nameOfMonths.value[endDateActiveMonth.month]; + } + } + } else if (selectMode === 'month' && beginDate.year === endDate.year) { + endDateActiveMonth.year = endDateActiveMonth.year + 1; + } + endDateActiveMonth.displayTextOfYear = `${endDateActiveMonth.year}`; + + const dateObj = props.firstDayOfTheWeek === FirstDayOfTheWeek.Sunday ? endDate: beginDate; + const numberInTheYear = getWeekNumber(dateObj); + const selectedWeekInfo = { numberInTheYear, year: dateObj.year }; + + return { beginDateActiveMonth, endDateActiveMonth, dateRange, beginTime: beginTimeValue, endTime: endTimeValue, selectedWeekInfo }; + } + + return { dateRange }; + } + + return { normalizeDate, normalizeDateRange, setNewDateRange, getActiveMonth, getTimeStr, getMonthAndYear }; } diff --git a/packages/ui-vue/components/date-picker/src/composition/use-quick-selector.tsx b/packages/ui-vue/components/date-picker/src/composition/use-quick-selector.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ab8a411ea1a1694a3621763ade0db378d76eea46 --- /dev/null +++ b/packages/ui-vue/components/date-picker/src/composition/use-quick-selector.tsx @@ -0,0 +1,148 @@ +import { + format, addDays, subDays, startOfToday, + startOfWeek, endOfWeek, startOfMonth, + endOfMonth, startOfYear, endOfYear, + subMonths, subWeeks, subYears, + isWeekend, addBusinessDays, + startOfQuarter, endOfQuarter, subQuarters +} from 'date-fns'; + + +export interface QuickSelectorItem { + label: string; // 显示的文本 + value: any; // 选择的值 + description?: string; // 描述 + code: string; // 唯一标识 + disabled?: boolean; // 是否禁用 +} + + +export function useQuickSelector(props: any, context: any) { + + const { locales } = props; + const { quickSelectors } = locales; + + const singleSelectors = [ + { label: '今天', value: startOfToday(), description: '选择今天日期', code: 'today' }, + { label: '昨天', value: subDays(startOfToday(), 1), description: '选择昨天日期', code: 'yesterday' }, + { label: '明天', value: addDays(startOfToday(), 1), description: '选择明天日期', code: 'tomorrow' }, + { label: '本周一', value: startOfWeek(startOfToday(), { weekStartsOn: 1 }), description: '本周一', code: 'thisMonday' }, + { label: '下周一', value: addDays(startOfWeek(startOfToday(), { weekStartsOn: 1 }), 7), description: '下周一', code: 'nextMonday' }, + { label: '本月初', value: startOfMonth(startOfToday()), description: '本月第一天', code: 'firstDayOfMonth' }, + { label: '本月末', value: endOfMonth(startOfToday()), description: '本月最后一天', code: 'lastDayOfMonth' }, + { label: '上月初', value: startOfMonth(subMonths(startOfToday(), 1)), description: '上月第一天', code: 'firstDayOfLastMonth' }, + { label: '年初', value: startOfYear(startOfToday()), description: '今年第一天', code: 'firstDayOfYear' }, + { label: '年末', value: endOfYear(startOfToday()), description: '今年最后一天', code: 'lastDayOfYear' }, + { label: '本季度初', value: startOfQuarter(startOfToday()), description: '本季度第一天', code: 'firstDayOfQuarter' }, + { label: '本季度末', value: endOfQuarter(startOfToday()), description: '本季度最后一天', code: 'lastDayOfQuarter' } + ]; + + const rangeSelectors = [ + { + label: '今天', + value: [startOfToday(), startOfToday()], + description: '选择今天日期', + code: 'today' + }, + { + label: '昨天', + value: [subDays(startOfToday(), 1), subDays(startOfToday(), 1)], + description: '选择昨天日期', + code: 'yesterday' + }, + { + label: '本周', + value: [startOfWeek(startOfToday(), { weekStartsOn: 1 }), endOfWeek(startOfToday(), { weekStartsOn: 1 })], + description: '本周一至周日', + code: 'thisWeek' + }, + { + label: '上周', + value: [ + startOfWeek(subWeeks(startOfToday(), 1)), + endOfWeek(subWeeks(startOfToday(), 1)) + ], + description: '上周一至周日', + code: 'lastWeek' + }, + { + label: '过去7天', + value: [subDays(startOfToday(), 6), startOfToday()], + description: '7天前至今天', + code: 'past7Days' + }, + { + label: '过去30天', + value: [subDays(startOfToday(), 29), startOfToday()], + description: '30天前至今天', + code: 'past30Days' + }, + { + label: '本月', + value: [startOfMonth(startOfToday()), endOfMonth(startOfToday())], + description: '本月1号至月末', + code: 'thisMonth' + }, + { + label: '上月', + value: [ + startOfMonth(subMonths(startOfToday(), 1)), + endOfMonth(subMonths(startOfToday(), 1)) + ], + description: '上月1号至月末', + code: 'lastMonth', + }, + { + label: '本季度', + value: [startOfQuarter(startOfToday()), endOfQuarter(startOfToday())], + description: '本季度第一天至本季度最后一天', + code: 'thisQuarter' + }, + { + label: '上季度', + value: [ + startOfQuarter(subQuarters(startOfToday(), 1)), + endOfQuarter(subQuarters(startOfToday(), 1)) + ], + description: '上季度第一天至最后一天', + code: 'lastQuarter' + }, + { + label: '今年', + value: [startOfYear(startOfToday()), endOfYear(startOfToday())], + description: '本年1月1日至12月31日', + code: 'thisYear' + }, + { + label: '去年', + value: [ + startOfYear(subYears(startOfToday(), 1)), + endOfYear(subYears(startOfToday(), 1)) + ], + description: '去年1月1日至12月31日', + code: 'lastYear' + }, + { + label: '最近三个月', + value: [subMonths(startOfToday(), 2), startOfToday()], + description: '3个月前至今天', + code: 'recent3Months' + }, + ]; + + const translate = (items: QuickSelectorItem[]) => { + return items.map(item => { + item.label = quickSelectors[item.code] || item.label; + return item; + }); + }; + + const quickSelectorList = { + single: translate(singleSelectors), + range: translate(rangeSelectors) + }; + + return { + quickSelectorList + }; +} diff --git a/packages/ui-vue/components/date-picker/src/composition/use-week.ts b/packages/ui-vue/components/date-picker/src/composition/use-week.ts index facb37e7576df8941998ea16380b001f0e43555d..2b40969034a285f827f874353ae3441fcf41e59c 100644 --- a/packages/ui-vue/components/date-picker/src/composition/use-week.ts +++ b/packages/ui-vue/components/date-picker/src/composition/use-week.ts @@ -13,25 +13,49 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Period } from "../types/common"; +import { FirstDayOfTheWeek, Period } from "../types/common"; import { UseWeek } from "./types"; export function useWeek(): UseWeek { - function getNowWeekTime(date: Date): Period { - date.setDate(date.getDate() - ((date.getDay() + 6) % 7)); - const from = { - year: date.getFullYear(), - month: date.getMonth() + 1, - day: date.getDate() - }; - date.setDate(date.getDate() + 6); - const to = { - year: date.getFullYear(), - month: date.getMonth() + 1, - day: date.getDate() - }; - return { from, to }; + function getNowWeekTime(date: Date, firstDayOfWeek: FirstDayOfTheWeek) { + if (firstDayOfWeek === FirstDayOfTheWeek.Monday) { + date.setDate(date.getDate() - ((date.getDay() + 6) % 7)); + const from = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + date.setDate(date.getDate() + 6); + const to = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + return { from, to }; + } else { + if (date.getDay() !== 0) { + date.setDate(date.getDate() - date.getDay()); + } + + const begin = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + + date.setDate(date.getDate() + 6 - date.getDay()); + const end = { + year: date.getFullYear(), + month: date.getMonth() + 1, + day: date.getDate() + }; + + return { + from: begin, + to: end + }; + } } return { getNowWeekTime }; diff --git a/packages/ui-vue/components/date-picker/src/date-picker.component.tsx b/packages/ui-vue/components/date-picker/src/date-picker.component.tsx index fb8556fc7d8eeef13a5b9ed20413e9da8f958852..6a3f7b0620afbfb6984ca12a5060d0e158dcd337 100644 --- a/packages/ui-vue/components/date-picker/src/date-picker.component.tsx +++ b/packages/ui-vue/components/date-picker/src/date-picker.component.tsx @@ -23,6 +23,8 @@ import { DateObject } from './types/common'; import './date-picker.css'; import { useDate } from './composition/use-date'; +import { useDatePickerLocale } from './composition/use-locales'; + export default defineComponent({ name: 'FDatePicker', @@ -30,14 +32,12 @@ export default defineComponent({ emits: ['update:modelValue', 'datePicked'] as (string[] & ThisType) | undefined, setup(props: DatePickerProps, context: SetupContext) { const groupIcon = ''; - const modelValue = ref(props.modelValue); const buttonEdit = ref(null); - const { getMinDate, getMaxDate } = useDate(); - + const datePickerLocales = useDatePickerLocale(props); const maxDate = computed(() => { - return props.maxDate? getMaxDate(props.maxDate) : MAX_DATE; + return props.maxDate ? getMaxDate(props.maxDate) : MAX_DATE; }); const minDate = computed(() => { return props.minDate ? getMinDate(props.minDate) : MIN_DATE; @@ -49,7 +49,6 @@ export default defineComponent({ const minYear = ref(1900); /** 是否允许日期范围 */ const enablePeriod = ref(props.enablePeriod); - const realValue = ref(modelValue.value); /** 是否高亮周六 */ const highlightSaturday = ref(props.highlightSaturday); /** 是否高亮周日 */ @@ -62,8 +61,7 @@ export default defineComponent({ const showWeekNumber = ref(props.showWeekNumber); /** 显示方式 */ const selectMode = ref(props.selectMode); - /** 是否显示时间 */ - const displayTime = ref(props.displayTime); + /** 禁用周末 */ const disableWeekends = ref(props.disableWeekends); /** 禁用特定日 */ @@ -73,6 +71,8 @@ export default defineComponent({ // /** 禁用至该范围 */ // const disableUntil = ref([props.disableUntil]); + nameOfMonths.value = datePickerLocales.monthLabels; + const disable = ref(props.disabled); const readonly = ref(props.readonly); const enableClear = ref(props.enableClear); @@ -80,6 +80,23 @@ export default defineComponent({ const { formatTo, parseToDate } = useDateFormat(); + /** + * 可能只有beginValue或者endValue的值,modelValue没有设置值 + * @returns + */ + function getInitModelValue() { + if (props.modelValue) { + return props.modelValue; + } + if (props.enablePeriod && (props.beginValue || props.endValue)) { + return props.beginValue + '~' + props.endValue; + } + return ''; + } + // 更改初始化modelValue,否则会出现setModelValue调用时候不触发更新的问题 + const modelValue = ref(getInitModelValue()); + + const realValue = ref(modelValue.value); function onClickButton() { } const displayDate = computed(() => { @@ -88,13 +105,23 @@ export default defineComponent({ }); function setModelValue(dateValue: string) { + if (enablePeriod.value) { + if (modelValue.value !== dateValue) { + modelValue.value = dateValue; + realValue.value = dateValue; + context.emit('update:modelValue', dateValue); + context.emit('datePicked', dateValue); + } + return; + } const formattedValue = formatTo(dateValue, props.valueFormat); - if (realValue.value !== formattedValue) { + if (modelValue.value !== formattedValue) { modelValue.value = formattedValue; realValue.value = formattedValue; context.emit('update:modelValue', formattedValue); - context.emit('datePicked', realValue.value); + context.emit('datePicked', formattedValue); } + } function closeCalendarPanel() { @@ -104,22 +131,33 @@ export default defineComponent({ } function onDatePicked(dateValue: DateObject) { - for(const key in {...dateValue}) { - if (dateValue[key] != null) { - dateValue[key] = dateValue[key].toString().padStart(2, '0'); - } - } + if (!enablePeriod.value && typeof dateValue === 'object') { + let dateValueString = `${dateValue.year}-${dateValue.month}-${dateValue.day}`; + if (props.showTime) { + dateValueString += ` ${dateValue.hour}:${dateValue.minute}:${dateValue.second}`; + } + + if (selectMode.value === 'month') { + dateValueString = `${dateValue.year}-${dateValue.month}`; + } - let dateValueString = `${dateValue.year}-${dateValue.month}-${dateValue.day}`; - if (props.showTime) { - dateValueString += ` ${dateValue.hour}:${dateValue.minute}:${dateValue.second}`; + if (selectMode.value === 'year') { + dateValueString = `${dateValue.year}`; + } + + setModelValue(dateValueString); + } else { + if (typeof dateValue === 'string') { + setModelValue(dateValue); + } } - setModelValue(dateValueString); + closeCalendarPanel(); } function onClearDate() { setModelValue(''); + closeCalendarPanel(); } function setMaxAndMinYear() { @@ -140,22 +178,57 @@ export default defineComponent({ watch( [() => props.disabled, () => props.editable, () => props.enableClear, () => props.readonly], - ([newDisabled, newEditable, newEnableClear, newReadonly]) => { + ([newDisabled, newEditable, newEnableClear, newReadonly]) => { disable.value = newDisabled; editable.value = newEditable; enableClear.value = newEnableClear; readonly.value = newReadonly; } ); + /** + * 监听初始值变更,避免仅通过初始值的变化不能引起变更的问题 + */ + watch(() => props.beginValue, (newValue) => { + if (!modelValue.value) { + modelValue.value = newValue; + } else { + const parts = modelValue.value.split('~'); + if (parts.length === 1) { + // 只有结束日期的情况 + modelValue.value = `${newValue}`; + } else if (parts.length === 2) { + // 有开始和结束日期的情况 + modelValue.value = `${newValue}~${parts[1]}`; + } + } + } + ); + /** + * 监听结束值变更,避免仅通过初始值的变化不能引起变更的问题 + */ + watch(() => props.endValue, (newValue) => { + if (!modelValue.value) { + modelValue.value = '~' + newValue; + } else { + const parts = modelValue.value.split('~'); + if (parts.length === 1) { + // 只有结束日期的情况 + modelValue.value = `${parts[0]}~${newValue}`; + } else if (parts.length === 2) { + // 有开始和结束日期的情况 + modelValue.value = `${parts[0]}~${newValue}`; + } + } + }); watch([() => props.maxDate, () => props.minDate], ([newMaxDate, newMinDate]) => { setMaxAndMinYear(); }); onMounted(() => { - if (modelValue.value) { - setModelValue(modelValue.value); - } + // if (modelValue.value) { + // setModelValue(modelValue.value); + // } setMaxAndMinYear(); }); @@ -164,17 +237,21 @@ export default defineComponent({ onDatePicked(selectedDate); }; + return () => { return enablePeriod.value ? - - : + locales={datePickerLocales} + onConfirm={onConfirm} + onClear={onClearDate} + > : , default: [] }, /** 是否高亮周六 */ @@ -64,7 +62,7 @@ export const datePickerProps = { /** 定界符 */ // periodDelimiter: { Type: String, default: '' }, /** 显示方式 */ - selectMode: { Type: String, default: 'day' }, + selectMode: { Type: String as PropType, default: 'day' }, /** 存储格式 */ valueFormat: { Type: String, default: 'yyyy-MM-dd' }, /** 显示格式 */ @@ -79,6 +77,14 @@ export const datePickerProps = { * 背景文字 */ placeholder: { type: String, default: '请选择日期' }, + /** 区间——开始提示文字 */ + beginPlaceholder:{ type: String, default: '请选择开始日期' }, + /** 区间——结束提示文字 */ + endPlaceholder:{ type: String, default: '请选择结束日期' }, + /** 区间——开始值 */ + beginValue:{type: String, default: ''}, + /** 区间——结束值 */ + endValue:{type: String, default: ''}, /** * 作为内嵌编辑器被创建后默认选中文本 */ @@ -87,18 +93,15 @@ export const datePickerProps = { * 是否展示时分秒 */ showTime: { type: Boolean, default: false }, - /** 显示第二个日期输入框 */ - showEndDate: { type: Boolean, default: true }, + showWeekNumber: {type: Boolean, default: false}, + weekTitle: { type: String, default: '周' }, /** 显示中间图标 */ showMiddleIcon: { type: Boolean, default: true }, - /** 开始日期 */ - startDateValue: { type: String }, - /** 结束日期 */ - endDateValue: { type: String }, - /** 展示日期范围面板 */ - showPeriod: { type: Boolean, default: true }, /** 启用删除 */ enableClear: { type: Boolean, default: true }, + firstDayOfTheWeek:{ type: String as PropType, default: FirstDayOfTheWeek.Sunday }, + /** 启用快捷选择列表 */ + enableQuickSelect: { type: Boolean, default: false } } as Record; export type DatePickerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/date-picker/src/designer/date-picker.design.component.tsx b/packages/ui-vue/components/date-picker/src/designer/date-picker.design.component.tsx index 42e51a9ff95d0b73f7c59417e0d7aa048ceb00f7..88f3005037f34723ea758d8ef6a75a7c799126e9 100644 --- a/packages/ui-vue/components/date-picker/src/designer/date-picker.design.component.tsx +++ b/packages/ui-vue/components/date-picker/src/designer/date-picker.design.component.tsx @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { defineComponent, inject, onMounted, ref, SetupContext } from 'vue'; +import { defineComponent, inject, onMounted, ref, SetupContext,computed} from 'vue'; import FButtonEditDesign from '@farris/ui-vue/components/button-edit'; +import FInputGroupDesign from '@farris/ui-vue/components/input-group'; import { DesignerItemContext } from '@farris/ui-vue/components/designer-canvas'; import { useDesignerComponent } from '@farris/ui-vue/components/designer-canvas'; import { DatePickerDesignProps, datePickerDesignProps } from '../date-picker.props'; @@ -38,12 +39,57 @@ export default defineComponent({ }); context.expose(componentInstance.value); + const buttonEditClass = computed(() => { + const classObject = { + 'f-button-edit': true, + 'f-cmp-inputgroup': true, + 'f-button-edit-nowrap': true, + 'f-cmp-datepicker':true + } as Record; + return classObject; + }); + + const inputGroupClass = computed(() => { + const classObject = { + 'f-cmp-inputgroup': true, + 'input-group': true, + // 'f-state-disable': disabled.value, + 'f-state-editable': true, + 'align-items-center': true + // 'f-state-readonly': readonly.value, + // 'f-state-focus': hasFocused.value + }; + return classObject; + }); + function getRangeHtml(){ + return
+
+
+ + + +
+
+ +
+
+
; + } return () => { - return ( + return (props.enablePeriod?getRangeHtml(): = [SUN, MON, TUE, WED, THU, FRI, SAT]; + + export type SelectMode = 'day' | 'week' | 'month' | 'year'; // export const weekDays = ['Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thur', 'Fri.', 'Sat.']; -export const weekDays = ['日', '一', '二', '三', '四', '五', '六']; +// export const weekDays = ['日', '一', '二', '三', '四', '五', '六']; +// export const WeekDayLabels = { su: 'Sun', mo: 'Mon', tu: 'Tue', we: 'Wed', th: 'Thu', fr: 'Fri', sa: 'Sat' }; +export const WeekDayLabels = { Sun: '日', Mon: '一', Tue: '二', Wed: '三', Thu: '四', Fri: '五', Sat: '六' }; export enum MonthTag { previous = 1, current = 2, next = 3 } +export enum FirstDayOfTheWeek { + Sunday = 'Sun', + Monday = 'Mon', +}; diff --git a/packages/ui-vue/components/designer-canvas/src/components/designer-inner-item.component.tsx b/packages/ui-vue/components/designer-canvas/src/components/designer-inner-item.component.tsx index d3b415032dc26553562261e07d7b766711683efd..781b227ee9291515c5b16cb227ea348624743e76 100644 --- a/packages/ui-vue/components/designer-canvas/src/components/designer-inner-item.component.tsx +++ b/packages/ui-vue/components/designer-canvas/src/components/designer-inner-item.component.tsx @@ -1,6 +1,6 @@ import { Ref, SetupContext, computed, defineComponent, inject, onMounted, provide, ref, watch, onBeforeUnmount, withModifiers } from 'vue'; import { DesignerButtonItem, DesignerInnerItemPropsType, designerInnerItemProps } from '../composition/props/designer-inner-item.props'; -import { DraggingResolveContext, UseDragula } from '../composition/types'; +import { DesignerHostService, DraggingResolveContext, UseDragula } from '../composition/types'; import { ComponentSchema, DesignerComponentInstance, DesignerItemContext } from '../types'; import { canvasChanged, setPositionOfButtonGroup, setPositionOfButtonGroupInContainer, resetPositionOfButtonGroup } from '../composition/designer-canvas-changed'; import { useDesignerInnerComponent } from '../composition/function/use-designer-inner-component'; @@ -25,6 +25,7 @@ const FDesignerInnerItem = defineComponent({ const parent = inject('design-item-context'); const designItemContext = { designerItemElementRef, componentInstance, schema: schema.value, parent, setupContext: context as SetupContext }; provide('design-item-context', designItemContext); + const designerHostService = inject('designer-host-service'); const designerItemClass = computed(() => { const customButtons = componentInstance.value?.getCustomButtons && componentInstance.value.getCustomButtons(); @@ -202,7 +203,7 @@ const FDesignerInnerItem = defineComponent({ const innerComponentInstance = useDesignerInnerComponent(innerComponentElementRef, designItemContext); return innerComponentInstance.value; } - + onMounted(() => { if (designerItemElementRef.value) { const draggableContainer = designerItemElementRef.value.querySelector( @@ -223,6 +224,7 @@ const FDesignerInnerItem = defineComponent({ designerItemElementRef.value.componentInstance = componentInstance; designerItemElementRef.value.designItemContext = designItemContext; } + componentInstance.value.setComponentBasicInfoMap(designerHostService); } bindingScrollEvent(); canvasChanged.value++; diff --git a/packages/ui-vue/components/designer-canvas/src/components/designer-item.component.tsx b/packages/ui-vue/components/designer-canvas/src/components/designer-item.component.tsx index dad0cc36439d025869692b2b9480984d01548219..8522e3a9ae6c90757851598ebf428653b76723a7 100644 --- a/packages/ui-vue/components/designer-canvas/src/components/designer-item.component.tsx +++ b/packages/ui-vue/components/designer-canvas/src/components/designer-item.component.tsx @@ -26,11 +26,22 @@ const FDesignerItem = defineComponent({ const useDragulaComposition = inject('canvas-dragula'); const componentInstance = ref() as Ref; const parent = inject('design-item-context'); - const designItemContext = { designerItemElementRef, componentInstance, schema: schema.value, parent, setupContext: context as SetupContext }; + /** + * 使用这种方式更新schema,不会切换parent、designItemContext.schema、componentInstance.schema的关联 + * @param newSchema + */ + function updateContextSchema(newSchema) { + if (newSchema) { + Object.assign(schema.value, newSchema); + } + } + const designItemContext = { designerItemElementRef, componentInstance, schema: schema.value, parent, setupContext: context as SetupContext, updateContextSchema }; provide('design-item-context', designItemContext); const useFormSchema = inject('useFormSchema'); const designerHostService = inject('designer-host-service'); + // 外部容器引入的表单,记录所属容器id + const externalContainerId = inject('external-container-id'); const designerItemClass = computed(() => { const componentClass = props.modelValue.appearance ? (props.modelValue.appearance.class as string) || '' : ''; const customButtons = componentInstance.value?.getCustomButtons && componentInstance.value.getCustomButtons(); @@ -79,7 +90,9 @@ const FDesignerItem = defineComponent({ payload.preventDefault(); payload.stopPropagation(); } - + if (designerHostService?.designerContext?.checkCanDeleteControl && !designerHostService.designerContext.checkCanDeleteControl(schemaToRemove)) { + return; + } // 连同所属组件一起删除,使用场景如data-grid、form控件等。 if (componentInstance.value.triggerBelongedComponentToDeleteWhenDeleted) { const belongedComponentInstance = componentInstance.value.getBelongedComponentInstance(componentInstance); @@ -199,7 +212,7 @@ const FDesignerItem = defineComponent({ componentId={componentId.value} onSelectionChange={onSelectionChange} onDragEnd={onDragEnd} - complete-form-schema={props.completeFormSchema}> + > ))} ) : Component ? ( @@ -215,10 +228,16 @@ const FDesignerItem = defineComponent({ ); } + function getExternalFormSchema() { + const externalFormInfo = useFormSchema.externalFormSchema.get(externalContainerId); + if (externalFormInfo?.content) { + return externalFormInfo?.content; + } + } function renderChildComponentContent(viewSchema: ComponentSchema) { const componentKey = viewSchema.type; if (componentKey === 'component-ref') { - const formSchema = props.completeFormSchema ? props.completeFormSchema : useFormSchema?.getFormSchema(); + const formSchema = externalContainerId ? getExternalFormSchema() : useFormSchema?.getFormSchema(); const componentSchema = formSchema.module.components.find((component: any) => component.id === viewSchema.component); const componentSchemaRef = ref(componentSchema); @@ -227,24 +246,17 @@ const FDesignerItem = defineComponent({ key={componentSchema.id} v-model={componentSchemaRef.value} componentId={componentSchema.id} - complete-form-schema={props.completeFormSchema} onSelectionChange={onSelectionChange} onDragEnd={onDragEnd}>; } - // // eslint-disable-next-line prefer-const - // let componentSchema = useFormSchema?.getFormSchema().module.components - // .find((component: any) => component.id === viewSchema.component); - // if (componentSchema) { - - // return ; - // } } } - watch( - () => props.modelValue, - (value: any) => { - schema.value = value; - id.value = `${value.id}-component`; + + watch([() => props.modelValue, () => props.componentId], + ([newValue, newComponentId]) => { + schema.value = newValue; + id.value = `${newValue.id}-component`; + newComponentId && (componentId.value = newComponentId); triggerRef(designerItemElementRef); }, { @@ -303,6 +315,7 @@ const FDesignerItem = defineComponent({ } componentInstance.value.belongedComponentId = componentId.value; + componentInstance.value.setComponentBasicInfoMap(designerHostService); } bindingScrollEvent(); @@ -352,11 +365,12 @@ const FDesignerItem = defineComponent({ } return () => { + const prefixId = externalContainerId ? `${externalContainerId}-` : ''; return ( schema.value.type === 'component-ref' ? renderChildComponentContent(schema.value) :
= {}; const componentPropsConverter: Record = {}; @@ -64,7 +68,7 @@ const componentPropertyConfigConverter: Record = {}; let hasLoaded = false; /** - * 加载设计时组件 + * 加载设计时组件,后续移除该方法,通过registerDesignerComponents注册组件 */ function loadDesignerRegister() { if (!hasLoaded) { @@ -94,6 +98,7 @@ function loadDesignerRegister() { FListNav.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FNav.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FLookup.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); + FLanguageTextbox.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FNumberRange.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FNumberSpinner.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); FOrder.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter); @@ -131,4 +136,26 @@ function loadDesignerRegister() { } } -export { componentMap, componentPropsConverter, componentPropertyConfigConverter, loadDesignerRegister }; +/** + * 加载设计时组件 + */ +function registerDesignerComponents(components: any[]) { + if (hasLoaded) { + return; + } + + hasLoaded = true; + const registerContext:RegisterContext = { + schemaMap: schemaMapForDesigner, + propertyConfigSchemaMap: propertyConfigSchemaMapForDesigner, + propertyEffectMap: propertyEffectMapForDesigner, + schemaResolverMap: schemaResolverMapForDesigner + }; + + components.forEach(component => { + component.registerDesigner && component.registerDesigner(componentMap, componentPropsConverter, componentPropertyConfigConverter, registerContext); + }); +} + + +export { componentMap, componentPropsConverter, componentPropertyConfigConverter, loadDesignerRegister, registerDesignerComponents }; diff --git a/packages/ui-vue/components/designer-canvas/src/composition/class/designer-canvas.css b/packages/ui-vue/components/designer-canvas/src/composition/class/designer-canvas.css index 4d9aa5ab868fdc0034b56328bb4345a17cd7a6c7..da77ba920d56bddfc7dec164369f87d75fd183a7 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/class/designer-canvas.css +++ b/packages/ui-vue/components/designer-canvas/src/composition/class/designer-canvas.css @@ -189,6 +189,14 @@ /* z-index: 500; 此处样式会导致拖拽过程中镜像元素在工具箱范围内被遮挡,故先屏蔽 */ } +/* 移动画布样式 */ +.editorDiv .editorPanel.Mobile { + min-width: 414px; + width: 414px; + margin: 20px auto; + height: calc(100% - 40px); +} + /** 卡片区块 **/ .editorDiv .f-struct-like-card { display: block; diff --git a/packages/ui-vue/components/designer-canvas/src/composition/designer-canvas-changed.ts b/packages/ui-vue/components/designer-canvas/src/composition/designer-canvas-changed.ts index 87a3003a94f255108c58c75a3140d9961e63a872..867da936d1b060e718f9ffafd7d62fc6527dd999 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/designer-canvas-changed.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/designer-canvas-changed.ts @@ -4,6 +4,9 @@ import { ref } from "vue"; /** 用于响应画布发生变更 */ export const canvasChanged = ref(0); +export function changeCanvas() { + canvasChanged.value++; +} /** * 判断DOM 是否在可视区域内 * @param el 元素 diff --git a/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts b/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts index 65284aaf0ff0abb7383b8a487fdbdaec5912587a..8cabedec6998c01de89a05a54c9676e323637a9d 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/dg-control.ts @@ -85,5 +85,7 @@ export const DgControl = { 'list-nav': { type: 'list-nav', name: '列表导航' }, - 'filter-bar': { type: 'filter-bar', name: '筛选条' } + 'filter-bar': { type: 'filter-bar', name: '筛选条' }, + + 'language-textbox': { type: 'language-textbox', name: '多语输入框' }, }; diff --git a/packages/ui-vue/components/designer-canvas/src/composition/function/drag-resolve.tsx b/packages/ui-vue/components/designer-canvas/src/composition/function/drag-resolve.tsx index 4ead40183e4be989919063444bff989f8b0d2788..e9761fd4465af1727b7b0b4d268fbe5d93da2fbc 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/function/drag-resolve.tsx +++ b/packages/ui-vue/components/designer-canvas/src/composition/function/drag-resolve.tsx @@ -161,6 +161,15 @@ export function dragResolveService(designerHostService: DesignerHostService) { modalEditorRef?.modalRef?.value.close(); } } + /** + * 跳过绑定字段 + */ + function onSkipBindingField() { + componentResolveContext.bindingSourceContext = null; + if (modalEditorRef?.modalRef?.value.close) { + modalEditorRef?.modalRef?.value.close(); + } + } /** * 绑定字段弹窗 */ @@ -174,7 +183,13 @@ export function dragResolveService(designerHostService: DesignerHostService) { componentSchema: { editor: { type: componentResolveContext.componentType } } }; const bindingSettings = { enable: false }; - return () => (<> ); + return () => (<> ); } /** * 工具箱拖拽控件:弹出绑定字段的窗口 diff --git a/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-component.ts b/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-component.ts index e64ade5f6bc1ed2dbf2f01970e936320b24964f2..0c28cf311a348bbc51e1af7e0bbd4d244bb33cff 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-component.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-component.ts @@ -1,7 +1,8 @@ import { Ref, ref } from "vue"; import { DesignerHostService, DesignerHTMLElement, DraggingResolveContext, UseDesignerRules } from "../types"; import { ComponentSchema, DesignerComponentInstance, DesignerItemContext } from "../../types"; -import { getSchemaByType } from '../../../../dynamic-resolver/src/schema-resolver'; +import { getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { DgControl } from "../dg-control"; export function useDesignerComponent( elementRef: Ref, @@ -12,6 +13,8 @@ export function useDesignerComponent( const styles = (designerRules && designerRules.getStyles && designerRules.getStyles()) || ''; const designerClass = (designerRules && designerRules.getDesignerClass && designerRules.getDesignerClass()) || ''; const componentInstance = ref(); + + let useFormSchema: any; /** * 校验组件是否支持移动 */ @@ -131,7 +134,7 @@ export function useDesignerComponent( */ function addNewChildComponentSchema(resolveContext: DraggingResolveContext, designerHostService: DesignerHostService) { const { componentType } = resolveContext; - let componentSchema = getSchemaByType(componentType, resolveContext, designerHostService) as ComponentSchema; + let componentSchema = getSchemaByTypeForDesigner(componentType, resolveContext, designerHostService) as ComponentSchema; if (designerRules && designerRules.onResolveNewComponentSchema) { componentSchema = designerRules.onResolveNewComponentSchema(resolveContext, componentSchema); } @@ -181,6 +184,18 @@ export function useDesignerComponent( } } + /** + * 删除控件相关的组件通讯 + */ + function removeCommunicationInComponent(designerHostService: DesignerHostService) { + if (!designerHostService || !designItemContext?.schema) { + return; + } + const componentSchema = designItemContext.schema; + const { formSchemaUtils } = designerHostService; + formSchemaUtils.removeCommunicationInComponent(componentSchema); + } + /** * 控件删除后事件 */ @@ -193,6 +208,9 @@ export function useDesignerComponent( // 若控件配置了表达式,需要删除表达式 removeExpressionsInComponent(designerHostService); + // 若控件触发了组件通讯,需要删除通讯配置 + removeCommunicationInComponent(designerHostService); + // 递归触发子级控件的删除后事件 if (designItemContext?.schema.contents) { designItemContext.schema.contents.map(content => { @@ -223,10 +241,66 @@ export function useDesignerComponent( } + /** + * 配置控件的基础信息(展示标题、路径) + */ + function setComponentBasicInfoMap(designerHostService?: DesignerHostService) { + if (!designItemContext?.schema?.id) { + return; + } + if (!useFormSchema && designerHostService) { + useFormSchema = designerHostService.formSchemaUtils; + } + if (designerRules?.setComponentBasicInfoMap) { + designerRules.setComponentBasicInfoMap(); + return; + } + let componentTitle = ''; + if (designerRules?.getComponentTitle) { + componentTitle = designerRules.getComponentTitle(); + } else { + const { text, title, label, mainTitle, name, type } = designItemContext.schema; + componentTitle = text || title || label || mainTitle || name || DgControl[type]?.name;; + } + if (componentTitle) { + useFormSchema.getControlBasicInfoMap().set(designItemContext.schema.id, { + componentTitle, + parentPathName: componentTitle + }); + } + } + + /** + * 标题类的属性变更后,更新控件的路径信息 + */ + function resetComponentBasicInfoMap(event: any) { + const { changeObject } = event; + const { propertyID, propertyValue } = changeObject; + if (['text', 'title', 'label', 'name', 'mainTitle'].includes(event?.changeObject?.propertyID)) { + if (propertyID && propertyValue) { + setComponentBasicInfoMap(); + + // 更新依赖当前组件的其他组件的路径信息。暂时认为路径信息中当前组件在第一位 + if (useFormSchema) { + const controlBasicInfoMap = useFormSchema.getControlBasicInfoMap(); + const componentIdsReliedOn = controlBasicInfoMap.keys().toArray().filter(componentId => controlBasicInfoMap.get(componentId)?.reliedComponentId === designItemContext?.schema?.id); + if (componentIdsReliedOn?.length) { + componentIdsReliedOn.forEach(componentId => { + const pathArray = controlBasicInfoMap.get(componentId).parentPathName.split(' > '); + pathArray[0] = propertyValue; + controlBasicInfoMap.get(componentId).parentPathName = pathArray.join(' > '); + }) + } + } + } + } + } + /** * 控件属性变更后事件 */ function onPropertyChanged(event: any) { + resetComponentBasicInfoMap(event); if (designerRules && designerRules.onPropertyChanged) { return designerRules.onPropertyChanged(event); } @@ -255,7 +329,9 @@ export function useDesignerComponent( triggerBelongedComponentToDeleteWhenDeleted: !!designerRules && designerRules.triggerBelongedComponentToDeleteWhenDeleted || ref(false), onRemoveComponent, getCustomButtons, - onPropertyChanged + onPropertyChanged, + setComponentBasicInfoMap, + updateContextSchema:designItemContext?.updateContextSchema } as DesignerComponentInstance; return componentInstance as any; diff --git a/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-inner-component.ts b/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-inner-component.ts index 07336537dc9a387694c1cc72443e44471bd62e1e..46c731227f2550ff997415e030d9e7346a5e8e3d 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-inner-component.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/function/use-designer-inner-component.ts @@ -1,7 +1,8 @@ import { inject, Ref, ref } from "vue"; import { DesignerHostService, DesignerHTMLElement, DraggingResolveContext, UseDesignerRules } from "../types"; import { ComponentSchema, DesignerComponentInstance, DesignerItemContext } from "../../types"; -import { getSchemaByType } from '../../../../dynamic-resolver/src/schema-resolver'; +import { getSchemaByTypeForDesigner } from '@farris/ui-vue/components/dynamic-resolver'; +import { DgControl } from "../dg-control"; export function useDesignerInnerComponent( elementRef: Ref, @@ -10,6 +11,7 @@ export function useDesignerInnerComponent( ): Ref { const styles = (designerRules && designerRules.getStyles && designerRules.getStyles()) || ''; const componentInstance = ref(); + let useFormSchema: any; /** * 校验组件是否支持移动 */ @@ -123,7 +125,7 @@ export function useDesignerInnerComponent( function addNewChildComponentSchema(resolveContext: DraggingResolveContext) { const { componentType } = resolveContext; const designerHostServer = inject('designer-host-service') as DesignerHostService; - let componentSchema = getSchemaByType(componentType, resolveContext, designerHostServer) as ComponentSchema; + let componentSchema = getSchemaByTypeForDesigner(componentType, resolveContext, designerHostServer) as ComponentSchema; if (designerRules && designerRules.onResolveNewComponentSchema) { componentSchema = designerRules.onResolveNewComponentSchema(resolveContext, componentSchema); } @@ -152,11 +154,54 @@ export function useDesignerInnerComponent( } function onRemoveComponent() { + } + /** + * 配置控件的基础信息(展示标题、路径) + */ + function setComponentBasicInfoMap(designerHostService?: DesignerHostService) { + if (!designItemContext?.schema) { + return; + } + if (designerRules?.setComponentBasicInfoMap) { + designerRules.setComponentBasicInfoMap(); + return; + } + let componentTitle = ''; + + if (designerRules?.getComponentTitle) { + componentTitle = designerRules.getComponentTitle(); + } else { + const { text, title, label, mainTitle, name, type } = designItemContext.schema; + componentTitle = text || title || label || mainTitle || name || DgControl[type]?.name; + } + if (componentTitle) { + if (!useFormSchema && designerHostService) { + useFormSchema = designerHostService.formSchemaUtils; + } + useFormSchema.getControlBasicInfoMap().set(designItemContext.schema.id, { + componentTitle, + parentPathName: componentTitle + }); + } + } + + /** + * 标题类的属性变更后,更新控件的路径信息 + */ + function resetComponentBasicInfoMap(event: any) { + const { changeObject } = event; + const { propertyID, propertyValue } = changeObject; + if (['text', 'title', 'label', 'name', 'mainTitle'].includes(event?.changeObject?.propertyID)) { + if (propertyID && propertyValue) { + setComponentBasicInfoMap(); + } + } } /** * 控件属性变更后事件 */ function onPropertyChanged(event: any) { + resetComponentBasicInfoMap(event); if (designerRules && designerRules.onPropertyChanged) { return designerRules.onPropertyChanged(event); } @@ -185,7 +230,8 @@ export function useDesignerInnerComponent( triggerBelongedComponentToMoveWhenMoved: !!designerRules && designerRules.triggerBelongedComponentToMoveWhenMoved || ref(false), triggerBelongedComponentToDeleteWhenDeleted: !!designerRules && designerRules.triggerBelongedComponentToDeleteWhenDeleted || ref(false), onPropertyChanged, - getCustomButtons + getCustomButtons, + setComponentBasicInfoMap } as DesignerComponentInstance; return componentInstance as Ref; diff --git a/packages/ui-vue/components/designer-canvas/src/composition/function/use-dragula.ts b/packages/ui-vue/components/designer-canvas/src/composition/function/use-dragula.ts index 0dddca3267549a83ff74de046031379b285c6322..fc243576dba6248f4643b57cb35f445b2a6fd8a4 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/function/use-dragula.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/function/use-dragula.ts @@ -6,7 +6,7 @@ import { canvasChanged } from '../designer-canvas-changed'; import { ComponentSchema } from '../../types'; import { dragResolveService } from './drag-resolve'; -export function useDragula(designerHostService: DesignerHostService): UseDragula { +export function useDragula(designerHostService: DesignerHostService, designerContext: any): UseDragula { let dragulaInstance: any; @@ -138,6 +138,9 @@ export function useDragula(designerHostService: DesignerHostService): UseDragula } const sourceControlSchema = componentResolveContext.componentSchema; if (sourceControlSchema) { + if (designerContext?.appendIdentifyForNewControl) { + designerContext.appendIdentifyForNewControl(sourceControlSchema); + } addNewControlToTarget(target, sourceControlSchema, sibling); } }); diff --git a/packages/ui-vue/components/designer-canvas/src/composition/props/designer-canvas.props.ts b/packages/ui-vue/components/designer-canvas/src/composition/props/designer-canvas.props.ts index ab5965b8ccc281fa958b4670dab5e40b9088e824..13d2a86ecb1bfa2536f2c26b84a31c8daaf50edf 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/props/designer-canvas.props.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/props/designer-canvas.props.ts @@ -6,6 +6,9 @@ export const designerCanvasProps = { */ modelValue: { type: Object, default: {} }, componentId: { type: String, default: '' }, + components:{ type: Array }, + canvasMode:{ type: String, default: 'PC' }, + } as Record; export type DesignerCanvasPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/designer-canvas/src/composition/props/designer-item.props.ts b/packages/ui-vue/components/designer-canvas/src/composition/props/designer-item.props.ts index 5b03a3707b3f3655a733060f443c11177ff34c3e..a674600f00b8102af205824a112d4e6dc8db5238 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/props/designer-item.props.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/props/designer-item.props.ts @@ -14,9 +14,7 @@ export const designerItemProps = { * 组件值 */ modelValue: { type: Object }, - ignore: { type: Boolean, default: false }, - /** 完整的表单元数据----目前只用于组合表单中的子表单 */ - completeFormSchema: { type: Object } + ignore: { type: Boolean, default: false } } as Record; export type DesignerItemPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/designer-canvas/src/composition/rule/use-dragula-common-rule.ts b/packages/ui-vue/components/designer-canvas/src/composition/rule/use-dragula-common-rule.ts index 4e1d7cbe604142cee85a81132c790e90b3dae5f3..2a618e6b6d206c7aca73760c245e2e9a7ec7478c 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/rule/use-dragula-common-rule.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/rule/use-dragula-common-rule.ts @@ -38,8 +38,8 @@ export function useDragulaCommonRule() { return false; } } - // 限制筛选方案 - if (draggingContext.componentType === DgControl['query-solution'].type) { + // 限制筛选方案、筛选条 + if ([DgControl['query-solution'].type, DgControl['filter-bar'].type].includes(draggingContext.componentType)) { return false; } // 限制小分组 diff --git a/packages/ui-vue/components/designer-canvas/src/composition/types.ts b/packages/ui-vue/components/designer-canvas/src/composition/types.ts index 4861b5b0acab3259faf2d94f85b8d5097c3d07a0..04062e51409a010b73093cf5fd6a58384331182f 100644 --- a/packages/ui-vue/components/designer-canvas/src/composition/types.ts +++ b/packages/ui-vue/components/designer-canvas/src/composition/types.ts @@ -28,6 +28,7 @@ export interface DesignerHostService { metadataService?: any; formStateMachineUtils: any; schemaService?: any; + designerContext?: any; [key: string]: any; } /** @@ -164,4 +165,10 @@ export interface UseDesignerRules { /** 获取可拖拽的上层容器 */ getDraggableDesignItemElement?: (context: DesignerItemContext) => Ref | null; + + /** 获取组件的展示标题 */ + getComponentTitle?: () => string; + + /** 配置组件的基础信息(展示标题、路径) */ + setComponentBasicInfoMap?: () => void; } diff --git a/packages/ui-vue/components/designer-canvas/src/designer-canvas.component.tsx b/packages/ui-vue/components/designer-canvas/src/designer-canvas.component.tsx index c3d23011d154213d51e5b5f061beb1b966dcf6a0..7f541e6569f714db0f9fde3d8cb7aaf3012d9f8b 100644 --- a/packages/ui-vue/components/designer-canvas/src/designer-canvas.component.tsx +++ b/packages/ui-vue/components/designer-canvas/src/designer-canvas.component.tsx @@ -1,16 +1,18 @@ import { computed, defineComponent, inject, onMounted, onUnmounted, provide, ref, watch } from 'vue'; import { ComponentSchema, DesignerComponentInstance, DesignerItemContext } from './types'; -import { canvasChanged, setPositionOfButtonGroupInContainer } from './composition/designer-canvas-changed'; +import { canvasChanged, changeCanvas, setPositionOfButtonGroupInContainer } from './composition/designer-canvas-changed'; import { designerCanvasProps, DesignerCanvasPropsType } from './composition/props/designer-canvas.props'; import { useDragula } from './composition/function/use-dragula'; import { DesignerHostService, UseDragula } from './composition/types'; import FDesignerItem from './components/designer-item.component'; import './composition/class/designer-canvas.css'; import './composition/class/control.css'; -import { loadDesignerRegister } from './components/maps'; +import { loadDesignerRegister, registerDesignerComponents } from './components/maps'; import { F_MODAL_SERVICE_TOKEN } from '../../modal'; import { canvasKey, refreshCanvas } from './composition/update-cancas'; +import { setDesignerContextForDesigner } from '../../dynamic-resolver/src/resolver/schema/schema-resolver-design'; +import { setDesignerContext } from '../../dynamic-resolver/src/resolver/schema/schema-resolver'; export default defineComponent({ name: 'FDesignerCanvas', @@ -24,9 +26,12 @@ export default defineComponent({ const designerItemElementRef = ref(); const componentInstance = ref(); const componentId = ref(props.componentId); + const { canvasMode } = props; + let resizeObserver: ResizeObserver | null; let resizeObserverTimer; - + /** 设计器上下文 */ + const designerContext = inject('designerContext') as any; const designerHostService = { eventsEditorUtils: inject('eventsEditorUtils'), formSchemaUtils: inject('useFormSchema'), @@ -38,13 +43,22 @@ export default defineComponent({ useFormCommand: inject('useFormCommand'), modalService: inject(F_MODAL_SERVICE_TOKEN), formStateMachineUtils: inject('useFormStateMachine'), - messagerService: inject('FMessageBoxService') + messagerService: inject('FMessageBoxService'), + designerContext }; + setDesignerContext(designerContext); + setDesignerContextForDesigner(designerContext); provide('designer-host-service', designerHostService); - const useDragulaComposition = useDragula(designerHostService); + const useDragulaComposition = useDragula(designerHostService, designerContext); + + // 后需统一采用registerDesignerComponents注册控件 + if (props.components) { + registerDesignerComponents(props.components); + } else { + loadDesignerRegister(); + } - loadDesignerRegister(); provide('canvas-dragula', useDragulaComposition); provide('design-item-context', { designerItemElementRef, @@ -62,6 +76,23 @@ export default defineComponent({ return classObject; }); + const designerEditorPanelClass = computed(() => { + const classObject = { + 'editorPanel': true, + 'd-flex': true, + 'flex-fill': true, + 'flex-column': true, + [canvasMode]: true + } as Record; + return classObject; + }); + + watch([() => props.modelValue, () => props.componentId], + ([newSchema, newComponentId]) => { + schema.value = newSchema; + componentId.value = newComponentId; + } + ); watch(canvasChanged, () => { setPositionOfButtonGroupInContainer(designerCanvasElementRef.value); @@ -116,11 +147,13 @@ export default defineComponent({ resizeObserver = null; } }); - context.expose({ refreshCanvas, designerCanvasElementRef }); + + context.expose({ refreshCanvas, changeCanvas, designerCanvasElementRef, designerCanvasContainerElementRef, designerHostService }); + return () => { return (
-
+
{schema.value && }
diff --git a/packages/ui-vue/components/designer-canvas/src/types.ts b/packages/ui-vue/components/designer-canvas/src/types.ts index 8da297b5d48a8aa57046a972fdb03bc8788460c1..08e8ebb36cf8de0d4237c7d2ad5042ef075f6f61 100644 --- a/packages/ui-vue/components/designer-canvas/src/types.ts +++ b/packages/ui-vue/components/designer-canvas/src/types.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-use-before-define */ import { Ref, SetupContext } from "vue"; import { DesignerHostService, DesignerHTMLElement, DraggingResolveContext } from "./composition/types"; @@ -79,6 +78,10 @@ export interface DesignerComponentInstance { /** 控件属性变更后事件 */ onPropertyChanged?: (event: any) => void; + + /** 配置控件的基础信息(展示标题、路径) */ + setComponentBasicInfoMap: (designerHostService?: DesignerHostService) => void; + updateContextSchema?:(newSchema:any)=>void; } export interface DesignerItemContext { @@ -92,6 +95,7 @@ export interface DesignerItemContext { parent?: DesignerItemContext; setupContext?: SetupContext; + updateContextSchema?:(newSchema:any)=>void } @@ -101,7 +105,7 @@ export interface DesignerItemContext { export interface DesignerComponentButton { id: string; title: string; - text?:string; + text?: string; icon: string; class?: string; onClick: (payload: MouseEvent) => void; diff --git a/packages/ui-vue/components/designer-outline/src/composition/use-data-view.ts b/packages/ui-vue/components/designer-outline/src/composition/use-data-view.ts index 8bd11f1e130de6728d129ee28a15dafc9043fd0e..09c7190ad5f643aa09373dfac36635d79beb56c6 100644 --- a/packages/ui-vue/components/designer-outline/src/composition/use-data-view.ts +++ b/packages/ui-vue/components/designer-outline/src/composition/use-data-view.ts @@ -12,29 +12,29 @@ export function useDataView( useOutlineNodeComposition: UseOutlineNode ): UseDataView { - const domJsonComponents = props.data.module ? ref(props.data.module.components[0]) : ref(props.data); const { getIcon, getTitle } = useOutlineNodeComposition; /** 处理子组件 */ - function handleChildComponent(componentsItem: any, findComponents: any, number: number, parentComponent: any) { + function handleChildComponent(componentsItem: any, findComponents: any, number: number, parentComponent: any, componentId: string) { const childComponentItem = props.data.module?.components.find((component: any) => component.id === componentsItem.component); if (childComponentItem) { - handleInputData([childComponentItem], findComponents, number, parentComponent); + handleInputData([childComponentItem], findComponents, number, parentComponent, componentId); } } /** 处理数据 */ - function handleInputData(components: any, findComponents: any, number: number, parentComponent: any) { + function handleInputData(components: any, findComponents: any, number: number, parentComponent: any, componentId: string) { components.forEach((componentsItem: any) => { if (componentsItem.type === 'component-ref') { - handleChildComponent(componentsItem, findComponents, number, parentComponent); + handleChildComponent(componentsItem, findComponents, number, parentComponent, componentId); return; } const findComponentsItem = { originalId: componentsItem.id, layer: number, originalParent: parentComponent?.id, + componentId: componentId, name: getTitle(componentsItem, parentComponent), type: componentsItem.type, controlIcon: getIcon(componentsItem), @@ -43,7 +43,7 @@ export function useDataView( findComponents.push(cloneDeep(findComponentsItem)); if (componentsItem.contents || componentsItem.buttons) { - handleInputData(componentsItem.contents || componentsItem.buttons, findComponents, number + 1, componentsItem); + handleInputData(componentsItem.contents || componentsItem.buttons, findComponents, number + 1, componentsItem, componentId); } }); return findComponents; @@ -94,7 +94,7 @@ export function useDataView( * @param inputData * @returns */ - function appendFormPageNode(inputData: any[]) { + function appendFormPageNode(inputDatas: any[]) { // 表单顶层节点 const frameNode = { originalId: props.data.module.id, @@ -106,24 +106,40 @@ export function useDataView( rawSchema: props.data.module }; - inputData[0].originalParent = frameNode.originalId; - inputData.unshift(frameNode); - return frameNode; + const appendInputDatas: any[] = []; + inputDatas.forEach((inputData: any) => { + inputData[0].originalParent = frameNode.originalId; + appendInputDatas.push(...inputData); + }); + + appendInputDatas.unshift(frameNode); + + return appendInputDatas; } + /** 获取treeview所需数据 */ function getData() { - let outputDataWithTreeViewFrame; - if (domJsonComponents.value) { - const components: any[] = []; - - components.push(domJsonComponents.value); - const findComponents: any = []; - const number = 1; - const inputData = handleInputData(components, findComponents, number, null); - appendFormPageNode(inputData); - const outputData = getSortedData(inputData); - outputDataWithTreeViewFrame = formTreeViewFrame(outputData); + const domJsonComponents = props.data.module ? ref(props.data.module.components) : ref([props.data]); + if (!domJsonComponents.value) { + return ; } + + const inputDatas: any[] = []; + domJsonComponents.value.forEach((component: any) => { + if (component) { + const components: any[] = []; + components.push(component); + const number = 1; + const findComponents: any = []; + const inputData = handleInputData(components, findComponents, number, null, component.id); + inputDatas.push(inputData); + } + }); + + const appendInputDatas = appendFormPageNode(inputDatas); + const outputData = getSortedData(appendInputDatas); + + const outputDataWithTreeViewFrame = formTreeViewFrame(outputData); return outputDataWithTreeViewFrame; } diff --git a/packages/ui-vue/components/designer-outline/src/composition/use-outline-node.ts b/packages/ui-vue/components/designer-outline/src/composition/use-outline-node.ts index 1204c1088e4d8a9abeb3052654ed7441018ae3fe..bc6c23abcc522e2bfe05b9d36c0fd481839b9e00 100644 --- a/packages/ui-vue/components/designer-outline/src/composition/use-outline-node.ts +++ b/packages/ui-vue/components/designer-outline/src/composition/use-outline-node.ts @@ -109,8 +109,7 @@ export function useOutlineNode(designerHostService: DesignerHostService, context case ComponentType.Frame: { return '根组件'; } - case ComponentType.dataGrid: - case ComponentType.table: { + case ComponentType.dataGrid: { const treeGrid = designerHostService?.formSchemaUtils.selectNode(componentsItem, (item) => item.type === (DgControl['tree-grid'] && DgControl['tree-grid'].type)); if (treeGrid) { return '树表格组件'; @@ -123,21 +122,15 @@ export function useOutlineNode(designerHostService: DesignerHostService, context case ComponentType.listView: { return '列表视图组件'; } - case ComponentType.modalFrame: { - return '弹窗页面'; - } - case ComponentType.appointmentCalendar: { - return '预约日历组件'; + case ComponentType.form: { + return '卡片组件'; } default: { - if (componentType.startsWith('form')) { - return '卡片组件'; - } + return '组件'; } } - - return '组件'; } + function getTitle(componentsItem: any, parentComponentsItem: any) { const text = componentsItem.name || componentsItem.text || componentsItem.label || componentsItem.title || componentsItem.mainTitle; @@ -174,7 +167,7 @@ export function useOutlineNode(designerHostService: DesignerHostService, context if (newSelectedElement) { newSelectedElement.click(); } - context.emit('selectionChanged', newSelection.rawSchema); + context.emit('selectionChanged', newSelection); } function triggerOutsideClick(schemaId: string) { diff --git a/packages/ui-vue/components/designer-outline/src/types.ts b/packages/ui-vue/components/designer-outline/src/types.ts index 4ad3310d602378f38bf85ce58686e0c67e155deb..be7789b776b92d0e63f3bcc110d8a5e4db567303 100644 --- a/packages/ui-vue/components/designer-outline/src/types.ts +++ b/packages/ui-vue/components/designer-outline/src/types.ts @@ -18,25 +18,14 @@ export enum ComponentType { listView = 'list-view', /** - * 卡片类(待优化,目前类型中带有控件列布局信息) + * 卡片类 */ form = 'form', /** * 附件 */ - attachmentPanel = 'attachment-panel', - - /** - * 子表弹出编辑后创建的模态框组件---运行态是动态创建的 - */ - modalFrame = 'modal-frame', - - /** 表格类 */ - table = 'table', - - /** 预约日历 */ - appointmentCalendar = 'appointment-calendar' + attachmentPanel = 'attachment-panel' } /** diff --git a/packages/ui-vue/components/designer-toolbox/src/toolbox.component.tsx b/packages/ui-vue/components/designer-toolbox/src/toolbox.component.tsx index 5fe6c8018746417c701d3e7b22ec8ea2171cd646..807fefdf6f35f314e5b607cb1ab5213ca4340463 100644 --- a/packages/ui-vue/components/designer-toolbox/src/toolbox.component.tsx +++ b/packages/ui-vue/components/designer-toolbox/src/toolbox.component.tsx @@ -2,7 +2,6 @@ import { defineComponent, ref, watch } from 'vue'; import { ToolboxPropsType, toolboxProps } from './toolbox.props'; import { ToolboxCategory, ToolboxItem } from './types'; -import toolboxItems from './toolbox.json'; import './toolbox.css'; export default defineComponent({ @@ -10,7 +9,7 @@ export default defineComponent({ props: toolboxProps, emits: [], setup(props: ToolboxPropsType) { - const controlCategoryList = ref(toolboxItems); + const controlCategoryList = ref(props.toolboxItems); const dragularCompostion = ref(props.dragula); function onClickCardHeader(payload: MouseEvent, category: any) { diff --git a/packages/ui-vue/components/designer-toolbox/src/toolbox.props.ts b/packages/ui-vue/components/designer-toolbox/src/toolbox.props.ts index 2e80179788e28021498827602a9d0df14d605b4e..3ca6647825cfe597282493dc7d569115ca786d74 100644 --- a/packages/ui-vue/components/designer-toolbox/src/toolbox.props.ts +++ b/packages/ui-vue/components/designer-toolbox/src/toolbox.props.ts @@ -1,8 +1,10 @@ import { ExtractPropTypes } from 'vue'; +import { ToolboxCategory } from './types'; export const toolboxProps = { id: { type: String, default: '' }, - dragula: { type: Object } + dragula: { type: Object }, + toolboxItems: { type: Array, default: [] } }; export type ToolboxPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/designer.ts b/packages/ui-vue/components/designer.ts index b8aabcb5d13652f718de7901a55435d27b2a81f5..8e8648dab10a6aadd7ee980240a61b162be910b9 100644 --- a/packages/ui-vue/components/designer.ts +++ b/packages/ui-vue/components/designer.ts @@ -3,11 +3,12 @@ export * from './designer-canvas'; export { FDesignerOutline } from './designer-outline'; export { FDesignerToolbox } from './designer-toolbox'; export { FFlowCanvas } from './flow-canvas'; -export { F_LOOKUP_HTTP_SERVICE_TOKEN, LookupSchemaRepositoryToken } from './lookup'; +export { F_LOOKUP_HTTP_SERVICE_TOKEN, LookupSchemaRepositoryToken, ExternalLookupPropertyConfig ,lookupDataSourceConverter} from './lookup'; export { FPropertyPanel, SchemaDOMMapping, type FormPropertyChangeObject } from './property-panel'; export { FEventParameter } from './event-parameter'; export * from './schema-selector'; export * from './dynamic-resolver'; export * from './field-selector'; export { resolverMap } from './dynamic-view'; +export { ModalProperty } from "./modal"; diff --git a/packages/ui-vue/components/discussion-editor/discussion-editor.component.tsx b/packages/ui-vue/components/discussion-editor/discussion-editor.component.tsx index aeb6a64ce6d60e50a09c01bf88c49c2c4764e9c9..b1af9644d9296f0307091d81b044380356a7cd31 100644 --- a/packages/ui-vue/components/discussion-editor/discussion-editor.component.tsx +++ b/packages/ui-vue/components/discussion-editor/discussion-editor.component.tsx @@ -1,4 +1,4 @@ - + /* eslint-disable no-use-before-define */ /** * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. @@ -19,9 +19,9 @@ import { computed, defineComponent, onMounted, ref, SetupContext } from 'vue'; import { discussionEditorProps, DiscussionEditorProps } from './discussion-editor.props'; import { MsgInfo, editAttachFile } from './src/types/interface'; import FInputGroup from '@farris/ui-vue/components/input-group'; -import { LocaleService } from '../locale/src/lib/locale.service'; import { useDiscussionEditor } from './src/composition/use-discussion-editor'; import './discussion-editor.scss'; +import { LocaleService } from '../locale'; export default defineComponent({ name: 'FDiscussionEditor', @@ -111,7 +111,6 @@ export default defineComponent({ const searchPersonnelList: any = {}; const showSearchList = ref(false); const permissionList = ref(); - let localeService: LocaleService; const groupIcon = ''; const { personSearchUrl, personnelsDisplayKey, personModalVisible, relativeVisible, selectedPersonnels, @@ -125,7 +124,7 @@ export default defineComponent({ ]; permission = permissionList.value[0]; options = { maxUploads: 3, maxFileSize: 10240, allowedContentTypes: ['.jpg', '.pdf'] }; - placeholder = LocaleService.getLocaleValue('discussionGroup.placeholder'); + placeholder = LocaleService.getLocaleValue('discussionGroup.placeholder') as string; } ); diff --git a/packages/ui-vue/components/drawer/src/drawer.component.tsx b/packages/ui-vue/components/drawer/src/drawer.component.tsx index ff64f33d2763e1df335bcbf3d59ecce5000dcb5f..cb67d45cde09474e707478399fd38fe869b0a745 100644 --- a/packages/ui-vue/components/drawer/src/drawer.component.tsx +++ b/packages/ui-vue/components/drawer/src/drawer.component.tsx @@ -19,13 +19,14 @@ import { computed, defineComponent, ref, SetupContext, Teleport, Transition, wat import FButton from '@farris/ui-vue/components/button'; import { DrawerProps, drawerProps } from "./drawer.props"; import './drawer.css'; +import { useDrawerLocale } from "./locale/locale"; export default defineComponent({ name: 'FDrawer', props: drawerProps, emits: ['afterClose', 'update:modelValue'] as (string[] & ThisType) | undefined, setup(props: DrawerProps, context: SetupContext) { - + const { drawerLocale } = useDrawerLocale() const modelValue = ref(props.modelValue); // const host = computed(() => { @@ -37,7 +38,7 @@ export default defineComponent({ // }); const drawerContainerClass = computed(() => { const showNotInBody = typeof props.host === 'string' ? props.host !== 'body' : - document.querySelector(props.host) !== document.body; + document.querySelector(props.host) !== document.body; return { 'f-drawer': true, // 在某个DOM内部打开抽屉 @@ -127,41 +128,41 @@ export default defineComponent({ }, ['stop'])}>
} - -
-
-
-
- {context.slots.headerTemplate ? context.slots.headerTemplate() : props.title} -
- {props.showClose && -
- { - onClose(e as MouseEvent); - }, ['stop'])}> -
- } -
-
- {context.slots.content?.()} -
- diff --git a/packages/ui-vue/components/drawer/src/locale/locale.ts b/packages/ui-vue/components/drawer/src/locale/locale.ts new file mode 100644 index 0000000000000000000000000000000000000000..9539f2683fef36fa3fca0dd78fd531cae6c89e5b --- /dev/null +++ b/packages/ui-vue/components/drawer/src/locale/locale.ts @@ -0,0 +1,13 @@ +import { LocaleService } from '../../../locale'; +/** + * 此处当做静态处理 + * @param props + * @returns + */ +export function useDrawerLocale() { + const drawerLocale = { + cancel: LocaleService.getLocaleValue('drawer.cancel'), + confirm: LocaleService.getLocaleValue('drawer.confirm') + }; + return { drawerLocale }; +} diff --git a/packages/ui-vue/components/drawer/src/locales/designer/en.json b/packages/ui-vue/components/drawer/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..843a37826aeab8277aa34cef510f54d879ffb2aa --- /dev/null +++ b/packages/ui-vue/components/drawer/src/locales/designer/en.json @@ -0,0 +1,3 @@ +{ + "drawer": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/drawer/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/drawer/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..843a37826aeab8277aa34cef510f54d879ffb2aa --- /dev/null +++ b/packages/ui-vue/components/drawer/src/locales/designer/zh-CHS.json @@ -0,0 +1,3 @@ +{ + "drawer": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/drawer/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/drawer/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..843a37826aeab8277aa34cef510f54d879ffb2aa --- /dev/null +++ b/packages/ui-vue/components/drawer/src/locales/designer/zh-CHT.json @@ -0,0 +1,3 @@ +{ + "drawer": {} +} \ No newline at end of file diff --git a/packages/ui-vue/components/drawer/src/locales/ui/en.json b/packages/ui-vue/components/drawer/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..0df615ad2ca134435865955553ce57053179760b --- /dev/null +++ b/packages/ui-vue/components/drawer/src/locales/ui/en.json @@ -0,0 +1,6 @@ +{ + "drawer": { + "cancel": "Cancel", + "confirm": "Confirm" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/drawer/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/drawer/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..6c05f8d49c5db8965943a068c66a8cd50d4f628d --- /dev/null +++ b/packages/ui-vue/components/drawer/src/locales/ui/zh-CHS.json @@ -0,0 +1,6 @@ +{ + "drawer": { + "cancel": "取消", + "confirm": "确定" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/drawer/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/drawer/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..47a14a3d0ab1d238cc2d3bb2ead9224c374756cb --- /dev/null +++ b/packages/ui-vue/components/drawer/src/locales/ui/zh-CHT.json @@ -0,0 +1,6 @@ +{ + "drawer": { + "cancel": "取消", + "confirm": "確定" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx index 9a9982d429778aa82b2428bbba5a219a216ca30d..4e7d2d2e61981161546a745b72e2fef6fe488312 100644 --- a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx +++ b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.component.tsx @@ -22,6 +22,16 @@ export default defineComponent({ const type = ref(props.type); const editorRef = ref(); const errors = ref(props.errors); + /** + * showLabel属性和showLabelType属性,应该只会修改其中一个属性 + * 优先识别showLable属性的修改 + */ + const realShowLabelType = computed(() => { + if (!showLabel.value) { + return 'none'; + } + return props.showLabelType; + }); const { resolveEditorProps, resolveEditorType, getChangeFunctionName, getClearFunctionName } = useTypeResolver(); @@ -38,7 +48,7 @@ export default defineComponent({ function onChange(newValue: any, newModelValue: any, parameters: any) { modelValue.value = newModelValue !== undefined ? newModelValue : newValue; context.emit('update:modelValue', modelValue.value); - context.emit('change', modelValue.value, { newValue, newModelValue, parameters}); + context.emit('change', modelValue.value, { newValue, newModelValue, parameters }); } function onClear() { @@ -67,6 +77,9 @@ export default defineComponent({ } } else if (editorType === 'lookup' && editor.value['onUpdate:idValue'] && typeof editor.value['onUpdate:idValue'] === 'function') { editorProps['onUpdate:idValue'] = editor.value['onUpdate:idValue']; + editorProps.id = id.value; + } else if (editorType === 'collection-property-editor' && editor.value['onSelectionChange'] && typeof editor.value['onSelectionChange'] === 'function') { + editorProps['onSelectionChange'] = editor.value['onSelectionChange']; } else if (editorType === 'property-editor') { editorProps['onBeforeOpenVariables'] = editor.value['onBeforeOpenVariables']; } @@ -115,9 +128,7 @@ export default defineComponent({
- {showLabel.value && ( - - )} +
{renderConditionEditor.value()} {renderValidationMessage()} diff --git a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.design.component.tsx b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.design.component.tsx index 80cdec0c46b3484eed2d32820e3d11abd063d8f4..b3fb46a810df4ab62dd69817fbbe1a18b01ea9cb 100644 --- a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.design.component.tsx +++ b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.design.component.tsx @@ -23,7 +23,16 @@ export default defineComponent({ const editorRef = ref(); const designerHostService = inject('designer-host-service') as DesignerHostService; const designItemContext = inject('design-item-context') as DesignerItemContext; - + /** + * showLabel属性和showLabelType属性,应该只会修改其中一个属性 + * 优先识别showLable属性的修改 + */ + const realShowLabelType = computed(() => { + if (!showLabel.value) { + return 'none'; + } + return props.showLabelType; + }); const { resolveEditorProps, resolveEditorType } = useTypeResolverDesign(); const conditionItemClass = computed(() => { const classObject = { @@ -63,12 +72,15 @@ export default defineComponent({ showLabel.value = newShowLabel; }, { deep: true } ); + // 外部容器引入的表单,不启用校验 + const externalContainerId = inject('external-container-id'); + const enableValidation = !externalContainerId; const { checkBindingFieldValidation } = useFormBindingResolverDesign(designerHostService, designItemContext, props); function renderLabel() { - const isValidBinding = checkBindingFieldValidation(); + const isValidBinding = enableValidation ? checkBindingFieldValidation() : true; const inValidTip = '绑定信息已失效,请切换绑定或移除控件'; - return ; + return ; } context.expose({ editorRef }); @@ -78,7 +90,7 @@ export default defineComponent({
- {showLabel.value && renderLabel()} + {renderLabel()}
{renderConditionEditor.value()}
diff --git a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.props.ts b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.props.ts index 66860ea7b2ca50d3a425acd4c9f8156731c49232..387c16ec9dfede082242967a86bfdd03d520609f 100644 --- a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.props.ts +++ b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-group/dynamic-form-group.props.ts @@ -5,6 +5,10 @@ import { schemaMapper } from '../../schema/schema-mapper'; import formGroupSchema from '../../schema/form-group.schema.json'; import { createFormGroupEditorResolver } from '../../../../dynamic-resolver/src/editor-resolver'; import { FormValidationInfo } from '../validation-message/validation-message.props'; +/** + * 显示且占位visible、占位不显示reserve-space、不占位不显示none + */ +export type FormGroupLabelType = 'visible' | 'reserve-space' | 'none'; export const dynamicFormGroupProps = { id: { type: String, default: '' }, @@ -19,6 +23,10 @@ export const dynamicFormGroupProps = { visible: { type: Boolean, default: true }, required: { type: Boolean, default: false }, showLabel: { type: Boolean, default: true }, + /** + * visible相当于showLabel为true,none相当于showLabel为false + */ + showLabelType:{type:String as PropType,default:'visible'}, type: { type: String as PropType, default: 'input-group' }, componentId: { type: String, default: '' }, errors: { type: Object as PropType, default: null }, diff --git a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-label/dynamic-form-label.component.tsx b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-label/dynamic-form-label.component.tsx index 99a5dcc6a657d674e3fa4d04ea3e41b17b4d1e42..a1e1858db73b470d99ab276e3daf66694579b346 100644 --- a/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-label/dynamic-form-label.component.tsx +++ b/packages/ui-vue/components/dynamic-form/src/component/dynamic-form-label/dynamic-form-label.component.tsx @@ -32,16 +32,26 @@ export default defineComponent({ const labelColor = computed(() => { return valid.value ? '' : 'color:red'; }); - + const labelClass = computed(() => { + return { + 'col-form-label': true, + // 指定必填,当显示模式是隐藏或者没有指定标签名时,宽度不限定 + 'f-width-auto': required.value && (props.showType === 'none' || !text.value) + }; + }); + const showLabel=computed(()=>{ + return props.showType === 'reserve-space' || required.value || text.value && props.showType !== 'none'; + }); + // 显示模式是保留空间或者指定了必填或者text.value有值 return () => { return ( - text.value &&
; }; } }); diff --git a/packages/ui-vue/components/dynamic-form/src/response-form.props.ts b/packages/ui-vue/components/dynamic-form/src/response-form.props.ts index c376d9e5a05782f115d55aee478ff37d3ad8e0fb..afa7f032be29d77fcd32a6f5b316c5732f35f60d 100644 --- a/packages/ui-vue/components/dynamic-form/src/response-form.props.ts +++ b/packages/ui-vue/components/dynamic-form/src/response-form.props.ts @@ -8,7 +8,9 @@ export const responseFormProps = { customClass: { type: String, default: '' }, customStyle: { type: String, defaut: '' }, /** 控制是否可见 */ - visible: { type: Boolean, default: true } + visible: { type: Boolean, default: true }, + /** 内部控件布局是否响应国际化 */ + adaptForLanguage:{ type: Boolean, default: true } } as Record; export type ResponseFormPropsType = ExtractPropTypes; diff --git a/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json b/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json index 73ebf8b5d14502226937772f47d5f60371a521fe..7583ec2dbfa816d215948b275bd510b0b876016d 100644 --- a/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json +++ b/packages/ui-vue/components/dynamic-form/src/schema/form-group.schema.json @@ -65,6 +65,16 @@ "description": "", "type": "object", "default": null + }, + "name": { + "description": "The name string of form group component", + "type": "string", + "default": "form group" + }, + "showLabelType":{ + "description": "", + "type": "string", + "default": "visible" } }, "required": [ diff --git a/packages/ui-vue/components/dynamic-form/src/schema/response-form.schema.json b/packages/ui-vue/components/dynamic-form/src/schema/response-form.schema.json index 3046f3bd872429661180a44ceac708de619e6c35..bdf40ec621ffbd0c6bea666a032b129f38eb2c4d 100644 --- a/packages/ui-vue/components/dynamic-form/src/schema/response-form.schema.json +++ b/packages/ui-vue/components/dynamic-form/src/schema/response-form.schema.json @@ -41,6 +41,11 @@ "description": "", "type": "boolean", "default": false + }, + "adaptForLanguage": { + "description": "", + "type": "boolean", + "default": true } }, "required": [ diff --git a/packages/ui-vue/components/dynamic-form/src/types.ts b/packages/ui-vue/components/dynamic-form/src/types.ts index 2b73430a7b77a1d3e7c344bcb4d60388ba21acde..b82cec43461d0b465579f7cf85d50f0b55927d94 100644 --- a/packages/ui-vue/components/dynamic-form/src/types.ts +++ b/packages/ui-vue/components/dynamic-form/src/types.ts @@ -4,7 +4,7 @@ export type EditorType = 'button-edit' | 'check-box' | 'check-group' | 'combo-li 'response-layout-editor-setting' | 'switch' | 'grid-field-editor' | 'field-selector' | 'schema-selector' | 'mapping-editor' | 'textarea' | 'response-form-layout-setting' | 'binding-selector' | 'query-solution-config' | 'solution-preset' | 'item-collection-editor' | 'menu-lookup' | 'response-layout-splitter' | 'json-editor' | 'property-editor' | 'sort-condition-editor' | - 'filter-condition-editor' | 'expression-editor' | 'code-editor'; + 'filter-condition-editor' | 'expression-editor' | 'code-editor' | 'collection-property-editor' | 'language-textbox'; export interface EditorConfig { /** 编辑器类型 */ diff --git a/packages/ui-vue/components/dynamic-resolver/index.ts b/packages/ui-vue/components/dynamic-resolver/index.ts index 2500f1c640b379624595da03ef41d4e872cc7251..975ca9f81442792f418ace3100fdbbde8a43002b 100644 --- a/packages/ui-vue/components/dynamic-resolver/index.ts +++ b/packages/ui-vue/components/dynamic-resolver/index.ts @@ -8,6 +8,9 @@ export * from './src/selection-item-resolver'; export * from './src/editor-resolver'; export * from './src/visible-prop-resolver'; export * from './src/event-handler-resolver'; -export * from './src/schema-resolver'; +export * from './src/resolver/schema/schema-resolver'; +export * from './src/resolver/schema/schema-resolver-design'; export * from './src/update-columns-resolver'; -export { propertyConfigSchemaMap } from './src/property-config-resolver'; +export { propertyConfigSchemaMap, propertyEffectMap } from './src/resolver/property-config/property-config-resolver'; +export { propertyConfigSchemaMapForDesigner, propertyEffectMapForDesigner } from './src/resolver/property-config/property-config-resolver-design'; + diff --git a/packages/ui-vue/components/dynamic-resolver/src/binding-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/binding-resolver.ts index a006ae3408a801d16528ee1743c9651183458dd6..1c1c7525f8cab0bf0cb6248073d337734fed67d7 100644 --- a/packages/ui-vue/components/dynamic-resolver/src/binding-resolver.ts +++ b/packages/ui-vue/components/dynamic-resolver/src/binding-resolver.ts @@ -4,7 +4,10 @@ export function createFormBindingResolver(): BindingResolver { function resolve(schema: Record, bindingData: BindingData) { const { id } = schema || {}; // 支持空绑定 - if (!schema.binding || Object.keys(schema.binding).length < 1) { + if (schema.binding === undefined) { + return {}; + } + if (schema.binding && Object.keys(schema.binding).length < 1) { return {}; } const { field } = schema.binding || {}; diff --git a/packages/ui-vue/components/dynamic-resolver/src/converter/change-formatter-enum.converter.ts b/packages/ui-vue/components/dynamic-resolver/src/converter/change-formatter-enum.converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..58405f9532e7e910d4fe100abb586a7da9871255 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/converter/change-formatter-enum.converter.ts @@ -0,0 +1,11 @@ +import { ComponentSchema } from "../../../designer-canvas/src/types"; +import { PropertyConverter, SchemaService } from "../types"; + +export default { + convertFrom: (schema: ComponentSchema, propertyKey: string, schemaService: SchemaService) => { + if (schema.formatter?.data && propertyKey === 'formatterEnumData' && !schema.formatterEnumData) { + return schema.formatter?.data; + } + return schema.formatterEnumData; + } +} as PropertyConverter; diff --git a/packages/ui-vue/components/dynamic-resolver/src/converter/change-formatter.converter.ts b/packages/ui-vue/components/dynamic-resolver/src/converter/change-formatter.converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..c80a7099350e89c3c28e468f818106a8677f8e05 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/converter/change-formatter.converter.ts @@ -0,0 +1,55 @@ +import { ComponentSchema } from "../../../designer-canvas/src/types"; +import { PropertyConverter, SchemaService } from "../types"; + +export default { + convertTo: (schema: ComponentSchema, propertyKey: string, propertyValue: any, schemaService: SchemaService) => { + // eslint-disable-next-line no-self-assign + if (schema.type === 'data-grid-column') { + if (schema.formatter) { + schema.formatter[propertyKey] = propertyValue; + } else { + schema.formatter = { + [propertyKey]: propertyValue + }; + } + } + }, + convertFrom: (schema: ComponentSchema, propertyKey: string, schemaService: SchemaService) => { + if (schema.formatter) { + if (propertyKey === 'trueText') { + return schema.formatter.trueText; + } + if (propertyKey === 'falseText') { + return schema.formatter.falseText; + } + if (propertyKey === 'prefix') { + return schema.formatter.prefix; + } + if (propertyKey === 'suffix') { + return schema.formatter.suffix; + } + if (propertyKey === 'precision') { + return schema.formatter.precision; + } + if (propertyKey === 'decimal') { + return schema.formatter.decimal; + } + if (propertyKey === 'thousand') { + return schema.formatter.thousand; + } + if (propertyKey === 'dateFormat') { + return schema.formatter.dateFormat; + } + if (propertyKey === 'customFormat') { + return schema.formatter.customFormat; + } + } + if (schema.formatter.type) { + return schemaService.getRealEditorType(schema.formatter.type); + } + if (schema.formatter.data) { + return schemaService.getRealEditorType(schema.formatter.data); + } + return ''; + } +} as PropertyConverter; diff --git a/packages/ui-vue/components/dynamic-resolver/src/converter/column-command.converter.ts b/packages/ui-vue/components/dynamic-resolver/src/converter/column-command.converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..a796743b58b2ea13a62bb3825bd62be737d1c3a2 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/converter/column-command.converter.ts @@ -0,0 +1,40 @@ +import { ComponentSchema } from "../../../designer-canvas/src/types"; +import { PropertyConverter, SchemaService } from "../types"; + +export default { + convertTo: (schema: ComponentSchema, propertyKey: string, propertyValue: any, schemaService: SchemaService) => { + // eslint-disable-next-line no-self-assign + + if (schema.command) { + schema.command[propertyKey] = propertyValue; + } else { + schema.command = { + [propertyKey]: propertyValue + }; + } + if (propertyKey === 'enable' && propertyValue) { + if (!schema.command.commands) { + schema.command.commands = [ + { + "text": "编辑", + "type": "primary", + "command": "edit" + }, + { + "text": "删除", + "type": "danger", + "command": "remove" + } + ]; + } + } + }, + convertFrom: (schema: ComponentSchema, propertyKey: string, schemaService: SchemaService) => { + if (schema.command) { + if (propertyKey === 'enable') { + return schema.command.enable; + } + } + return ''; + } +} as PropertyConverter; diff --git a/packages/ui-vue/components/dynamic-resolver/src/converter/column-option.converter.ts b/packages/ui-vue/components/dynamic-resolver/src/converter/column-option.converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..3345120b5b5c082d253c8bbf3cba4e72785a1b4a --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/converter/column-option.converter.ts @@ -0,0 +1,31 @@ +import { ComponentSchema } from "../../../designer-canvas/src/types"; +import { PropertyConverter, SchemaService } from "../types"; + +export default { + convertTo: (schema: ComponentSchema, propertyKey: string, propertyValue: any, schemaService: SchemaService) => { + // eslint-disable-next-line no-self-assign + if (schema.column) { + schema.column[propertyKey] = propertyValue; + } else { + schema.column = { + [propertyKey]: propertyValue + }; + } + if (propertyKey === 'fitColumns' && propertyValue) { + if (!schema.column.fitMode) { + schema.column.fitMode = 'average'; + } + } + }, + convertFrom: (schema: ComponentSchema, propertyKey: string, schemaService: SchemaService) => { + if (schema.column) { + if (propertyKey === 'fitColumns') { + return schema.column.fitColumns; + } + if(propertyKey === 'fitMode') { + return schema.column.fitMode; + } + } + return ''; + } +} as PropertyConverter; diff --git a/packages/ui-vue/components/dynamic-resolver/src/converter/size.converter.ts b/packages/ui-vue/components/dynamic-resolver/src/converter/size.converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..dda81e3bc2ca7133d3d19c88fecb61f73b3b78c7 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/converter/size.converter.ts @@ -0,0 +1,14 @@ +import { ComponentSchema } from "../../../designer-canvas/src/types"; +import { PropertyConverter, SchemaService } from "../types"; + +export default { + convertTo: (schema: ComponentSchema, propertyKey: string, propertyValue: any, schemaService: SchemaService) => { + if (!schema.size) { + schema.size = {}; + } + schema.size[propertyKey] = propertyValue; + }, + convertFrom: (schema: ComponentSchema, propertyKey: string, schemaService: SchemaService) => { + return schema.size ? schema.size[propertyKey] : schema[propertyKey]; + } +} as PropertyConverter; diff --git a/packages/ui-vue/components/dynamic-resolver/src/converter/summary.converter.ts b/packages/ui-vue/components/dynamic-resolver/src/converter/summary.converter.ts new file mode 100644 index 0000000000000000000000000000000000000000..691ca95677415520e52cdf5324541d878c38ba36 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/converter/summary.converter.ts @@ -0,0 +1,42 @@ +import { ComponentSchema } from "../../../designer-canvas/src/types"; +import { PropertyConverter, SchemaService } from "../types"; + +export default { + convertTo: (schema: ComponentSchema, propertyKey: string, propertyValue: any, schemaService: SchemaService) => { + // eslint-disable-next-line no-self-assign + if (schema.summary) { + schema.summary[propertyKey] = propertyValue; + } else { + schema.summary = { + [propertyKey]: propertyValue + }; + } + if (propertyKey === 'enable' && propertyValue) { + // 启用合计行 + if (!schema.summary) { + schema.summary = { + enable: propertyValue, + groupFields: [] + }; + } else { + if(!schema.summary.groupFields) { + schema.summary.groupFields = []; + } + } + } + }, + convertFrom: (schema: ComponentSchema, propertyKey: string, schemaService: SchemaService) => { + if (schema.summary) { + if (propertyKey === 'enable') { + return schema.summary.enable; + } + } + if (schema.type === 'data-grid-column') { + if (schema.enableSummary === undefined) { + return false; + } + return schema.enableSummary; + } + return ''; + } +} as PropertyConverter; diff --git a/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts index 5ad10da4e6f8d3f93feb7e1d8df702ff31645cde..35b09bb6951bb980a8fbcecc42f5060024df3aab 100644 --- a/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts +++ b/packages/ui-vue/components/dynamic-resolver/src/property-config-resolver.ts @@ -3,12 +3,16 @@ import { EffectFunction, PropertyConverter, SchemaService } from './types'; import { EditorConfig } from "../../dynamic-form"; import { useObjectExpression } from './object-expression'; import { ComponentSchema } from "../../designer-canvas/src/types"; -import { resolveSchemaWithDefaultValue } from "./schema-resolver"; +import { resolveSchemaWithDefaultValue } from "./resolver/schema/schema-resolver"; import appearanceConverter from './converter/appearance.converter'; import buttonsConverter from "./converter/buttons.converter"; import propertyEditorConverter from "./converter/property-editor.converter"; import typeConverter from "./converter/type.converter"; import changeEditorConverter from "./converter/change-editor.converter"; +import changeFormatterConverter from './converter/change-formatter.converter'; +import columnCommandConverter from './converter/column-command.converter'; +import columnOptionConverter from './converter/column-option.converter'; +import SummaryConverter from './converter/summary.converter'; import fieldSelectorConverter from "./converter/field-selector.converter"; import paginationConverter from "./converter/pagination.converter"; import rowNumberConverter from "./converter/row-number.converter"; @@ -16,6 +20,7 @@ import gridSelectionConverter from "./converter/grid-selection.converter"; import { ElementPropertyConfig, PropertyEntity } from "../../property-panel/src/composition/entity/property-entity"; import itemsCountConverter from "./converter/items-count.converter"; import formGroupLabelConverter from "./converter/form-group-label.converter"; +import changeFormatterEnumConverter from './converter/change-formatter-enum.converter'; const propertyConfigSchemaMap = {} as Record; const propertyConverterMap = new Map([ @@ -25,11 +30,16 @@ const propertyConverterMap = new Map([ ['/converter/items-count.converter', itemsCountConverter], ['/converter/type.converter', typeConverter], ['/converter/change-editor.converter', changeEditorConverter], + ['/converter/change-formatter.converter', changeFormatterConverter], + ['/converter/column-command.converter', columnCommandConverter], + ['/converter/column-option.converter', columnOptionConverter], + ['/converter/summary.converter', SummaryConverter], ['/converter/form-group-label.converter', formGroupLabelConverter], ['/converter/field-selector.converter', fieldSelectorConverter], ['/converter/pagination.converter', paginationConverter], ['/converter/row-number.converter', rowNumberConverter], - ['/converter/grid-selection.converter', gridSelectionConverter] + ['/converter/grid-selection.converter', gridSelectionConverter], + ['/converter/change-formatter-enum.converter', changeFormatterEnumConverter] ]); const propertyEffectMap = {} as Record; const propertyEditorMap = new Map([ @@ -147,7 +157,12 @@ function getPropertyEntities( } // 获取属性时,如果没有convertForm,并且通过在Schema上获取得值是空,那就获取defaultValue属性值或者是空 const editingSchemaValue = editingSchema[propertyKey]; - return Object.prototype.hasOwnProperty.call(propertySchema, 'defaultValue') && (editingSchemaValue === undefined || typeof editingSchemaValue == 'string' && editingSchemaValue === '') ? propertySchema['defaultValue'] : editingSchemaValue; + if(Object.prototype.hasOwnProperty.call(propertySchema, 'defaultValue') && (editingSchemaValue === undefined || typeof editingSchemaValue == 'string' && editingSchemaValue === '')){ + return propertySchema['type']==='boolean'? propertySchema['defaultValue']:propertySchema['defaultValue']||''; + }else{ + return editingSchemaValue; + } + } return null; }, diff --git a/packages/ui-vue/components/dynamic-resolver/src/props-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/props-resolver.ts index 8884abfc4391baa04395f2139a9e621e86fe3f4f..4149639d269f7eaa860917a848a9fdd2dfe981f6 100644 --- a/packages/ui-vue/components/dynamic-resolver/src/props-resolver.ts +++ b/packages/ui-vue/components/dynamic-resolver/src/props-resolver.ts @@ -1,7 +1,9 @@ import { DesignerHostService } from './../../designer-canvas/src/composition/types'; -import { mappingSchemaToProps, resolveSchemaToProps, schemaMap, schemaResolverMap } from './schema-resolver'; +import { mappingSchemaToProps, resolveSchemaToProps, schemaMap, schemaResolverMap } from './resolver/schema/schema-resolver'; import { DynamicResolver, EffectFunction, MapperFunction, SchemaResolverFunction } from './types'; -import { propertyConfigSchemaMap, propertyEffectMap } from './property-config-resolver'; +import { propertyConfigSchemaMap, propertyEffectMap } from './resolver/property-config/property-config-resolver'; +import { schemaMapForDesigner, schemaResolverMapForDesigner } from './resolver/schema/schema-resolver-design'; +import { propertyConfigSchemaMapForDesigner as propertyConfigSchemaMapForDesigner, propertyEffectMapForDesigner } from './resolver/property-config/property-config-resolver-design'; export function createPropsResolver>( componentPropsObject: T, @@ -20,6 +22,12 @@ export function createPropsResolver>( schemaResolverMap[defaultSchema.title] = schemaResolver; propertyConfigSchemaMap[defaultSchema.title] = propertyConfig; propertyEffectMap[defaultSchema.title] = propertyEffect; + + schemaMapForDesigner[defaultSchema.title] = defaultSchema; + schemaResolverMapForDesigner[defaultSchema.title] = schemaResolver; + propertyConfigSchemaMapForDesigner[defaultSchema.title] = propertyConfig; + propertyEffectMapForDesigner[defaultSchema.title] = propertyEffect; + return (schemaValue: Record = {}, mergeDefaults: boolean = true) => { if (!mergeDefaults) { return mappingSchemaToProps(schemaValue, schemaMapper); diff --git a/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/property-config-resolver-design.ts b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/property-config-resolver-design.ts new file mode 100644 index 0000000000000000000000000000000000000000..85449d9b22f613b7d25af71dca312bd839ed5fca --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/property-config-resolver-design.ts @@ -0,0 +1,14 @@ +import { EffectFunction } from '../../types'; +import { usePropertyConfigResolver } from './use-property-config-resolver'; +import { resolveSchemaWithDefaultValueForDesigner } from "../schema/schema-resolver-design"; + +const propertyConfigSchemaMapForDesigner = {} as Record; +const propertyEffectMapForDesigner = {} as Record; + +const { getPropertyConfigBySchema, getPropertyConfigByType, propertyConverterMap } = + usePropertyConfigResolver(propertyConfigSchemaMapForDesigner, propertyEffectMapForDesigner, resolveSchemaWithDefaultValueForDesigner); + +export { + getPropertyConfigBySchema as getPropertyConfigBySchemaForDesigner, getPropertyConfigByType as getPropertyConfigByTypeForDesigner, + propertyConfigSchemaMapForDesigner, propertyConverterMap as propertyConverterMapForDesigner, propertyEffectMapForDesigner +}; diff --git a/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/property-config-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/property-config-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..728806ffbf21bb4593bb749e652f50fa251d64d4 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/property-config-resolver.ts @@ -0,0 +1,11 @@ +import { EffectFunction } from '../../types'; +import { usePropertyConfigResolver } from './use-property-config-resolver'; +import { resolveSchemaWithDefaultValue } from "../schema/schema-resolver"; + +const propertyConfigSchemaMap = {} as Record; +const propertyEffectMap = {} as Record; + +const { getPropertyConfigBySchema, getPropertyConfigByType, propertyConverterMap } = + usePropertyConfigResolver(propertyConfigSchemaMap, propertyEffectMap, resolveSchemaWithDefaultValue); + +export { getPropertyConfigBySchema, getPropertyConfigByType, propertyConfigSchemaMap, propertyConverterMap, propertyEffectMap }; diff --git a/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/use-property-config-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/use-property-config-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..92c81d4486ce50c05e1e3f3dbf19efbc61ea94db --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/resolver/property-config/use-property-config-resolver.ts @@ -0,0 +1,265 @@ +import { computed, inject, ref } from "vue"; +import { EffectFunction, PropertyConverter, SchemaService } from '../../types'; +import { EditorConfig } from "../../../../dynamic-form"; +import { useObjectExpression } from '../../object-expression'; +import { ComponentSchema } from "../../../../designer-canvas/src/types"; +import appearanceConverter from '../../converter/appearance.converter'; +import buttonsConverter from "../../converter/buttons.converter"; +import propertyEditorConverter from "../../converter/property-editor.converter"; +import typeConverter from "../../converter/type.converter"; +import changeEditorConverter from "../../converter/change-editor.converter"; +import changeFormatterConverter from '../../converter/change-formatter.converter'; +import columnCommandConverter from '../../converter/column-command.converter'; +import columnOptionConverter from '../../converter/column-option.converter'; +import SummaryConverter from '../../converter/summary.converter'; +import fieldSelectorConverter from "../../converter/field-selector.converter"; +import paginationConverter from "../../converter/pagination.converter"; +import rowNumberConverter from "../../converter/row-number.converter"; +import gridSelectionConverter from "../../converter/grid-selection.converter"; +import { ElementPropertyConfig, PropertyEntity } from "../../../../property-panel/src/composition/entity/property-entity"; +import itemsCountConverter from "../../converter/items-count.converter"; +import formGroupLabelConverter from "../../converter/form-group-label.converter"; +import sizeConverter from "../../converter/size.converter"; +import changeFormatterEnumConverter from '../../converter/change-formatter-enum.converter'; + +export function usePropertyConfigResolver(propertyConfigSchemaMap: Record, propertyEffectMap: Record, + resolveSchemaWithDefaultValue: (schemaValue: Record) => Record) { + const propertyConverterMap = new Map([ + ['/converter/appearance.converter', appearanceConverter], + ['/converter/buttons.converter', buttonsConverter], + ['/converter/property-editor.converter', propertyEditorConverter], + ['/converter/items-count.converter', itemsCountConverter], + ['/converter/type.converter', typeConverter], + ['/converter/change-editor.converter', changeEditorConverter], + ['/converter/change-formatter.converter', changeFormatterConverter], + ['/converter/column-command.converter', columnCommandConverter], + ['/converter/column-option.converter', columnOptionConverter], + ['/converter/summary.converter', SummaryConverter], + ['/converter/form-group-label.converter', formGroupLabelConverter], + ['/converter/field-selector.converter', fieldSelectorConverter], + ['/converter/pagination.converter', paginationConverter], + ['/converter/row-number.converter', rowNumberConverter], + ['/converter/grid-selection.converter', gridSelectionConverter], + ['/converter/size.converter', sizeConverter], + ['/converter/change-formatter-enum.converter', changeFormatterEnumConverter] + ]); + const propertyEditorMap = new Map([ + ['string', { type: 'input-group', enableClear: false }], + ['boolean', { + "type": "combo-list", + "textField": "name", + "valueField": "value", + "idField": "value", + "enableClear": false, + "editable": false, + "data": [ + { + "value": true, + "name": "是" + }, + { + "value": false, + "name": "否" + } + ] + }], + ['enum', { "type": "combo-list", "maxHeight": 128, "enableClear": false, "editable": false }], + ['array', { "type": "button-edit" }], + ['number', { "type": "number-spinner", "placeholder": "" }], + ['events-editor', { "type": "events-editor", hide: true }] + ]); + + const useObjectExpressionComponstion = useObjectExpression(); + + function generateBooleanValue(pvalueSchema: Record, propertyConfigMap: Record) { + return () => useObjectExpressionComponstion.parseValueSchema(pvalueSchema, propertyConfigMap); + } + + function isVisible(propertySchemaKeys: string[], propertySchema: Record, propertyConfigMap: Record) + : boolean | (() => boolean) { + if (propertySchemaKeys.includes('visible') && propertySchema.visible !== undefined) { + return typeof propertySchema.visible === 'boolean' ? + () => Boolean(propertySchema.visible) : + propertySchema.visible === undefined ? true : generateBooleanValue(propertySchema.visible, propertyConfigMap); + } + return () => true; + } + + function isReadonly(propertySchemaKeys: string[], propertySchema: Record, propertyConfigMap: Record) { + if (propertySchemaKeys.includes('readonly') && propertySchema.readonly !== undefined) { + return typeof propertySchema.readonly === 'boolean' ? + () => Boolean(propertySchema.readonly) : + generateBooleanValue(propertySchema.readonly, propertyConfigMap); + } + return () => false; + } + + function tryGetPropertyConverter(propertySchema: Record, categoryConverter): PropertyConverter | null { + const $converter = propertySchema['$converter'] || categoryConverter; + if (typeof $converter === 'string') { + if ($converter && propertyConverterMap.has($converter)) { + return propertyConverterMap.get($converter) || null; + } + } + return $converter || null; + } + + /** + * + * @param propertiesInCategory + * 举例: + * visible: { + description: "运行时组件是否可见", + title: "是否可见", + type: "boolean" + } + 其中type属性 这个属性用来控制编辑器是哪一种,对应关系在propertyEditorMap中定义,boolean指定了下拉 + * @param propertyConfigMap + * @param editingSchema + * @param rawSchema + * @param schemaService + * @returns + */ + function getPropertyEntities( + propertiesInCategory: Record, + propertyConfigMap: Record, + editingSchema: ComponentSchema, + rawSchema: ComponentSchema, + schemaService: SchemaService, + componentId = '', + categoryConverter: any = '' + ): PropertyEntity[] { + const propertyEntities = Object.keys(propertiesInCategory).map((propertyKey: string) => { + const updateCount = ref(1); + const propertyID = propertyKey; + const propertySchema = propertiesInCategory[propertyKey]; + const propertySchemaKeys = Object.keys(propertySchema); + const propertyName = propertySchema.title; + const propertyType = propertySchema.type; + const defaultEditor = propertyEditorMap.get(propertyType) || { type: 'input-group', enableClear: false }; + const editor = propertySchema.editor ? Object.assign({}, defaultEditor, propertySchema.editor) as EditorConfig : Object.assign({}, defaultEditor); + const visible = isVisible(propertySchemaKeys, propertySchema, propertyConfigMap); + const readonly = isReadonly(propertySchemaKeys, propertySchema, propertyConfigMap); + editor.readonly = editor.readonly === undefined ? readonly() : editor.readonly; + const cascadeConfig = propertySchema.type === 'cascade' ? getPropertyEntities(propertySchema.properties, propertyConfigMap, editingSchema, rawSchema, schemaService, componentId, categoryConverter) : []; + const hideCascadeTitle = true; + let converter = tryGetPropertyConverter(propertySchema, categoryConverter); + // const propertyValue = ref(converter ? converter.convertFrom(schema, propertyKey) : schema[propertyKey]); + const propertyValue = computed({ + get() { + if (updateCount.value) { + // class、style 统一处理 + if (['class', 'style'].find(id => id === propertyID) && !converter) { + converter = propertyConverterMap.get('/converter/appearance.converter') || null; + } + if (converter && converter.convertFrom) { + return converter.convertFrom(editingSchema, propertyKey, schemaService, componentId); + } + // 获取属性时,如果没有convertForm,并且通过在Schema上获取得值是空,那就获取defaultValue属性值或者是空 + const editingSchemaValue = editingSchema[propertyKey]; + if(Object.prototype.hasOwnProperty.call(propertySchema, 'defaultValue') && (editingSchemaValue === undefined || typeof editingSchemaValue == 'string' && editingSchemaValue === '')){ + // 布尔类型有可能初始是false + return propertySchema['type']==='boolean'? propertySchema['defaultValue']:propertySchema['defaultValue']||''; + }else{ + return editingSchemaValue; + } + } + return null; + }, + set(newValue) { + updateCount.value += 1; + if (converter && converter.convertTo) { + converter.convertTo(rawSchema, propertyKey, newValue, schemaService, componentId); + converter.convertTo(editingSchema, propertyKey, newValue, schemaService, componentId); + } else { + rawSchema[propertyKey] = newValue; + editingSchema[propertyKey] = newValue; + } + } + }); + const { refreshPanelAfterChanged, description, isExpand, parentPropertyID } = propertySchema; + const propertyEntity = { propertyID, propertyName, propertyType, propertyValue, editor, visible, readonly, cascadeConfig, hideCascadeTitle, refreshPanelAfterChanged, description, isExpand, parentPropertyID }; + propertyConfigMap[propertyID] = propertyEntity; + return propertyEntity; + }); + return propertyEntities; + } + + function getPropertyConfigByType(schemaType: string, schemaService: SchemaService, schema = {} as ComponentSchema): ElementPropertyConfig[] { + const propertyConfigMap = {} as Record; + const propertyConfigSchema = propertyConfigSchemaMap[schemaType]; + if (propertyConfigSchema && propertyConfigSchema.categories) { + const propertyConfigs = Object.keys(propertyConfigSchema.categories).map((categoryId: string) => { + const propertyCategory = propertyConfigSchema.categories[categoryId]; + const categoryName = propertyCategory?.title; + const properties = getPropertyEntities(propertyCategory.properties || {}, propertyConfigMap, {} as ComponentSchema, schema, schemaService); + return { categoryId, categoryName, properties }; + }); + return propertyConfigs; + } + return []; + } + + function tryToResolveReference(categoryId: string, propertyCategory: Record, rawSchema: ComponentSchema, schemaService: SchemaService, componentId = '') { + const refSchemaPath = propertyCategory.$ref.schema; + const $converter = propertyCategory.$ref.converter; + const refSchema = rawSchema[refSchemaPath]; + + const schemaType = refSchema.type; + const editingSchema = resolveSchemaWithDefaultValue(refSchema) as ComponentSchema; + const propertyConfigMap = {} as Record; + const propertyConfigSchema = propertyConfigSchemaMap[schemaType]; + if (propertyConfigSchema && propertyConfigSchema.categories) { + const propertyCategory = propertyConfigSchema.categories[categoryId]; + const categoryName = propertyCategory?.title; + if ($converter) { + Object.keys(propertyCategory.properties).forEach((propertyKey: any) => { + propertyCategory.properties[propertyKey].$converter = $converter; + }); + } + const propertiesInCategory = propertyCategory?.properties || {}; + const properties = getPropertyEntities(propertiesInCategory, propertyConfigMap, editingSchema, refSchema, schemaService, componentId); + return { categoryId, categoryName, properties }; + + } + return { categoryId, categoryName: '', properties: [] }; + } + + function getPropertyConfigBySchema(rawSchema: ComponentSchema, schemaService: SchemaService, designerItem: any, componentId: string, propertyConfig?: Record): ElementPropertyConfig[] { + const schemaType = rawSchema.type; + const editingSchema = resolveSchemaWithDefaultValue(rawSchema) as ComponentSchema; + const propertyConfigMap = {} as Record; + + // 先从ConfigMap中取简单类属性,若找不到,则在控件实例中取复杂属性 + let propertyConfigSchema = propertyConfig || propertyConfigSchemaMap[schemaType]; + if (propertyConfigSchema && Object.keys(propertyConfigSchema).length === 0 && designerItem && designerItem.getPropConfig) { + propertyConfigSchema = designerItem.getPropConfig(componentId); + } + if (propertyConfigSchema && propertyConfigSchema.categories) { + const propertyConfigs = [] as Array; + Object.keys(propertyConfigSchema.categories).map((categoryId: string) => { + const propertyCategory = propertyConfigSchema.categories[categoryId]; + if (propertyCategory.$ref) { + propertyConfigs.push(tryToResolveReference(categoryId, propertyCategory, rawSchema, schemaService, componentId) as ElementPropertyConfig); + return; + } + const categoryName = propertyCategory?.title; + const tabId = propertyCategory?.tabId; + const tabName = propertyCategory?.tabName; + const hide = propertyCategory?.hide; + const hideTitle = propertyCategory?.hideTitle; + const properties = getPropertyEntities(propertyCategory.properties || {}, propertyConfigMap, editingSchema, rawSchema, schemaService, componentId, propertyCategory['$converter']); + const { setPropertyRelates } = propertyCategory; + const parentPropertyID = propertyCategory?.parentPropertyID; + propertyConfigs.push({ categoryId, categoryName, tabId, tabName, hide, properties, hideTitle, setPropertyRelates, parentPropertyID }); + }); + return propertyConfigs; + } + return []; + } + + return { + getPropertyConfigBySchema, getPropertyConfigByType, propertyConverterMap + }; +} + diff --git a/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/schema-resolver-design.ts b/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/schema-resolver-design.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e612597186ef396e57ddf4bf6df957f470ecb14 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/schema-resolver-design.ts @@ -0,0 +1,14 @@ +import { SchemaResolverFunction } from "../../types"; +import { useSchemaResolver } from "./use-schema-resolver"; + +const schemaMapForDesigner = {} as Record; +const schemaResolverMapForDesigner = {} as Record; + +const { getSchemaByType, resolveSchemaWithDefaultValue, resolveSchemaToProps, mappingSchemaToProps, setDesignerContext } = + useSchemaResolver(schemaMapForDesigner, schemaResolverMapForDesigner); + +export { + getSchemaByType as getSchemaByTypeForDesigner, resolveSchemaWithDefaultValue as resolveSchemaWithDefaultValueForDesigner, + resolveSchemaToProps as resolveSchemaToPropsForDesigner, schemaMapForDesigner, schemaResolverMapForDesigner, + mappingSchemaToProps as mappingSchemaToPropsForDesigner, setDesignerContext as setDesignerContextForDesigner +}; diff --git a/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/schema-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..a77a5fa1e99fa605d60c3b34640a76faf4bc2ac9 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/schema-resolver.ts @@ -0,0 +1,10 @@ +import { SchemaResolverFunction } from "../../types"; +import { useSchemaResolver } from "./use-schema-resolver"; + +const schemaMap = {} as Record; +const schemaResolverMap = {} as Record; + +const { getSchemaByType, resolveSchemaWithDefaultValue, resolveSchemaToProps, mappingSchemaToProps, setDesignerContext } = + useSchemaResolver(schemaMap, schemaResolverMap); + +export { getSchemaByType, resolveSchemaWithDefaultValue, resolveSchemaToProps, schemaMap, schemaResolverMap, mappingSchemaToProps, setDesignerContext }; diff --git a/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/use-schema-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/use-schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..241dc5318071f0bac49e48b5961db129192e0131 --- /dev/null +++ b/packages/ui-vue/components/dynamic-resolver/src/resolver/schema/use-schema-resolver.ts @@ -0,0 +1,143 @@ +import { cloneDeep, isPlainObject } from "lodash-es"; +import { MapperFunction, SchemaResolverFunction } from "../../types"; +import { DesignerHostService } from "@farris/ui-vue/components/designer-canvas"; + +export function useSchemaResolver(schemaMap: Record, schemaResolverMap: Record) { + + let designerContext; + function getSchemaValueByDefault(defaultSchema: Record): Record { + const { properties, title, ignore: ignoreList } = defaultSchema as Record; + const canIgnoreProperty = ignoreList && Array.isArray(ignoreList); + const resolvedSchema = Object.keys(properties).reduce((propsObject: Record, propKey: string) => { + if (!canIgnoreProperty || !ignoreList.find(item => item === propKey)) { + propsObject[propKey] = (properties[propKey].type === 'object' && !!properties[propKey].properties) ? + getSchemaValueByDefault(properties[propKey]) : cloneDeep(properties[propKey].default); + } + return propsObject; + }, {}); + if (title && (!canIgnoreProperty || !ignoreList.find(item => item === 'id'))) { + const typePrefix = title.toLowerCase().replace(/-/g, '_'); + resolvedSchema.id = `${typePrefix}_${Math.random().toString().slice(2, 6)}`; + } + return resolvedSchema; + } + + /** + * 获取控件元数据,只组装必填的字段 + */ + function getRequiredSchemaValueByDefault(defaultSchema: Record): Record { + const { properties, title, required: requiredProperty } = defaultSchema as Record; + if (requiredProperty && Array.isArray(requiredProperty)) { + const resolvedSchema = requiredProperty.reduce((propsObject: Record, propKey: string) => { + + propsObject[propKey] = (properties[propKey].type === 'object' && !!properties[propKey].properties) ? + getSchemaValueByDefault(properties[propKey]) : cloneDeep(properties[propKey].default); + + return propsObject; + }, {}); + if (title && requiredProperty.find(item => item === 'id')) { + const typePrefix = title.toLowerCase().replace(/-/g, '_'); + resolvedSchema.id = `${typePrefix}_${Math.random().toString().slice(2, 6)}`; + } + return resolvedSchema; + } + return { + type: title + }; + + } + + function getSchemaByType(componentType: string, resolveContext: Record = {}, designerHostService?: DesignerHostService) + : Record | null { + const defaultSchema = schemaMap[componentType]; + if (defaultSchema) { + let componentSchema = getRequiredSchemaValueByDefault(defaultSchema); + const schemaResolver = schemaResolverMap[componentType]; + componentSchema = schemaResolver ? schemaResolver({ getSchemaByType }, componentSchema, resolveContext, designerHostService) + : componentSchema; + if (designerContext?.appendIdentifyForNewControl) { + designerContext.appendIdentifyForNewControl(componentSchema); + } + return componentSchema; + } + return null; + } + + function resolveSchema(schemaValue: Record, defaultSchema: Record): Record { + + const resolvedSchema = getSchemaValueByDefault(defaultSchema); + + Object.keys(resolvedSchema).reduce((resolvedSchema: Record, propKey: string) => { + if (Object.prototype.hasOwnProperty.call(schemaValue, propKey)) { + // 解决属性是对象类型,默认值被冲掉的情况 + // 增加非判断,解决针对属性时对象,但是schemaValue=null或者undefined情况 + if (resolvedSchema[propKey] && isPlainObject(resolvedSchema[propKey]) && (isPlainObject(schemaValue[propKey] || !schemaValue[propKey]))) { + Object.assign(resolvedSchema[propKey], schemaValue[propKey] || {}); + } else { + resolvedSchema[propKey] = schemaValue[propKey]; + } + } + + return resolvedSchema; + }, resolvedSchema); + + return resolvedSchema; + }; + + function mappingSchemaToProps(resolvedSchema: Record, schemaMapper: Map) { + const props = Object.keys(resolvedSchema) + .filter((propKey: string) => resolvedSchema[propKey] != null) + .reduce((resolvedProps: Record, propKey: string) => { + if (schemaMapper.has(propKey)) { + const mapper = schemaMapper.get(propKey) as string | MapperFunction; + if (typeof mapper === 'string') { + resolvedProps[mapper] = resolvedSchema[propKey]; + } else { + const mapperResult = (mapper as MapperFunction)(propKey, resolvedSchema[propKey], resolvedSchema); + Object.assign(resolvedProps, mapperResult); + } + } else { + resolvedProps[propKey] = resolvedSchema[propKey]; + } + return resolvedProps; + }, {}); + return props; + } + + function resolveSchemaToProps( + schemaValue: Record, + defaultSchema: Record, + schemaMapper: Map = new Map() + ): Record { + const resolvedSchema = resolveSchema(schemaValue, defaultSchema); + const props = mappingSchemaToProps(resolvedSchema, schemaMapper); + return props; + } + + function resolveSchemaWithDefaultValue(schemaValue: Record): Record { + const componentType = schemaValue.type; + if (componentType) { + const defaultSchema = schemaMap[componentType]; + if (!defaultSchema) { + return schemaValue; + } + const resolvedSchema = resolveSchema(schemaValue, defaultSchema); + const editorType = schemaValue.editor?.type || ''; + /* 解决schemeValue结构如下图场景,在editor下,获取不到date-picker类型的默认值的问题 + * {type:'input-group',...,editor:{type:'date-picker',...}} + */ + if (editorType) { + const defaulEditorSchema = schemaMap[editorType]; + const resolvedEditorSchema = resolveSchema(schemaValue.editor, defaulEditorSchema); + resolvedSchema.editor = resolvedEditorSchema; + } + return resolvedSchema; + } + return schemaValue; + } + + function setDesignerContext(designerContext1) { + designerContext = designerContext1; + } + return { getSchemaByType, resolveSchemaWithDefaultValue, resolveSchemaToProps, mappingSchemaToProps, setDesignerContext }; +} diff --git a/packages/ui-vue/components/dynamic-resolver/src/schema-resolver.ts b/packages/ui-vue/components/dynamic-resolver/src/schema-resolver.ts deleted file mode 100644 index d9adc5dfc3560c0b31e2e2666fdd9c1a60e537fb..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/dynamic-resolver/src/schema-resolver.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { cloneDeep, isPlainObject } from "lodash-es"; -import { DesignerHostService } from "../../designer-canvas/src/composition/types"; -import { MapperFunction, SchemaResolverFunction } from "./types"; - -const schemaMap = {} as Record; -const schemaResolverMap = {} as Record; - -function getSchemaValueByDefault(defaultSchema: Record): Record { - const { properties, title, ignore: ignoreList } = defaultSchema as Record; - const canIgnoreProperty = ignoreList && Array.isArray(ignoreList); - const resolvedSchema = Object.keys(properties).reduce((propsObject: Record, propKey: string) => { - if (!canIgnoreProperty || !ignoreList.find(item => item === propKey)) { - propsObject[propKey] = (properties[propKey].type === 'object' && !!properties[propKey].properties) ? - getSchemaValueByDefault(properties[propKey]) : cloneDeep(properties[propKey].default); - } - return propsObject; - }, {}); - if (title && (!canIgnoreProperty || !ignoreList.find(item => item === 'id'))) { - const typePrefix = title.toLowerCase().replace(/-/g, '_'); - resolvedSchema.id = `${typePrefix}_${Math.random().toString().slice(2, 6)}`; - } - return resolvedSchema; -} -/** - * 获取控件元数据,只组装必填的字段 - */ -function getRequiredSchemaValueByDefault(defaultSchema: Record): Record { - const { properties, title, required: requiredProperty } = defaultSchema as Record; - if (requiredProperty && Array.isArray(requiredProperty)) { - const resolvedSchema = requiredProperty.reduce((propsObject: Record, propKey: string) => { - - propsObject[propKey] = (properties[propKey].type === 'object' && !!properties[propKey].properties) ? - getSchemaValueByDefault(properties[propKey]) : cloneDeep(properties[propKey].default); - - return propsObject; - }, {}); - if (title && requiredProperty.find(item => item === 'id')) { - const typePrefix = title.toLowerCase().replace(/-/g, '_'); - resolvedSchema.id = `${typePrefix}_${Math.random().toString().slice(2, 6)}`; - } - return resolvedSchema; - } - return { - type: title - }; - -} -function getSchemaByType(componentType: string, resolveContext: Record = {}, designerHostService?: DesignerHostService) - : Record | null { - const defaultSchema = schemaMap[componentType]; - if (defaultSchema) { - let componentSchema = getRequiredSchemaValueByDefault(defaultSchema); - const schemaResolver = schemaResolverMap[componentType]; - componentSchema = schemaResolver ? schemaResolver({ getSchemaByType }, componentSchema, resolveContext, designerHostService) - : componentSchema; - return componentSchema; - } - return null; -} - -function resolveSchema(schemaValue: Record, defaultSchema: Record): Record { - - const resolvedSchema = getSchemaValueByDefault(defaultSchema); - - Object.keys(resolvedSchema).reduce((resolvedSchema: Record, propKey: string) => { - if (Object.prototype.hasOwnProperty.call(schemaValue, propKey)) { - // 解决属性是对象类型,默认值被冲掉的情况 - // 增加非判断,解决针对属性时对象,但是schemaValue=null或者undefined情况 - if (resolvedSchema[propKey] && isPlainObject(resolvedSchema[propKey]) && (isPlainObject(schemaValue[propKey] || !schemaValue[propKey]))) { - Object.assign(resolvedSchema[propKey], schemaValue[propKey] || {}); - } else { - resolvedSchema[propKey] = schemaValue[propKey]; - } - } - - return resolvedSchema; - }, resolvedSchema); - - return resolvedSchema; -}; - -function mappingSchemaToProps(resolvedSchema: Record, schemaMapper: Map) { - const props = Object.keys(resolvedSchema) - .filter((propKey: string) => resolvedSchema[propKey] != null) - .reduce((resolvedProps: Record, propKey: string) => { - if (schemaMapper.has(propKey)) { - const mapper = schemaMapper.get(propKey) as string | MapperFunction; - if (typeof mapper === 'string') { - resolvedProps[mapper] = resolvedSchema[propKey]; - } else { - const mapperResult = (mapper as MapperFunction)(propKey, resolvedSchema[propKey], resolvedSchema); - Object.assign(resolvedProps, mapperResult); - } - } else { - resolvedProps[propKey] = resolvedSchema[propKey]; - } - return resolvedProps; - }, {}); - return props; -} - -function resolveSchemaToProps( - schemaValue: Record, - defaultSchema: Record, - schemaMapper: Map = new Map() -): Record { - const resolvedSchema = resolveSchema(schemaValue, defaultSchema); - const props = mappingSchemaToProps(resolvedSchema, schemaMapper); - return props; -} - -function resolveSchemaWithDefaultValue(schemaValue: Record): Record { - const componentType = schemaValue.type; - if (componentType) { - const defaultSchema = schemaMap[componentType]; - if (!defaultSchema) { - return schemaValue; - } - const resolvedSchema = resolveSchema(schemaValue, defaultSchema); - const editorType = schemaValue.editor?.type || ''; - /* 解决schemeValue结构如下图场景,在editor下,获取不到date-picker类型的默认值的问题 - * {type:'input-group',...,editor:{type:'date-picker',...}} - */ - if (editorType) { - const defaulEditorSchema = schemaMap[editorType]; - const resolvedEditorSchema = resolveSchema(schemaValue.editor, defaulEditorSchema); - resolvedSchema.editor = resolvedEditorSchema; - } - return resolvedSchema; - } - return schemaValue; -} - -export { getSchemaByType, resolveSchemaWithDefaultValue, resolveSchemaToProps, schemaMap, schemaResolverMap, mappingSchemaToProps }; diff --git a/packages/ui-vue/components/dynamic-view/src/components/maps.ts b/packages/ui-vue/components/dynamic-view/src/components/maps.ts index 0e8a427200d3fd149ef6e45bdfbe46d4aeffe654..d78c36d7164599fdb657a52b61bf4d09e03136ea 100644 --- a/packages/ui-vue/components/dynamic-view/src/components/maps.ts +++ b/packages/ui-vue/components/dynamic-view/src/components/maps.ts @@ -69,6 +69,10 @@ import FPropertyEditor from '@farris/ui-vue/components/property-editor'; import FExpressionEditor from '@farris/ui-vue/components/expression-editor'; import FCodeEditor from '@farris/ui-vue/components/code-editor'; import FHtmlTemplate from '@farris/ui-vue/components/html-template'; +import FCollectionPropertyEditor from '@farris/ui-vue/components/collection-property-editor'; +import FModal from '@farris/ui-vue/components/modal'; +import FExternalContainer from '@farris/ui-vue/components/external-container'; +import FLanguageTextbox from '@farris/ui-vue/components/language-textbox'; const componentMap: Record = {}; const componentPropsConverter: Record = {}; @@ -154,6 +158,10 @@ function loadRegister() { FPropertyEditor.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); FCodeEditor.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); FHtmlTemplate.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); + FCollectionPropertyEditor.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); + FModal.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); + FExternalContainer.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); + FLanguageTextbox.register(componentMap, componentPropsConverter, componentPropertyConfigConverter, resolverMap); } } diff --git a/packages/ui-vue/components/dynamic-view/src/composition/use-binding-data.ts b/packages/ui-vue/components/dynamic-view/src/composition/use-binding-data.ts index f569b2703eed7ba4a3c62b6268058dd0ced1e0ff..a25415a81118799b99c8291015c7441aed7d4993 100644 --- a/packages/ui-vue/components/dynamic-view/src/composition/use-binding-data.ts +++ b/packages/ui-vue/components/dynamic-view/src/composition/use-binding-data.ts @@ -10,6 +10,9 @@ export function useBindingData(modelValue: Ref>, setupContex if (modelValue.value) { modelValue.value[elementId] = value; } + if (!field) { + return; + } setupContext.emit('update:modelValue', { elementId, field, value, modelValue: modelValue.value }); } diff --git a/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx b/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx index f654b4c4120db6fd61d112f2e663960bd05c6bbd..a22fc7fe349faed79f9e774607fecb54c183b24e 100644 --- a/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx +++ b/packages/ui-vue/components/dynamic-view/src/dynamic-view.component.tsx @@ -19,6 +19,8 @@ const FDynamicView = defineComponent({ const schema = ref(props.schema); const modelValue = ref(props.modelValue); const callback = ref(props.callback); + const { customComponentRenders } = props; + const schemaMap: Map = new Map(); const dataSourceMap: Map = new Map(); loadRegister(); @@ -31,8 +33,8 @@ const FDynamicView = defineComponent({ function resolveModels(viewSchema: Record) { const componentType = viewSchema.type; - const { dataSource, binding } = viewSchema; - if (!dataSource && !binding) { + const { dataSource, binding = undefined } = viewSchema; + if (!dataSource && binding === undefined) { return {}; } if (dataSource) { @@ -127,6 +129,11 @@ const FDynamicView = defineComponent({ ...resolveModels(viewSchema), ...resolveExtraProps(viewSchema) }; + + if (componentKey === 'component') { + viewProps['code'] = schema.value?.module?.code; + } + const props = { ...viewProps, key: viewSchema.id, @@ -142,7 +149,14 @@ const FDynamicView = defineComponent({ function render(viewSchema: Record) { const componentKey = viewSchema.type; + const Component = componentMap[componentKey]; + + // 使用自定义渲染器渲染组件 + if (customComponentRenders && customComponentRenders[componentKey]) { + return customComponentRenders[componentKey](viewSchema, Component); + } + // 渲染ComponentRef组件 if (componentKey === 'component-ref') { const componentSchema = schema.value?.module?.components .find((component: any) => component.id === viewSchema.component); @@ -154,7 +168,6 @@ const FDynamicView = defineComponent({ if (viewSchema.id) { schemaMap.set(viewSchema.id, viewSchema); } - const Component = componentMap[componentKey]; if (!Component) { return null; } @@ -178,11 +191,14 @@ const FDynamicView = defineComponent({ // Object.assign(props, eventProps); // } const createNode = (componentType: any, props: Record, children?: null | undefined | any[]) => { + let vnode; if (children && children.length > 0) { - return createVNode(componentType, { ...props }, children); + vnode = createVNode(componentType, { ...props }, children); } else { - return createVNode(componentType, { ...props }, null); + vnode = createVNode(componentType, { ...props }, null); } + + return vnode; }; // componentState.set(viewSchema.id, reactive({ props })); // const reactivedProps = componentState.get(viewSchema.id).props; @@ -201,6 +217,9 @@ const FDynamicView = defineComponent({ component.$forceUpdate(); } } + function getControlValue(id: string) { + return bindingData.getValue(id); + } function getSchema(id: string) { return schemaMap.get(id); } @@ -324,12 +343,12 @@ const FDynamicView = defineComponent({ if (!currentState) { state.set(viewSchema.id, reactive({ props: modelProps })); } else { - mergeWith(currentState?.props, modelProps, (objValue, srcValue) => { - if (Array.isArray(srcValue)) { - return srcValue; - } - return undefined; + const currentProps = { ...currentState?.props }; + Object.keys(modelProps).forEach(key => { + currentProps[key] = modelProps[key]; }); + Object.assign(currentState?.props, currentProps); + } // state.set(viewSchema.id, reactive({ props: { ...currentProps, ...modelProps } })); } @@ -347,6 +366,8 @@ const FDynamicView = defineComponent({ if (!frameComponent) { return null; } + + frameComponent['formCode'] = schema.value?.module?.code; return frameComponent; } @@ -394,7 +415,7 @@ const FDynamicView = defineComponent({ convertSchemaToProps(viewSchema); }); - setupContext.expose({ componentManager, rerender, getProps, invoke, setProps, selectItemById, getSchema, setSchema, convertPartialSchemaToProps }); + setupContext.expose({ componentManager, rerender, getProps, invoke, setProps, selectItemById, getSchema, setSchema, convertPartialSchemaToProps, getControlValue }); return () => { const components: Record[] = schema.value?.module?.components; diff --git a/packages/ui-vue/components/dynamic-view/src/dynamic-view.props.ts b/packages/ui-vue/components/dynamic-view/src/dynamic-view.props.ts index 116990adad7e34cc0dcab7cbd951e2eff72bf80b..17eb610b3e2e2686bdda86693f6a846a666f4da6 100644 --- a/packages/ui-vue/components/dynamic-view/src/dynamic-view.props.ts +++ b/packages/ui-vue/components/dynamic-view/src/dynamic-view.props.ts @@ -12,6 +12,11 @@ export const dynamicViewProps = { /** * 回调 */ - callback: { type: Function as PropType<(type: string, ...args: unknown[]) => any>, default: () => { } } + callback: { type: Function as PropType<(type: string, ...args: unknown[]) => any>, default: () => { } }, + + /** + * 自定义组件渲染器 + */ + customComponentRenders: { type: Object, default: null } }; export type DynamicViewProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-combo-tree.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-combo-tree.ts index 33832f56870d7b0e2bafb0526f6ff24b3f7e4ecc..140e0324cf0ced705151a8933bdc8251e3657034 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-combo-tree.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-combo-tree.ts @@ -1,4 +1,5 @@ import { computed, watch } from "vue"; +import { useI18n } from 'vue-i18n'; import { editorMap, EditorType, EventParameterProps } from "../../event-parameter.props"; import { UseEditorInput } from "../type"; import { VisualData } from "@farris/ui-vue/components/data-view"; @@ -6,7 +7,9 @@ import { VisualData } from "@farris/ui-vue/components/data-view"; export default function ( props: EventParameterProps, ): UseEditorInput { - const shouldRenderAppendButton = computed(() => props.editorType === EditorType.Default); + const { t: getLocaleValue } = useI18n(); + const shouldRenderAppendButton = computed(() => props.editor.type === EditorType.Default + || props.editorType === EditorType.Default); function checkAndProcessBeforeAssign(modelValue: string) { const foundDataItem = props.data.find((dataItem: any) => { @@ -23,11 +26,11 @@ export default function ( return modelValue; } function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'combo-tree', componentProps: { data: props.data, - placeholder: '请选择', + placeholder: getLocaleValue('eventParameter.comboTree.placeholder'), enableSearch: false, enableClear: true, editable: false, @@ -50,8 +53,8 @@ export default function ( watch(() => props.data, (newData: any, oldData: any) => { if (newData !== oldData) { - if (editorMap[props.editorType]?.type === 'combo-tree') { - editorMap[props.editorType].componentProps!.data = newData; + if (editorMap[props.editor?.type || props.editorType]?.type === 'combo-tree') { + editorMap[props.editor?.type || props.editorType].componentProps!.data = newData; } } }); diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-custom-editor.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-custom-editor.ts new file mode 100644 index 0000000000000000000000000000000000000000..832157d5b7210fc7db952fc64325764f7412dfc8 --- /dev/null +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-custom-editor.ts @@ -0,0 +1,20 @@ +import { editorMap, EventParameterProps } from "../../event-parameter.props"; +import { UseEditorInput } from "../type"; + +export default function ( + props: EventParameterProps, +): UseEditorInput { + + function createEditorProps(): void { + editorMap[props.editor?.type || props.editorType] = { + type: 'custom', + componentProps: { + }, + render: props.editor?.customRender + }; + } + + function onValueChange() { } + + return { createEditorProps, onValueChange }; +} diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-filter-condition-editor.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-filter-condition-editor.ts index a569f83d8c75ecd165729bd0b6849fa0172576cd..82dd488c0d1eb8d035f1d8e4ca61f9bae86c4344 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-filter-condition-editor.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-filter-condition-editor.ts @@ -54,7 +54,7 @@ export default function ( } function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'filter-condition-editor', componentProps: { fields: getFieldTreeNodes(), diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-input.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-input.ts index 2b51ac37e735a36191cd4cb15e34616cfe51e54a..3443f356f24f2538b18469dd527b1ca118832aea 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-input.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-input.ts @@ -5,10 +5,10 @@ import { UseEditorInput } from "../type"; export default function ( props: EventParameterProps, ): UseEditorInput { - const shouldRenderAppendButton = computed(() => props.editorType === EditorType.Default); - + const shouldRenderAppendButton = computed(() => props.editor.type === EditorType.Default + || props.editorType === EditorType.Default); function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'input-group', componentProps: { } diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-json-editor.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-json-editor.ts index 284237996fe9cc4fddbb5bbe8220676d6ee11c33..45934a54966a122b071ee57fe813c3e84d0d5192 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-json-editor.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-json-editor.ts @@ -1,10 +1,11 @@ +import { useI18n } from 'vue-i18n'; import { editorMap, EventParameterProps, EditorControlSource } from "../../event-parameter.props"; import { UseBaseEditor } from "../type"; export default function ( props: EventParameterProps ): UseBaseEditor { - + const { t: getLocaleValue } = useI18n(); function getParameterDescriptors(): any[] { const controlSource = props.editorControlSource as EditorControlSource; const schemaValue = controlSource?.context?.schema?.value; @@ -18,7 +19,7 @@ export default function ( } return []; } catch (error) { - console.error(`Expected array of parameter schema for JsonEditor, but received invalid JSON.`, error); + console.error(`${getLocaleValue('eventParameter.jsonEditor.error')}`, error); return []; } } @@ -35,7 +36,7 @@ export default function ( } function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'json-editor', componentProps: { parameterDescriptors: getParameterDescriptors(), @@ -45,11 +46,11 @@ export default function ( formData: props.formData, }, beforeOpen, - dialogTitle: '可配置参数编辑器', - keyColumnTitle: '参数', - valueColumnTitle: '参数值', - addButtonText: '添加配置参数', - keyColumnPlaceholder: '请输入参数', + dialogTitle: getLocaleValue('eventParameter.jsonEditor.dialogTitle'), + keyColumnTitle: getLocaleValue('eventParameter.jsonEditor.keyColumnTitle'), + valueColumnTitle: getLocaleValue('eventParameter.jsonEditor.valueColumnTitle'), + addButtonText: getLocaleValue('eventParameter.jsonEditor.addButtonText'), + keyColumnPlaceholder: getLocaleValue('eventParameter.jsonEditor.keyColumnPlaceholder'), }, }; } diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-menu-lookup.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-menu-lookup.ts index 296746d0426a18c0671fd9cd3521e8bb91fe4642..e00dedf97863150833265bd9f967e9a8bb423ecd 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-menu-lookup.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-menu-lookup.ts @@ -22,7 +22,7 @@ export default function ( } function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'menu-lookup', componentProps: { fetchNodeApi: getFetchNodeApiUrl, diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-select.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-select.ts index 703f54f0c5c746dace44dd42705ba8e54a34da25..9183cb77e097c39f3fcaa9773e5da92b6d754858 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-select.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-select.ts @@ -13,7 +13,7 @@ export default function ( } function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'combo-list', componentProps: { data: props.data, diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-sort-condition-editor.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-sort-condition-editor.ts index b35c2c6a31f485831eb362e97bcfe42c3e6dcf96..247f834868b969a040f1b82709fa0e4231d06d63 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-sort-condition-editor.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-sort-condition-editor.ts @@ -73,7 +73,7 @@ export default function ( } function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'sort-condition-editor', componentProps: { fields: getFieldTreeNodes(), diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-switch.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-switch.ts index 9189802320379b3e2ccfb0eb6612413218f1dd12..0cee660012bdbf5fcb7fa179be8966621ef6fe81 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/editors/use-switch.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-switch.ts @@ -13,7 +13,7 @@ export default function ( } function createEditorProps(): void { - editorMap[props.editorType] = { + editorMap[props.editor?.type || props.editorType] = { type: 'switch', componentProps: { }, diff --git a/packages/ui-vue/components/event-parameter/src/composition/editors/use-work-flow.ts b/packages/ui-vue/components/event-parameter/src/composition/editors/use-work-flow.ts new file mode 100644 index 0000000000000000000000000000000000000000..8dab5c1af70b39e518c314d2c8fd832be3126d2e --- /dev/null +++ b/packages/ui-vue/components/event-parameter/src/composition/editors/use-work-flow.ts @@ -0,0 +1,28 @@ +import { editorMap, EventParameterProps } from "../../event-parameter.props"; +import { UseBaseEditor } from "../type"; + +export default function ( + props: EventParameterProps +): UseBaseEditor { + + // 待树列表滚动条问题修复后再启用分层加载 + const enableLayeredLoading = false; + function convertEventParamValue2EditorValue(eventParamValue: any): any { + if (typeof eventParamValue === 'object') { + return { id: props.modelValue, name: props.modelValue }; + } + return { id: eventParamValue, name: eventParamValue }; + } + function createEditorProps(): void { + editorMap[props.editorType] = { + type: 'custom', + componentProps: { + }, + convertEventParamValue2EditorValue, + }; + } + + function onValueChange() { } + + return { createEditorProps, onValueChange }; +} diff --git a/packages/ui-vue/components/event-parameter/src/composition/type.ts b/packages/ui-vue/components/event-parameter/src/composition/type.ts index 76550b4c08be94a7a5d70752089d95b283dbc87d..cb0b68b0dda7e45a5e1ca145620b71906421b76a 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/type.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/type.ts @@ -32,9 +32,10 @@ export interface UseEditorProxy { } export interface UseEditorInput extends UseBaseEditor { - shouldRenderAppendButton: ComputedRef; + shouldRenderAppendButton?: ComputedRef; } + export interface UseParameterEditor { /** * 组件结构数据 diff --git a/packages/ui-vue/components/event-parameter/src/composition/use-editor.ts b/packages/ui-vue/components/event-parameter/src/composition/use-editor.ts index 0a76834cb9bb9757fab48b3138e80999abc17b15..d1a771bfb64fc2dc0006726b57fe9f921a913c42 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/use-editor.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/use-editor.ts @@ -1,4 +1,4 @@ -import { toRef } from "vue"; +import { ref, toRef } from "vue"; import { editorMap, EditorType, EventParameterProps } from "../event-parameter.props"; import { UseEditorProxy } from "./type"; import useEditorSelect from './editors/use-select'; @@ -9,11 +9,13 @@ import useMenuLookup from './editors/use-menu-lookup'; import useJsonEditor from './editors/use-json-editor'; import useSortConditionEditor from "./editors/use-sort-condition-editor"; import useFilterConditionEditor from "./editors/use-filter-condition-editor"; +import useWorkFlow from './editors/use-work-flow'; +import useCustomEditor from './editors/use-custom-editor'; export default function ( props: EventParameterProps, ): UseEditorProxy { - const editorType = toRef(props, 'editorType'); + const editorType = ref(props.editor?.type || props.editorType); // 下拉组件 const useEditorSelectComposition = useEditorSelect(props); @@ -31,7 +33,10 @@ export default function ( const useSortConditionEditorComposition = useSortConditionEditor(props); // 过滤条件编辑器组件 const useFilterConditionEditorComposition = useFilterConditionEditor(props); - + // 流程分类 + const useWorkFlowComposition = useWorkFlow(props); + // 自定义组件 + const useCustomEditorComposition = useCustomEditor(props); function getEditorConfig() { const editorTypeValue = editorType.value; if ( @@ -57,6 +62,8 @@ export default function ( // 数字 } else if (editorTypeValue === EditorType.Switch) { useEditorSwitchComposition.createEditorProps(); + } else if (editorTypeValue === EditorType.Custom) { + useCustomEditorComposition.createEditorProps(); } else { useEditorInputComposition.createEditorProps(); } diff --git a/packages/ui-vue/components/event-parameter/src/composition/use-general-editor.ts b/packages/ui-vue/components/event-parameter/src/composition/use-general-editor.ts index 0c1792aebd4e8511caa3898c142e220c8aced5fa..13211dadc90b015c0d0c8c61ce31730a835403eb 100644 --- a/packages/ui-vue/components/event-parameter/src/composition/use-general-editor.ts +++ b/packages/ui-vue/components/event-parameter/src/composition/use-general-editor.ts @@ -1,13 +1,15 @@ import { reactive, ref, watch } from "vue"; +import { useI18n } from 'vue-i18n'; import { EventParameterProps } from "../event-parameter.props"; export function useGeneralEditor( props: EventParameterProps, ) { + const { t: getLocaleValue } = useI18n(); const tabs = reactive([ { id: 'tabField', - title: '字段', + title: getLocaleValue('eventParameter.generalEditor.field'), treeConfigs: { id: 'tabFieldTree', columns: [{ field: 'name' }], @@ -19,7 +21,7 @@ export function useGeneralEditor( }, { id: 'tabVar', - title: '变量', + title: getLocaleValue('eventParameter.generalEditor.tabVar'), treeConfigs: { id: 'tabVarTree', data: props.varData, @@ -28,7 +30,7 @@ export function useGeneralEditor( }, { id: 'tabForm', - title: '表单组件', + title: getLocaleValue('eventParameter.generalEditor.form'), treeConfigs: { id: 'tabFormTree', data: props.formData, diff --git a/packages/ui-vue/components/event-parameter/src/event-parameter.component.tsx b/packages/ui-vue/components/event-parameter/src/event-parameter.component.tsx index d1d04525a6d516413f7356678cdfb2a079e4caa6..b0924755b34ede0501650a7392d71014d4ccda63 100644 --- a/packages/ui-vue/components/event-parameter/src/event-parameter.component.tsx +++ b/packages/ui-vue/components/event-parameter/src/event-parameter.component.tsx @@ -39,6 +39,7 @@ export default defineComponent({ const { getEditorConfig } = useEditor(props); watch( [ + () => props.editor, () => props.editorType, () => props.data ], @@ -63,7 +64,7 @@ export default defineComponent({ } function getModelValue(modelValue: any): any { - if(editorConfig.value?.checkAndProcessBeforeAssign){ + if (editorConfig.value?.checkAndProcessBeforeAssign) { return editorConfig.value.checkAndProcessBeforeAssign(modelValue); } if (editorConfig.value?.convertEventParamValue2EditorValue) { @@ -83,7 +84,7 @@ export default defineComponent({ modelValue.value = getModelValue(newModelValue.value); }, ); - + watch( editorConfig, () => { @@ -92,7 +93,7 @@ export default defineComponent({ ); watch(() => props.data, () => { - modelValue.value = getModelValue(modelValue.value); + modelValue.value = getModelValue(modelValue.value); }); const modalService = inject(F_MODAL_SERVICE_TOKEN) as any; @@ -132,7 +133,8 @@ export default defineComponent({ // 编辑器类型 type: editorConfig.value?.type, // 根据不同编辑器,设置不同编辑器的属性 - ...editorConfig.value?.componentProps + ...editorConfig.value?.componentProps, + readonly: props.readonly }; }); @@ -349,10 +351,23 @@ export default defineComponent({ return textareaRef.value.elementRef; }; + const onAfterConfirm = (e) => { + const value = e.items[0].id; + context.emit('update:modelValue', value); + context.emit('valueChange', value); + }; + + const onAfterClear = () => { + const value = modelValue.value.id; + context.emit('update:modelValue', value); + context.emit('valueChange', value); + }; + context.expose({ getInputRef }); return () => { - return ; context?: any; /** 将事件参数的值转化为参数编辑器所需要的格式 */ @@ -14,6 +14,8 @@ export interface EditorConfig { convertEditorValue2EventParamValue?: (editorValue: any) => any; checkAndProcessBeforeAssign?:(modelValue: string) => any; + // 自定义模板 + render?: () => VNode | null; } @@ -54,11 +56,18 @@ export enum EditorType { AppIdSelector = 'AppIdSelector', ComboLookup = 'ComboLookup', ConfigurationParameterEditor = 'ConfigurationParameterEditor', - FieldMappingEditor = 'FieldMappingEditor' + FieldMappingEditor = 'FieldMappingEditor', + Custom = 'Custom' }; export const editorMap: EditorMap = reactive({}); +interface ParameterEditor { + type: string; + // 外部组件自定义模板 + customRender?: () => VNode | null; +} + export const eventParameterProps = { /** 编辑器类型 */ data: { type: Object as PropType>, default: [] }, @@ -96,6 +105,7 @@ export const eventParameterProps = { activeViewModelFieldData: { type: Array, default: [] }, /** 是否在“参数编辑器”弹框中显示“表单组件”树 */ showOutline: { type: Boolean, default: true }, + editor: { type: Object as PropType, default: {type: 'Default'}} } as Record; export type EventParameterProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/event-parameter/src/locales/designer/en.json b/packages/ui-vue/components/event-parameter/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/event-parameter/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/event-parameter/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/event-parameter/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/event-parameter/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/event-parameter/src/locales/ui/en.json b/packages/ui-vue/components/event-parameter/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..83882c619d57d3ba65d2a8eba935bdd02ec0313c --- /dev/null +++ b/packages/ui-vue/components/event-parameter/src/locales/ui/en.json @@ -0,0 +1,26 @@ +{ + "eventParameter": { + "title": "Parameter Editor", + "ok": "confirm", + "cancel": "cancel", + "workFlowClass": { + "title": "Please select a process category" + }, + "generalEditor": { + "field": "field", + "tabVar": "variable", + "form": "form components" + }, + "jsonEditor": { + "dialogTitle": "Configurable parameter editor", + "keyColumnTitle": "parameter", + "valueColumnTitle": "parameter value", + "addButtonText": "Add configuration parameters", + "keyColumnPlaceholder": "Please enter the parameters", + "error": "Expected array of parameter schema for JsonEditor, but received invalid JSON" + }, + "comboTree": { + "placeholder": "Please select" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/event-parameter/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/event-parameter/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..111aeb5600d7181d3d4d118886e1c2aa19c150c0 --- /dev/null +++ b/packages/ui-vue/components/event-parameter/src/locales/ui/zh-CHS.json @@ -0,0 +1,26 @@ +{ + "eventParameter": { + "title": "参数编辑器", + "ok": "确定", + "cancel": "取消", + "workFlowClass": { + "title": "请选择流程分类" + }, + "generalEditor": { + "field": "字段", + "tabVar": "变量", + "form": "表单组件" + }, + "jsonEditor": { + "dialogTitle": "可配置参数编辑器", + "keyColumnTitle": "参数", + "valueColumnTitle": "参数值", + "addButtonText": "添加配置参数", + "keyColumnPlaceholder": "请输入参数", + "error": "JsonEditor的参数预期是数组,但收到无效的JSON" + }, + "comboTree": { + "placeholder": "请选择" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/event-parameter/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/event-parameter/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..78b2267b8cf94afe9610010427f4028f5b8677e3 --- /dev/null +++ b/packages/ui-vue/components/event-parameter/src/locales/ui/zh-CHT.json @@ -0,0 +1,26 @@ +{ + "eventParameter": { + "title": "參數編輯器", + "ok": "確定", + "cancel": "取消", + "workFlowClass": { + "title": "請選擇流程分類" + }, + "generalEditor": { + "field": "欄位", + "tabVar": "變數", + "form": "表單元件" + }, + "jsonEditor": { + "dialogTitle": "可配置參數編輯器", + "keyColumnTitle": "參數", + "valueColumnTitle": "參數值", + "addButtonText": "添加配置參數", + "keyColumnPlaceholder": "請輸入參數", + "error": "JsonEditor的參數預期是數位,但收到無效的JSON" + }, + "comboTree": { + "placeholder": "請選擇" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.component.tsx b/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..346f34f132c7e7349bc93792406dd545e93bbc19 --- /dev/null +++ b/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.component.tsx @@ -0,0 +1,168 @@ +import { SetupContext, defineComponent, ref, onBeforeMount, inject } from "vue"; +import { boundEventSelectorProps, BoundEventSelectorProps } from "./bound-event-selector.props"; +import FInputGroup from '@farris/ui-vue/components/input-group'; +import { cloneDeep } from "lodash-es"; +import './bound-event-selector.css'; +import { FNotifyService } from "@farris/ui-vue/components/notify"; + +/** + * 选择已有方法 + */ +export default defineComponent({ + name: 'FBoundEventSelector', + props: boundEventSelectorProps, + emits: ['cancel', 'submit'] as (string[] & ThisType) | undefined, + setup(props: BoundEventSelectorProps, context: SetupContext) { + const viewModelDisplay = ref(); + const viewModelData = ref([]); + const groupIcon = ref(''); + const searchValue = ref(''); + const selectedCommand = ref(); + + onBeforeMount(() => { + if (props.getEventPath) { + const eventInfo = props.getEventPath(); + viewModelDisplay.value = eventInfo.viewModelDisplay; + const { actionWithPath } = eventInfo; + actionWithPath.forEach(vmItem => { + vmItem.sourceComponent.map.forEach(mapItem => { + mapItem['active'] = false; + mapItem['hide'] = false; + }); + }); + viewModelData.value = cloneDeep(actionWithPath); + + } + }); + /** 匹配满足条件的方法并显示 */ + function matchCommand(changeValue = '') { + changeValue = changeValue.replace(/ /g, '').replace(/>/g, '').toLowerCase(); + if (changeValue === '') { + changeValue = ' '; + } + viewModelData.value.forEach(viewModelDataItem => { + const pathString = viewModelDataItem.path; + viewModelDataItem.sourceComponent.map.forEach(mapItem => { + const commandString = mapItem.command.name; + const eventString = mapItem.event.name; + const commandLabelString = mapItem.command.label; + const searchContent = `${pathString}${commandString}${eventString}${commandLabelString}`.toLowerCase(); + if (!searchContent.includes(changeValue)) { + mapItem['hide'] = true; + } else { + mapItem['hide'] = false; + } + }); + }); + + } + + function renderViewModelSearchBar() { + return ( + + ); + } + + function viewModelItemClass(mapItem: any) { + const classObject = { + 'f-vm-item': !mapItem.active, + 'f-vm-item-focus': mapItem.active + } as Record; + return classObject; + } + + function getViewModelCommand(value: any, viewModelDataItem: any) { + let count = 0; + viewModelData.value.forEach(vmItem => { + vmItem.sourceComponent.map.forEach(mapItem => { + if (!mapItem.command['isInvalid'] && value.controller.id === mapItem.controller.id && value.command.id === mapItem.command.id && value.event.label === mapItem.event.label && viewModelDataItem.sourceComponent.id === vmItem.sourceComponent.id) { + value['active'] = !value['active']; + selectedCommand.value = cloneDeep(value); + } + else { + if (mapItem.command['isInvalid'] && value.controller.id === mapItem.controller.id && value.command.id === mapItem.command.id && value.event.label === mapItem.event.label && viewModelDataItem.sourceComponent.id === vmItem.sourceComponent.id) { + count++; + if (count === 1) { + const notifyService = new FNotifyService(); + notifyService.info({ position: 'top-center', message: '该方法已失效' }); + selectedCommand.value = null; + } + } + mapItem['active'] = false; + } + }); + }); + } + + function viewModelItemCommandClass(mapItem: any) { + const classObject = { + 'f-event-func': !mapItem.command.isInvalid, + 'f-event-func-invalid': mapItem.command.isInvalid + } as Record; + return classObject; + + } + + function renderViewModelMapItem(viewModelDataItem: any) { + return viewModelDataItem.sourceComponent.map.filter((mapItem: any) => !mapItem.hide) + .map((mapItem: any) => { + if (!mapItem.command.isInvalid) { + return ( +
getViewModelCommand(mapItem, viewModelDataItem)}> +
+
+
+
+
+ {mapItem.command.name}({mapItem.command.label}) +
+
+
{viewModelDataItem.path}{mapItem.event.name}
+
+ ); + } + }); + } + + function renderViewModels() { + return viewModelData.value.length > 0 && +
+ {viewModelData.value.map((viewModelDataItem: any) => { + return renderViewModelMapItem(viewModelDataItem); + })} +
; + + } + + function onCancel() { + context.emit('cancel'); + } + + function onSubmit() { + if (!selectedCommand.value) { + props.notifyService.info({ position: 'top-center', message: '请选择需要绑定的方法' }); + return; + } + context.emit('submit', { + selectedCommand: selectedCommand.value + }); + } + return () => { + return ( +
+ {renderViewModelSearchBar()} + {renderViewModels()} + +
+ ); + }; + }, +}); diff --git a/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.css b/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.css new file mode 100644 index 0000000000000000000000000000000000000000..1f67d8d95317bb41ffca55898c35ed07d125dc08 --- /dev/null +++ b/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.css @@ -0,0 +1,142 @@ +.f-vm-all { + min-width: 180px; + height: 100%; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.f-vm-search { + height: 60px; + background-color: #fff; + width: 96%; + z-index: 2; + margin: 0px 2%; +} + +.f-vm-searchBar { + margin: 15px 10px; +} + +.f-vm-viewModel { + display: block; + overflow: auto; + flex: 1; +} + +.f-vm-viewModel>:first-child { + border-top: 1px solid #e5e9ef; + border-top-left-radius: 8px; + border-top-right-radius: 8px; +} + +.f-vm-viewModel>:last-child { + border-bottom: 1px solid #e5e9ef; + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; +} + +.f-event-commandItem { + display: flex; +} + +.f-icon-command { + width: 16px; + height: 16px; + background-color: #DBF4E7; + margin: 10px 6px 4px 16px; + display: flex; + padding: 1px 2px 0px 3px; + vertical-align: top; + border-radius: 2px; + align-items: center; +} + +input::-webkit-input-placeholder { + font-family: PingFangSC-Regular; + font-size: 13px; + color: #B4BCCC; + font-weight: 400; +} + +.f-event-path { + margin: 5px 50px; + color: #949BA7; + font-family: PingFangSC-Regular; + font-size: 12px; + font-weight: 400; + margin: 0px 0px 8px 16px; +} + +.f-event-func { + margin: 9px 0 4px 0px; + font-family: PingFangSC-Regular; + font-size: 13px; + vertical-align: top; + color: #2d2f33; + font-weight: 400; + height: 18px; + display: inline-block; + max-width: 92%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.f-event-func-invalid { + margin: 9px 0 4px 0px; + font-family: PingFangSC-Regular; + font-size: 13px; + vertical-align: top; + color: #949BA7; + font-weight: 400; + height: 18px; + display: inline-block; + max-width: 92%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + + +.f-vm-item { + margin: 0px 30px; + border-bottom: 1px solid #e5e9ef; + border-left: 1px solid #e5e9ef; + border-right: 1px solid #e5e9ef; +} + +.f-vm-item:hover { + cursor: pointer; + border-bottom: 1px solid #e5e9ef; + border-left: 1px solid #e5e9ef; + border-right: 1px solid #e5e9ef; + background-color: #EDF5FC !important; +} + +.f-vm-viewModel>:first-child .f-vm-item:hover { + border-top: 1px solid #e5e9ef; + border-top-left-radius: 8px; + border-top-right-radius: 8px; +} + +.f-vm-viewModel>:last-child>:last-child .f-vm-item:hover { + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; +} + +.f-vm-item-focus { + border-bottom: 1px solid #e5e9ef; + border-left: 1px solid #e5e9ef; + border-right: 1px solid #e5e9ef; + background-color: #EDF5FC !important; + margin: 0px 30px; +} + +.f-vm-item-focus:hover { + border-bottom: 1px solid #e5e9ef; + border-left: 1px solid #e5e9ef; + border-right: 1px solid #e5e9ef; + background-color: #EDF5FC !important; + margin: 0px 30px; +} \ No newline at end of file diff --git a/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.props.ts b/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..bfc533bc4e8d9d66addd957edc72a230d0f682c4 --- /dev/null +++ b/packages/ui-vue/components/events-editor/src/components/bound-event-selector/bound-event-selector.props.ts @@ -0,0 +1,8 @@ +import { ExtractPropTypes } from "vue"; + +export const boundEventSelectorProps = { + getEventPath: { type: Function, default: null }, + notifyService: { type: Object } +} as Record; + +export type BoundEventSelectorProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.component.tsx b/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.component.tsx index ce09887fbb2f5e642eef325fa9dd2e88f5ed3b18..aa63ee50ac7e16ee095e0860ef46812cbbe91055 100644 --- a/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.component.tsx @@ -6,7 +6,7 @@ import FSelectParameters from "./select-parameter.component"; import FSelectCommand from "./select-command.component"; import "./combine-form.scss"; import { useCommunication } from "./use-communication"; -import { Communication } from "../../types"; +import { Communication } from "./types"; import FSelectSourceControl from './select-source-control.component'; /** @@ -22,8 +22,9 @@ export default defineComponent({ const { saveCommunication, resolveCommunication } = useCommunication(useFormSchema); const communication = ref() as Ref; communication.value = resolveCommunication(props); - const shouldShowSourceControl = ref(false); - + const shouldShowSourceControl = ref(props.sourceCommunication?.needSelectSourceControl); + const shouldShowTargetPanel = computed(() => communication.value?.source?.componentId && communication.value?.source?.event); + const parameterRef = ref(); onMounted(() => { }); @@ -41,6 +42,9 @@ export default defineComponent({ saveCommunication(communication.value); context.emit('changed', communication.value); + if (parameterRef.value) { + parameterRef.value.closeAllParameterPanel(); + } } /** 切换目标命令 */ function onSelectTargetCommand(targetCommand: any) { @@ -48,27 +52,65 @@ export default defineComponent({ communication.value.target.commandViewmodelId = targetCommand.viewmodelId; context.emit('changed', communication.value); + + if (parameterRef.value) { + parameterRef.value.closeAllParameterPanel(); + } } /** 变更参数 */ function onParameterChanged() { context.emit('changed', communication.value); } /** 修改源表单控件 */ - function onSelectSourceControl() { + function onSelectSourceControl(source: any) { + communication.value.source.componentId = source.componentId; + communication.value.source.event = ''; + if (parameterRef.value) { + parameterRef.value.closeAllParameterPanel(); + } + } + /** 修改源表单事件 */ + function onSelectSourceControlEvent(source: any) { + communication.value.source.componentId = source.componentId; + communication.value.source.event = source.event; + + saveCommunication(communication.value); + context.emit('changed', communication.value); + if (parameterRef.value) { + parameterRef.value.closeAllParameterPanel(); + } + } + /** 删除源控件(删除通讯) */ + function onDeleteControl() { + context.emit('delete'); + } + + /** 新增参数 */ + function onAddParameter() { + if (parameterRef.value) { + parameterRef.value.addParameter(); + } } return () => { return ( -
- {communication.value?.source?.componentId ? '' : } - - {communication.value?.target?.formId ? <> - - +
+ {shouldShowSourceControl.value && + } + {shouldShowTargetPanel.value && <> + + {communication.value?.target?.formId && <> + + + } - : ''} + } -
+
); }; } diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.scss b/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.scss index 09fb32f37b23f026e8c83fccceaf5c9b5e3929e5..5691998d6091e0bc5b5d1b06444e5f2b7d56d4da 100644 --- a/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.scss +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/combine-form.scss @@ -4,23 +4,47 @@ .param-container { .selected-item { display: flex; - padding: 0 8px; align-items: center; .f-icon { color: #8DA3CE; } + .form-control-tip { + padding: 2px; + background: #F0F3F9; + border-radius: 5px; + color: #8da3ce; + width: 72px; + + >div { + color: #5b89fe; + background: #fff; + display: flex; + justify-content: center; + padding: 0 8px; + border-radius: 4px; + } + } + .form-control { - border-top: 0; - border-right: 0; - border-left: 0; + border: 0; flex: 1; border-radius: 0; box-shadow: unset !important; text-overflow: ellipsis; white-space: nowrap; overflow: hidden !important; + background-color: inherit; + padding-right: 20px; + } + + .form-control:focus { + border-bottom: 1px solid rgb(217, 222, 231); + } + + .f-icon-circle { + font-size: 8px; } } @@ -53,11 +77,14 @@ border-radius: 6px; } - .item:hover, - .item.selected { + .item:hover { background: #EDF5FF; } + .item.selected { + background: #dae9ff; + } + .item.disabled { color: #b4bccc; } @@ -67,18 +94,46 @@ cursor: default; } } + + .remove-icon { + right: 0; + position: absolute; + } } .param-container { .param-panel { padding: 6px; background-color: #F7FAFF; - box-shadow: 0px 14px 20px 0px rgba(3, 18, 51, 0.07); + box-shadow: 0px 0px 20px 0px rgba(3, 18, 51, 0.07); border-radius: 6px; .col-form-label { margin-bottom: 0; } } + + .form-control { + padding-right: 10px !important; + } + } + + .form-container.invalid, + .param-container.invalid { + .remove-icon { + right: 14px; + } + } + + .fv-grid-row-selected { + border-radius: 6px; + } + + .f-icon-flowline-warning { + font-size: 14px; + } + + .f-icon-yxs_delete { + font-size: 13px; } } \ No newline at end of file diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/select-command.component.tsx b/packages/ui-vue/components/events-editor/src/components/combine-form/select-command.component.tsx index bba47212feecfff07a7bb08ff246cb726bd3420f..344aeda4ae57771ad85fee1443d91da714af6c6c 100644 --- a/packages/ui-vue/components/events-editor/src/components/combine-form/select-command.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/select-command.component.tsx @@ -1,5 +1,5 @@ import { SetupContext, defineComponent, ref, inject, computed, onMounted, watch, Ref } from "vue"; -import { Communication } from "../../types"; +import { Communication } from "./types"; import { combineFormProps, CombineFormProps } from './combine-form.props'; /** @@ -8,7 +8,7 @@ import { combineFormProps, CombineFormProps } from './combine-form.props'; export default defineComponent({ name: 'FSelectCommand', props: combineFormProps, - emits: ['selectCommand'] as (string[] & ThisType) | undefined, + emits: ['selectCommand', 'addParameter'] as (string[] & ThisType) | undefined, setup(props: CombineFormProps, context: SetupContext) { const communication = ref(props.communication) as Ref; const useFormSchema = inject('useFormSchema') as any; @@ -16,24 +16,43 @@ export default defineComponent({ const targetCommands: any = ref([]); const selectedTargetCommand = ref(); const shouldShowCommandList = ref(false); - const displayCommandName = computed(() => selectedTargetCommand.value?.name); + const displayCommandName = computed(() => selectedTargetCommand.value?.name || selectedTargetCommand.value?.code); + /** 当前已选目标表单是否已失效(被移除) */ + const isInValidCommand = ref(false); watch(() => props.communication, (newData) => { communication.value = newData; }, { deep: true }); - /** 获取所有模型下的变量 */ - function getAllCommands(viewModels: any[]) { - if (viewModels?.length === 0) { + /** 获取初始选中的目标命令 */ + function resolveInitialTargetCommand() { + if (communication.value?.target?.commandCode) { + const { commandCode, commandViewmodelId } = communication.value.target; + const command = targetCommands.value.find(command => command.code === commandCode && command.viewmodelId === commandViewmodelId); + selectedTargetCommand.value = command ? command : { code: communication.value.target.commandCode }; + isInValidCommand.value = !command; + } else { + selectedTargetCommand.value = null; + isInValidCommand.value = false; + } + } + + /** 获取所有模型下的命令 */ + function getAllCommands(formSchema: any) { + const { viewmodels: viewModels, components } = formSchema.module; + if (viewModels?.length === 0 || components?.length === 0) { return []; } targetCommands.value = []; + const viewModelNameResolver = props.sourceCommunication?.parameterData?.getViewModelName; viewModels.forEach(viewModel => { if (!viewModel || !viewModel.commands || viewModel.commands.length === 0) { return []; } + const component = components.find(item => item.viewModel === viewModel.id); + const viewModelName = viewModelNameResolver && component ? viewModelNameResolver(component, viewModel.name) : ''; targetCommands.value.push({ - id: viewModel.id, code: viewModel.code, name: viewModel.name + id: viewModel.id, code: viewModel.code, name: viewModelName || viewModel.name }); viewModel.commands.forEach(command => { if (!command.isInvalid) { @@ -43,25 +62,23 @@ export default defineComponent({ }); } + /** 组装目标页面的命令列表 */ function getTargetCommands() { if (!communication.value?.target?.formId) { return []; } // 当前表单 if (communication.value.target.formId === formMetaBasicInfo.id) { - getAllCommands(useFormSchema.getViewModels()); + getAllCommands(useFormSchema.getFormSchema()); } else { // 外部表单 const targetFormSchema = useFormSchema.externalFormSchema.get(communication.value?.target?.externalContainerId); - if (targetFormSchema?.content?.module?.viewmodels) { - getAllCommands(targetFormSchema.content.module.viewmodels); + if (targetFormSchema?.content?.module) { + getAllCommands(targetFormSchema.content); } } - if (communication.value?.target?.commandCode) { - const { commandCode, commandViewmodelId } = communication.value.target; - selectedTargetCommand.value = targetCommands.value.find(command => command.code === commandCode && command.viewmodelId === commandViewmodelId); - } + resolveInitialTargetCommand(); } onMounted(() => { @@ -87,16 +104,22 @@ export default defineComponent({ if (targetCommand.isCommandNode) { shouldShowCommandList.value = false; selectedTargetCommand.value = targetCommand; + isInValidCommand.value = false; context.emit("selectCommand", targetCommand); } } + + function onAddParameter() { + context.emit("addParameter"); + } return () => { return ( -
-
- - +
+
+
目标命令
+ + {isInValidCommand.value ? : ''}
{shouldShowCommandList.value ?
请选择目标命令 @@ -104,7 +127,10 @@ export default defineComponent({ return onSelectCommand(targetCommand)}>{targetCommand.name}; })}
: ''} -
+ + {displayCommandName.value && !isInValidCommand.value && + < div class="f-icon f-icon-home-add mr-2" title="新增参数" style="font-size: 11px;position: absolute;top: 8px;right: 0;" onClick={onAddParameter}>
} +
); }; } diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/select-parameter.component.tsx b/packages/ui-vue/components/events-editor/src/components/combine-form/select-parameter.component.tsx index c732e4efdd30e72f5dfbca1deac0d59dc42a2d40..452d40bc154e16e5a655557f05e5fbd487fea5f9 100644 --- a/packages/ui-vue/components/events-editor/src/components/combine-form/select-parameter.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/select-parameter.component.tsx @@ -1,9 +1,12 @@ import { FDynamicFormGroup } from "../../../../dynamic-form"; -import { SetupContext, defineComponent, ref, inject, computed, onMounted, watch, Ref } from "vue"; +import { SetupContext, defineComponent, ref, inject, computed, onMounted, watch, Ref, onUnmounted } from "vue"; import { combineFormProps, CombineFormProps } from './combine-form.props'; import { useGuid } from "@farris/ui-vue/components/common"; import { VisualData } from "../../../../data-view"; -import { Communication } from "../../types"; +import { Communication, CommunicationParameter } from "./types"; +import { cloneDeep } from "lodash-es"; +import { FNotifyService } from "@farris/ui-vue/components/notify"; +import FEventParameter from "@farris/ui-vue/components/event-parameter"; /** * 配置通讯参数 @@ -13,6 +16,9 @@ export default defineComponent({ props: combineFormProps, emits: ['changed'] as (string[] & ThisType) | undefined, setup(props: CombineFormProps, context: SetupContext) { + const notifyService = new FNotifyService(); + notifyService.globalConfig = { position: 'top-center' }; + /** 当前通讯数据 */ const communication = ref(props.communication) as Ref; /** 参数列表 */ @@ -26,34 +32,63 @@ export default defineComponent({ const targetVariablePlainData: any = ref([]); /** 是否显示参数编辑面板 */ const shouldShowParam = ref({}); + /** 目标变量是否失效 */ + const isInValidParam = ref({}); /** 变量文本框的展示文本 */ const paramDispayText = computed(() => (parameter: any) => { if (parameter.sourceValue && parameter.targetVariable) { - const targetVariable = targetVariablePlainData.value.find(param => param.statePath === parameter.targetVariableViewModelId); + const targetVariable = targetVariablePlainData.value.find(param => param.statePath === `${parameter.targetVariableViewModelId}.${parameter.targetVariable}`); if (targetVariable && targetVariable.name) { + isInValidParam.value[parameter.targetVariable] = false; return `${parameter.sourceValue}-->${targetVariable.name}`; + } else { + isInValidParam.value[parameter.targetVariable] = true; + return `${parameter.sourceValue}-->${parameter.targetVariable}`; } - return `${parameter.sourceValue}-->${parameter.targetVariable}`; } }); + watch(() => props.communication, (newDatas) => { communication.value = newDatas; + parameters.value = communication.value.paramMappings || []; }, { deep: true }); + /** 校验参数:删除源参数和目标参数都为空的条目 */ + function checkParameterValidation() { + + if (parameters.value.length) { + parameters.value = parameters.value.filter(paramMapping => { + if (!paramMapping.sourceValue && !paramMapping.targetVariable) { + return false; + } + return true; + }); + } + communication.value.paramMappings = parameters.value; + } + onUnmounted(() => { + checkParameterValidation(); + }); + /** 获取所有模型下的变量,组装成树结构 */ - function getAllVariables(viewModels: any[]) { - if (viewModels?.length === 0) { + function getAllVariables(formSchema: any) { + const { viewmodels: viewModels, components } = formSchema.module; + if (viewModels?.length === 0 || components?.length === 0) { return []; } const vmTree = [] as any; targetVariablePlainData.value = []; + const viewModelNameResolver = props.sourceCommunication?.parameterData?.getViewModelName; + viewModels.forEach(viewModel => { if (!viewModel || !viewModel.states || viewModel.states.length === 0) { return []; } const children = [] as any; + const component = components.find(item => item.viewModel === viewModel.id); + const viewModelName = viewModelNameResolver && component ? viewModelNameResolver(component, viewModel.name) : ''; viewModel.states.forEach(variable => { const data = { ...variable, statePath: viewModel.id + '.' + variable.code }; children.push({ data, children: [] }); @@ -61,7 +96,7 @@ export default defineComponent({ }); const rootVm = { - data: { id: viewModel.id, code: viewModel.code, name: viewModel.name, statePath: viewModel.id }, + data: { id: viewModel.id, code: viewModel.code, name: viewModelName || viewModel.name, statePath: viewModel.id }, children }; vmTree.push(rootVm); @@ -75,26 +110,35 @@ export default defineComponent({ } // 当前表单 if (communication.value.target.formId === formMetaBasicInfo.id) { - targetVariableTreeData.value = getAllVariables(useFormSchema.getViewModels()); + targetVariableTreeData.value = getAllVariables(useFormSchema.getFormSchema()); } else { // 外部表单 const targetFormSchema = useFormSchema.externalFormSchema.get(communication.value.target.externalContainerId); - if (targetFormSchema?.content?.module?.viewmodels) { - targetVariableTreeData.value = getAllVariables(targetFormSchema?.content?.module.viewmodels); + if (targetFormSchema?.content?.module) { + targetVariableTreeData.value = getAllVariables(targetFormSchema?.content); } } } - + /** + * 关闭参数编辑面板 + */ + function closeAllParameterPanel() { + Object.keys(shouldShowParam.value).forEach(paramId => { + shouldShowParam.value[paramId] = false; + }); + } /** 新增参数 */ - function onAddParameter() { + function addParameter() { + closeAllParameterPanel(); + const newParamId = useGuid().guid(); parameters.value.push({ - id: useGuid().guid(), + id: newParamId, sourceValue: '', targetVariable: '', targetVariableViewModelId: '' }); communication.value.paramMappings = parameters.value; - context.emit('changed'); + shouldShowParam.value[newParamId] = true; } /** 删除参数 */ @@ -102,19 +146,38 @@ export default defineComponent({ payload.stopPropagation(); parameters.value = parameters.value.filter(item => item.id !== parameter.id); communication.value.paramMappings = parameters.value; + + closeAllParameterPanel(); context.emit('changed'); } - /** 点击已有参数,控制是否显示参数编辑面板 */ + /** 点击已有参数,打开参数编辑面板 */ function onClickSelectedParam(parameter: any) { - shouldShowParam.value[parameter.id] = !shouldShowParam.value[parameter.id]; + if (!shouldShowParam.value[parameter.id]) { + closeAllParameterPanel(); + shouldShowParam.value[parameter.id] = true; + } } + /** 提交参数面板 */ + function submitParam(parameter: any, parameterForEditor: any) { + if (!parameterForEditor.sourceValue) { + notifyService.warning('请选择源参数'); + return; + } + if (!parameterForEditor.targetVariable) { + notifyService.warning('请选择目标参数'); + return; + } + shouldShowParam.value[parameter.id] = false; + Object.assign(parameter, parameterForEditor); + delete parameter.statePath; + context.emit('changed'); + } /** 关闭参数面板 */ - function closeParam(parameter: any) { + function cancelParam(parameter: any) { shouldShowParam.value[parameter.id] = false; } - onMounted(() => { assembleTargetVariableTreeData(); }); @@ -124,16 +187,21 @@ export default defineComponent({ }, { deep: true }); /** 修改源参数 */ - function onChangeSourceParam() { - context.emit('changed'); + function onChangeSourceParam(newValue: string, parameter: any, selections: any) { + // context.emit('changed'); } /** 切换目标参数后,记录参数所属的模型路径 */ function onChangeTargetVariable(newValue: string, parameter: any, selections: any) { if (selections.newValue?.length) { - parameter.targetVariableViewModelId = selections.newValue[0].statePath; + const { statePath } = selections.newValue[0]; + if (statePath?.indexOf('.') > 0) { + const paths = statePath.split('.'); + parameter.targetVariableViewModelId = paths[0]; + parameter.targetVariable = paths[1]; + } } - context.emit('changed'); + // context.emit('changed'); } /** 配置目标参数的编辑器 */ @@ -141,7 +209,7 @@ export default defineComponent({ return { type: 'combo-tree', textField: 'name', - valueField: 'code', + valueField: 'statePath', idField: 'statePath', data: targetVariableTreeData.value, editable: false, @@ -153,7 +221,7 @@ export default defineComponent({ return visualData; } // 其他参数中已使用的变量,不可再次选择 - const existedParam = parameters.value.find(param => param.id !== currentParameter.id && param.targetVariableViewModelId === visualData.raw.statePath); + const existedParam = parameters.value.find(param => param.id !== currentParameter.id && `${param.targetVariableViewModelId}.${param.targetVariable}` === visualData.raw.statePath); if (existedParam) { visualData.disabled = true; } @@ -161,54 +229,81 @@ export default defineComponent({ } }; } - /** 渲染参数 */ - function renderParameterPanel() { - if (parameters.value.length) { - const souceParameterEditor = { type: 'input-group', enableClear: false }; + /** 渲染参数面板 */ + function renderParamPanel(parameter: CommunicationParameter) { + const parameterForEditor = cloneDeep(parameter); + parameterForEditor['statePath'] = `${parameterForEditor.targetVariableViewModelId}.${parameterForEditor.targetVariable}`; + const targetParameterEditor = resolveVariableTreeEditor(parameter); + // 获取源页面的schema结构,以便于组装源页面相关的字段、变量等树形结构 + let sourceFormSchema = useFormSchema.getFormSchema(); + if (communication.value.source.formId !== formMetaBasicInfo.id) { + const externalMetadata = useFormSchema.externalFormSchema.get(communication.value.source.externalContainerId); + if (externalMetadata?.content) { + sourceFormSchema = externalMetadata?.content; + } + } + const { assembleSchemaFieldsByComponent, assembleOutline, assembleStateVariables } = props.sourceCommunication.parameterData; + + return
+
+
+
+ +
+
+
+ + + onChangeTargetVariable(newValue, parameterForEditor, selections)}> + +
+ + +
+
; + } + + context.expose({ addParameter, closeAllParameterPanel }); + + const paramContainerClass = computed(() => (isInValid: boolean) => { + return { + 'param-container': true, + 'mt-2': true, + 'invalid': isInValid + }; + }); + return () => { + if (parameters.value.length) { return parameters.value.map(parameter => { - const targetParameterEditor = resolveVariableTreeEditor(parameter); const displayText = paramDispayText.value(parameter); - return (
-
- - onClickSelectedParam(parameter)}> -
-
onDeleteParameter(event, parameter)}>
+ const isInValid = isInValidParam.value[parameter.targetVariable]; + return (
+
+
参数映射
+ onClickSelectedParam(parameter)}> + {isInValid ? : ''} + +
+
onDeleteParameter(event, parameter)}>
- {shouldShowParam.value[parameter.id] ?
- - {/* */} - onChangeTargetVariable(newValue, parameter, selections)}> - -
- -
-
: ''} - + {shouldShowParam.value[parameter.id] ? renderParamPanel(parameter) : ''}
); }); } return ''; - } - - return () => { - return ( - <> - {renderParameterPanel()} -
-
新增参数 -
- - - ); }; } }); diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/select-source-control.component.tsx b/packages/ui-vue/components/events-editor/src/components/combine-form/select-source-control.component.tsx index 8ac8aa3e3e8c47e300d7bca2fed963a6feac3c40..a45113d4bac13026c9cc7dd53d5c7136d5e07be8 100644 --- a/packages/ui-vue/components/events-editor/src/components/combine-form/select-source-control.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/select-source-control.component.tsx @@ -2,8 +2,9 @@ import { SetupContext, defineComponent, ref, inject, computed, onMounted, Ref, w import { combineFormProps, CombineFormProps } from './combine-form.props'; import { useOutlineNode } from '../../../../designer-outline/src/composition/use-outline-node'; import { useDataView } from "../../../../designer-outline/src/composition/use-data-view"; -import { FTreeView } from "./../../../../tree-view"; +import { FTreeView } from "@farris/ui-vue/components/tree-view"; import "./combine-form.scss"; +import { schemaMap } from "@farris/ui-vue/components/dynamic-resolver"; /** * 选择源表单的控件以及事件 @@ -11,7 +12,7 @@ import "./combine-form.scss"; export default defineComponent({ name: 'FSelectSourceControl', props: combineFormProps, - emits: ['selectControl'] as (string[] & ThisType) | undefined, + emits: ['selectSourceEvent', 'deleteControl'] as (string[] & ThisType) | undefined, setup(props: CombineFormProps, context: SetupContext) { /** 当前通讯数据 */ const communication = ref(props.communication); @@ -23,14 +24,33 @@ export default defineComponent({ const sourceControlTreeData: any = ref([]); /** 当前选择的源控件 */ const selectedSourceControl = ref(); - /** 控件文本框展示文本 */ - const dispayControlName = computed(() => selectedSourceControl.value?.name); + /** 控件大纲树实例 */ const controlTreeRef = ref(); - /** 是否显示事件下拉列表 */ + /** 是否显示控件下拉列表 */ const shouldShowControlList = ref(false); /** 是否显示事件下拉列表 */ const shouldShowEventList = ref(false); + /** 事件列表 */ + const eventListData = ref>([]); + /** 当前选择的事件 */ + const selectedEvent = ref(); + /** 是否初始加载 */ + const isInit = ref(true); + /** 当前已选源控件是否已失效(被移除) */ + const isInValidControl = ref(false); + + /** 控件文本框展示文本 */ + const dispayControlName = computed(() => { + if (isInValidControl.value) { + return selectedSourceControl.value?.id || ''; + } + let displayName = selectedSourceControl.value?.name; + if (displayName && selectedEvent.value?.eventName) { + displayName += `-${selectedEvent.value?.eventName}`; + } + return displayName; + }); watch(() => props.communication, (newDatas) => { @@ -49,10 +69,41 @@ export default defineComponent({ sourceControlTreeData.value = getData(); controlTreeRef.value.updateDataSource(sourceControlTreeData.value); } + + } + /** + * 定位并回显已选控件 + */ + function assembleInitControl() { + if (!communication.value?.source?.componentId) { + isInValidControl.value = false; + return; + } + const selectedItem = sourceControlTreeData.value?.find(data => data.originalId === communication.value.source.componentId); + if (selectedItem) { + controlTreeRef.value.selectItemById(selectedItem.id); + isInValidControl.value = false; + } else { + isInValidControl.value = true; + selectedSourceControl.value = { id: communication.value.source.componentId }; + } + } + + /** + * 定位并回显已选事件 + */ + function assembleInitControlEvent() { + if (communication.value?.source?.event && eventListData.value?.length) { + selectedEvent.value = eventListData.value.find(event => event.eventCode === communication.value?.source?.event); + } + } onMounted(() => { assembleSourceOutline(); + assembleInitControl(); + assembleInitControlEvent(); + isInit.value = false; }); /** 点击控件行,控制是否显示大纲树列表 */ @@ -60,11 +111,39 @@ export default defineComponent({ shouldShowControlList.value = !shouldShowControlList.value; } + /** 组装控件对应的事件 */ + function resolveEventList() { + eventListData.value = []; + const controlSchema = selectedSourceControl.value.rawSchema; + const controlType = controlSchema.type === 'form-group' && controlSchema.editor ? controlSchema.editor.type : controlSchema.type; + + const eventMap = schemaMap[controlType]?.events; + if (eventMap) { + Object.keys(eventMap).forEach(eventCode => { + eventListData.value.push({ eventCode, eventName: eventMap[eventCode] }); + }); + } + } /** 选择源控件 */ function onChangedSouceControl(targetControl: any) { shouldShowControlList.value = false; - selectedSourceControl.value = targetControl[0]; - // context.emit("selectForm", targetForm); + isInValidControl.value = false; + const isControlChanged = selectedSourceControl.value !== targetControl[0]; + if (isControlChanged) { + selectedSourceControl.value = targetControl[0]; + resolveEventList(); + } + if (!isInit.value) { + shouldShowEventList.value = true; + } + + if (!isInit.value && isControlChanged) { + selectedEvent.value = null; + context.emit("selectSourceControl", { + componentId: selectedSourceControl.value.rawSchema.id, + event: '' + }); + } } const controlListPanelClass = computed(() => { return { @@ -73,16 +152,56 @@ export default defineComponent({ }; }); + const eventItemClass = computed(() => (itemCode: string) => { + return { + 'item': true, + 'selected': selectedEvent.value?.eventCode === itemCode + }; + }); + /** 选择事件 */ + function onSelectEvent(event: any) { + shouldShowEventList.value = false; + selectedEvent.value = event; + + context.emit("selectSourceEvent", { + componentId: selectedSourceControl.value.rawSchema.id, + event: selectedEvent.value?.eventCode + + }); + } + /** + * 删除源控件(删除通讯) + * @param event + */ + function onDeleteControl() { + context.emit("deleteControl"); + } + + const controlContainerClass = computed(() => { + return { + 'form-container': true, + 'mt-2': true, + 'position-relative': true, + 'invalid': isInValidControl.value + }; + }); + return () => { return ( -
-
- - +
+
+
+
+
源控件
+ + {isInValidControl.value ? : ''} + +
+
请选择源控件 -
+ {shouldShowEventList.value && <> +
+ 请选择事件 + {eventListData.value.map(event => { + return onSelectEvent(event)}>{event.eventName}; + })} +
+ } +
); }; diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/select-target-form.component.tsx b/packages/ui-vue/components/events-editor/src/components/combine-form/select-target-form.component.tsx index 27395ae40a92636cda6e52b3eb8f251b6319fcd2..fdd545fd814da0cd326f0db89db7947dee148991 100644 --- a/packages/ui-vue/components/events-editor/src/components/combine-form/select-target-form.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/select-target-form.component.tsx @@ -1,5 +1,5 @@ import { SetupContext, defineComponent, ref, inject, computed, onMounted, Ref, watch } from "vue"; -import { Communication } from "../../types"; +import { Communication } from "./types"; import { combineFormProps, CombineFormProps } from './combine-form.props'; /** @@ -21,12 +21,29 @@ export default defineComponent({ /** 是否显示页面下拉列表 */ const shouldShowFormList = ref(false); /** 文本框展示文本 */ - const dispayFormName = computed(() => selectedTargetForm.value?.name); + const dispayFormName = computed(() => selectedTargetForm.value?.name || selectedTargetForm.value?.code); + /** 当前已选目标表单是否已失效(被移除) */ + const isInValidTarget = ref(false); watch(() => props.communication, (newDatas) => { communication.value = newDatas; }, { deep: true }); + /** 获取初始选中的目标页面 */ + function resolveInitialTargetForm() { + if (communication.value?.target?.formId) { + const targetForm = targetForms.value.find(form => form.id === communication.value.target.formId && form.containerId === communication.value.target.externalContainerId); + isInValidTarget.value = !targetForm; + selectedTargetForm.value = targetForm ? targetForm : + { + containerId: communication.value.target.externalContainerId, + id: communication.value.target.formId, + code: communication.value.target.formCode + }; + } else { + isInValidTarget.value = false; + } + } /** 组装目标页面数据列表 */ function getTargetForms() { const externalFormMap = useFormSchema.externalFormSchema; @@ -50,19 +67,17 @@ export default defineComponent({ name: formMetaBasicInfo.name }); } - if (communication.value?.target?.formId) { - selectedTargetForm.value = targetForms.value.find(form => form.id === communication.value.target.formId); - } + resolveInitialTargetForm(); } onMounted(() => { getTargetForms(); }); - const formItemClass = computed(() => (itemId: string) => { + const formItemClass = computed(() => (item: any) => { return { 'item': true, - 'selected': selectedTargetForm.value?.id === itemId + 'selected': selectedTargetForm.value?.id === item.id && selectedTargetForm.value?.containerId === item.containerId }; }); @@ -74,20 +89,23 @@ export default defineComponent({ /** 选择目标页面 */ function onSelectForm(targetForm: any) { shouldShowFormList.value = false; + isInValidTarget.value = false; selectedTargetForm.value = targetForm; context.emit("selectForm", targetForm); } return () => { return (
-
- - +
+
目标页面
+ + {isInValidTarget.value ? : ''}
{shouldShowFormList.value ?
请选择目标页面 {targetForms.value.map(targetForm => { - return onSelectForm(targetForm)}>{targetForm.name}; + return onSelectForm(targetForm)} + title={targetForm.containerId ? `所属外部容器:${targetForm.containerId}` : '当前页面'}>{targetForm.name}; })}
: ''}
diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/types.ts b/packages/ui-vue/components/events-editor/src/components/combine-form/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..2c8a4246997bfdf3d460475b895787531bc720fe --- /dev/null +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/types.ts @@ -0,0 +1,61 @@ +/** + * 组件通讯中源页面配置 + */ +export interface CommunicationSource { + /** 源页面id */ + formId: string; + /** 源页面编号 */ + formCode?: string; + /** 源页面所属的外部容器id。 */ + externalContainerId?: string; + /** 源控件id */ + componentId: string; + /** 源控件所在的路径 */ + // componentPath?: string; + /** 源控件事件编号 */ + event: string; +} + +/** + * 组件通讯中目标页面配置 + */ +export interface CommunicationTarget { + /** 目标页面id */ + formId: string; + /** 目标页面编号 */ + formCode?: string; + /** 目标页面所属的外部容器id */ + externalContainerId: string; + + /** 目标页面的命令编号 */ + commandCode: string; + /** 目标页面的命令所属viewmodel id */ + commandViewmodelId: string; +} + +/** + * 组件通讯中参数配置 + */ +export interface CommunicationParameter { + id: string; + /** 源参数值 */ + sourceValue: string, + /** 目标页面的变量编号 */ + targetVariable: string; + /** 目标页面的变量所属viewmodel id */ + targetVariableViewModelId: string; +} + +/** + * 组件通讯 + */ +export interface Communication { + /** 唯一编号 */ + id: string; + /** 源页面配置 */ + source: CommunicationSource; + /** 目标页面配置 */ + target: CommunicationTarget + /** 参数 */ + paramMappings?: Array; +} diff --git a/packages/ui-vue/components/events-editor/src/components/combine-form/use-communication.ts b/packages/ui-vue/components/events-editor/src/components/combine-form/use-communication.ts index 19964998eb4dd44393a6e7ceadd30d4769889cc1..80f8a2103296a5420bc18e89e9568f48afa11b84 100644 --- a/packages/ui-vue/components/events-editor/src/components/combine-form/use-communication.ts +++ b/packages/ui-vue/components/events-editor/src/components/combine-form/use-communication.ts @@ -1,7 +1,8 @@ import { useGuid } from "@farris/ui-vue/components/common"; import { ref } from "vue"; -import { Communication, InteractionItem } from "../../types"; +import { Communication } from "./types"; import { CombineFormProps } from "./combine-form.props"; +import { InteractionItem } from "../../types"; export function useCommunication(useFormSchema: any) { @@ -69,6 +70,7 @@ export function useCommunication(useFormSchema: any) { } } + return { communication, resolveCommunication, diff --git a/packages/ui-vue/components/events-editor/src/components/command-source/command-source.component.tsx b/packages/ui-vue/components/events-editor/src/components/command-source/command-source.component.tsx index ab985b51af5bc623c11b0c3116aca1b57a3f6cd8..27f5ac9f641fc1f684cabbd7b63d1ba5ff78ebf2 100644 --- a/packages/ui-vue/components/events-editor/src/components/command-source/command-source.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/command-source/command-source.component.tsx @@ -1,6 +1,6 @@ -import { SetupContext, computed, defineComponent, inject, ref, watch, onMounted } from "vue"; +import { SetupContext, computed, defineComponent, inject, ref, onMounted } from "vue"; import { CommandSourceProps, commandSourceProps } from "./command-source.props"; -import { Command, CommandItem, MethodChangedEventArgs, PropertyItem } from "../../types"; +import { MethodChangedEventArgs } from "../../types"; import { FSchemaSelector } from '@farris/ui-vue/components/schema-selector'; import { F_MODAL_SERVICE_TOKEN } from '@farris/ui-vue/components/modal'; import { UseMethods } from '../../composition/types'; @@ -9,6 +9,8 @@ import './command-source.css'; import FCommandList from '../command-list/command-list.component'; import { ControllerSchemaRepositorySymbol } from "@farris/ui-vue/components/common"; import { FNotifyService as NotifyService } from "@farris/ui-vue/components/notify"; +import FBoundEventSelector from '../bound-event-selector/bound-event-selector.component'; + /** * 命令来源 * 选择事件后,点击【+】展开的下拉面板,下拉面板的内容: @@ -28,8 +30,7 @@ export default defineComponent({ emits: [ 'addInternalCommandListChanged', 'newImportChanged', - 'currentCommandChanged', - 'originalDataChanged', + 'selectBoundMethod', 'click', 'existChanged', 'newFunctionChanged', @@ -42,33 +43,17 @@ export default defineComponent({ const formCommandService = inject('useFormCommand') as any; const notifyService = new NotifyService(); const { checkIfNewControllerExists, addNewController } = methodsComposition; - const haveBoundCommand = ref(false); - const runtimeCustom = ref(props.canAddNewMethod); const controllers = ref(props.controllers); - - const shouldShowAddNewMethod = computed(() => { - return !runtimeCustom.value; - }); - - const shouldShowBondEvents = computed(() => { - return haveBoundCommand.value; - }); - - const actionButtonStyle = computed(() => { - return { - 'font-size': '15px', - 'margin': '5px', - 'color': '#226eff', - 'flex': '1 1 0' - }; - }); const useFormSchema = inject('useFormSchema') as any; const modalService = inject(F_MODAL_SERVICE_TOKEN) as any; let newFuncModal: any; + let existedMethodModal: any; const shoulShowCommunication = ref(false); + // 屏蔽目前不支持配置通讯的事件 + const unSupportedEventCodes = ['onClickLinkCommand']; onMounted(() => { - shoulShowCommunication.value = useFormSchema.externalFormSchema.size > 0; + shoulShowCommunication.value = !unSupportedEventCodes.includes(props.event.label) && useFormSchema.externalFormSchema.size > 0; }); /** * 「引入控制器」-方法绑定 @@ -130,13 +115,40 @@ export default defineComponent({ context.emit('newFunctionChanged'); } + function onCancelExsitedMethodModal() { + if (existedMethodModal && existedMethodModal.destroy) { + existedMethodModal.destroy(); + } + } + function onSubmitExsitedMethodModal(value: any) { + const { selectedCommand } = value; + methodsComposition.selectBoundCommand(selectedCommand, props.iteractionDisplayOrder); + onCancelExsitedMethodModal(); + context.emit('selectBoundMethod'); + } + /** 渲染已有方法窗口 */ + function renderBoundEvents() { + const { getEventPath } = methodsComposition; + return ; + } function showBoundEvents() { + existedMethodModal = modalService.open({ + title: '已有方法', + width: 950, + height: 500, + fitContent: false, + render: renderBoundEvents, + showButtons: false, + enableEsc: false, + draggable: true + }); } - function renderBondEvents() { - - } function onClose() { context.emit('close', null); @@ -157,44 +169,25 @@ export default defineComponent({ return (
-
+
openMetadataSelector()}>引入控制器
- {shouldShowAddNewMethod.value &&
-
+
+
generateNewFunc()}>添加新方法
-
} - {shouldShowBondEvents.value && renderBondEvents()} - {/*
-
+
+ {props.haveBoundCommand &&
+
showBoundEvents()}>已有方法
-
*/} +
} {shoulShowCommunication.value &&
-
-
openFormCommunicationPanel()}>配置组合表单通讯
+
+
openFormCommunicationPanel()}>添加组件通讯事件
}
); } - function renderControllerTitle(controller: Command) { - return ( -
-
- {controller.controllerName.name} -
-
- ); - } - - function renderMethod(methods: CommandItem[]) { - return methods.map((command: CommandItem) => ( -
-
{command.label}
-
- )); - } - function onSelectMethodChanged(selectedMethodChangeArgs: MethodChangedEventArgs) { context.emit('selectMethod', selectedMethodChangeArgs); } diff --git a/packages/ui-vue/components/events-editor/src/components/command-source/command-source.css b/packages/ui-vue/components/events-editor/src/components/command-source/command-source.css index 20b1a81b3371ca4274e7c822aa38ec9beecc18d7..b90808987d26dda8fe382fbdd999de6a91511826 100644 --- a/packages/ui-vue/components/events-editor/src/components/command-source/command-source.css +++ b/packages/ui-vue/components/events-editor/src/components/command-source/command-source.css @@ -11,9 +11,9 @@ padding: 10px 10px 0 10px; display: flex; flex-direction: column; - } - - .f-page-events-editor-command-source::before { +} + +.f-page-events-editor-command-source::before { content: ''; width: 0; height: 0; @@ -23,9 +23,9 @@ border-width: 7px; border-style: solid; border-color: transparent transparent #e2e5e6; - } - - .f-page-events-editor-command-source::after { +} + +.f-page-events-editor-command-source::after { content: ''; width: 0; height: 0; @@ -35,18 +35,18 @@ border-width: 7px; border-style: solid dashed dashed; border-color: transparent transparent #ffffff; - } - - .f-page-command-source-choice { +} + +.f-page-command-source-choice { overflow-x: hidden; overflow-y: scroll; padding: 5px 0px; border-radius: 3px; min-width: 200px; flex: 2 2 0; - } - - /* .f-page-command-source-choice{ +} + +/* .f-page-command-source-choice{ height: 315px; overflow-x:hidden; overflow-y: scroll; @@ -54,9 +54,9 @@ border-radius: 3px; min-width: 200px; } */ - - - .f-page-events-editor-switch { + + +.f-page-events-editor-switch { background: #EFF5FF; border-radius: 10px; margin: 10px; @@ -64,9 +64,9 @@ height: 32px; display: flex; text-align: center; - } - - .namelist { +} + +.namelist { font-family: PingFangSC-Regular; font-size: 14px; color: #446a9fd9; @@ -74,9 +74,9 @@ margin: 8px 13px 8px 13px; flex-grow: 1; cursor: pointer; - } - - .namelist-focus { +} + +.namelist-focus { opacity: 0.6; font-family: PingFangSC-Regular; font-size: 14px; @@ -89,28 +89,42 @@ box-shadow: 0 4px 18px 0 rgb(2 75 193 / 20%); border-radius: 10px; cursor: pointer; - } - - .f-page-internals-new-command { +} + +.f-page-internals-new-command { margin: 0px 2px 0px 13px; - } - - .f-page-internals-new-command-func1 { +} + +.f-page-internals-new-command-func1 { display: flex; margin: 4px 1px; - } - - .f-page-internals-new-command-func2 { +} + +.f-page-internals-new-command-func2 { display: flex; margin: 4px 1px; - } - - .f-page-internals-new-command-func3 { +} + +.f-page-internals-new-command-func3 { display: flex; margin: 4px 1px; - } - - .f-function-class { +} + +.f-page-internals-new-command .f-icon { + font-size: 15px; + margin: 5px; + color: #226eff; + flex: 1 1 0; +} + +.f-page-internals-new-command .fd-i-Family { + font-size: 14px; + margin: 5px; + color: #226eff; + flex: 1 1 0; +} + +.f-function-class { margin: 0 0 0 5px; font-family: PingFangSC-Regular; color: #4f6b9d; @@ -121,9 +135,9 @@ line-height: 26px; font-weight: 400; flex: 10; - } - - .f-page-internals-close { +} + +.f-page-internals-close { font-family: PingFangSC-Regular; font-size: 13px; margin: 7.75px 10px; @@ -134,13 +148,13 @@ color: #425159; font-weight: 400; text-align: center; - } - - .f-command-breakline { +} + +.f-command-breakline { color: #B4BCCC; margin: 2px; overflow: hidden; white-space: nowrap; text-overflow: clip; height: 10px; - } \ No newline at end of file +} \ No newline at end of file diff --git a/packages/ui-vue/components/events-editor/src/components/command-source/command-source.props.ts b/packages/ui-vue/components/events-editor/src/components/command-source/command-source.props.ts index b8ad427ed39bddb83b4cb900b5f088eeb5ef81a4..f32e07c41125f5a709820f46f41072ab3b485edb 100644 --- a/packages/ui-vue/components/events-editor/src/components/command-source/command-source.props.ts +++ b/packages/ui-vue/components/events-editor/src/components/command-source/command-source.props.ts @@ -11,8 +11,11 @@ export const commandSourceProps = { /** 判断是否为:点击“导入此方法”后,绑定了新增值 */ exit: { Type: Number, default: -1 }, /** 接收是否显示「可选择方法」功能 */ - haveBoundcommand: { Type: Boolean, default: false }, - controllers: { type: Array, default: [] } + haveBoundCommand: { Type: Boolean, default: false }, + controllers: { type: Array, default: [] }, + iteractionDisplayOrder: { Type: Number, default: 0 }, + /** 当前编辑的事件编号、事件名称 */ + event: { Type: Object } } as Record; export type CommandSourceProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/components/interaction-button/interaction-button.component.tsx b/packages/ui-vue/components/events-editor/src/components/interaction-button/interaction-button.component.tsx index 510b7f12169bc8a3d08cb967b3fc1013b77e6f48..6285906eb2f6799070e191c6bdf9d4df01a72c09 100644 --- a/packages/ui-vue/components/events-editor/src/components/interaction-button/interaction-button.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/interaction-button/interaction-button.component.tsx @@ -32,7 +32,7 @@ export default defineComponent({ context.emit('clickEventItem', eventItem); } function onClickActionButton() { - if (candidateEvents.value.length === 0) { + if (!props.sourceCommunication?.needSelectSourceControl && candidateEvents.value.length === 0) { notifyService.info({ position: 'top-center', message: '事件已全部绑定' }); return; } diff --git a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx index 62c580021c7db60d8e67bde00f63d5de81706994..a0f408cccd2c0365bc6a82c902c5846ce3339cf3 100644 --- a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.component.tsx @@ -1,6 +1,6 @@ import { SetupContext, computed, defineComponent, inject, ref } from "vue"; import { InteractionItemProps, interactionItemProps } from "./interaction-item.props"; -import { Command, CommandItem, Communication, InteractionItem, MethodChangedEventArgs } from "../../types"; +import { Command, CommandItem, InteractionItem, MethodChangedEventArgs } from "../../types"; import FCommandSource from '../command-source/command-source.component'; import FParameterEditor from '../parameter-editor/parameter-editor.component'; import { UseMethods } from "../../composition/types"; @@ -9,6 +9,7 @@ import { useGuid } from "@farris/ui-vue/components/common"; import FSelectSourceControl from '../combine-form/select-source-control.component'; import FCombineForm from "../combine-form/combine-form.component"; import { useCommunication } from "../combine-form/use-communication"; +import { Communication } from "../combine-form/types"; /** * 事件交互项 @@ -63,8 +64,8 @@ export default defineComponent({ const toggleButtonIconClass = computed(() => { const classObject = { 'f-icon': true, - 'f-icon-arrow-chevron-down': shouldShowMethod.value, - 'f-icon-arrow-chevron-left': !shouldShowMethod.value + 'f-icon-arrow-chevron-down': shouldShowMethod.value || shouldShowCommunicationPanel.value, + 'f-icon-arrow-chevron-left': !shouldShowMethod.value && !shouldShowCommunicationPanel.value }; return classObject; }); @@ -75,38 +76,47 @@ export default defineComponent({ function onClickToggleButton() { const switchOption = interaction.value.showSwitch[0]; - // 切换方法名显示状态 - switchOption.showSection[switchOption.showSwitchNumber][3] = - !switchOption.showSection[switchOption.showSwitchNumber][3]; // 切换通讯窗口显示状态 - if (interaction.value.communication) { + const hasCommunication = interaction.value.communication !== undefined; + if (hasCommunication) { shouldShowCommunicationPanel.value = !shouldShowCommunicationPanel.value; + } else { + // 切换方法名显示状态 + switchOption.showSection[switchOption.showSwitchNumber][3] = + !switchOption.showSection[switchOption.showSwitchNumber][3]; + } context.emit('toggle', props.displayOrder); } function onClickAddActionButton() { const hasBoundCommand = interaction.value.command !== null; - const hasCommunication = !!interaction.value.communication; + const hasCommunication = interaction.value.communication !== undefined; if (hasBoundCommand || hasCommunication) { - const switchOption = interaction.value.showSwitch[0]; - toggleSelectingMethod(switchOption); - // 切换通讯窗口显示状态 - if (interaction.value.communication) { - shouldShowCommunicationPanel.value = !shouldShowCommunicationPanel.value; + if (hasCommunication) { + shouldShowCommunicationPanel.value = shouldShowCommandSource.value; } + const switchOption = interaction.value.showSwitch[0]; + toggleSelectingMethod(switchOption, hasCommunication); + context.emit('addAction', props.displayOrder); } } function onCloseMethodList(command: CommandItem) { - if (command === null && interaction.value.command === null) { + const hasCommunication = interaction.value.communication !== undefined; + if (command === null && interaction.value.command === null && !hasCommunication) { context.emit('delete', props.displayOrder); } + + // 切换通讯窗口显示状态 + if (interaction.value.communication) { + shouldShowCommunicationPanel.value = shouldShowCommandSource.value; + } const switchOption = interaction.value.showSwitch[0]; - toggleSelectingMethod(switchOption); + toggleSelectingMethod(switchOption, hasCommunication); } /** * 生成不重复的方法名 @@ -156,10 +166,15 @@ export default defineComponent({ const cmpId = controllerName.id; const newMethod = { id: guid(), label: newName, name: `${method.name}${suffix}`, handlerName, hasPath, cmpId, property, componentLists }; interaction.value.command = updateCommandHasPath(controller, newMethod); + if (interaction.value.command && useFormSchema.designerMode === 'PC_RTC') { + interaction.value.command.isRtcCommand = true; + } + interaction.value.controller = { id: controllerName.id, label: controllerName.label, - name: controllerName.name + name: controllerName.name, + isCommon: controllerName.isCommon }; const switchOption = interaction.value.showSwitch[0]; @@ -190,7 +205,7 @@ export default defineComponent({ handlerName: '未绑定方法', cmpId: '', shortcut: {}, - isRTCmd: undefined, + isRtcCommand: useFormSchema.designerMode === 'PC_RTC' ? true : undefined, isNewGenerated: undefined, extensions: [], isInvalid: false @@ -217,7 +232,30 @@ export default defineComponent({ switchOption.showSection[switchOption.showSwitchNumber][3] = false; // 显示通讯窗口 shouldShowCommunicationPanel.value = true; + + interaction.value.communication = null; + } + + /** + * 选择「已有方法」后,切换页面的显示状态 + */ + function onSelectBoundMethod() { + const switchOption = interaction.value.showSwitch[0]; + // 1. 切换至已绑定方法状态 + switchOption.showSwitchNumber = 2; + // 2. 隐藏方法列表 + switchOption.showSection[switchOption.showSwitchNumber][2] = false; + // 3. 显示方法名 + switchOption.showSection[switchOption.showSwitchNumber][3] = true; + methodSelectedChanged.value = false; + + // 隐藏组件通讯窗口 + shouldShowCommunicationPanel.value = false; + // 清除已有的通讯 + clearCommunication(interaction.value); + emitFinalState(false, null); } + function renderMethodSource() { return onNewFunctionChangedHandler()} onSwitchToCommunicationPanel={onSwitchToCommunicationPanel} + haveBoundCommand={props.haveBoundCommand} + iteractionDisplayOrder={props.displayOrder} + onSelectBoundMethod={onSelectBoundMethod} + event={interaction.value.event} >; } /** @@ -252,13 +294,24 @@ export default defineComponent({ interaction.value.command = cloneDeep(changedCommand); emitFinalState(false, null); } + + function onViewSource($event) { + const { controller } = interaction.value; + if (controller.isCommon) { + return; + } + props.viewSourceHandle?.({ ...$event, ...interaction.value }); + } + function renderMethodInfo() { return onParameterEditorConfirmHandler()} onChange={(event) => onParameterEditorChangeHandler(event)} onTargetChange={(event) => onParameterEditorTargetChangeHandler(event)} + onViewSource={($event) => onViewSource($event)} >; } @@ -274,13 +327,12 @@ export default defineComponent({ communication={interaction.value.communication} event={interaction.value.event.label} sourceCommunication={props.sourceCommunication} - onChanged={onCombineFormChanged}> + onChanged={onCombineFormChanged} + onDelete={onClickDeleteButton}> ; } - function onSelectSourceControl() { - } return () => { return (
@@ -298,8 +350,6 @@ export default defineComponent({
} {shouldShowCommandSource.value && renderMethodSource()} {shouldShowMethod.value && renderMethodInfo()} - {/* {shouldShowSelectControlPanel.value && } */} - {shouldShowCommunicationPanel.value && renderCommunicationPanel()}
diff --git a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts index e75849cbfb03297709e00f9f3f32dc755da561ae..22f54926b8ed17a07cef2416a7d75f85d9f21742 100644 --- a/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts +++ b/packages/ui-vue/components/events-editor/src/components/interaction-item/interaction-item.props.ts @@ -7,6 +7,9 @@ export const interactionItemProps = { interaction: { type: Object as PropType, default: {} }, controllers: { type: Array, default: [] }, sourceCommunication: { type: Object, default: {} }, + viewSourceHandle: {type: Function as PropType<() => void>, default: () => { }}, + /** 接收是否显示「可选择方法」功能 */ + haveBoundCommand: { Type: Boolean, default: false } } as Record; export type InteractionItemProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx b/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx index 65368ecfabdeb2fe8fb186e801c94cda94651c25..aebf9705de793862f592a23c36830e303d56c1b2 100644 --- a/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx +++ b/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.component.tsx @@ -1,8 +1,8 @@ -import { SetupContext, computed, defineComponent, reactive, ref } from "vue"; +import { SetupContext, computed, defineComponent, reactive, ref, inject } from "vue"; import { ParameterEditorProps, parameterEditorProps } from "./parameter-editor.props"; import ComboList from '@farris/ui-vue/components/combo-list'; import FEventParameter from "@farris/ui-vue/components/event-parameter"; -import { CommandItem } from "../../types"; +import { CommandItem, ControllerName } from "../../types"; import './parameter-editor.css'; import { FNotifyService as NotifyService } from "@farris/ui-vue/components/notify"; import { cloneDeep } from "lodash-es"; @@ -20,10 +20,11 @@ import { cloneDeep } from "lodash-es"; export default defineComponent({ name: 'FParameterEditor', props: parameterEditorProps, - emits: ['confirm', 'change', 'targetChange'] as (string[] & ThisType) | undefined, + emits: ['confirm', 'change', 'targetChange', 'viewSource'] as (string[] & ThisType) | undefined, setup(props: ParameterEditorProps, context: SetupContext) { const notifyService = new NotifyService(); const command = ref(cloneDeep(props.command)); + const controller = ref(props.controller); const commandItemIndex = ref(0); // 方法被废弃 const showAbandonedIcon = computed(() => command.value.id === 'abandoned'); @@ -35,6 +36,7 @@ export default defineComponent({ const shouldShowEditButton = computed(() => command.value.id !== 'abandoned' && command.value.id !== 'deleted'); const shouldShowParameterEditorGroup = ref(props.showParameter); + const useFormSchema = inject('useFormSchema') as any; function getDefaultTargetComponentVM(): string { const { targetComponent } = command.value; @@ -143,7 +145,7 @@ export default defineComponent({ context.emit('change', command.value); } - function renderParameterEditor(propertyItem) { + function renderParameterEditor(propertyItem: any, commandValue: any) { // todo: ide-parameter-editor const data = reactive(propertyItem.context.data); const { @@ -151,16 +153,24 @@ export default defineComponent({ assembleOutline, assembleStateVariables, assembleSchemaFieldsUnderBoundEntity, + getEditor } = propertyItem.context.generalData; + const { designerMode } = useFormSchema; + const onChangeValue = (value) => { + onEditorChangeHandler(value, propertyItem); + onEditorBlurHandler(); + }; return {/* 参数第三行-参数值输入框 */}
- {renderParameterEditor(propertyItem)} + {renderParameterEditor(propertyItem, command.value)}
; }); @@ -226,6 +236,19 @@ export default defineComponent({ {renderParameterEditors()}
; } + + function onViewSource($event: MouseEvent) { + $event.stopPropagation(); + context.emit('viewSource', { command: command.value, event: $event }); + } + + const commandStyles = computed(() => { + return { + 'cursor': showAbandonedIcon.value || controller.value.isCommon || controller.value?.label.indexOf(useFormSchema?.getModule().code) === -1 ? 'default': 'pointer' + }; + }); + + /** * 显示方法名称和编辑按钮 * 显示参数列表 @@ -237,7 +260,8 @@ export default defineComponent({
{/* 参数编辑第一行-方法名称 */}
- {command.value.name} + {command.value.name} +
{showAbandonedIcon.value && renderAbandonedIcon()} {showDeletedIcon.value && renderDeletedIcon()} diff --git a/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.props.ts b/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.props.ts index dbda29fb735651940212b9d7a7e2e3c57c472c22..187618ec884954d852f9551f377237b036edc4f1 100644 --- a/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.props.ts +++ b/packages/ui-vue/components/events-editor/src/components/parameter-editor/parameter-editor.props.ts @@ -3,7 +3,8 @@ import { CommandItem } from "../../types"; export const parameterEditorProps = { command: { type: Object as PropType, default: {} }, - showParameter: { type: Boolean, default: false } + showParameter: { type: Boolean, default: false }, + controller: { type: Object, default: {} } } as Record; export type ParameterEditorProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/components/view-model/view-model.component.tsx b/packages/ui-vue/components/events-editor/src/components/view-model/view-model.component.tsx deleted file mode 100644 index 350001f0661c31c0d606af7eaebd631e7378b6fe..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/events-editor/src/components/view-model/view-model.component.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { SetupContext, defineComponent, ref } from "vue"; -import { ViewModelProps, viewModelProps } from "./view-model.props"; -import FInputGroup from '@farris/ui-vue/components/input-group'; - -export default defineComponent({ - name: 'FViewModel', - props: viewModelProps, - emits: [], - setup(props: ViewModelProps, context: SetupContext) { - const viewModelData = ref([]); - const groupIcon = ref(''); - const searchValue = ref(''); - - function onChange() { - - } - - function onClear() { - - } - - function onClickHandle() { - - } - - function renderViewModelSearchBar() { - return ( - - ); - } - - function viewModelItemClass(mapItem: any) { - const classObject = { - 'f-vm-item': !mapItem.active, - 'f-vm-item-focus': mapItem.active - } as Record; - return classObject; - } - - function getViewModelCommand(mapItem: any, viewModelDataItem: any) { - - } - - function viewModelItemCommandClass(mapItem: any) { - const classObject = { - 'f-event-func': !mapItem.command.isInvalid, - 'f-event-func-invalid': mapItem.command.isInvalid - } as Record; - return classObject; - - } - - function renderViewModelMapItem(viewModelDataItem: any) { - return viewModelDataItem.sourceComponent.map.filter((mapItem: any) => !mapItem.hide) - .map((mapItem: any) => { - return ( -
getViewModelCommand(mapItem, viewModelDataItem)}> -
-
-
-
-
- {mapItem.command.name}({mapItem.command.label}) -
-
-
{viewModelDataItem.path}{mapItem.event.name}
-
- ); - }); - } - - function renderViewModels() { - return viewModelData.value.length > 0 && -
- {viewModelData.value.map((viewModelDataItem: any) => { - return renderViewModelMapItem(viewModelDataItem); - })}; -
; - - } - - return () => { - return ( -
- {renderViewModelSearchBar()} - {renderViewModels()} -
- ); - }; - }, -}); diff --git a/packages/ui-vue/components/events-editor/src/components/view-model/view-model.props.ts b/packages/ui-vue/components/events-editor/src/components/view-model/view-model.props.ts deleted file mode 100644 index 6e51417f5c7629814f4b1fcf0af542076e14600f..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/events-editor/src/components/view-model/view-model.props.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ExtractPropTypes } from "vue"; - -export const viewModelProps = {} as Record; - -export type ViewModelProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/composition/types.ts b/packages/ui-vue/components/events-editor/src/composition/types.ts index 8d476544dbd613afbb6c17239498d2e961978cfa..e2481f9ac911a90da6b5a25df7f2437e78b94f86 100644 --- a/packages/ui-vue/components/events-editor/src/composition/types.ts +++ b/packages/ui-vue/components/events-editor/src/composition/types.ts @@ -58,17 +58,8 @@ export interface UseEvents { export interface UseMethods { controllers: Ref; - - // onAddInternalCommandListChanged: (value: any[]) => void; - onClosed: (actionIndex: number) => void; - - // onExistChanged: (value: number) => void; - - onCurrentCommandChanged: (currentCommand: any[], actionIndex: number, commandItemIndex: number) => void; - // onNewImportChanged: (value: boolean) => void; - - toggleSelectingMethod: (switchOption: SwitchOption) => void; + toggleSelectingMethod: (switchOption: SwitchOption, hasCommunication?: boolean) => void; emitFinalState: (addNewFunctionState, event) => void; getComponentList: () => ComponentItem[]; updateTarget: (command) => void; @@ -76,5 +67,9 @@ export interface UseMethods { addNewController: (webCommand) => void; checkIfNewControllerExists: (importData) => boolean; getMethodsOnViewModel: () => Array; - getViewModeId: () => string + getViewModeId: () => string; + /** 获取「已有方法」的路径等信息 */ + getEventPath: () => any; + /** 「已有方法」-选择方法后事件 */ + selectBoundCommand: (selectedCommand: any, iteractionDisplayOrder: number) => void; } diff --git a/packages/ui-vue/components/events-editor/src/composition/use-events-editor-util.ts b/packages/ui-vue/components/events-editor/src/composition/use-events-editor-util.ts index 193bdba87e731d0e761f7c39c896a6edef1a402f..5eb7cc977e5b14ab7323e19cf32c5b38b5c86695 100644 --- a/packages/ui-vue/components/events-editor/src/composition/use-events-editor-util.ts +++ b/packages/ui-vue/components/events-editor/src/composition/use-events-editor-util.ts @@ -218,6 +218,23 @@ export function useEventsEditorUtil(): UseEventsEditorUtil { name: '移除版本附件行', id: 'b441b6a7-9e13-9f75-77a2-10b2c1f7ef26', handlerName: 'RemoveAttachmentItem', + }, + { + name: '上传并批量新增行', + id: 'e6fc25ca-853b-0b2d-76c9-a1f7a253679b', + handlerName: 'UploadAndBatchAddRows', + } + ] + }, + { + controllerId: 'e353b4e9-e073-4e18-b9f7-ec4cc8ac72b1', + controllerLabel: 'PrintService', + controllerName: '打印控制器', + commands: [ + { + name: '根据数据条件批量打印', + id: '774fa275-2da1-e927-e8bf-d27d9886a168', + handlerName: 'printMulti', } ] }, diff --git a/packages/ui-vue/components/events-editor/src/composition/use-interaction.ts b/packages/ui-vue/components/events-editor/src/composition/use-interaction.ts index ceb023eaa6e4fbab4310902d693c27636ec34957..8ce9fe45e111767ce6d9212d2358c8bf38b47a8f 100644 --- a/packages/ui-vue/components/events-editor/src/composition/use-interaction.ts +++ b/packages/ui-vue/components/events-editor/src/composition/use-interaction.ts @@ -38,6 +38,11 @@ export function useInteraction( /** 通讯 */ communication: boundEventItem.communication } as InteractionItem; + + // 若当前绑定组件通讯,则隐藏方法名的面板 + if (boundEventItem.communication) { + interaction.showSwitch[0].showSection[2][3] = false; + } return interaction; } @@ -56,11 +61,11 @@ export function useInteraction( // 需要先选择源控件 if (initialData.value.sourceCommunication?.needSelectSourceControl) { newInteraction.showSwitch[0].showSwitchNumber = 2; - // 隐藏方法列表 - newInteraction.showSwitch[0].showSection[2][2] = false; - // 隐藏方法名 - newInteraction.showSwitch[0].showSection[2][3] = false; - interactions.value.unshift(newInteraction); + // 隐藏方法列表 + newInteraction.showSwitch[0].showSection[2][2] = false; + // 隐藏方法名 + newInteraction.showSwitch[0].showSection[2][3] = false; + interactions.value.push(newInteraction); } else { // 选择事件 newInteraction.showSwitch[0].showSwitchNumber = 1; @@ -94,7 +99,7 @@ export function useInteraction( // 2. 移除交互 interactions.value.splice(interactionIndex, 1); - // 移除通讯 + // 3、移除通讯 const { clearCommunication } = useCommunication(useFormSchema); clearCommunication(interactionToBeRemoved); } diff --git a/packages/ui-vue/components/events-editor/src/composition/use-methods.ts b/packages/ui-vue/components/events-editor/src/composition/use-methods.ts index 6798e51052ef993904e3c4dbac9ccbc82a90ca58..9d1424c6ae2fd764ffc09c37e370b15a75ebfd6a 100644 --- a/packages/ui-vue/components/events-editor/src/composition/use-methods.ts +++ b/packages/ui-vue/components/events-editor/src/composition/use-methods.ts @@ -13,16 +13,6 @@ export function useMethods( const { interactions } = useInteractionComposition; const { setComponentLists } = useEventsEditorUtil(); const controllers = ref(initialData.value.internalCommandList); - /** 方法-导入新方法值变化事件 */ - // function onNewImportChanged(value: boolean) { - // newImport.value = value; - // initTargetComponent(initialData.value); - // } - - /** 内置构件选择“导入新方法”后的值 */ - // function onAddInternalCommandListChanged(value: any[]) { - // addInternalCommandList.value = cloneDeep(value); - // } /** 方法来源关闭按钮事件 */ function onClosed(actionIndex: number) { @@ -41,18 +31,6 @@ export function useMethods( // } } - /** 方法绑定-下拉框中用户选择绑定的变量 */ - function onCurrentCommandChanged(currentCommand: any[], actionIndex: number, commandItemIndex: number) { - // eventIndex.value = actionIndex; - // currentCommand = cloneDeep(currentCommand); - // interactions.value[actionIndex].showSwitch[commandItemIndex].showSwitchNumber = 2; - // // 除当前用户点击的这一项展开之外,其余全部收折 - // interactions.value[actionIndex].showSwitch[commandItemIndex].showSection[2][4] = true; - // // 关闭浮层 - // showPopover.value = false; - // hasAddNewFunction = false; - } - /** 如果最后用户绑定的方法中有新增的控制器,则传出该方法控制器相关参数 */ function emitNewController(boundEventsList: BoundEventItem[]) { const newController: NewControllerItem[] = []; @@ -71,7 +49,7 @@ export function useMethods( if (initialData.value.repititionCommand.length !== 0) { initialData.value.repititionCommand.forEach((repititionCommandItem: any) => { boundEventsList.forEach(boundEventsListItem => { - if (repititionCommandItem.command.id === boundEventsListItem.controller.id) { + if (repititionCommandItem.command.id === boundEventsListItem.command?.id) { repititionCommandItem.command.params = cloneDeep(boundEventsListItem.command?.property); const targetComponent = boundEventsListItem.command?.targetComponent; boundEventsListItem.command?.componentLists.forEach((componentListsItem: any) => { @@ -87,6 +65,7 @@ export function useMethods( } return undefined; } + /** 值传出事件 */ function emitFinalState(addNewFunctionState = false, events = null) { initialData.value.newFuncEvents = events ? cloneDeep(events) : null; @@ -97,6 +76,7 @@ export function useMethods( const controller = cloneDeep(interaction.controller); const command = cloneDeep(interaction.command); const boundEvents = cloneDeep(interaction.event); + const communication = cloneDeep(interaction.communication); const currentInterface: BoundEventItem = { controller, command, boundEvents, communication }; boundEventList.push(cloneDeep(currentInterface)); @@ -104,9 +84,9 @@ export function useMethods( const newController = initialData.value.newController?.length && boundEventList.length ? cloneDeep(emitNewController(boundEventList)) : []; // 传出前更新repititionCommand的值 - // if (initialData.value.repititionCommand) { - // initialData.value.repititionCommand = updateRepititionCommand(boundEventList); - // } + if (initialData.value.repititionCommand) { + initialData.value.repititionCommand = updateRepititionCommand(boundEventList); + } // 当前界面显示的所有事件 const savedCommandList: SavedCommandChanged = { /** 拼接名称时的前缀 */ @@ -150,24 +130,28 @@ export function useMethods( switchOption.showSection[switchOption.showSwitchNumber][2] = false; } - function showMethodName(switchOption: SwitchOption) { + function showMethodName(switchOption: SwitchOption, hasCommunication = false) { // 1. 显示方法名 - switchOption.showSection[switchOption.showSwitchNumber][3] = true; + if (!hasCommunication) { + switchOption.showSection[switchOption.showSwitchNumber][3] = true; + } } - function hideMethodName(switchOption: SwitchOption) { + function hideMethodName(switchOption: SwitchOption, hasCommunication = false) { // 1. 隐藏方法名 - switchOption.showSection[switchOption.showSwitchNumber][3] = false; + if (!hasCommunication) { + switchOption.showSection[switchOption.showSwitchNumber][3] = false; + } } - function toggleSelectingMethod(switchOption: SwitchOption) { + function toggleSelectingMethod(switchOption: SwitchOption, hasCommunication = false) { const isSelectingMethod = switchOption.showSection[switchOption.showSwitchNumber][2]; if (isSelectingMethod) { hideMethodList(switchOption); - showMethodName(switchOption); + showMethodName(switchOption, hasCommunication); } else { showMethodList(switchOption); - hideMethodName(switchOption); + hideMethodName(switchOption, hasCommunication); } } /** @@ -239,12 +223,79 @@ export function useMethods( } return checkExist; } + /** 获取已有事件 */ + function getEventPath() { + if (initialData.value?.getEventPath) { + return initialData.value.getEventPath(); + } + } + + /** 方法绑定-遍历判断当前的方法是否被其他事件绑定,若已绑定,则及时更新最新值; */ + function iterateBoundEvents(currentIteraction, newCommand) { + interactions.value.forEach(dataItem => { + if (dataItem.command?.label === newCommand.label) { + dataItem.command = newCommand; + } + }); + } + + /** 方法绑定-下拉框中用户选择绑定的变量 */ + function onCurrentCommandChanged(currentIteraction, newCommand) { + // 遍历判断当前的方法是否被其他事件绑定,若已绑定,则及时更新最新值; + iterateBoundEvents(currentIteraction, newCommand); + } + /** 「已有方法」-选择方法后事件 */ + function selectBoundCommand(selectedCommand, iteractionDisplayOrder: number) { + const currentIteraction = interactions.value[iteractionDisplayOrder]; + // 绑定暂未绑定的命令时,自动默认当前组件下的当前事件 + selectedCommand['event'] = !selectedCommand['event']['label'] ? cloneDeep(currentIteraction.event) : selectedCommand['event']; + let commandExist = false; + if (!initialData.value.repititionCommand) { + initialData.value.repititionCommand = []; + } + initialData.value.repititionCommand.forEach(repititionCommandItem => { + // 确保用户点击的方法在存储时不重复 + if (repititionCommandItem.command.id === selectedCommand.command.id && repititionCommandItem.event.label === selectedCommand.event.label && repititionCommandItem.targetComponent.viewModelId === selectedCommand.targetComponent.viewModelId) { + commandExist = true; + } + if (repititionCommandItem.command.id === selectedCommand.command.id && repititionCommandItem.event.label === selectedCommand.event.label && repititionCommandItem.targetComponent.viewModelId !== selectedCommand.targetComponent.viewModelId) { + // 仅修改了目标组件值,则替换 + repititionCommandItem = cloneDeep(selectedCommand); + } + }); + // 没有点击过该方法,则增加 + if (!commandExist) { + initialData.value.repititionCommand.push(cloneDeep(selectedCommand)); + } + let newCommand = { + id: selectedCommand.command.id, + label: selectedCommand.command.label, + name: selectedCommand.command.name, + handlerName: selectedCommand.command.handlerName, + cmpId: selectedCommand.controller.id, + componentLists: [], + hasPath: false, + targetComponent: selectedCommand.targetComponent.id, + property: cloneDeep(selectedCommand.command.params), + shortcut: {}, + isRtcCommand: selectedCommand.command['isRtcCommand'], + isNewGenerated: selectedCommand.command['isNewGenerated'] || false, + isInvalid: false, + }; + const controller = { + controllerName: selectedCommand.controller + }; + newCommand = setComponentLists(controller, newCommand, initialData.value); + + currentIteraction.command = cloneDeep(newCommand); + currentIteraction.controller = cloneDeep(selectedCommand.controller); + onCurrentCommandChanged(currentIteraction, newCommand); + } return { controllers, onClosed, emitFinalState, - onCurrentCommandChanged, toggleSelectingMethod, getViewModeId, getComponentList, @@ -252,6 +303,8 @@ export function useMethods( updateCommandHasPath, addNewController, checkIfNewControllerExists, - getMethodsOnViewModel + getMethodsOnViewModel, + getEventPath, + selectBoundCommand }; } diff --git a/packages/ui-vue/components/events-editor/src/events-editor.component.tsx b/packages/ui-vue/components/events-editor/src/events-editor.component.tsx index 313defcd4008681f44d8a1b215aa73db4733a73c..c5bec9a214b965b56e7bb9c4a7709a81fc576f86 100644 --- a/packages/ui-vue/components/events-editor/src/events-editor.component.tsx +++ b/packages/ui-vue/components/events-editor/src/events-editor.component.tsx @@ -62,14 +62,27 @@ export default defineComponent({ function renderInteraction(interaction: InteractionItem, displayOrder: number) { const switchElement = interaction.showSwitch[0]; const collapsed = switchElement.showSection[switchElement.showSwitchNumber][3]; + + // 检查是否需要显示「已有方法」 + let haveBoundCommand = false; + if (props.initialData.getEventPath) { + if (props.initialData.getEventPath().actionWithPath.length !== 0) { + haveBoundCommand = true; + } else { + haveBoundCommand = false; + } + } return ( ); } @@ -77,7 +90,11 @@ export default defineComponent({ /** 根据入参展示所有已绑定事件的状态 */ function renderInteractionList() { return interactions.value?.map((interaction: InteractionItem, displayOrder: number) => { - return
+ let itemKey = interaction.event.label; + if (interaction.communication?.id) { + itemKey = `${interaction.communication?.id}_itemKey`; + } + return
{renderInteraction(interaction, displayOrder)}
; }); @@ -90,7 +107,7 @@ export default defineComponent({ /** 新增一条源控件 */ function onAddSourceControl() { - addNewInteraction(); + addNewInteraction({ label: 'onCommunication', name: '组件通讯' }); } return () => { return ( diff --git a/packages/ui-vue/components/events-editor/src/events-editor.css b/packages/ui-vue/components/events-editor/src/events-editor.css index 00262c893a8fe7f39e99506d293e73b2443229a9..f226ac597f9271a395ec6e8b54f031794d004c2e 100644 --- a/packages/ui-vue/components/events-editor/src/events-editor.css +++ b/packages/ui-vue/components/events-editor/src/events-editor.css @@ -21,7 +21,7 @@ .f-page-events-editor-events { width: 98%; - max-height: 136px; + max-height: 260px; background: #ffffff; box-shadow: 0px 2px 10px 0px rgba(0, 0, 0, 0.15); border-color: #6ca0f5; @@ -159,3 +159,7 @@ width: 50px; border: 1px transparent; } + +.f-page-events-editor-content .events-display-order:not(:last-child) .combine-form-container { + box-shadow: 0px 2px 0px 0px rgba(3, 18, 51, 0.07) +} \ No newline at end of file diff --git a/packages/ui-vue/components/events-editor/src/events-editor.props.ts b/packages/ui-vue/components/events-editor/src/events-editor.props.ts index 9f255bbfd14d2703a73eff92cab2312404adc1cb..8ba507b48039b5cad5ad74802802d6c39da474f7 100644 --- a/packages/ui-vue/components/events-editor/src/events-editor.props.ts +++ b/packages/ui-vue/components/events-editor/src/events-editor.props.ts @@ -8,7 +8,8 @@ import propertyConfig from './property-config/events-editor.property-config.json import { initialData } from '../src/data/initial-data'; export const eventsEditorProps = { - initialData: { type: Object as PropType, default: initialData } + initialData: { type: Object as PropType, default: initialData }, + viewSourceHandle: {type: Function as PropType<() => void>, default: () => { }} } as Record; export type EventsEditorProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json b/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json index ae6254393b7f5762ac7d0397ff842c12da495c73..c5f92905004c64dbd4d2601e8c86e7b5a034a9dc 100644 --- a/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json +++ b/packages/ui-vue/components/events-editor/src/schema/events-editor.schema.json @@ -18,7 +18,12 @@ "description": "", "type": "object", "default": {} - } + }, + "viewSourceHandle": { + "description": "", + "type": "function", + "default": null + } }, "required": [ "id", diff --git a/packages/ui-vue/components/events-editor/src/types.ts b/packages/ui-vue/components/events-editor/src/types.ts index 7e4225a471afdea7d70ec6e0349f06225f46c5a2..dd8076eb44149fc468017b2c3ee56609da8a2694 100644 --- a/packages/ui-vue/components/events-editor/src/types.ts +++ b/packages/ui-vue/components/events-editor/src/types.ts @@ -1,3 +1,4 @@ +import { Communication } from "./components/combine-form/types"; export interface PropertyItem { /** 参数名 */ @@ -43,8 +44,8 @@ export interface CommandItem { isNewGenerated?: boolean; extensions?: []; isInvalid?: boolean; - /** isRTCmd=true 运行时定制的命令,可以删改 */ - isRTCmd?: boolean; + /** isRtcCommand=true 运行时定制的命令,可以删改 */ + isRtcCommand?: boolean; } export interface ControllerName { @@ -54,6 +55,8 @@ export interface ControllerName { label: string; /** 控制器中文名 */ name: string; + /** 是否为内置控制器 */ + isCommon?: boolean; } /** 内置构件对应的单个控制器方法列表 */ @@ -199,52 +202,6 @@ export interface SwitchOption { showSwitchNumber: number; } - -/** 组件通讯 */ -export interface Communication { - /** 唯一编号 */ - id: string; - - source: { - /** 源页面id */ - formId: string; - /** 源页面编号 */ - formCode?: string; - /** 源页面所属的外部容器id。 */ - externalContainerId?: string; - /** 源控件id */ - componentId: string; - /** 源控件所在的路径 */ - // componentPath?: string; - /** 源控件事件编号 */ - event: string; - }; - - target: { - /** 目标页面id */ - formId: string; - /** 目标页面编号 */ - formCode?: string; - /** 目标页面所属的外部容器id */ - externalContainerId: string; - - /** 目标页面的命令编号 */ - commandCode: string; - /** 目标页面的命令所属viewmodel id */ - commandViewmodelId: string; - } - /** 参数 */ - paramMappings?: Array<{ - id: string; - /** 源参数值 */ - sourceValue: string, - /** 目标页面的变量编号 */ - targetVariable: string; - - targetVariableViewModelId: string; - }>; -} - export interface InteractionItem { /** 当前事件名称及label */ event: EventItem; @@ -257,3 +214,4 @@ export interface InteractionItem { communication?: Communication | null; } +export * from "./components/combine-form/types"; diff --git a/packages/ui-vue/components/expression-editor/src/expression-editor.component.tsx b/packages/ui-vue/components/expression-editor/src/expression-editor.component.tsx index 04f7199bc99a6a937222c69ef82e691968280b88..a4a8a99a666871e546e94ae445e1eab1d45e46a0 100644 --- a/packages/ui-vue/components/expression-editor/src/expression-editor.component.tsx +++ b/packages/ui-vue/components/expression-editor/src/expression-editor.component.tsx @@ -135,7 +135,7 @@ export default defineComponent({ {props.showMessage &&
提示信息: {props.showMessageType &&
} - - - - + {props.showDataPanel && + + + + }
); diff --git a/packages/ui-vue/components/expression-editor/src/expression-editor.props.ts b/packages/ui-vue/components/expression-editor/src/expression-editor.props.ts index 25e6d52092eb13a19da5c79f68a476132623891c..4733dc32542fb9b1b1863fb582177d79edd86839 100644 --- a/packages/ui-vue/components/expression-editor/src/expression-editor.props.ts +++ b/packages/ui-vue/components/expression-editor/src/expression-editor.props.ts @@ -1,4 +1,4 @@ - + /** * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. * @@ -58,7 +58,11 @@ export const expressionEditorProps = { }, showMessageType: { type: Boolean, - default: false + default: false + }, + showDataPanel: { + type: Boolean, + default: true } } as Record; diff --git a/packages/ui-vue/components/expression-editor/src/expression-textbox.component.tsx b/packages/ui-vue/components/expression-editor/src/expression-textbox.component.tsx index d28972cf0041fef94f533ef624d477d660a1b3d8..98fd2ac8ed78905773d134eb458af57942169633 100644 --- a/packages/ui-vue/components/expression-editor/src/expression-textbox.component.tsx +++ b/packages/ui-vue/components/expression-editor/src/expression-textbox.component.tsx @@ -1,4 +1,4 @@ -import { defineComponent, inject, ref } from "vue"; +import { defineComponent, inject, ref, watch } from "vue"; import FButtonEdit from '../../button-edit/src/button-edit.component'; import { expressionTextboxProps, ExpressionTextboxProps } from "./expression-textbox.props"; @@ -133,6 +133,10 @@ export default defineComponent({ } } + watch(() => props.modelValue, (newValue) => { + modelValue.value = newValue; + }); + return () => { return
; diff --git a/packages/ui-vue/components/expression-editor/src/expression-textbox.props.ts b/packages/ui-vue/components/expression-editor/src/expression-textbox.props.ts index afde95bc56b21feb683af9053a202c7808b8a67c..557708f02362fbbfc85390d0b0a6a258fb34bbe3 100644 --- a/packages/ui-vue/components/expression-editor/src/expression-textbox.props.ts +++ b/packages/ui-vue/components/expression-editor/src/expression-textbox.props.ts @@ -91,8 +91,10 @@ export const expressionTextboxProps = { type: Function as PropType<() => Promise>, default: null }, - - + showDataPanel: { + type: Boolean, + default: true + } } as Record; diff --git a/packages/ui-vue/components/expression-editor/src/locales/designer/en.json b/packages/ui-vue/components/expression-editor/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..ea087988d4f13ad18c14c671d5c71a03560ac614 --- /dev/null +++ b/packages/ui-vue/components/expression-editor/src/locales/designer/en.json @@ -0,0 +1,25 @@ +{ + "expressionEditor": { + "title": "Expression Editor", + "buttons": { + "ok": "OK", + "cancel": "Cancel", + "clear": "Clear" + }, + "messagerType": { + "info": "Info", + "warning": "Warning", + "error": "Error" + }, + "tipLabel": "Tip Message:", + "variable": { + "dataEntity": "Data Entity", + "placeholder": "Please enter the name or code, press Enter to query", + "label": "Variable" + }, + "functions": { + "emptyMessage": "No matching items found", + "placeholder": "Please enter the function name, press Enter to query" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/expression-editor/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/expression-editor/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..579ec67cdf85ade6e1093c913d5656d15c4df235 --- /dev/null +++ b/packages/ui-vue/components/expression-editor/src/locales/designer/zh-CHS.json @@ -0,0 +1,25 @@ +{ + "expressionEditor": { + "title": "表达式编辑器", + "buttons": { + "ok": "确定", + "cancel": "取消", + "clear": "清空" + }, + "messagerType": { + "info": "提示", + "warning": "警告", + "error": "错误" + }, + "tipLabel": "提示信息:", + "variable": { + "dataEntity": "数据实体", + "placeholder": "请输入名称或编号,按回车查询", + "label": "变量" + }, + "functions": { + "emptyMessage": "没有与搜索条件匹配的项", + "placeholder": "请输入函数名称,按回车查询" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/expression-editor/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/expression-editor/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..1ef72274d6defd17b5a69cefe22459d227bd823f --- /dev/null +++ b/packages/ui-vue/components/expression-editor/src/locales/designer/zh-CHT.json @@ -0,0 +1,25 @@ +{ + "expressionEditor": { + "title": "表達式編輯器", + "buttons": { + "ok": "確定", + "cancel": "取消", + "clear": "清空" + }, + "messagerType": { + "info": "提示", + "warning": "警告", + "error": "錯誤" + }, + "tipLabel": "提示信息:", + "variable": { + "dataEntity": "資料實體", + "placeholder": "請輸入名稱或編號,按 Enter 查詢", + "label": "變數" + }, + "functions": { + "emptyMessage": "沒有與搜索條件匹配的項", + "placeholder": "請輸入函數名稱,按 Enter 查詢" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/expression-editor/src/schema/expression-editor.schema.json b/packages/ui-vue/components/expression-editor/src/schema/expression-editor.schema.json index 963f26b1e654c62ac4f40a409294df982fb06859..1389e3887a6d0a86b05597d84d79111f08ee478a 100644 --- a/packages/ui-vue/components/expression-editor/src/schema/expression-editor.schema.json +++ b/packages/ui-vue/components/expression-editor/src/schema/expression-editor.schema.json @@ -95,6 +95,11 @@ "description": "The showMessageType of Expression Editor", "type": "boolean", "default": false + }, + "showDataPanel":{ + "description": "Show right panel", + "type": "boolean", + "default": true } }, "required": [ diff --git a/packages/ui-vue/components/external-container/index.ts b/packages/ui-vue/components/external-container/index.ts index b07ee734b7a960f153a5e8983042d2b52e41acf9..1c67bd6a028d46fefcce5d0f7bfa8ceaee5f457b 100644 --- a/packages/ui-vue/components/external-container/index.ts +++ b/packages/ui-vue/components/external-container/index.ts @@ -26,7 +26,7 @@ export default { install(app: App): void { app.component(FExternalContainer.name as string, FExternalContainer); }, - register(componentMap: Record, propsResolverMap: Record): void { + register(componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record): void { componentMap['external-container'] = FExternalContainer; propsResolverMap['external-container'] = propsResolver; }, diff --git a/packages/ui-vue/components/external-container/src/designer/external-container.design.component.tsx b/packages/ui-vue/components/external-container/src/designer/external-container.design.component.tsx index 9cc280f98baddd36d96fc4732d01f3175f7143c9..255a41efaafcc16e0d69aa7e51cec2a497d3b1ae 100644 --- a/packages/ui-vue/components/external-container/src/designer/external-container.design.component.tsx +++ b/packages/ui-vue/components/external-container/src/designer/external-container.design.component.tsx @@ -1,4 +1,4 @@ -import { defineComponent, inject, ref, onMounted, computed, watch } from 'vue'; +import { defineComponent, inject, ref, onMounted, computed, watch, provide } from 'vue'; import { FDesignerItem, DesignerItemContext, DesignerHostService, useDesignerComponent } from '@farris/ui-vue/components/designer-canvas'; import { FLoadingService } from '@farris/ui-vue/components/loading'; import { externalContainerProps, ExteranlContainerPropsType } from '../external-container.props'; @@ -27,6 +27,8 @@ export default defineComponent({ const externalFormSchemaComponent = computed(() => externalFormSchema.value?.module?.components[0]); /** 是否显示引入表单的占位区域 */ const shouldShowImportSchema = computed(() => !externalFormSchema.value); + /** 记录外部容器id */ + provide('external-container-id', componentInstance.value.schema.id); /** 将元数据内容记录到全局service里 */ function recordExternalForm() { @@ -45,26 +47,26 @@ export default defineComponent({ async function loadFormMetadata() { const formBasicInfo = designerHostService.formSchemaUtils.getFormMetadataBasicInfo(); if (externalComponent.value?.id) { - const instance = loadingService?.show(); - designerHostService.metadataService.getPickMetadata(formBasicInfo.relativePath, externalComponent.value) + const instance = loadingService?.show({ message: '数据加载中,请稍候...' }); + return designerHostService.metadataService.getPickMetadata(formBasicInfo.relativePath, externalComponent.value) .then((res: any) => { const metadata = JSON.parse(res?.metadata.content); externalFormSchema.value = metadata.Contents; instance.value.close(); - recordExternalForm(); }, (error) => { - messagerService.exception(error?.error || `查询表单【${externalComponent.value.name}】失败`); + messagerService.error(error?.response?.data.Message || `查询表单【${externalComponent.value.name}】失败`); instance.value.close(); }); } - } - watch(() => props.externalComponent, (newValue) => { + watch(() => props.externalComponent, async (newValue) => { if (!externalComponent.value || !newValue || JSON.stringify(externalComponent.value) !== JSON.stringify(newValue)) { externalComponent.value = newValue; - loadFormMetadata(); + await loadFormMetadata(); + // 触发更新属性面板 + designItemContext?.setupContext?.emit('dragEnd'); } else { externalComponent.value = newValue; } @@ -83,52 +85,16 @@ export default defineComponent({ context.expose(componentInstance.value); - // function createShadowDom() { - // const shadowRoot = elementRef.value.attachShadow({ mode: 'open' }); - - // const linkElements = document.querySelectorAll('link[rel="stylesheet"]'); - // const cssFiles: any = []; - // linkElements.forEach((link: any) => { - // cssFiles.push(link.href); - // }); - // const parentStyles = document.querySelectorAll('style'); - - - // // 创建一个新的 Vue 应用实例 - // const app = createApp({ - // setup() { - // // const useFormSchemaComposition = useFormSchema(); - // // provide('useFormSchema', useFormSchemaComposition); - - // return () => <> - //
- // - // ; - // } - // }); - // cssFiles.forEach(cssFile => { - // const link = document.createElement('link'); - // link.rel = 'stylesheet'; - // link.href = cssFile; - // shadowRoot.appendChild(link); - // }); - // parentStyles.forEach((style) => { - // const clonedStyle = style.cloneNode(true); - // shadowRoot.appendChild(clonedStyle); - // }); - // // 挂载到 Shadow DOM - // app.mount(shadowRoot); - // } - function createShadowDom() { + function renderExternalForm() { return <>
- + ; } - function onFormSelected(formMetadaInfo: any) { + async function onFormSelected(formMetadaInfo: any) { componentInstance.value.schema.externalComponent = formMetadaInfo; externalComponent.value = formMetadaInfo; - loadFormMetadata(); + await loadFormMetadata(); // 触发更新属性面板 designItemContext?.setupContext?.emit('dragEnd'); @@ -140,7 +106,7 @@ export default defineComponent({ return () => { return
- {shouldShowImportSchema.value ? renderImportExternalSchema() : createShadowDom()} + {shouldShowImportSchema.value ? renderImportExternalSchema() : renderExternalForm()}
; }; } diff --git a/packages/ui-vue/components/external-container/src/designer/import-external-schema.component.tsx b/packages/ui-vue/components/external-container/src/designer/import-external-schema.component.tsx index 996e0f09649b6773ff2f38bb3546e9694f3da419..2a2d0dde85e708ee1d8ac6364fc59d01fce187be 100644 --- a/packages/ui-vue/components/external-container/src/designer/import-external-schema.component.tsx +++ b/packages/ui-vue/components/external-container/src/designer/import-external-schema.component.tsx @@ -5,6 +5,7 @@ import { FSchemaSelector } from '@farris/ui-vue/components/schema-selector'; import { FormSchemaRepositorySymbol } from '@farris/ui-vue/components/common'; import { FNotifyService } from '@farris/ui-vue/components/notify'; import './import-external-schema.css'; +import { FLoadingService } from '@farris/ui-vue/components/loading'; export default defineComponent({ name: 'FImportExteranlSchema', @@ -15,15 +16,17 @@ export default defineComponent({ const selectedItem = ref(); const notifyService: any = new FNotifyService(); notifyService.globalConfig = { position: 'top-center' }; + const loadingService: any | null = inject('FLoadingService'); const formBasicInfo = designerHostService.formSchemaUtils.getFormMetadataBasicInfo(); const schemaSelectorRef = ref(); async function checkFormValidtion(selectionFormInfo: any) { const formBasicInfo = designerHostService.formSchemaUtils.getFormMetadataBasicInfo(); - + const instance = loadingService.show(); return designerHostService.metadataService.getPickMetadata(formBasicInfo.relativePath, selectionFormInfo) .then((res: any) => { + instance.value?.close(); const metadata = JSON.parse(res?.metadata.content); const formSchema = metadata.Contents; const { templateId, name } = formSchema.module; @@ -31,11 +34,12 @@ export default defineComponent({ if (['list-component', 'tree-component'].includes(templateId)) { return true; } - const message = `表单【${name}】不属于组件类模板,无法被导入到当前表单。`; + const message = `表单【${name}】不属于组件类模板,无法被引入到当前表单。`; designerHostService.messagerService.warning(message); return false; }, (error) => { - designerHostService.exception(error?.error || `查询表单【${selectionFormInfo.name}】失败`); + designerHostService.messagerService.error(error?.response?.data.Message || `查询表单【${selectionFormInfo.name}】失败`); + instance.value?.close(); return false; }); } @@ -76,17 +80,22 @@ export default defineComponent({ context.emit('close'); } function onSelectionChange($event) { - selectedItem.value = $event[0]; + selectedItem.value = $event?.length ? $event[0] : null; } function renderFormSelectorComponent() { const formSelectorParams = { formBasicInfo }; + const viewOptions = designerHostService.formSchemaUtils.designerMode === 'PC_RTC' ? + [ + { id: 'total', title: '全部', type: 'Card', dataSource: 'Total', pagination: false } + ] : + [ + { id: 'recommend', title: '推荐', type: 'Card', dataSource: 'Recommand', pagination: false }, + { id: 'total', title: '全部', type: 'Card', dataSource: 'Total', pagination: false } + ]; return () => ( { - return
{context.slots.default && context.slots.default()}
; + return
{context.slots.default && context.slots.default()}
; }; } }); diff --git a/packages/ui-vue/components/external-container/src/external-container.props.ts b/packages/ui-vue/components/external-container/src/external-container.props.ts index 989e624e9c18ea56d794e62db07f4345b8c60fa3..902339cdf046edbcc55f0aeeff642eceff96d779 100644 --- a/packages/ui-vue/components/external-container/src/external-container.props.ts +++ b/packages/ui-vue/components/external-container/src/external-container.props.ts @@ -7,6 +7,7 @@ import externalContainerSchema from './schema/external-container.schema.json'; export const externalContainerProps = { customClass: { type: String, default: '' }, + customStyle: { type: String, default: '' }, componentType: { type: String, default: '' }, url: { type: String, default: '' }, schema: { type: Object }, diff --git a/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts b/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts index 3e711af4009644277eb40e8c7051eabfc998325e..fb407faff4b89cab467e50809ad7bed72a177158 100644 --- a/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts +++ b/packages/ui-vue/components/external-container/src/property-config/external-container.property-config.ts @@ -1,10 +1,10 @@ -import { inject } from "vue"; import { FormSchemaRepositorySymbol } from "@farris/ui-vue/components/common"; import { DesignerComponentInstance } from "@farris/ui-vue/components/designer-canvas"; import { BaseControlProperty } from "@farris/ui-vue/components/property-panel"; import { FLoadingService } from "@farris/ui-vue/components/loading"; import { externalContainerConverter } from "./external-container.converter"; +import { FNotifyService } from "@farris/ui-vue/components/notify"; export class ExternalContainerProperty extends BaseControlProperty { @@ -18,16 +18,15 @@ export class ExternalContainerProperty extends BaseControlProperty { // 外观 this.propertyConfig.categories['appearance'] = this.getAppearanceConfig(propertyData); // 引入表单 - this.propertyConfig.categories['content'] = this.getContentConfig(); + this.propertyConfig.categories['content'] = this.getContentConfig(propertyData); this.propertyConfig.categories['eventsEditor'] = this.getEventPropConfig(propertyData); return this.propertyConfig; } - private getContentConfig() { + private getContentConfig(propertyData: any) { const self = this; - const loadingService: any | null = inject('FLoadingService'); return { description: '外部表单', @@ -46,10 +45,14 @@ export class ExternalContainerProperty extends BaseControlProperty { editorParams: { formBasicInfo: this.formSchemaUtils.getFormMetadataBasicInfo() }, - viewOptions: [ - { id: 'recommend', title: '推荐', type: 'Card', dataSource: 'Recommand', pagination: false }, - { id: 'total', title: '全部', type: 'Card', dataSource: 'Total', pagination: false } - ], + viewOptions: this.formSchemaUtils.designerMode === 'PC_RTC' ? + [ + { id: 'total', title: '全部', type: 'Card', dataSource: 'Total', pagination: true } + ] : + [ + { id: 'recommend', title: '推荐', type: 'Card', dataSource: 'Recommand', pagination: true }, + { id: 'total', title: '全部', type: 'Card', dataSource: 'Total', pagination: true } + ], repositoryToken: FormSchemaRepositorySymbol, validateFunction: (selection: any) => { if (selection.id) { @@ -58,7 +61,11 @@ export class ExternalContainerProperty extends BaseControlProperty { } }, $converter: externalContainerConverter - + } + }, + setPropertyRelates(changeObject: any) { + if (changeObject?.propertyID === 'externalComponent') { + self.syncCommunicationAfterFormChanged(changeObject, propertyData); } } }; @@ -66,9 +73,11 @@ export class ExternalContainerProperty extends BaseControlProperty { private checkFormValidtion(selectionFormInfo: any) { const formBasicInfo = this.designerHostService.formSchemaUtils.getFormMetadataBasicInfo(); + const loadingInstance = FLoadingService.show(); return this.designerHostService.metadataService.getPickMetadata(formBasicInfo.relativePath, selectionFormInfo) .then((res: any) => { + (loadingInstance.value as any).close(); const metadata = JSON.parse(res?.metadata.content); const formSchema = metadata.Contents; const { templateId, name } = formSchema.module; @@ -76,11 +85,12 @@ export class ExternalContainerProperty extends BaseControlProperty { if (['list-component', 'tree-component'].includes(templateId)) { return true; } - const message = `表单【${name}】不属于组件类模板,无法被导入到当前表单。`; + const message = `表单【${name}】不属于组件类模板,无法被引入到当前表单。`; this.designerHostService.messagerService.warning(message); return false; }, (error) => { - this.designerHostService.exception(error?.error || `查询表单【${selectionFormInfo.name}】失败`); + (loadingInstance.value as any).close(); + this.designerHostService.messagerService.error(error?.response?.data.Message || `查询表单【${selectionFormInfo.name}】失败`); return false; }); } @@ -94,11 +104,8 @@ export class ExternalContainerProperty extends BaseControlProperty { ]; const self = this; const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { initialData } - }; + const properties = self.createBaseEventProperty(initialData); + return { title: '事件', hideTitle: true, @@ -116,4 +123,34 @@ export class ExternalContainerProperty extends BaseControlProperty { } }; } + /** + * 切换外部表单后,同步更新组件通讯中的源表单(场景:已配置组件通讯,再切换外部表单) + */ + private syncCommunicationAfterFormChanged(changeObject, propertyData) { + if (!propertyData.onCommunication) { + return; + } + const communicationIds = propertyData.onCommunication.replace(/communication:/g, '').split(';'); + if (!communicationIds?.length) { + return; + } + const formSchema = this.designerHostService.formSchemaUtils.getFormSchema(); + const communications = formSchema.module.communications || []; + let needNotify = false; + communicationIds.map(communicationId => { + const communication = communications.find(item => item.id === communicationId); + if (communication?.source && communication.source.formId !== propertyData.externalComponent.id) { + communication.needDelete = true; + needNotify = true; + } + }); + formSchema.module.communications = formSchema.module.communications.filter(item => !item.needDelete); + if (needNotify) { + propertyData.onCommunication = ''; + const notifyService: any = new FNotifyService(); + notifyService.globalConfig = { position: 'top-center' }; + notifyService.warning({ message: '切换引入表单后,请重新配置组件通讯。' }); + } + + } } diff --git a/packages/ui-vue/components/external-container/src/schema/external-container.schema.json b/packages/ui-vue/components/external-container/src/schema/external-container.schema.json index d8018464bbb60da795be52921e5841777d21cc6e..024f8c90bd22cc31ab9909ee3535f7f787fd5d94 100644 --- a/packages/ui-vue/components/external-container/src/schema/external-container.schema.json +++ b/packages/ui-vue/components/external-container/src/schema/external-container.schema.json @@ -47,5 +47,8 @@ "id", "type", "externalComponent" - ] + ], + "events": { + "onCommunication": "通讯事件" + } } \ No newline at end of file diff --git a/packages/ui-vue/components/field-selector/src/field-selector.component.tsx b/packages/ui-vue/components/field-selector/src/field-selector.component.tsx index d84c047b8551c21e1656060efce971cbba9039a8..3d1f3fba4f58fff482fd37f0730036fa9ba4b1e5 100644 --- a/packages/ui-vue/components/field-selector/src/field-selector.component.tsx +++ b/packages/ui-vue/components/field-selector/src/field-selector.component.tsx @@ -135,7 +135,7 @@ export default defineComponent({ return (, designerHostService?: DesignerHostService) { + const formSchemaUtils = designerHostService?.formSchemaUtils; + const targetComponentInstance = context.parentComponentInstance; + + /** 数据加载控制器(LoadCommands)id*/ + const loadCommandsWebCmdId = '54bddc89-5f7e-4b91-9c45-80dd6606cfe9'; + /** 列表控制器(ListController)id */ + const listControllerWebCmdId = '70b4abd4-9f2c-4b7c-90e9-6ac6f4b74c72'; + + /** + * 组装筛选条容器和筛选条元数据,并插入组件schema + */ + function createFilterContainer(): { filterBar: any, filterBarContainer: any } | any { + const filterBarContainer = getSchemaByType(DgControl['content-container'].type); + const filterBarMetadata = getSchemaByType(DgControl['filter-bar'].type); + if (!filterBarContainer || !filterBarMetadata) { + return; + } + const suffixId = Math.random().toString(36).substr(2, 4); + Object.assign(filterBarContainer, { + id: 'filter-container-' + suffixId, + appearance: { + class: 'f-filter-container' + }, + contents: [filterBarMetadata] + }); + + Object.assign(filterBarMetadata, { + id: 'filter-' + suffixId, + fields: [] + }); + + return { filterBar: filterBarMetadata, filterBarContainer }; + } + + + function createFilterCommand(): { filterInGridViewModel: any, filterInRootViewModel: any } { + + const rootViewModel = formSchemaUtils.getViewModelById('root-viewmodel'); + let filterInRootViewModel: any; + + // 1、在根组件中预置过滤并加载命令 + if (rootViewModel && rootViewModel.commands) { + const filterCommand = rootViewModel.commands.find(command => command.handlerName === 'Filter' && command.cmpId === loadCommandsWebCmdId); + if (filterCommand) { + filterInRootViewModel = filterCommand; + if (filterCommand.params && filterCommand.params.length) { + const filterParam = filterCommand.params.find(p => p.name === 'filter'); + if (filterParam) { + filterParam.value = ''; + } + } + } else { + filterInRootViewModel = { + id: useGuid().guid(), + code: `${rootViewModel.id.replace(/-/g, '').replace('component', '').replace('viewmodel', '')}Filter1`, + name: '过滤并加载数据1', + params: [ + { + name: 'filter', + shownName: '过滤条件', + value: '', + defaultValue: null + }, + { + name: 'sort', + shownName: '排序条件', + value: '', + defaultValue: null + } + ], + handlerName: 'Filter', + cmpId: loadCommandsWebCmdId + }; + rootViewModel.commands.push(filterInRootViewModel); + } + } + + // 2、在表格所在的组件中预置过滤列表数据命令 + const viewModelId = formSchemaUtils.getViewModelIdByComponentId(targetComponentInstance.belongedComponentId); + const viewModel = formSchemaUtils.getViewModelById(viewModelId); + let filterInGridViewModel: any; + if (viewModel && viewModel.commands) { + const filterCommand = viewModel.commands.find(command => command.handlerName === 'Filter' && command.cmpId === listControllerWebCmdId); + if (filterCommand) { + filterInGridViewModel = filterCommand; + if (filterCommand.params && filterCommand.params.length && filterInRootViewModel) { + const commandName = filterCommand.params.find(p => p.name === 'commandName'); + if (commandName) { + commandName.value = filterInRootViewModel.code; + } + const frameId = filterCommand.params.find(p => p.name === 'frameId'); + if (frameId) { + frameId.value = '#{root-component}'; + } + } + } else { + filterInGridViewModel = { + id: useGuid().guid(), + code: `${viewModel.id.replace(/-/g, '').replace('component', '').replace('viewmodel', '')}Filter1`, + name: '过滤列表数据1', + params: [ + { + name: 'commandName', + shownName: '过滤回调方法', + value: filterInRootViewModel.code + }, + { + name: 'frameId', + shownName: '目标组件', + value: '#{root-component}' + } + ], + handlerName: 'Filter', + cmpId: listControllerWebCmdId + }; + viewModel.commands.push(filterInGridViewModel); + } + } + + return { filterInGridViewModel, filterInRootViewModel }; + + } + /** + * 为命令添加构件引用(ListController.webcmd、LoadCommands.webcmd) + */ + function addWebCmdForFilterCommand(filterInRootViewModel: any, filterInGridViewModel: any) { + const webCmds = formSchemaUtils.getCommands(); + if (!webCmds) { + return; + } + let listCmd = webCmds.find(cmd => cmd.id === listControllerWebCmdId); + if (!listCmd) { + listCmd = { + id: listControllerWebCmdId, + path: 'Gsp/Web/webcmp/bo-webcmp/metadata/webcmd', + name: 'ListController.webcmd', + refedHandlers: [] + }; + formSchemaUtils.getCommands().push(listCmd); + } + if (filterInGridViewModel && !listCmd.refedHandlers.find(item => item.host === filterInGridViewModel.id)) { + listCmd.refedHandlers.push( + { + host: filterInGridViewModel.id, + handler: 'Filter' + } + ); + + } + let loadCmd = webCmds.find(cmd => cmd.id === loadCommandsWebCmdId); + if (!loadCmd) { + loadCmd = { + id: loadCommandsWebCmdId, + path: 'Gsp/Web/WebCmp/bo-webcmp/metadata/webcmd/data-commands', + name: 'LoadCommands.webcmd', + refedHandlers: [] + }; + formSchemaUtils.getCommands().push(loadCmd); + } + if (filterInRootViewModel && !loadCmd.refedHandlers.find(item => item.host === filterInRootViewModel.id)) { + loadCmd.refedHandlers.push( + { + host: filterInRootViewModel.id, + handler: 'Filter' + } + ); + + } + + } + function createFilterBar() { + + // 1、组装筛选条容器和筛选条 + const { filterBar, filterBarContainer } = createFilterContainer(); + + // 2、创建筛选条相关命令 + const { filterInRootViewModel, filterInGridViewModel } = createFilterCommand(); + + // 3、将查询命令绑定到筛选条组件 + if (filterBar && filterInGridViewModel) { + filterBar.onQuery = filterInGridViewModel.code; + } + + // 4、预置筛选条查询数据的构件 + addWebCmdForFilterCommand(filterInRootViewModel, filterInGridViewModel); + + // 5、因为涉及到新增【ListController、LoadCommands控制器】,所以这里需要获取控制器信息,方便交互面板展示命令数据 + const formCommandService = designerHostService?.useFormCommand; + if (formCommandService) { + formCommandService.checkCommands(); + } + + return filterBarContainer; + } + + function checkCanAcceptFilterBar() { + + } + + return { createFilterBar }; +} diff --git a/packages/ui-vue/components/filter-bar/src/filter-bar.props.ts b/packages/ui-vue/components/filter-bar/src/filter-bar.props.ts index bab437b2a0db9820380c98362433a3493c45b843..52da2a5e39b7e8e78ec14e239e394ea0f7d0b4bc 100644 --- a/packages/ui-vue/components/filter-bar/src/filter-bar.props.ts +++ b/packages/ui-vue/components/filter-bar/src/filter-bar.props.ts @@ -12,15 +12,7 @@ export type FilterMode = 'editable' | 'display-only'; export const filterBarProps = { /** 被绑定数据 */ data: { - type: Array, default: [{ - id: 'name', - fieldCode: 'name', - fieldName: '名称', - compareType: '0', - valueType: 0, - value: '示例1', - relation: '1' - }] + type: Array, default: [] }, /** 筛选分类 */ fields: {type: Array, default: [] }, diff --git a/packages/ui-vue/components/filter-bar/src/property-config/filter-bar.property-config.ts b/packages/ui-vue/components/filter-bar/src/property-config/filter-bar.property-config.ts index 9b3482eba5b9c005e70302945b30fa59048f763a..7557a13e3033fe70b88c336251f614d6f9e6a1a5 100644 --- a/packages/ui-vue/components/filter-bar/src/property-config/filter-bar.property-config.ts +++ b/packages/ui-vue/components/filter-bar/src/property-config/filter-bar.property-config.ts @@ -43,7 +43,7 @@ export class FilterBarPropertyConfig extends InputBaseProperty { description: "筛选条字段设置", title: "筛选条字段", type: "", - refreshPanelAfterChanged: true, + refreshPanelAfterChanged: true, editor: { type: "filter-bar-config", fieldsConfig: allFields, @@ -87,13 +87,8 @@ export class FilterBarPropertyConfig extends InputBaseProperty { ]; const self = this; const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { - initialData - } - }; + const properties = self.createBaseEventProperty(initialData); + this.propertyConfig.categories['eventsEditor'] = { title: '事件', hideTitle: true, diff --git a/packages/ui-vue/components/filter-bar/src/schema/filter-bar.schema.json b/packages/ui-vue/components/filter-bar/src/schema/filter-bar.schema.json index 8b1166684f560dffb4e42205c6f01e053bf5c6e5..7009b7d632c10a0dc507e0f9cfe5f8758bc6f174 100644 --- a/packages/ui-vue/components/filter-bar/src/schema/filter-bar.schema.json +++ b/packages/ui-vue/components/filter-bar/src/schema/filter-bar.schema.json @@ -39,6 +39,10 @@ "description": "", "type": "boolean", "default": true + }, + "fields": { + "description": "", + "type": "array" } }, "required": [ diff --git a/packages/ui-vue/components/filter-bar/src/schema/schema-resolver.ts b/packages/ui-vue/components/filter-bar/src/schema/schema-resolver.ts index b02bdf93eec9060948f579c53aa81e3963a7d706..909ae7f31d22c523369612266ba5ad7153fc063c 100644 --- a/packages/ui-vue/components/filter-bar/src/schema/schema-resolver.ts +++ b/packages/ui-vue/components/filter-bar/src/schema/schema-resolver.ts @@ -1,5 +1,15 @@ -import { DynamicResolver } from "../../../dynamic-resolver"; +import { DesignerComponentInstance, DesignerHostService } from "@farris/ui-vue/components/designer-canvas"; +import { DynamicResolver } from "@farris/ui-vue/components/dynamic-resolver"; +import { buildFilterBar } from '../composition/build-filter-bar'; -export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record): Record { +export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record, designerHostService?: DesignerHostService): Record { + const parentComponentInstance = context.parentComponentInstance as DesignerComponentInstance; + if (parentComponentInstance && designerHostService) { + const filterBarCreator = buildFilterBar(context, designerHostService); + const filterBarContainer = filterBarCreator.createFilterBar(); + if (filterBarContainer) { + return filterBarContainer; + } + } return schema; } diff --git a/packages/ui-vue/components/filter-condition-editor/src/locales/ui/en.json b/packages/ui-vue/components/filter-condition-editor/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..e26ebd9947534316a34f6215abf8ca723358e2ec --- /dev/null +++ b/packages/ui-vue/components/filter-condition-editor/src/locales/ui/en.json @@ -0,0 +1,38 @@ +{ + "filterConditionEditor": { + "cancelButton": "Cancel", + "okButton": "OK", + "addWhere": "Add", + "clear": "Clear", + "moveTop": "Top", + "moveUp": "Up", + "moveDown": "Down", + "moveBottom": "Bottom", + "leftBrackets": "Left Brackets", + "field": "Field Name", + "operator": "Operator", + "value": "Value", + "valueType": "Value type", + "expressType": { + "value": "Value", + "express": "Express", + "frontExpress": "Front Express" + }, + "rightBrackets": "Right Brackets", + "relation": "Relation", + "relationValue": { + "and": "And", + "or": "Or" + }, + "designTab": "Design", + "jsonTab": "JSON", + "sqlTab": "Sql", + "title": "Filter Designer", + "message": "Are you sure you want to clear all current data?", + "validate": { + "bracket": "The brackets do not match, please check", + "relation": "The condition relationship is incomplete, please check", + "field": "Condition field is not set, please check" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/filter-condition-editor/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/filter-condition-editor/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..47624c20ad040b9a646d1525fbe704f409bee093 --- /dev/null +++ b/packages/ui-vue/components/filter-condition-editor/src/locales/ui/zh-CHS.json @@ -0,0 +1,38 @@ +{ + "filterConditionEditor": { + "cancelButton": "取消", + "okButton": "确定", + "addWhere": "添加子句", + "clear": "清空", + "moveTop": "置顶", + "moveUp": "上移", + "moveDown": "下移", + "moveBottom": "置底", + "leftBrackets": "左括号", + "field": "字段", + "operator": "操作符", + "value": "值", + "valueType": "值类型", + "expressType": { + "value": "值", + "express": "表达式", + "frontExpress": "表单表达式" + }, + "rightBrackets": "右括号", + "relation": "关系", + "relationValue": { + "and": "并且", + "or": "或者" + }, + "designTab": "设计器", + "jsonTab": "源代码", + "sqlTab": "Sql预览", + "title": "条件编辑器", + "message": "确认要清空当前所有数据吗?", + "validate": { + "bracket": "左右括号不匹配,请检查", + "relation": "条件关系不完整,请检查", + "field": "条件字段未设置,请检查" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/filter-condition-editor/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/filter-condition-editor/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..96f14a76c1085cd0d31376a0becbdc66eaf8094a --- /dev/null +++ b/packages/ui-vue/components/filter-condition-editor/src/locales/ui/zh-CHT.json @@ -0,0 +1,38 @@ +{ + "filterConditionEditor": { + "cancelButton": "取消", + "okButton": "確定", + "addWhere": "添加子句", + "clear": "清空", + "moveTop": "置頂", + "moveUp": "上移", + "moveDown": "下移", + "moveBottom": "置底", + "leftBrackets": "左括號", + "field": "字段", + "operator": "操作符", + "value": "值", + "valueType": "值類型", + "expressType": { + "value": "值", + "express": "錶達式", + "frontExpress": "表單表達式" + }, + "rightBrackets": "右括號", + "relation": "關係", + "relationValue": { + "and": "並且", + "or": "或者" + }, + "designTab": "設計器", + "jsonTab": "源代碼", + "sqlTab": "Sql預覽", + "title": "條件編輯器", + "message": "確認要清空當前所有數據嗎?", + "validate": { + "bracket": "左右括號不匹配,請檢查", + "relation": "條件關系不完整,請檢查", + "field": "條件字段未設置,請檢查" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/index.ts b/packages/ui-vue/components/index.ts index 86097beba66b628b6eed8033855c99feafdb3610..29386a671b3f3fe512b245429c8f3fa74fc99b75 100644 --- a/packages/ui-vue/components/index.ts +++ b/packages/ui-vue/components/index.ts @@ -39,6 +39,7 @@ import DynamicForm from './dynamic-form'; import FilterBar from './filter-bar'; import ImageCropper from './image-cropper'; import FInputGroup from './input-group'; +import LanguageTextbox from './language-textbox'; import Layout from './layout'; import ListNav from './list-nav'; import Loading from './loading'; @@ -93,6 +94,7 @@ import JsonEditor from './json-editor'; import DynamicView from './dynamic-view'; import CodeEditor from './code-editor'; import HtmlTemplate from './html-template'; +import Locale from './locale'; import '../public/assets/farris-all.css'; // export all components modules export * from './components'; @@ -101,8 +103,9 @@ export * from './designer'; // 不注册设计时组件 export default { - install(app: App): void { - app.use(Accordion) + install(app: App, options?: any): void { + app.use(Locale, options) + .use(Accordion) .use(Avatar) .use(BorderEditor) .use(Button) @@ -129,6 +132,7 @@ export default { .use(FilterBar) .use(ImageCropper) .use(FInputGroup) + .use(LanguageTextbox) .use(Layout) .use(ListNav) .use(Loading) diff --git a/packages/ui-vue/components/input-group/src/components/text-edit.component.tsx b/packages/ui-vue/components/input-group/src/components/text-edit.component.tsx index e5731f7bafbadf72e23bd0859efc27ffd9df3b47..df6c1540f1de9be9acea5e4c7e0944b82d83e80b 100644 --- a/packages/ui-vue/components/input-group/src/components/text-edit.component.tsx +++ b/packages/ui-vue/components/input-group/src/components/text-edit.component.tsx @@ -48,6 +48,7 @@ export default function ( return () => ( props.maxLength ? props.maxLength : (props.modelValue ? props.modelValue.length : 0)); + const onInput = (e: any) => { e.stopPropagation(); context.emit('update:modelValue', e.target?.value); context.emit('valueChange', e.target?.value); }; + const textareaClass = computed(() => { return { 'form-control': true, @@ -26,9 +29,9 @@ export default function ( const shouldShowTextareaClearButton = computed(() => props.enableClear && !props.readonly && !props.disabled); - + const localePlaceholder= getLocalePlaceholderText(props.placeholder); const realPlaceholder = computed(() => { - return props.disabled || props.readonly ? '' : props.placeholder; + return props.disabled || props.readonly ? '' : localePlaceholder; }); const textareaClearIconStyle = computed(() => { @@ -99,4 +102,4 @@ export default function ( } return { renderTextarea, renderCount, renderClear }; -} +} \ No newline at end of file diff --git a/packages/ui-vue/components/input-group/src/composition/locale.ts b/packages/ui-vue/components/input-group/src/composition/locale.ts new file mode 100644 index 0000000000000000000000000000000000000000..e0e553a023fe5fc8c0bf14188cb2e5e92e8bb571 --- /dev/null +++ b/packages/ui-vue/components/input-group/src/composition/locale.ts @@ -0,0 +1,9 @@ + +import { LocaleService } from '@farris/ui-vue/components/locale'; +export function getLocalePlaceholderText(filterText) { + if (!filterText) { + // 允许为空 + return ''; + } + return LocaleService.getRealPropertyValue(filterText, '请输入', 'input-group.placeholder') +}; \ No newline at end of file diff --git a/packages/ui-vue/components/input-group/src/composition/use-text-box.ts b/packages/ui-vue/components/input-group/src/composition/use-text-box.ts index c7196ed74af9d179a89ce4aa9041621bdfdcdc5f..f89dce16c0a4f49aacd796532a9e5cf77055071f 100644 --- a/packages/ui-vue/components/input-group/src/composition/use-text-box.ts +++ b/packages/ui-vue/components/input-group/src/composition/use-text-box.ts @@ -1,7 +1,7 @@ import { SetupContext, computed, ref, watch } from "vue"; import { InputGroupProps } from "../input-group.props"; import { UseTextBox } from "./types"; - +import { LocaleService } from '@farris/ui-vue/components/locale'; export function useTextBox( props: InputGroupProps, context: SetupContext @@ -12,8 +12,8 @@ export function useTextBox( /** 文本在输入框中的对齐方式 */ const textAlign = ref(props.textAlign); const showBorder = ref(props.showBorder); - - const textBoxPlaceholder = computed(() => ((props.disabled || props.readonly) && !props.forcePlaceholder ? '' : props.placeholder)); + const localePlaceholder= LocaleService.getRealPropertyValue(props.placeholder, '请输入', 'input-group.placeholder'); + const textBoxPlaceholder = computed(() => ((props.disabled || props.readonly) && !props.forcePlaceholder ? '' : localePlaceholder)); const canFocus = computed(() => props.editable || !props.readonly); diff --git a/packages/ui-vue/components/input-group/src/property-config/input-group.property-config.ts b/packages/ui-vue/components/input-group/src/property-config/input-group.property-config.ts index aaae75c2910c3f8feac0974df58a14e5026dbc0d..1fb81647188c77a84b9df8b571039727c3ca967c 100644 --- a/packages/ui-vue/components/input-group/src/property-config/input-group.property-config.ts +++ b/packages/ui-vue/components/input-group/src/property-config/input-group.property-config.ts @@ -64,7 +64,8 @@ export class InputGroupProperty extends InputBaseProperty { data: inputFormatValidationTypes }, refreshPanelAfterChanged: true, - $converter: typeConverter + $converter: typeConverter, + parentPropertyID: 'formatValidation' } }, @@ -74,7 +75,8 @@ export class InputGroupProperty extends InputBaseProperty { title: "输入错误提示", type: "string", description: "输入错误提示", - $converter: commonConverter + $converter: commonConverter, + parentPropertyID: 'formatValidation' }; } @@ -83,8 +85,8 @@ export class InputGroupProperty extends InputBaseProperty { title: "匹配正则", type: "string", description: "匹配正则", - - $converter: commonConverter + $converter: commonConverter, + parentPropertyID: 'formatValidation' }; } return formatValidationSchema; diff --git a/packages/ui-vue/components/input-group/src/ui/en.json b/packages/ui-vue/components/input-group/src/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..86a01dbe66338d66ed5cce00929a7df65fed5712 --- /dev/null +++ b/packages/ui-vue/components/input-group/src/ui/en.json @@ -0,0 +1,5 @@ +{ + "input-group": { + "placeholder": "Enter" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/input-group/src/ui/zh-CHS.json b/packages/ui-vue/components/input-group/src/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..1ee9de2faa9aa1ae96fe1bcc1d0f6ff030c8c227 --- /dev/null +++ b/packages/ui-vue/components/input-group/src/ui/zh-CHS.json @@ -0,0 +1,5 @@ +{ + "input-group": { + "placeholder": "请输入" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/input-group/src/ui/zh-CHT.json b/packages/ui-vue/components/input-group/src/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..6762cce184eab1dc9f317f5673e344c515a7bcd5 --- /dev/null +++ b/packages/ui-vue/components/input-group/src/ui/zh-CHT.json @@ -0,0 +1,5 @@ +{ + "input-group": { + "placeholder": "請輸入" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/language-textbox/index.ts b/packages/ui-vue/components/language-textbox/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b8ce851759089791cb7df63977aabb2e9b0c5f0 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/index.ts @@ -0,0 +1,39 @@ + +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { App, Plugin } from 'vue'; +import FLanguageTextboxDesign from './src/designer/language-textbox.design.component'; +import FLanguageTextbox from './src/language-textbox.component'; +import { propsResolver } from './src/language-textbox.props'; + +export * from './src/language-textbox.props'; +export * from './src/types'; + +FLanguageTextbox.install = (app: App) => { + app.component(FLanguageTextbox.name as string, FLanguageTextbox); +}; +FLanguageTextbox.register = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record) => { + componentMap['language-textbox'] = FLanguageTextbox; + propsResolverMap['language-textbox'] = propsResolver; +}; + +FLanguageTextbox.registerDesigner = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record) => { + componentMap['language-textbox'] = FLanguageTextboxDesign; + propsResolverMap['language-textbox'] = propsResolver; +}; + +export { FLanguageTextbox }; +export default FLanguageTextbox as typeof FLanguageTextbox & Plugin; diff --git a/packages/ui-vue/components/language-textbox/src/components/language-contents.component.tsx b/packages/ui-vue/components/language-textbox/src/components/language-contents.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f0ed6ad0d42815d460d30af34b039d6d752cb6be --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/components/language-contents.component.tsx @@ -0,0 +1,54 @@ +import { defineComponent, nextTick, onMounted, ref, watch } from "vue"; +import { FInputGroup } from "@farris/ui-vue/components/input-group"; +import { LanguageItem } from "../types"; + +export default defineComponent({ + name: 'LanguageContents', + props: { + languages: { type: Array, default: [] }, + modelValue: { type: Object, default: null }, + id: { type: String, default: '' }, + maxLength: { type: Number, default: undefined } + }, + emits: ['update:modelValue'], + setup(props, context) { + + const languages = ref(props.languages); + const data = ref(props.modelValue || {}); + const inputsRef = ref(); + + onMounted(()=> { + setTimeout(() => { + inputsRef.value?.querySelector('input')?.focus(); + }, 130); + }); + + return () => { + return
    + { languages.value?.map((item: LanguageItem) => { + return
  • +
    +
    + +
    + { + data.value[item.code] = value; + context.emit('update:modelValue', data.value); + }} + updateOn="change" + maxLength={props.maxLength} + > +
    +
    +
    +
  • ; + }) } +
; + }; + } +}); diff --git a/packages/ui-vue/components/language-textbox/src/designer/language-textbox.design.component.tsx b/packages/ui-vue/components/language-textbox/src/designer/language-textbox.design.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b1c674f1516d0da6aad1c2918388a32d0d326efd --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/designer/language-textbox.design.component.tsx @@ -0,0 +1,37 @@ +import { defineComponent, inject, onMounted, ref } from 'vue'; +import { useDesignerComponent, DesignerItemContext } from '@farris/ui-vue/components/designer-canvas'; +import { useLanguageTextboxDesignerRules } from './use-language-textbox.rules'; + +export default defineComponent({ + name: 'FLanguageTextboxDesign', + props: { + placeholder: { type: String, default: '' } + }, + setup(props, context) { + + const elementRef = ref(); + const designItemContext = inject('design-item-context') as DesignerItemContext; + const designerHostService = inject('designer-host-service'); + const designerRulesComposition = useLanguageTextboxDesignerRules(designItemContext, designerHostService); + const componentInstance = useDesignerComponent(elementRef, designItemContext, designerRulesComposition); + + + onMounted(() => { + elementRef.value.componentInstance = componentInstance; + }); + + context.expose(componentInstance.value); + + + return () => { + return
+
+ +
+ +
+
+
; + }; + } +}); diff --git a/packages/ui-vue/components/language-textbox/src/designer/use-language-textbox.rules.ts b/packages/ui-vue/components/language-textbox/src/designer/use-language-textbox.rules.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc7e910cf83a86c9b3759ce2d84c658bb268ab71 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/designer/use-language-textbox.rules.ts @@ -0,0 +1,15 @@ + +import { ComponentSchema, DesignerComponentInstance, DesignerItemContext, UseDesignerRules } from "@farris/ui-vue/components/designer-canvas"; +import { LanuageTextBoxPropertyConfig } from "../property-config/language-textbox.property-config"; + +export function useLanguageTextboxDesignerRules(designItemContext: DesignerItemContext, designerHostService): UseDesignerRules { + // 构造属性配置方法 + function getPropsConfig(componentId: string, componentInstance: DesignerComponentInstance) { + const schema = designItemContext.schema as ComponentSchema; + const inputGroupProps = new LanuageTextBoxPropertyConfig(componentId, designerHostService); + return inputGroupProps.getPropertyConfig(schema, componentInstance); + } + + return { getPropsConfig } as UseDesignerRules; + +} diff --git a/packages/ui-vue/components/language-textbox/src/language-textbox.component.tsx b/packages/ui-vue/components/language-textbox/src/language-textbox.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..ca6de572986591959ffe9226b37150d4373020e8 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/language-textbox.component.tsx @@ -0,0 +1,121 @@ +import { computed, defineComponent, inject, onMounted, ref, toRefs, watch } from "vue"; + +import FButtonEdit from '@farris/ui-vue/components/button-edit'; + +import { languageTextBoxProps } from "./language-textbox.props"; +import LanguageContents from "./components/language-contents.component"; +import { LanguageItem } from "./types"; +import { useI18n } from 'vue-i18n'; + +export default defineComponent({ + name: 'FLanguageTextbox', + props: languageTextBoxProps, + emits: ['update:modelValue'], + setup(props, context) { + const {locale: currentLang} = useI18n(); + const { disabled, readonly, editable, modelValue } = toRefs(props); + + const displayText = computed(() => modelValue.value?.[currentLang.value]); + const buttonEditorRef = ref(); + + watch(() => props.modelValue, (newValue) => { + modelValue.value = newValue; + }); + + const updateLangOrder = (langValue: LanguageItem[]) => { + if (langValue?.length <= 1) { + return; + } + + let currentLangIndex = -1; + const curritem = langValue.find((lang, index) => { + const result = lang.code === currentLang.value; + if (result) { + currentLangIndex = index; + } + return result; + }); + + if (curritem) { + const item = Object.assign({}, curritem); + + langValue.splice(currentLangIndex, 1); + langValue.unshift(item); + } + + return langValue; + }; + + const languages = ref(); + + watch(() => props.languages, (newValue) => { + languages.value = updateLangOrder(newValue) || []; + }); + + function onDisplayTextChange($event: any) { + modelValue.value = modelValue.value || {}; + if (modelValue.value[currentLang.value] !== $event) { + modelValue.value[currentLang.value] = $event; + context.emit('update:modelValue', modelValue.value); + } + } + + const showPopover = computed(() => { + const popoverInstance = buttonEditorRef.value?.popoverRef; + if (popoverInstance && languages.value.length > 1) { + return popoverInstance.shown; + } + return false; + }); + + onMounted(() => { + languages.value = updateLangOrder(props.languages) || []; + }); + + const onUpdateModelValue = (value: any) => { + modelValue.value = value; + context.emit('update:modelValue', modelValue.value); + }; + + const onBeforeOpen = () => { + if (languages.value?.length <= 1) { + return false; + } + return true; + }; + + const onClear = () => { + modelValue.value = modelValue.value || {}; + if (modelValue.value[currentLang.value]) { + modelValue.value[currentLang.value] = ''; + context.emit('update:modelValue', modelValue.value); + } + }; + + return () => { + return + {showPopover.value && } + ; + } + } +}); diff --git a/packages/ui-vue/components/language-textbox/src/language-textbox.props.ts b/packages/ui-vue/components/language-textbox/src/language-textbox.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c3e3c4ab24e7f189a8b12d953745145c350cb0c --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/language-textbox.props.ts @@ -0,0 +1,26 @@ +import { ExtractPropTypes, PropType } from "vue"; +import { LanguageData, LanguageItem, LanguageTextMaxLength } from "./types"; +import { createPropsResolver } from "@farris/ui-vue/components/dynamic-resolver"; + +import { schemaMapper } from './schema/schema-mapper'; +import languageTextboxSchema from './schema/language-textbox.schema.json'; +import { schemaResolver } from './schema/schema-resolver'; + +export const languageTextBoxProps = { + id: { type: String, required: true }, + languages: { type: Array as PropType, default: [] }, + disabled: { type: Boolean, default: false }, + editable: { type: Boolean, default: false }, + readonly: { type: Boolean, default: false }, + placeholder: { type: String, default: '' }, + maxWords: { type: Object as PropType, default: null }, + modelValue: { type: Object as PropType, default: null }, + dropDownIcon: { type: String, default: '' }, + tabIndex: { type: Number, default: -1 }, + enableTitle: {type: Boolean, default: true }, + maxLength: { type: Number, default: null } +}; + +export type LanguageTextBoxProps = ExtractPropTypes; + +export const propsResolver = createPropsResolver(languageTextBoxProps, languageTextboxSchema, schemaMapper, schemaResolver); diff --git a/packages/ui-vue/components/language-textbox/src/property-config/language-textbox.property-config.ts b/packages/ui-vue/components/language-textbox/src/property-config/language-textbox.property-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..b635a8745edd095e355fad33772d0c859f48d110 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/property-config/language-textbox.property-config.ts @@ -0,0 +1,23 @@ +import { InputBaseProperty } from "@farris/ui-vue/components/property-panel"; + +export class LanuageTextBoxPropertyConfig extends InputBaseProperty { + constructor(componentId: string, designerHostService: any) { + super(componentId, designerHostService); + } + + getEditorProperties(propertyData: any) { + return this.getComponentConfig(propertyData, { type: "language-textbox" }, { + maxLength: { + description: "文本字数最大长度", + title: "最大长度", + type: "number", + editor: { + nullable: true, + min: 0, + useThousands: false, + max: propertyData.editor.maxLength + } + }, + }); + } +} diff --git a/packages/mobile-ui-vue/components/picker/src/schema/enum-field-input.schema.json b/packages/ui-vue/components/language-textbox/src/schema/language-textbox.schema.json similarity index 56% rename from packages/mobile-ui-vue/components/picker/src/schema/enum-field-input.schema.json rename to packages/ui-vue/components/language-textbox/src/schema/language-textbox.schema.json index 7cf654531f3a1d981c3f216c334cbe34e2a8e907..d9a5dc6e4c2cecfd03b8eeb3c91e927f27542a80 100644 --- a/packages/mobile-ui-vue/components/picker/src/schema/enum-field-input.schema.json +++ b/packages/ui-vue/components/language-textbox/src/schema/language-textbox.schema.json @@ -1,7 +1,7 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://farris-design.gitee.io/enum-field.schema.json", - "title": "enum-field", + "$id": "https://farris-design.gitee.io/input-group.schema.json", + "title": "language-textbox", "description": "A Farris Input Component", "type": "object", "properties": { @@ -12,7 +12,7 @@ "type": { "description": "The type string of Input Group component", "type": "string", - "default": "enum-field" + "default": "language-textbox" }, "appearance": { "description": "", @@ -32,43 +32,89 @@ "type": "object", "default": {} }, - "readonly": { - "type": "string", - "default": false + "formatValidation": { + "description": "", + "type": "object", + "default": {} }, - "title": { + "editable": { "description": "", - "type": "string", - "default": "" + "type": "boolean", + "default": true + }, + "enableLinkLabel": { + "description": "", + "type": "boolean", + "default": false }, "label": { "description": "", "type": "string", "default": "" }, - "lableWidth": { + "labelWidth": { "description": "", "type": "number" }, - "visible": { + "placeholder": { "description": "", - "type": "boolean", - "default": true + "type": "string", + "default": "" }, - "data": { + "readonly": { "description": "", - "type": "array", - "default": [] + "type": "boolean", + "default": false }, - "enableClear": { + "disabled": { "description": "", "type": "boolean", "default": false }, - "editable": { + "required": { "description": "", "type": "boolean", "default": false + }, + "tabIndex": { + "description": "", + "type": "number", + "default": -1 + }, + "maxLength": { + "description": "", + "type": "number", + "default": "" + }, + "visible": { + "description": "", + "type": "boolean", + "default": true + }, + "onBlur": { + "description": "", + "type": "string", + "default": "" + }, + "onClickLinkLabel": { + "description": "", + "type": "sting", + "default": "" + }, + "languages": { + "description": "语种列表", + "type": "array", + "default": [] + }, + "modelValue": { + "description": "绑定值", + "type": "object", + "default": {} + }, + "maxWords": { + "description": "各语种最大字符数", + "type": "object", + "default": null } }, "required": [ diff --git a/packages/ui-vue/components/language-textbox/src/schema/schema-mapper.ts b/packages/ui-vue/components/language-textbox/src/schema/schema-mapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..97964aee23bbb8b523c7692723ea02db00d4f4c0 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/schema/schema-mapper.ts @@ -0,0 +1,5 @@ +import { MapperFunction, resolveAppearance } from '../../../dynamic-resolver'; + +export const schemaMapper = new Map([ + ['appearance', resolveAppearance] +]); diff --git a/packages/ui-vue/components/language-textbox/src/schema/schema-resolver.ts b/packages/ui-vue/components/language-textbox/src/schema/schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..b02bdf93eec9060948f579c53aa81e3963a7d706 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/schema/schema-resolver.ts @@ -0,0 +1,5 @@ +import { DynamicResolver } from "../../../dynamic-resolver"; + +export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record): Record { + return schema; +} diff --git a/packages/ui-vue/components/language-textbox/src/types/index.ts b/packages/ui-vue/components/language-textbox/src/types/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..540535b6addf66917edb5327a3b1f75c9d743090 --- /dev/null +++ b/packages/ui-vue/components/language-textbox/src/types/index.ts @@ -0,0 +1,18 @@ +export interface LanguageItem { + /** 地区或国家代码 */ + code: string; + /** 名称 */ + name: string; + + default?: boolean; +} + +/** 字段数据;格式: { 'zh-cn': '姓名', 'en': 'name'... } */ +export interface LanguageData { + /** 地区或国家代码;值为当前字段的数据 */ + [code: string]: string; +} + +export interface LanguageTextMaxLength { + [langCode: string]: number; +} diff --git a/packages/ui-vue/components/list-nav/src/designer/list-nav.design.component.tsx b/packages/ui-vue/components/list-nav/src/designer/list-nav.design.component.tsx index 837298a80d7d65b51e5c751446ceea7d3101921d..fe4750cc0fe95885e7b13666513b3cf313502f35 100644 --- a/packages/ui-vue/components/list-nav/src/designer/list-nav.design.component.tsx +++ b/packages/ui-vue/components/list-nav/src/designer/list-nav.design.component.tsx @@ -1,18 +1,18 @@ - /** - * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/** +* Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ import { computed, defineComponent, inject, onMounted, ref, SetupContext } from 'vue'; import { listNavProps, ListNavProps } from '../list-nav.props'; import { useDesignerComponent } from '../../../designer-canvas/src/composition/function/use-designer-component'; @@ -30,8 +30,6 @@ export default defineComponent({ const designItemContext = inject('design-item-context') as DesignerItemContext; const designerRulesComposition = useDesignerRules(designItemContext, designerHostService); const componentInstance = useDesignerComponent(elementRef, designItemContext, designerRulesComposition); - componentInstance.value.canMove = false; - componentInstance.value.canNested = false; /** 导航位置 */ const navPosition = ref(props.position); /** 标题 */ @@ -54,21 +52,46 @@ export default defineComponent({ context.expose(componentInstance.value); + /** + * 切换收折状态 + * @param event + * @returns + */ + function toggleCollaspe(event) { + event && event.stopPropagation(); + if (props.disabled) { + return; + } + // 初始没有加上这个样式,是避免初始设置收起的时候,看到有収折的动画 + afterInitAnimatClass.value = true; + hideNav.value = !hideNav.value; + } + const listNavMainClass = computed(() => { + return { + 'f-list-nav-main': true, + 'd-none': hideNav.value + }; + }); return () => { return (
-
+
{context.slots.navHeader &&
{context.slots.navHeader()}
} {!context.slots.navHeader && title.value && (
{title.value}
)} + {context.slots.default && +
+ {context.slots.default()} +
} {context.slots.navContent &&
{context.slots.navContent()}
} {context.slots.navFooter && }
- {props.collapsible &&
+ {props.collapsible &&
toggleCollaspe(event)}>
}
diff --git a/packages/ui-vue/components/list-nav/src/designer/use-designer-rules.ts b/packages/ui-vue/components/list-nav/src/designer/use-designer-rules.ts index e9f2211d1a506122f0c0b0716945b0fd71b6f285..b1b36109ef3a98d0c0c2d0372fec7f0e0c5f074f 100644 --- a/packages/ui-vue/components/list-nav/src/designer/use-designer-rules.ts +++ b/packages/ui-vue/components/list-nav/src/designer/use-designer-rules.ts @@ -1,13 +1,14 @@ - - + + import { DesignerHostService, DraggingResolveContext, UseDesignerRules } from "../../../designer-canvas/src/composition/types"; import { useDragulaCommonRule } from "../../../designer-canvas/src/composition/rule/use-dragula-common-rule"; -import { DesignerItemContext } from "../../../designer-canvas/src/types"; import { UseTemplateDragAndDropRules } from "../../../designer-canvas/src/composition/rule/use-template-rule"; +import { DgControl, DesignerItemContext } from "@farris/ui-vue/components/designer-canvas"; +import { ListNavProperty } from "../property-config/list-nav.property-config"; export function useDesignerRules(designItemContext: DesignerItemContext, designerHostService?: DesignerHostService): UseDesignerRules { const dragAndDropRules = new UseTemplateDragAndDropRules(); - const { canMove, canAccept, canDelete } = dragAndDropRules.getTemplateRule(designItemContext, designerHostService); + const { canAccept } = dragAndDropRules.getTemplateRule(designItemContext, designerHostService); /** * 判断是否可以接收拖拽新增的子级控件 @@ -18,7 +19,15 @@ export function useDesignerRules(designItemContext: DesignerItemContext, designe if (!basalRule) { return false; } - return canAccept; + if (!canAccept) { + return false; + } + // 子级控件唯一且为外部容器,说明是模板预制的结构,那么不再接收其他控件 + if (designItemContext.schema.contents?.length === 1 && designItemContext.schema?.contents[0]?.type === DgControl['external-container'].type) { + return false; + } + + return true; } @@ -26,23 +35,30 @@ export function useDesignerRules(designItemContext: DesignerItemContext, designe return 'display: flex;flex-direction:column;height:100%';; } - function checkCanMoveComponent() { - return canMove; + return false; } function checkCanDeleteComponent() { - return canDelete; + return false; } function hideNestedPaddingInDesginerView() { - return !canMove && !canDelete; + return true; + } + /** + * 获取属性配置 + */ + function getPropsConfig(componentId: string) { + const listNavProp = new ListNavProperty(componentId, designerHostService); + const { schema } = designItemContext; + return listNavProp.getPropertyConfig(schema); } - return { canAccepts, getStyles, checkCanMoveComponent, checkCanDeleteComponent, - hideNestedPaddingInDesginerView + hideNestedPaddingInDesginerView, + getPropsConfig }; } diff --git a/packages/ui-vue/components/list-nav/src/list-nav.component.tsx b/packages/ui-vue/components/list-nav/src/list-nav.component.tsx index 8e0c4edb1838deac2b64d5a7305d42d544179385..a3c4e7759b4a824b5a97e99af4695d9517b79388 100644 --- a/packages/ui-vue/components/list-nav/src/list-nav.component.tsx +++ b/packages/ui-vue/components/list-nav/src/list-nav.component.tsx @@ -1,4 +1,4 @@ - + /** * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. * @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { getCustomClass } from '@farris/ui-vue/components/common'; import { computed, defineComponent, ref, SetupContext } from 'vue'; import { listNavProps, ListNavProps } from './list-nav.props'; @@ -50,13 +51,23 @@ export default defineComponent({ */ const navStyleSize = computed(() => { const result = {}; - const propName = ['top', 'bottom'].indexOf(navPosition.value) > -1?'height':'width'; + const propName = ['top', 'bottom'].indexOf(navPosition.value) > -1 ? 'height' : 'width'; result[propName] = !hideNav.value ? props.size + 'px' : '0px'; return result; }); + const listNavClass = computed(() => { + const classObject = { + 'f-list-nav': true, + 'f-list-nav-top': navPosition.value === 'top', + 'f-list-nav-left': navPosition.value === 'left', + 'f-list-nav-right': navPosition.value === 'right', + 'f-list-nav-bottom': navPosition.value === 'bottom' + } as Record; + return getCustomClass(classObject, props.customClass); + }); return () => { return ( -
+
{context.slots.navHeader &&
{context.slots.navHeader()}
} @@ -65,6 +76,7 @@ export default defineComponent({
{title.value}
)} + {context.slots.default &&
{context.slots.default()}
} {context.slots.navContent &&
{context.slots.navContent()}
} {context.slots.navFooter && }
diff --git a/packages/ui-vue/components/list-nav/src/list-nav.props.ts b/packages/ui-vue/components/list-nav/src/list-nav.props.ts index 10f0a99d931e7a97b90b3867ef6893bd8f8b50a5..592ea9f59705160a071a75ee72dffc20dc29bf45 100644 --- a/packages/ui-vue/components/list-nav/src/list-nav.props.ts +++ b/packages/ui-vue/components/list-nav/src/list-nav.props.ts @@ -1,4 +1,4 @@ - + /** * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. * @@ -19,11 +19,13 @@ import { createPropsResolver } from '../../dynamic-resolver'; import { schemaMapper } from './schema/schema-mapper'; import { schemaResolver } from './schema/schema-resolver'; import listNavSchema from './schema/list-nav.schema.json'; -import propertyConfig from './property-config/list-nav.property-config.json'; type positionValue = 'top' | 'left' | 'bottom' | 'right'; export const listNavProps = { + /** 自定义样式 */ + customClass: { type: String, default: '' }, + customStyle: { type: String, default: '' }, /** 位置 */ position: { Type: String as PropType, default: 'left' }, /** listNav名称 */ @@ -42,4 +44,4 @@ export const listNavProps = { export type ListNavProps = ExtractPropTypes; -export const propsResolver = createPropsResolver(listNavProps, listNavSchema, schemaMapper, schemaResolver, propertyConfig); +export const propsResolver = createPropsResolver(listNavProps, listNavSchema, schemaMapper, schemaResolver); diff --git a/packages/ui-vue/components/list-nav/src/property-config/list-nav.property-config.json b/packages/ui-vue/components/list-nav/src/property-config/list-nav.property-config.json deleted file mode 100644 index 75d5d8debe620a431fa413c1a2b53dc54226e5a7..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/list-nav/src/property-config/list-nav.property-config.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "title": "list-nav", - "description": "A Farris Component", - "type": "object", - "categories": { - "basic": { - "description": "Basic Infomation", - "title": "基本信息", - "properties": { - "id": { - "description": "组件标识", - "title": "标识", - "type": "string", - "readonly": true - }, - "type": { - "description": "类型", - "title": "类型", - "type": "string", - "readonly": true - } - } - } - } -} \ No newline at end of file diff --git a/packages/ui-vue/components/list-nav/src/property-config/list-nav.property-config.ts b/packages/ui-vue/components/list-nav/src/property-config/list-nav.property-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..1add265ea2d39b23a515b949d8ec93a8f4d51703 --- /dev/null +++ b/packages/ui-vue/components/list-nav/src/property-config/list-nav.property-config.ts @@ -0,0 +1,50 @@ + +import { canvasChanged } from "../../../../components/designer-canvas/src/composition/designer-canvas-changed"; +import { BaseControlProperty } from "../../../../components/property-panel/src/composition/entity/base-property"; + +export class ListNavProperty extends BaseControlProperty { + + public getPropertyConfig(propertyData: any) { + // 基本信息 + this.propertyConfig.categories['basic'] = this.getBasicPropConfig(propertyData); + // 外观 + this.propertyConfig.categories['appearance'] = this.getAppearanceConfig(propertyData); + + return this.propertyConfig; + } + + getAppearanceConfig(propertyData: any) { + const appearanceConfig = super.getAppearanceConfig(propertyData, { + width: { + title: "宽度", + type: "number", + editor: { + nullable: true, + min: 0, + precision: 0 + }, + $converter: "/converter/size.converter", + parentPropertyID: 'size' + }, + height: { + title: "高度", + type: "number", + editor: { + nullable: true, + min: 0, + precision: 0 + }, + $converter: "/converter/size.converter", + parentPropertyID: 'size', + visible: false + } + }, (changeObject, propertyData, parameters) => { + switch (changeObject.propertyID) { + case 'width': case 'height': + canvasChanged.value++; + break; + } + }); + return appearanceConfig; + } +} diff --git a/packages/ui-vue/components/list-nav/src/schema/list-nav.schema.json b/packages/ui-vue/components/list-nav/src/schema/list-nav.schema.json index 9f2ba066422458c12fb5392be0c6a094ad7b7943..123e36c00802f71d923adb74a7693e5fb073063c 100644 --- a/packages/ui-vue/components/list-nav/src/schema/list-nav.schema.json +++ b/packages/ui-vue/components/list-nav/src/schema/list-nav.schema.json @@ -49,6 +49,11 @@ "description": "", "type": "boolean", "default": true + }, + "position": { + "description": "", + "type": "string", + "default": "left" } }, "required": [ diff --git a/packages/ui-vue/components/list-nav/src/schema/schema-mapper.ts b/packages/ui-vue/components/list-nav/src/schema/schema-mapper.ts index 97964aee23bbb8b523c7692723ea02db00d4f4c0..f8263d255f92df6c457b1a4931097caa17542500 100644 --- a/packages/ui-vue/components/list-nav/src/schema/schema-mapper.ts +++ b/packages/ui-vue/components/list-nav/src/schema/schema-mapper.ts @@ -1,5 +1,12 @@ import { MapperFunction, resolveAppearance } from '../../../dynamic-resolver'; +function resolveSize(key: string, sizeObject: { width: number; height: number }) { + const customSize = sizeObject.width || sizeObject.height || ''; + return { size: customSize }; +} + export const schemaMapper = new Map([ - ['appearance', resolveAppearance] + ['appearance', resolveAppearance], + ['size', resolveSize] + ]); diff --git a/packages/ui-vue/components/list-view/src/components/data/data-area.component.tsx b/packages/ui-vue/components/list-view/src/components/data/data-area.component.tsx index ca98c793739d0191ee9b79e277041b5a78b4c5fc..b8518646f0e7ffd722166684f1de7269482997b0 100644 --- a/packages/ui-vue/components/list-view/src/components/data/data-area.component.tsx +++ b/packages/ui-vue/components/list-view/src/components/data/data-area.component.tsx @@ -1,15 +1,13 @@ import { computed, Ref, ref, SetupContext } from 'vue'; +import { useI18n } from 'vue-i18n'; import { ListViewProps } from '../../list-view.props'; import { UseDataView, UseGroupData, UseSelection, UseVisualData, VisualData } from '../../../../data-view'; import getSingleItem from '../item/single-item.component'; import getContentItem from '../item/content-item.component'; import getDraggableItem from '../item/draggable-item.component'; import getGroupItem from '../item/group-item.component'; -import { useHover } from '../../composition/use-hover'; -import { useDraggable } from '../../composition/use-draggable'; import { useRemove } from '../../composition/use-remove'; -import { useItem } from '../../composition/use-item'; import { UseDraggable, UseHover, UseItem } from '../../composition/types'; export default function ( @@ -24,10 +22,11 @@ export default function ( useItemCompostion: UseItem, useDraggableComposition: UseDraggable ) { + const { t: getLocaleValue } = useI18n(); const listViewType = ref(props.view); const cardView = ref(props.view === 'CardView'); const clickItem = ref({}); - const emptyMessage = ref('暂无数据'); + const emptyMessage = ref(getLocaleValue('listView.emptyMessage')); // const useDraggableComposition = useDraggable(props, context, dataViewComposition, // useHoverComposition); diff --git a/packages/ui-vue/components/list-view/src/composition/use-draggable.ts b/packages/ui-vue/components/list-view/src/composition/use-draggable.ts index 25aa7d44f56baab918d7cea8bf5f637e92ab2a7f..cd8ab7aa912b7b2d998b2039fdb5c6399c90f387 100644 --- a/packages/ui-vue/components/list-view/src/composition/use-draggable.ts +++ b/packages/ui-vue/components/list-view/src/composition/use-draggable.ts @@ -36,7 +36,7 @@ export function useDraggable( customDragIcon.style.cssText = ` position:absolute; left:${left}px; - top:${top}px; + top:${document.documentElement.scrollTop ? top + document.documentElement.scrollTop: top}px; z-index: 999999; border: 1px solid #e2e3e5; pointer-events: none; diff --git a/packages/ui-vue/components/list-view/src/composition/use-item.ts b/packages/ui-vue/components/list-view/src/composition/use-item.ts index f31e507091ca04339a6652503be85fb16c23a072..df8bb340af13f122bb79e52f2020c3c72980dadf 100644 --- a/packages/ui-vue/components/list-view/src/composition/use-item.ts +++ b/packages/ui-vue/components/list-view/src/composition/use-item.ts @@ -2,6 +2,7 @@ import { Ref, SetupContext, computed, ref } from "vue"; import { ListViewProps, MultiSelectMode } from "../list-view.props"; import { UseDraggable, UseHover, UseItem } from "./types"; import { UseRow, UseSelection, VisualData } from '@farris/ui-vue/components/data-view'; +import { getCustomClass } from "@farris/ui-vue/components/common"; export function useItem( props: ListViewProps, @@ -44,7 +45,7 @@ export function useItem( function updateSelectedItems() { selectedItems.value = getSelectedItems(); } - + function getKey(item: VisualData, index: number) { return item.raw[identifyField.value] != null ? item.raw[identifyField.value] : ''; } @@ -55,20 +56,24 @@ export function useItem( 'f-list-view-draggable-item': draggable.value, 'f-un-click': !item.checked, 'f-un-select': !!item.raw[disableField.value], - 'f-listview-active': - // 多选 - enableMultiSelect.value && isSelected(getKey(item, index)) || - // 单选 - (!enableMultiSelect.value && item.raw[identifyField.value] === currentSelectedDataId.value) + 'f-listview-active': + // 多选 + enableMultiSelect.value && isSelected(getKey(item, index)) || + // 单选 + (!enableMultiSelect.value && item.raw[identifyField.value] === currentSelectedDataId.value) , 'f-listview-hover': !isDragging.value && index === hoverIndex.value, 'moving': !!item.moving } as Record; - const customClassArray = customListViewItemClass.value.split(' '); - customClassArray.reduce((result, className) => { - result[className] = true; - return result; - }, classObject); + // 静态设置样式 + if (typeof props.itemClass === 'string') { + return getCustomClass(classObject, props.itemClass); + } + // 动态设置样式 + if (typeof props.itemClass === 'function') { + const customResult=props.itemClass(item); + return getCustomClass(classObject, customResult); + } return classObject; }; diff --git a/packages/ui-vue/components/list-view/src/composition/use-remove.ts b/packages/ui-vue/components/list-view/src/composition/use-remove.ts index aa9a42d61a652bc1e0695687e7767400b5d293a9..ac79d3a03b213424aed60b6398479fb63efd3652 100644 --- a/packages/ui-vue/components/list-view/src/composition/use-remove.ts +++ b/packages/ui-vue/components/list-view/src/composition/use-remove.ts @@ -14,6 +14,9 @@ export function useRemove( function removeItem(index: number) { if (index > -1 && index < dataView.value.length) { + if (props?.checkBeforeRemoveItem && !props.checkBeforeRemoveItem(dataView.value[index])) { + return; + } const removedItem = dataView.value.splice(index, 1); updateSelectedItems(); context.emit('removeItem', removedItem[0]); diff --git a/packages/ui-vue/components/list-view/src/list-view.component.tsx b/packages/ui-vue/components/list-view/src/list-view.component.tsx index 509fc943fa0053b269a49f2c19e70eb406cf71bf..8631196247593a1dbfd2df7aad38098c00cebcd8 100644 --- a/packages/ui-vue/components/list-view/src/list-view.component.tsx +++ b/packages/ui-vue/components/list-view/src/list-view.component.tsx @@ -34,6 +34,7 @@ export default defineComponent({ name: 'FListView', props: listViewProps, emits: ['afterSearch', 'checkValuesChange', 'clickItem', + 'selectItem', 'unSelectItem', 'selectionChange', 'removeItem', 'change', 'activeChange'] as (string[] & ThisType) | undefined, setup(props: ListViewProps, context: SetupContext) { const listViewContentRef = ref(); diff --git a/packages/ui-vue/components/list-view/src/list-view.props.ts b/packages/ui-vue/components/list-view/src/list-view.props.ts index c4ad1470ac981c47f55b57b63d431faa5da73457..6b2cbda324e948e7de41a30e0edb85a2415a0a3a 100644 --- a/packages/ui-vue/components/list-view/src/list-view.props.ts +++ b/packages/ui-vue/components/list-view/src/list-view.props.ts @@ -40,19 +40,24 @@ export const listViewProps = { customClass: { type: String, default: '' }, data: { type: Array, default: [] }, draggable: { type: Boolean, default: false }, - multiSelect: { Type: Boolean, default: false }, - multiSelectMode: { Type: String as PropType, default: 'OnCheck' }, - idField: { Type: String, default: 'id' }, - valueField: { Type: String, default: 'id' }, - textField: { Type: String, default: 'name' }, - titleField: { Type: String, default: 'name' }, - view: { Type: String as PropType, default: 'ContentView' }, - size: { Type: String as PropType, default: 'default' }, + multiSelect: { type: Boolean, default: false }, + multiSelectMode: { type: String as PropType, default: 'OnCheck' }, + idField: { type: String, default: 'id' }, + valueField: { type: String, default: 'id' }, + textField: { type: String, default: 'name' }, + titleField: { type: String, default: 'name' }, + view: { type: String as PropType, default: 'ContentView' }, + size: { type: String as PropType, default: 'default' }, placeholder: { type: String, default: '' }, - header: { Type: String as PropType, default: 'ContentHeader' }, - headerClass: { Type: String, default: '' }, - itemClass: { Type: String, default: '' }, - itemContentClass: { Type: String, default: '' }, + header: { type: String as PropType, default: 'ContentHeader' }, + headerClass: { type: String, default: '' }, + /** 可能根据数据每各项的样式不同 */ + itemClass: { + type: [String, Function] as PropType string)>, default: '', validator: (value: unknown): boolean => { + return typeof value === 'string' || typeof value === 'function' + } + }, + itemContentClass: { type: String, default: '' }, selectionValues: { type: Array, default: [] }, /** 分组配置 */ group: { type: Object }, @@ -73,7 +78,8 @@ export const listViewProps = { enableHighlightSearch: { type: Boolean, default: true }, /** 虚拟化渲染数据 */ virtualized: { type: Boolean, default: true }, - + /** 删除数据前的检查方法,返回值为true时可以删除当前数据 */ + checkBeforeRemoveItem: { type: Function, default: null } } as Record; export type ListViewProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/list-view/src/locales/ui/en.json b/packages/ui-vue/components/list-view/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..ea1b58797b1f693454244c62838885f29af0a905 --- /dev/null +++ b/packages/ui-vue/components/list-view/src/locales/ui/en.json @@ -0,0 +1,5 @@ +{ + "listView": { + "emptyMessage": "Empty Data" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/list-view/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/list-view/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..f767c602ce31305fb7bcc2ed841e37e73fd7c344 --- /dev/null +++ b/packages/ui-vue/components/list-view/src/locales/ui/zh-CHS.json @@ -0,0 +1,5 @@ +{ + "listView": { + "emptyMessage": "暂无数据" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/list-view/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/list-view/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..c399e66307a74aedef18fd54af3140923bdc80e6 --- /dev/null +++ b/packages/ui-vue/components/list-view/src/locales/ui/zh-CHT.json @@ -0,0 +1,5 @@ +{ + "listView": { + "emptyMessage": "暫無數據" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/loading/src/loading.component.tsx b/packages/ui-vue/components/loading/src/loading.component.tsx index 65a3f07af7fbc9b96e78226f0c56b7e8bad85d39..0577508a869b5f53b75b48c3d611a946d4466071 100644 --- a/packages/ui-vue/components/loading/src/loading.component.tsx +++ b/packages/ui-vue/components/loading/src/loading.component.tsx @@ -19,6 +19,8 @@ import { defineComponent, SetupContext, ref, onMounted, watch, computed } from ' import { LoadingProps, loadingProps } from './loading.props'; import { LOADING_STYLES } from './composition/types'; +import { useI18n } from 'vue-i18n'; + export default defineComponent({ name: 'FLoading', props: loadingProps, diff --git a/packages/ui-vue/components/loading/src/loading.props.ts b/packages/ui-vue/components/loading/src/loading.props.ts index aeaaf4cb17932d6153a163c507b30103244ee806..4b3f81bae7630fcb519047cfcd2d26c58fa65099 100644 --- a/packages/ui-vue/components/loading/src/loading.props.ts +++ b/packages/ui-vue/components/loading/src/loading.props.ts @@ -19,7 +19,7 @@ export const loadingProps = { /** 是否展示文案 */ showMessage: { type: Boolean, default: true }, /** 展示信息 */ - message: { type: String, default: '加载中,请稍后...' }, + message: { type: String, default: '正在加载,请稍候...' }, /** 默认展示状态,方便通过按钮点击控制loading */ isActive: { type: Boolean, default: false }, /** 图标大小 */ @@ -33,4 +33,16 @@ export const loadingProps = { /** 延迟300毫秒,解决异步加载出现一闪而逝的问题*/ delay: { type: Number, default: 300 } }; + + export type LoadingProps = Partial>; + +export const DefaultLoadingProps: LoadingProps = { + showMessage: true, + message: '正在加载,请稍候...', + isActive: false, + width: 30, + type: 0, + targetPosition: '', + delay: 300 +}; diff --git a/packages/ui-vue/components/loading/src/loading.service.tsx b/packages/ui-vue/components/loading/src/loading.service.tsx index 70816310ea9963ade28b3218d49f4898b0e1e414..565d5b38336fa176caf8e8194f18064d38e1c5ae 100644 --- a/packages/ui-vue/components/loading/src/loading.service.tsx +++ b/packages/ui-vue/components/loading/src/loading.service.tsx @@ -1,7 +1,9 @@ /* eslint-disable no-use-before-define */ -import { Ref, createApp, onMounted, onUnmounted, ref } from 'vue'; +import { App, Ref, createApp, onMounted, onUnmounted, ref } from 'vue'; import FLoading from './loading.component'; -import { LoadingProps } from './loading.props'; +import { DefaultLoadingProps, LoadingProps } from './loading.props'; +import { LocaleService } from '@farris/ui-vue/components/locale'; +import { useI18n } from 'vue-i18n'; let currentLoadingInstanceID = -1; const loadingInstances: { [key: number]: Ref } = {}; @@ -21,6 +23,14 @@ function initInstance(props?: any): Ref> { currentLoadingInstanceID = newInstanceId; const appInstance = createApp({ setup() { + const { t } = useI18n(); + const message = ref(props.message); + if (message.value === '正在加载,请稍候...') { + message.value = t('loading.message'); + } + + const options = { ...props, message: message.value}; + const loadingInstance = ref(); onUnmounted(() => { if (canChangePosition) { @@ -40,7 +50,7 @@ function initInstance(props?: any): Ref> { } loadingInstances[newInstanceId] = loadingInstance; return () => ( - + ); } }); @@ -49,6 +59,7 @@ function initInstance(props?: any): Ref> { parentContainer.style = parentContainer.style ? parentContainer.style : {}; parentContainer.appendChild(container); + appInstance.use(LocaleService.i18n); appInstance.mount(container); return loadingInstances[newInstanceId]; } @@ -75,7 +86,8 @@ function createInstanceId() { export default class LoadingService { static show(config?: LoadingProps): Ref> { - const newConfig = { isActive: true, target: document.body, targetPosition: '', ...config }; + const mergedConfig = config ? { ...DefaultLoadingProps, ...config } : { ...DefaultLoadingProps }; + const newConfig = { ...mergedConfig, isActive: true, target: document.body, targetPosition: '' }; return initInstance(newConfig); } diff --git a/packages/ui-vue/components/loading/src/locales/ui/en.json b/packages/ui-vue/components/loading/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..f132e0a3aefab1e9faf6b277c11ab00896a22e82 --- /dev/null +++ b/packages/ui-vue/components/loading/src/locales/ui/en.json @@ -0,0 +1,5 @@ +{ + "loading": { + "message": "Loading ..." + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/loading/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/loading/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..cf2f2237679a3a6afd7b9de71b6fe4d15a849dd5 --- /dev/null +++ b/packages/ui-vue/components/loading/src/locales/ui/zh-CHS.json @@ -0,0 +1,5 @@ +{ + "loading": { + "message": "正在加载,请稍候..." + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/loading/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/loading/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..17439d1138e655c6329676181babf16fa4ce5976 --- /dev/null +++ b/packages/ui-vue/components/loading/src/locales/ui/zh-CHT.json @@ -0,0 +1,5 @@ +{ + "loading": { + "message": "正在加載,請稍候..." + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/locale/index.ts b/packages/ui-vue/components/locale/index.ts index 6dda2316761b23a110c5db41a501a1d9eb0ab9aa..951c09cc79b1a18a8f4abc09250901eb070bf865 100644 --- a/packages/ui-vue/components/locale/index.ts +++ b/packages/ui-vue/components/locale/index.ts @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { App } from 'vue'; +import type { App, Plugin } from 'vue'; +import { LocaleService, LOCALE_SERVICE_INJECTION_TOKEN, LocaleConfig } from './src/lib'; -import { LocaleService } from './src/lib/locale.service'; - -export { LocaleService }; +export { LocaleService, LOCALE_SERVICE_INJECTION_TOKEN }; export default { - install(app: App): void { - app.provide('LocaleService', LocaleService); + async install(app: App, config?: LocaleConfig): Promise { + await LocaleService.setup(app, config); + app.provide(LOCALE_SERVICE_INJECTION_TOKEN, LocaleService); } -}; +} as Plugin; diff --git a/packages/ui-vue/components/locale/src/lib/composition/index.ts b/packages/ui-vue/components/locale/src/lib/composition/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..f5f0caf2be5d853545e5a00eb56e2aef09f39ec3 --- /dev/null +++ b/packages/ui-vue/components/locale/src/lib/composition/index.ts @@ -0,0 +1 @@ +export * from './use-resource-loader'; diff --git a/packages/ui-vue/components/locale/src/lib/composition/use-resource-loader.ts b/packages/ui-vue/components/locale/src/lib/composition/use-resource-loader.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e521d517d4d7ba3c3c1f2a95e581be549f803ef --- /dev/null +++ b/packages/ui-vue/components/locale/src/lib/composition/use-resource-loader.ts @@ -0,0 +1,19 @@ +import axios, { AxiosRequestConfig } from 'axios'; +import { UseResourceLoader } from '../types'; + +export function useResourceLoader(): UseResourceLoader { + function loadResource(url: string, config: AxiosRequestConfig = {}) { + const defaultOptions = { + timeout: 5000, + headers: { + 'Cache-Control': 'no-cache' + } + }; + config = { ...defaultOptions, ...config }; + const requestPromise = axios.get(url, config).then((response) => response.data); + return requestPromise; + } + return { + loadResource + }; +} \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/index.ts b/packages/ui-vue/components/locale/src/lib/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b0123d2090b1fb0f1c742add2b9301249a0879a9 --- /dev/null +++ b/packages/ui-vue/components/locale/src/lib/index.ts @@ -0,0 +1,2 @@ +export * from './types'; +export * from './locale.service'; \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locale.service.ts b/packages/ui-vue/components/locale/src/lib/locale.service.ts index 01e4a57679033258ebd12df19da92006b469fabe..fb9f0ce45e44ed7a6e1cce379edecce03118800f 100644 --- a/packages/ui-vue/components/locale/src/lib/locale.service.ts +++ b/packages/ui-vue/components/locale/src/lib/locale.service.ts @@ -1,122 +1,92 @@ -import { FARRIS_LOCALES } from './locales'; - -// export const FARRIS_LOCAL_CUSTOM_DATA = new InjectionToken('自定义语言数据, 格式:{ "languageCode": { "name": { "key": "value" } } }'); - +/** + * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createI18n, I18n, useI18n } from 'vue-i18n'; +import { DEFAULT_LOCALE_CONFIG, LocaleConfig, LocaleResources } from './types'; +import { App } from 'vue'; +import { useResourceLoader } from './composition'; export class LocaleService { - static langData: any; - static localeId: string; // Add this property - - constructor(public localeId: string, private localeData: any) { - - if (!localeId) { - localeId = 'zh-CHS'; - } - - /** 合并语言资源 */ - LocaleService.setLocaleData(localeData, localeId); + public static i18n: I18n; + private static config: LocaleConfig; + public static async setup(app: App, config: Partial = {}) { + LocaleService.config = { ...DEFAULT_LOCALE_CONFIG, ...config }; + const locale = LocaleService.config.locale; + LocaleService.i18n = createI18n({ + locale, + fallbackLocale: LocaleService.config.fallbackLocale, + messages: {}, + legacy: false, + globalInjection: true, + silentTranslationWarn: true, + silentFallbackWarn: true + }); + app.use(LocaleService.i18n); + await this.loadResources(); } - - /** 获取语言资源 - * path : 资源路径,如: - * getResources('zh-CHS') // 返回所有中文语言资源 - * getResources('zh-CHS.lookup') // 返回所有中文下帮助的资源 - */ - static getResources(path = '') { - if (path) { - return LocaleService.getValue(path, FARRIS_LOCALES); - } - return FARRIS_LOCALES; + public static getLocale() { + return LocaleService.i18n && LocaleService.i18n.global.locale; } - - static getComponentOptions(ctrlName: any) { - return LocaleService.langData[ctrlName]; - } - - /** 合并现有的多语资源 */ - static setLocaleData(localeData = null, localeId: any) { - if (localeData) { - // 解析得到提供的语言编码 - const langCodes = Object.keys(localeData); - langCodes.forEach((code: string) => { - const resName = Object.keys(localeData[code]); - resName.forEach(k => { - LocaleService.appendLanguageResource(k, localeData[code][k], code); - }); - }); - } - - LocaleService.langData = (FARRIS_LOCALES as Record>)[localeId]; - if (!LocaleService.langData) { - LocaleService.langData = FARRIS_LOCALES['zh-CHS']; - } - } - - static appendLanguageResource(key: string, data: any, lang: string = LocaleService.localeId) { - if (!lang) { - LocaleService.langData[key] = LocaleService.langData[key] || {}; - LocaleService.langData[key] = Object.assign(LocaleService.langData[key], data || {}); - } else { - (FARRIS_LOCALES as Record>)[lang][key] = data || {}; - } - } - static getLocaleValue(propertyName: string) { - const val = LocaleService.getValue(propertyName, LocaleService.langData); - if (val) { - return val; - } else { - return ''; - } + public static getLocaleValue(key: string) { + return LocaleService.i18n && LocaleService.i18n.global.te(key) || key; } /** - * 获取对象中指定字段的值。 field: 可以为带有层级结构的路径,如: user.firstName | name 等 - * data: 获取字段的数据源,一般为JSON对象 - * safe: 为true, 将html字符进行转码输出,默认为 false - */ - static getValue(field: string, data: any, safe = false) { - if (!data) { - return ''; - } - let resultVal = ''; - if (field.indexOf('.') === -1 && data.hasOwnProperty(field)) { - resultVal = data[field]; - } else { - resultVal = field.split('.').reduce((obj, key) => { - if (obj) { - return obj[key]; - } else { - return null; - } - }, data); - } - - if (safe) { - return LocaleService.formatterValue(resultVal); - } else { - return resultVal; + * 属性空值或者有值但等于默认值,返回默认值多语言属性;有值但不相等返回当前属性自有值 + * @param propertyValue + * @param defaultValue + * @param localeKey + * @returns + */ + public static getRealPropertyValue(propertyValue, defaultValue, localeKey) { + const { t } = useI18n(); + if (!propertyValue || propertyValue === defaultValue) { + return t(localeKey); } + return propertyValue } - static formatterValue(val: any) { - if (val === null || val === undefined || val === '') { - return ''; + private static async loadResources(config: Partial = {}): Promise> { + const resources: Record = {}; + // 加载当前语言 + const locale = LocaleService.config.locale; + const fallbackLocale = LocaleService.config.fallbackLocale; + resources[locale] = await LocaleService.loadLocaleResources(locale); + // 加载fallback语言 + if (fallbackLocale && locale !== fallbackLocale) { + resources[fallbackLocale] = await LocaleService.loadLocaleResources(fallbackLocale); + LocaleService.i18n.global.setLocaleMessage(fallbackLocale, resources[fallbackLocale]); } - - if (typeof val === 'string') { - return LocaleService.escapeHtml(val); - } - - return val; + LocaleService.i18n.global.setLocaleMessage(locale, resources[locale]); + return resources; } - static escapeHtml(str: any) { - if (str === null || str === undefined) { - return ''; + private static async loadLocaleResources(locale: string): Promise { + try { + let resources: LocaleResources = {}; + const { loadResource } = useResourceLoader(); + // 优先加载远程资源 + if (LocaleService.config.uri) { + const url = `${LocaleService.config.uri}/${locale}.json?version=${new Date().valueOf()}`; + resources = await loadResource(url); + } + // 合并本地资源 + if (LocaleService.config.localResources?.[locale]) { + resources = { ...resources, ...LocaleService.config.localResources[locale] }; + } + return resources; + } catch (error) { + console.warn(`Failed to load locale ${locale}:`, error); + return LocaleService.config.localResources?.[locale] || {}; } - return str - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/\"/g, '"') - .replace(/\'/g, ''') - .replace(/\//g, '/'); } } diff --git a/packages/ui-vue/components/locale/src/lib/locales/design/zh-CHT.json b/packages/ui-vue/components/locale/src/lib/locales/design/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..c5335671a318127d9cd4fe1b030e50f242a09fc5 --- /dev/null +++ b/packages/ui-vue/components/locale/src/lib/locales/design/zh-CHT.json @@ -0,0 +1,1242 @@ +{ + "design-controls": { + "expression": { + "readonlyEditor": "只讀表達式編輯器", + "requireEditor": "必填表達式編輯器", + "visibleEditor": "可見表達式編輯器", + "disableEditor": "禁用表達式編輯器", + "computeEditor": "計算表達式編輯器", + "validateEditor": "校驗表達式編輯器", + "dependencyEditor": "依賴表達式編輯器", + "dataPickingEditor": "幫助前表達式編輯器" + }, + "propertyTrueText": "是", + "propertyFalseText": "否", + "properties": { + "tab": { + "gridFieldEditor": "編輯器", + "tableTdEditor": "編輯器", + "eventEditor": "交互", + "appearance": "樣式", + "commands": "交互", + "expressons": "表達式" + }, + "common": { + "id": { + "name": "標識", + "description": "組件的ID" + }, + "type": { + "name": "控件類型", + "description": "組件的類型" + }, + "bingding": { + "name": "綁定", + "description": "綁定字段的編號。必須以小寫英文字母開頭, 並且只能由英文字母、數字組成!" + }, + "label": { + "name": "標簽", + "description": "標簽名稱" + }, + "showLabelType": { + "name": "標籤顯示", + "description": "標籤顯示方式: 1、顯示:顯示標籤2、占位:保留標籤空間,但不顯示文字3、不顯示:不顯示標籤", + "editor": { + "data": [ + { + "id": "visible", + "name": "顯示" + }, + { + "id": "reserve-space", + "name": "占位" + }, + { + "id": "none", + "name": "不顯示" + } + ] + } + }, + "class": { + "name": "class樣式", + "description": "組件的CSS樣式" + }, + "style": { + "name": "style樣式", + "description": "組件的內聯樣式" + }, + "responseLayout": { + "name": "響應式列寬", + "description": "響應式列寬" + }, + "visible": { + "name": "是否可見", + "description": "運行時組件是否可見", + "editor": { + "data": [ + { + "id": true, + "name": "是" + }, + { + "id": false, + "name": "否" + } + ] + } + }, + "readonly": { + "name": "只讀", + "description": "是否只讀", + "editor": { + "data": [ + { + "id": true, + "name": "是" + }, + { + "id": false, + "name": "否" + } + ] + } + }, + "required": { + "name": "必填", + "description": "是否必填", + "editor": { + "data": [ + { + "id": true, + "name": "是" + }, + { + "id": false, + "name": "否" + } + ] + } + }, + "placeholder": { + "name": "提示文字", + "description": "當控制項沒有值時在輸入框中顯示的文字" + }, + "compute": { + "name": "計算表達式", + "description": "計算表達式" + }, + "dependency": { + "name": "依賴表達式", + "description": "依賴表達式" + }, + "validate": { + "name": "校驗表達式", + "description": "校驗表達式" + }, + "dataPicking": { + "name": "幫助前表達式", + "description": "幫助前表達式" + } + }, + "category": { + "basic": "基本信息", + "appearance": "外觀", + "behavior": "行為", + "inputAppend": "擴展區域", + "gridFieldEditor_editorType": "編輯器類型", + "appearanceClass": "外聯樣式", + "appearanceStyle": "內聯樣式", + "custom": "高級", + "expressionData": "表達式", + "usual": "常規" + }, + "commands": { + "click": { + "name": "點擊事件" + }, + "inputAppendClickEvent": { + "name": "擴展按鈕點擊事件" + }, + "linkedLabelClick": { + "name": "標簽超鏈事件" + }, + "fieldValueChanging": { + "name": "綁定字段值變化前事件" + }, + "fieldValueChanged": { + "name": "綁定字段值變化後事件" + } + } + } + }, + "design-combo-list": { + "placeholder":"" + "properties": { + "editor": { + "editable": { + "name": "允許編輯" + }, + "enableClear": { + "name": "啟用清空" + }, + "dataSourceType": { + "name": "數據源類型" + }, + "data": { + "name": "數據" + }, + "url": { + "name": "服務端API" + }, + "body": { + "name": "服務端API參數" + }, + "textField": { + "name": "數據源顯示字段" + }, + "valueField": { + "name": "數據源值字段" + }, + "multiSelect": { + "name": "啟用多選" + }, + "maxLength": { + "name": "最大輸入長度" + } + } + }, + "commands": {} + }, + "design-lookup": { + "properties": { + "lookup": { + "enableTitle": { + "name": "懸停提示", + "description": "鼠標懸停顯示文本內容" + }, + "editable": { + "name": "允許編輯" + }, + "allowFreeInput": { + "name": "任意輸入" + }, + "enableClear": { + "name": "啟用清除按鈕" + }, + "dataSource": { + "name": "數據源", + "description": "數據源" + }, + "filterConditions": { + "name": "過濾條件" + }, + "displayType": { + "name": "展示類型", + "description": "類型: 樹列表、列表、雙列表、左樹右列表" + }, + "idField": { + "name": "標識字段", + "description": "數據源標識字段" + }, + "textField": { + "name": "文本字段", + "description": "顯示文本字段" + }, + "mappingFields": { + "name": "字段映射", + "description": "字段映射" + }, + "enableToSelect": { + "name": "選中已選值", + "description": "數據加載後是否選中現有值" + }, + "multiSelect": { + "name": "啟用多選", + "description": "啟用多選" + }, + "showSelections": { + "name": "顯示已選記錄", + "description": "顯示已選記錄" + }, + "separator": { + "name": "多選分隔符", + "description": "多選分隔符" + }, + "enableSearchBar": { + "name": "啟用搜索框", + "description": "顯示搜索工具條" + }, + "searchAnyField": { + "name": "允許查詢所有列", + "description": "顯示所有列" + }, + "enableFavorite": { + "name": "啟用收藏夾", + "description": "啟用收藏夾" + }, + "enableUserData": { + "name": "保存界面狀態", + "description": "保存界面狀態" + } + }, + "treeConfig": { + "treeToList": { + "name": "以列表形式展示", + "description": "以列表的形式展示樹結構數據" + }, + "navTreeToList": { + "name": "以列表形式展示", + "description": "以列表的形式展示樹結構數據" + }, + "loadTreeDataType": { + "name": "數據加載方式", + "description": "樹形數據加載方式" + }, + "enableFullTree": { + "name": "構造完整樹", + "description": "啟用構造完整樹" + }, + "onlySelectLeaf": { + "name": "僅選擇葉子節點", + "description": "僅選擇葉子節點" + }, + "enableCascade": { + "name": "啟用級聯選擇", + "description": "啟用級聯選擇" + }, + "showCascadeControl": { + "name": "顯示級聯選擇控件", + "description": "顯示級聯選擇控件" + }, + "cascadeValue": { + "name": "級聯狀態", + "description": "級聯選擇默認狀態" + }, + "expandLevel": { + "name": "默認展開層級", + "description": "默認展開層級: 0: 不展開; -1: 全部展開;>0: 展開到指定級數 " + } + }, + "dialog": { + "openType": { + "name": "打開方式", + "description": "窗口展示方式:彈出窗口、下拉面板" + }, + "title": { + "name": "標題", + "description": "幫助標題" + }, + "width": { + "name": "寬度", + "description": "窗口寬度,最小值:300px" + }, + "height": { + "name": "高度", + "description": "窗口高度,最小值:200px" + }, + "showNavigation": { + "name": "顯示導航欄", + "description": "顯示導航欄" + }, + "navigatorWidth": { + "name": "導航欄寬度", + "description": "導航欄寬度,最小200px, 最大為窗口寬度減去200px" + }, + "resizeable": { + "name": "允許調整窗口尺寸", + "description": "允許鼠標拖拽窗口邊緣調整尺寸" + }, + "rememberSize": { + "name": "記錄窗口尺寸", + "description": "記錄窗口尺寸" + }, + "enableEsc": { + "name": "允許ESC關閉", + "description": "允許ESC關閉" + }, + "showMaxButton": { + "name": "顯示最大化按鈕", + "description": "顯示最大化按鈕" + }, + "showCloseButton": { + "name": "顯示關閉按鈕", + "description": "顯示關閉按鈕" + } + }, + "pager": { + "showLimits": { + "name": "顯示條數選擇器", + "description": "顯示每頁條數選擇器" + }, + "sizeLimits": { + "name": "每頁顯示條數", + "description": "每頁可顯示條數" + }, + "size": { + "name": "默認顯示條數", + "description": "默認顯示條數" + } + } + }, + "commands": { + "clear": { + "name": "清除事件1" + }, + "lookupPicking": { + "name": "幫助前事件1" + }, + "lookupPicked": { + "name": "幫助後事件1" + }, + "beforeSelectData": { + "name": "數據選擇確認前事件1" + }, + "beforeShow": { + "name": "面板顯示前事件1" + }, + "beforeHide": { + "name": "面板隱藏前事件1" + }, + "onShown": { + "name": "面板打開事件1" + }, + "onHidden": { + "name": "面板關閉事件1" + } + } + }, + "design-input-group": { + "properties": { + "editor": { + "maxLength": { + "name": "最大長度", + "description": "文本字數最大長度" + }, + "enableViewPassword": { + "name": "啟用密碼" + } + }, + "formatValidation": { + "type": { + "name": "輸入類型", + "description": "輸入類型" + } + } + }, + "commands": {} + }, + "design-textarea": { + "properties": { + "editor": { + "resizable": { + "name": "拖拽調整大小" + }, + "autoHeight": { + "name": "自動高度" + } + } + }, + "commands": {} + }, + "design-button": { + "properties": { + "basic": { + "text": { + "name": "文本" + } + }, + "appearance": { + "icon": { + "name": "圖標" + }, + "split": { + "name": "下拉按鈕分離", + "description": "啟用下拉按鈕分離,可以單獨點擊按鈕。單獨點擊下拉按鈕展開下拉面板" + }, + "tipsEnable": { + "name": "啟用提示", + "description": "當啟用提示時,鼠標滑過按鈕會有消息提示框" + }, + "tipsText": { + "name": "提示消息內容", + "description": "消息提示框內的展示內容" + } + }, + "behavior": { + "visible": { + "name": "是否可見", + "description": "運行時組件是否可見" + }, + "disabled": { + "name": "禁用", + "description": "按鈕禁用狀態" + } + } + }, + "commands": {} + }, + "design-response-toolbar": { + "properties": { + "layout": { + "layout": { + "name": "", + "description": "配置布局容器內區塊個數及比例。" + } + } + }, + "commands": {} + }, + "design-response-toolbar-item": { + "properties": {}, + "commands": {} + }, + "design-content-container": { + "properties": {}, + "commands": {} + }, + "design-number-spinner": { + "properties": { + "editor": { + "precision": { + "name": "精度" + }, + "step": { + "name": "步長" + }, + "max": { + "name": "最大值" + }, + "min": { + "name": "最小值" + }, + "textAlign": { + "name": "對齊方式" + } + } + }, + "commands": {} + }, + "design-date-picker": { + "properties": { + "editor": { + "showTime": { + "name": "啟用時間選擇" + }, + "displayFormat": { + "name": "展示格式" + }, + "valueFormat": { + "name": "存儲格式" + }, + "minDate": { + "name": "最小日期" + }, + "maxDate": { + "name": "最大日期" + } + } + }, + "commands": {} + }, + "design-switch": { + "properties": { + "editor": { + "onLabel": { + "name": "打開標簽" + }, + "offLabel": { + "name": "關閉標簽" + }, + "onBackground": { + "name": "打開背景色", + "description": "值可以是顏色或者16進制顏色字符串,比如:blue或者#2A87FF" + }, + "offBackground": { + "name": "關閉背景色", + "description": "值可以是顏色或者16進制顏色字符串,比如:gray或者#D9DEE7" + }, + "size": { + "name": "尺寸" + }, + "trueValue": { + "name": "打開的值", + "description": "打開時的值" + }, + "falseValue": { + "name": "關閉的值", + "description": "關閉時的值" + } + } + }, + "commands": {} + }, + "design-radio-group": { + "properties": { + "editor": { + "direction": { + "name": "排列方向" + }, + "dataSourceType": { + "name": "數據源類型" + }, + "data": { + "name": "數據" + }, + "bindDataSource": { + "name": "綁定數據源" + }, + "textField": { + "name": "文本字段" + }, + "valueField": { + "name": "值字段" + } + } + }, + "commands": {} + }, + "design-check-box": { + "properties": { + "editor": { + "label": { + "name": "名稱" + }, + "trueValue": { + "name": "選中的值", + "description": "選中的值" + }, + "falseValue": { + "name": "未選中的值", + "description": "未選中的值" + } + } + }, + "commands": {} + }, + "design-check-group": { + "properties": { + "editor": { + "direction": { + "name": "排列方向" + }, + "dataSourceType": { + "name": "數據源類型" + }, + "data": { + "name": "數據" + }, + "bindDataSource": { + "name": "綁定數據源" + }, + "textField": { + "name": "文本字段" + }, + "valueField": { + "name": "值字段" + } + } + }, + "commands": {} + }, + { + "design-response-form": { + "properties": { + "appearance": { + "labelAutoOverflow": { + "name": "控件標籤換行", + "description": "控件標籤字數超長時,換行顯示。控件標籤與輸入框在一行展示時,此屬性有效。" + }, + "unifiedLayout": { + "name": "統一佈局配置", + "description": "統一配置卡片區域內所有控件的寬度,只支持標準模式" + } + } + }, + "commands": {} + }, + "design-response-layout": { + "properties": {}, + "commands": {} + }, + "design-response-layout-item": { + "properties": {}, + "commands": {} + }, + "design-tree-grid": { + "properties": { + "appearance": { + "columns": { + "name": "列設置", + "description": "列設置" + }, + "fit": { + "name": "適配父組件尺寸", + "description": "是否適配父組件尺寸" + }, + "showStripe": { + "name": "顯示條紋", + "description": "是否顯示條紋" + }, + "showBorder": { + "name": "顯示邊框", + "description": "是否顯示邊框" + } + }, + "pagination": { + "enable": { + "name": "啟用分頁" + }, + "showIndex": { + "name": "顯示頁碼" + }, + "showLimits": { + "name": "顯示每頁記錄數" + } + } + }, + "commands": {} + }, + "design-tree-grid-column": { + "properties": { + "appearance": { + "resizable": { + "name": "拖拽改變列寬", + "description": "允許拖拽改變列寬" + }, + "width": { + "name": "列寬", + "description": "列寬" + }, + "showTips": { + "name": "鼠標懸浮提示", + "description": "啟用鼠標懸浮提示" + }, + "halign": { + "name": "標題對齊方式", + "description": "標題對齊方式" + }, + "align": { + "name": "數據水平對齊方式", + "description": "數據水平對齊方式選擇" + }, + "valign": { + "name": "數據垂直對齊方式", + "description": "數據垂直對齊方式選擇" + }, + "columnTemplate": { + "name": "列模板", + "description": "列模板" + }, + "enableSummary": { + "name": "啟用合計", + "description": "啟用合計" + } + }, + "behavior": { + "formatterEnumData": { + "name": "數據" + } + } + }, + "commands": {} + }, + "design-data-grid": { + "properties": { + "appearance": { + "columns": { + "name": "列設置", + "description": "列設置" + }, + "showStripe": { + "name": "顯示條紋", + "description": "是否顯示條紋" + }, + "showSetting": { + "name": "顯示設置按鈕", + "description": "是否顯示設置按鈕" + } + }, + "selection": { + "multiSelect": { + "name": "啟用多選" + }, + "showCheckbox": { + "name": "顯示複選框" + }, + "showSelectAll": { + "name": "顯示全選" + } + }, + "rowNumber": { + "enable": { + "name": "顯示行號" + }, + "width": { + "name": "寬度" + }, + "heading": { + "name": "標題" + } + }, + "pagination": { + "enable": { + "name": "啟用分頁" + }, + "showIndex": { + "name": "顯示頁碼" + }, + "showLimits": { + "name": "顯示分頁條數" + } + }, + "command": { + "enable": { + "name": "啟用", + "description": "啟用操作列" + } + }, + "column": { + "fitColumns": { + "name": "啟用", + "description": "啟用填充列寬" + } + }, + "summary": { + "enable": { + "name": "啟用", + "description": "啟用合計行" + } + } + }, + "commands": {} + }, + "design-data-grid-column": { + "properties": { + "appearance": { + "resizable": { + "name": "拖拽改變列寬", + "description": "允許拖拽改變列寬" + }, + "width": { + "name": "列寬", + "description": "列寬" + }, + "showTips": { + "name": "鼠標懸浮提示", + "description": "啟用鼠標懸浮提示" + }, + "halign": { + "name": "標題對齊方式", + "description": "標題對齊方式" + }, + "align": { + "name": "數據水平對齊方式", + "description": "數據水平對齊方式選擇" + }, + "valign": { + "name": "數據垂直對齊方式", + "description": "數據垂直對齊方式選擇" + }, + "columnTemplate": { + "name": "列模板", + "description": "列模板" + }, + "enableSummary": { + "name": "啟用合計", + "description": "啟用合計" + } + }, + "behavior": { + "formatterEnumData": { + "name": "數據" + } + }, + "gridFieldEditor_editorType": { + "type": { + "name": "編輯器類型" + } + }, + "gridFieldEditor_editor": { + "maxLength": { + "name": "最大長度", + "description": "文本字數最大長度" + }, + "enableViewPassword": { + "name": "啟用密碼" + } + }, + "formatter": { + "type": { + "name": "類型" + }, + "trueText": { + "name": "布爾為true時文本", + "description": "布爾為true時文本" + }, + "falseText": { + "name": "布爾為false時文本", + "description": "布爾為false時文本" + }, + "prefix": { + "name": "前綴", + "description": "前綴" + }, + "suffix": { + "name": "後綴", + "description": "後綴" + }, + "precision": { + "name": "精度", + "description": "精度" + }, + "decimal": { + "name": "小數分隔符", + "description": "小數分隔符" + }, + "thousand": { + "name": "千分位", + "description": "千分位" + } + } + }, + "commands": {} + }, + "design-module": { + "properties": { + "basic": { + "id": { + "name": "表單元數據標識" + }, + "code": { + "name": "表單元數據編號" + }, + "name": { + "name": "表單元數據名稱" + } + } + }, + "commands": {} + }, + "design-component": { + "properties": { + "appearance": { + "name": { + "name": "組件名稱", + "description": "組件名稱" + } + }, + "behavior": { + "enableValidation": { + "name": "啟用校驗", + "description": "運行時組件是否啟用校驗" + } + } + }, + "commands": {} + }, + "design-tabs": { + "properties": { + "appearance": { + "titleWidth": { + "name": "標題區域寬度(%)", + "description": "標題區域寬度佔標籤頭部區域的橫向比例" + }, + "autoTitleWidth": { + "name": "標題自適應寬度", + "description": "是,則標題顯示全部字符;否,則標題最多顯示20個字符" + }, + "fill": { + "name": "填充", + "description": "flex佈局下,是否填充滿剩餘部分" + } + }, + "behavior": { + "activeId": { + "name": "默認選中標籤頁", + "description": "默認選中標籤頁" + } + } + }, + "commands": {} + }, + "design-tab-page": { + "properties": { + "basic": { + "title": { + "name": "標題", + "description": "標籤頁項的標題" + } + }, + "appearance": { + "customTitleClass": { + "name": "標題樣式", + "description": "為標籤頁的標題指定樣式" + }, + "tabWidth": { + "name": "標籤頁的標題寬度(px)", + "description": "為標籤頁的標題指定寬度" + } + }, + "behavior": { + "show": { + "name": "是否可見", + "description": "是否可見" + }, + "disabled": { + "name": "是否禁用", + "description": "是否禁用" + }, + "removeable": { + "name": "是否可移除", + "description": "是否可移除" + } + }, + "toolbar": { + "toolbarPosition": { + "name": "工具欄位置", + "description": "為工具欄指定位置" + } + } + }, + "commands": {} + }, + "design-tab-toolbar-item": { + "properties": { + "basic": { + "text": { + "name": "文本" + } + }, + "appearance": { + "split": { + "name": "下拉按鈕分離", + "description": "啟用下拉按鈕分離,可以單獨點擊按鈕。單獨點擊下拉按鈕展開下拉面板" + } + } + }, + "commands": {} + }, + "design-html-template": { + "properties": {}, + "commands": {} + }, + "design-time-picker": { + "properties": { + "editor": { + "editable": { + "name": "允許編輯" + }, + "format": { + "name": "格式" + }, + "showHeader": { + "name": "是否顯示時分秒標題" + }, + "use12Hours": { + "name": "是否使用12小時制" + }, + "hourStep": { + "name": "時步長" + }, + "minuteStep": { + "name": "分步長" + }, + "secondStep": { + "name": "秒步長" + } + } + }, + "commands": {} + }, + "design-section": { + "properties": { + "appearance": { + "fill": { + "name": "填充內容區域" + }, + "showHeader": { + "name": "顯示標題區域" + }, + "mainTitle": { + "name": "主標題" + }, + "subTitle": { + "name": "副標題" + }, + "enableAccordion": { + "name": "面板收折", + "description": "是否啟用分組面板的收折特性" + }, + "expandStatus": { + "name": "默認狀態", + "description": "默認面板是展開還是收起" + } + } + }, + "commands": {} + }, + "design-section-toolbar": { + "properties": {}, + "commands": {} + }, + "design-section-toolbar-item": { + "properties": { + "appearance": { + "split": { + "name": "下拉按鈕分離", + "description": "啟用下拉按鈕分離,可以單獨點擊按鈕。單獨點擊下拉按鈕展開下拉面板" + }, + "tipsEnable": { + "name": "啟用提示", + "description": "當啟用提示時,鼠標滑過按鈕會有消息提示框" + }, + "tipsText": { + "name": "提示消息內容", + "description": "消息提示框內的展示內容" + } + } + }, + "commands": {} + }, + "design-splitter": { + "properties": {}, + "commands": {} + }, + "design-splitter-pane": { + "properties": { + "appearance": { + "minWidth": { + "name": "最小寬度(px)", + "description": "面板最小寬度" + }, + "minHeight": { + "name": "最小高度(px)", + "description": "面板最小高度" + } + } + }, + "commands": {} + }, + "design-component-ref": { + "properties": {}, + "commands": {} + }, + "design-uploader": { + "properties": {}, + "commands": {} + }, + "design-page-header": { + "properties": { + "template": { + "titleContentClass": { + "name": "標題模板樣式", + "description": "標題模板外層容器的自定義樣式" + }, + "titleContentHtml": { + "name": "標題模板", + "description": "設置標題HTML模板,替代圖標和標題文字區域" + }, + "contentClass": { + "name": "內容模板樣式", + "description": "內容模板外層容器的自定義樣式" + }, + "contentHtml": { + "name": "內容模板", + "description": "設置頁頭中間區域的模版" + }, + "downContentClass": { + "name": "擴展模板樣式", + "description": "擴展模板外層容器的自定義樣式" + }, + "downContentHtml": { + "name": "擴展模板", + "description": "設置頁頭下方區域的模版" + } + } + }, + "commands": {} + }, + "design-page-footer": { + "properties": {}, + "commands": {} + }, + "design-tab-toolbar": { + "properties": {}, + "commands": {} + }, + "design-fieldset": { + "properties": {}, + "commands": {} + }, + "design-query-solution": { + "properties": { + "apperance": { + "isControlInline": { + "name": "控件標籤同行展示", + "description": "控件標籤同行展示" + }, + "presetQuerySolutionName": { + "name": "系統預置篩選方案名稱", + "description": "系統預置篩選方案名稱" + }, + "filterText": { + "name": "篩選按鈕文本", + "description": "篩選按鈕文本" + }, + "itemClass": { + "name": "篩選項樣式", + "description": "篩選項的公共樣式。如果篩選項定義了單獨的樣式,該項按照單獨的樣式展示" + }, + "expanded": { + "name": "展開", + "description": "展開" + } + }, + "control": { + "fields": { + "name": "篩選方案字段", + "description": "篩選方案字段設置" + }, + "presetFields": { + "name": "系統預置字段", + "description": "系統預置字段" + }, + "initQuery": { + "name": "初始化時查詢數據", + "description": "初始化時方案時查詢數據" + }, + "changeQuery": { + "name": "值變化時查詢數據", + "description": "值變化時查詢數據" + }, + "defaultValues": { + "name": "默認值綁定字段", + "description": "默認值綁定字段" + } + } + }, + "commands": {} + }, + "design-drawer": { + "properties": {}, + "commands": {} + }, + "design-external-container": { + "properties": {}, + "commands": {} + }, + "design-list-nav": { + "properties": {}, + "commands": {} + }, + "design-filter-bar": { + "properties": {}, + "commands": {} + }, + "design-language-textbox": { + "properties": {}, + "commands": {} + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/avatar.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/avatar.ts deleted file mode 100644 index a41192ec8ec9cc7b270dc7891e4505835c0f59bd..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/avatar.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const AVATAR_LOCALE = { - imgtitle: 'Amend', - typeError: 'Type error', - sizeError: 'Can not be larger than', - uploadError: 'Upload Fail!', - loadError: 'Load error.' -}; \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/batch-edit-dialog.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/batch-edit-dialog.ts deleted file mode 100644 index 8de247cd5e7ed9f38404c9981d90f7aa8f08fb87..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/batch-edit-dialog.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const BATCH_EDIT_DIALOG_LOCALE = { - title: 'Batch Edit', - appendText: 'Append new field', - appendTextTip: 'Append more field to edit', - okText: 'OK', - cancelText: 'Cancel', - field: 'Please select field', - fieldValue: 'Please input value', - appendTips: 'append more columns to edit.', - selected: 'select', - row: 'row', - confirmTitle: 'Info', - neverShow: 'Do not remind again', - confirmText: 'We will update {0} rows,Are you sure?' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/collapse.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/collapse.ts deleted file mode 100644 index 0f327701d0509313c7420e7da7298bb4c32a15c7..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/collapse.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const COLLAPSE_DIRECTIVE_LOCALE = { - expand: 'expand', - fold: 'fold' -} \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/combo.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/combo.ts deleted file mode 100644 index c2455f7fa2bd684b9402030504ac4182b33d419e..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/combo.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const COMBO_LOCALE = { - placeholder: 'Please Select', - emptyMsg: 'Empty Data', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/datagrid.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/datagrid.ts deleted file mode 100644 index 7311a46a408cae0b3c7dffa2c8f589e46868d498..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/datagrid.ts +++ /dev/null @@ -1,104 +0,0 @@ -export const DATAGRID_LOCALE = { - lineNumberTitle: 'NO.', - emptyMessage: 'Empty Data', - pagination: { - previousLabel: 'Prev Page', - nextLabel: 'Next Page', - message: 'Total {1} items ', - pagelist: { - firstText: 'Display', - lastText: 'items' - } - }, - filter: { - title: 'Conditions', - reset: 'Reset', - clear: 'Clear', - clearAll: 'Clear all conditions', - setting: 'Settings', - nofilter: '[ Empty ]', - checkAll: 'Check All', - and: 'And', - or: 'Or', - operators: { - equal: 'equal', - notEqual: 'not equal', - greater: 'greater than', - greaterOrEqual: 'greater than or equal', - less: 'less than', - lessOrEqual: 'less than or equal', - contains: 'contains', - notContains: 'not contains', - like: 'contains', - notLike: 'not contains', - in: 'in', - notIn: 'not in', - empty: 'empty', - notEmpty: 'not empty', - null: 'null', - notNull: 'not null' - }, - more: 'More' - }, - settings: { - visible: 'Visible', - sortting: 'Sortting', - title: 'Column Settings', - canchoose: 'Can choose', - choosed: 'Choosed', - asc: 'ASC', - desc: 'DESC', - cancelSort: 'Cancel sortting', - ok: 'OK', - cancel: 'Cancel', - reset: 'Reset', - conciseMode: 'Concise', - advancedMode: 'Advanced', - formatSetting: 'Column format', - properties: 'Column properties', - groupping: 'Groupping', - allColumns: 'All', - visibleColumns: 'Visible', - hiddenColumns: 'Hidden', - searchPlaceholder: 'Please enter a column name', - checkall: 'Show or hide all', - headeralign: 'Header alignment', - dataalign: 'Data alignment', - alignLeft: 'Left', - alignCenter: 'Center', - alignRight: 'Right', - summarytype: 'Summary type', - summarytext: 'Summary text', - summaryNone: 'None', - summarySum: 'Sum', - summaryMax: 'Max', - summaryMin: 'Min', - summarCount: 'Count', - summaryAverage: 'Average', - grouppingField: 'Groupping field', - moreGrouppingFieldWarningMessage: 'Up to 3 fields are set for grouping', - grouppingSummary: 'Group total', - addGrouppingFieldTip: 'Add groupping field', - removeGrouppingFieldTip: 'Remove groupping field', - grouppingSummaryType: 'Group total type', - grouppingSummaryText: 'Group total text', - restoreDefaultSettingsText: 'Are you sure you want to restore the default settings', - simple: { - title: 'Show Columns', - tip: 'The selected fields can be displayed in the list. Drag to adjust the display order in the list.', - count: 'show {0} columns' - } - }, - selectionData: { - clearAll: 'Clear all', - tooltip: 'Click here show list.', - currentLenth: `{0} items selected. ` - }, - groupRow: { - tips: 'Drag columns here to group data.', - removeColumn: 'Remove the group column.', - clearTip: 'Clear all grouped fields.', - clear: 'Empty' - } - -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/discussion-group.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/discussion-group.ts deleted file mode 100644 index be2a5cc7f446e710b066bf4e01070cd4b0c42eef..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/discussion-group.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const DISCUSSION_GROUP_LOCALE = { - submit: 'Submit', - cancel: 'Cancel', - colleague: 'Colleague', - all: 'For all to see', - related: 'Relevant personnel can see', - confirm: 'Confirm', - reply: 'Reply', - emptyMessage:'Empty Data', - placeholder: 'Please input search keywords', - notEmpty:'The submitted content cannot be empty', - selectEmployee:'Select the employee', - next: 'Next', - emptySelected:'Clear', - emptyRight:'Select on the left', - allOrg:'All', - selected:'Selected', - section:'Section ', - people:'Per ', - viewMore:'View more', - per:' ', - pcs:' ', - emptyList:'Empty contacts', - advancedQuery:'Select' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/filter-editor.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/filter-editor.ts deleted file mode 100644 index 66486232dbbe4126228a74cb13339b3cbab74914..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/filter-editor.ts +++ /dev/null @@ -1,78 +0,0 @@ -export const FILTER_EDITOR_LOCALE = { - // 取消 - cancelButton: 'Cancel', - // 确定 - okButton: 'OK', - // 添加子句 - addWhere: 'Add', - clear: 'Clear', - // 置顶 - moveTop: 'Top', - // 上移 - moveUp: 'Up', - // 下移 - moveDown: 'Down', - // 置底 - moveBottom: 'Bottom', - // 左括号 - leftBrackets: 'Left Brackets', - // 字段 - field: 'Field Name', - // 操作符 - operator: 'Operator', - // 值 - value: 'Value', - - valueType: 'Value type', - expressType: { - value: 'Value', - express: 'Express', - frontExpress: 'Front Express' - }, - // 右括号 - rightBrackets: 'Right Brackets', - // 关系 - relation: 'Relation', - relationValue: { - and: 'And', - or: 'Or' - }, - - // 设计器 - designTab: 'Design', - // 源代码 - jsonTab: 'JSON', - // Sql预览 - sqlTab: 'Sql', - title: 'Filter Designer', - message: 'Are you sure you want to clear all current data?', - validate: { - bracket: 'The brackets do not match, please check', - relation: 'The condition relationship is incomplete, please check', - field: 'Condition field is not set, please check' - } -}; - - -export const ENUM_EDITOR_LOCALE = { - // 取消 - cancelButton: 'Cancel', - // 确定 - okButton: 'OK', - // 添加子句 - addWhere: 'Add', - clear: 'Clear', - // 置顶 - moveTop: 'Top', - // 上移 - moveUp: 'Up', - // 下移 - moveDown: 'Down', - // 置底 - moveBottom: 'Bottom', - title: 'Enum Data Designer', - message: 'Are you sure you want to clear all current data?', - - value: 'Value', - name: 'Name' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/filter-panel.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/filter-panel.ts deleted file mode 100644 index 353318a9e330e63fa3d2b268f4f93babcf7f2407..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/filter-panel.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const FILTER_PANEL_LOCALE = { - filter: 'Filter', - confirm: 'OK', - cancel: 'Cancel', - reset: 'Reset', - advancedFilter: 'advancedFilter', - expand: 'Expand', - fold: 'Fold', - last1Month: 'LastOneMonth', - last3Month: 'LastThreeMonth', - last6Month: 'LastSixMonth', - pleaseInput: 'Please input ', - searchHistory: 'Search History', - searchResult: 'Search Result', - intervalFilter: 'Filter by interval', - beginPlaceHolder: 'Minimum value', - endPlaceHolder: 'Maximum value', - dateBeginPlaceHolder: 'Start date', - dateEndPlaceHolder: 'End date', - empty: 'Empty', - clear: 'Clear', - today:'Today', - yesterday: 'Yesterday', - checkall: 'Check all' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/footer.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/footer.ts deleted file mode 100644 index 4ca6ecb7a6e09cee249e3c43f96adc9b8d287d5f..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/footer.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const FOOTER_LOCALE = { - expandText: 'More Information', - collapseText: 'More Information' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/index.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/index.ts deleted file mode 100644 index 5883d00435a5f70a091e3e9fe69f11f7c5c6b81b..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { LISTVIEW_LOCALE } from './list-view'; -import { COMBO_LOCALE } from './combo'; -import { QUERY_SOLUTION_LOCALE } from './query-solution'; -import { QUERY_CONDITION_LOCALE } from './query-condition'; -import { RESPONSE_TOOLBAR_LOCALE } from './response-toolbar'; -import { PAGINATION_LOCALE } from './pagination'; -import { MESSAGER_LOCALE } from './messager'; -import { DATAGRID_LOCALE } from './datagrid'; -import { SECTION_LOCALE } from './section'; -import { LOADING_LOCALE } from './loading'; -import { FILTER_EDITOR_LOCALE, ENUM_EDITOR_LOCALE } from './filter-editor'; -import { NOTIFY_LOCALE } from './notify'; -import { SORT_EDITOR_LOCALE } from './sort-editor'; -import { TEXT_LOCALE } from './text'; -import { SIDEBAR_LOCALE } from './sidebar'; -import { TABS_LOCALE } from './tabs'; -import { MULTI_SELECT_LOCALE } from './multi-select'; -import { COLLAPSE_DIRECTIVE_LOCALE } from './collapse'; -import { LOOKUP_LOCALE } from './lookup'; -import { TREETABLE_LOCALE } from './treetable'; -import { AVATAR_LOCALE } from './avatar'; -import { LIST_FILTER_LOCALE } from './list-filter'; -import { PROGRESS_STEP_LOCALE } from './progress-step'; -import { LANGUAGE_LABEL_LOCALE } from './language-label'; -import { VERIFY_DETAIL_LOCALE } from './verify-detail'; -import { BATCH_EDIT_DIALOG_LOCALE } from './batch-edit-dialog'; -import { PAGE_WALKER_LOCALE } from './page-walker'; -import { FOOTER_LOCALE } from './footer'; -import { DISCUSSION_GROUP_LOCALE } from './discussion-group'; -import { TAG_LOCALE } from './tag'; -import { NUMERIC_LOCALE } from './numeric'; -import { FILTER_PANEL_LOCALE } from './filter-panel'; -import { SCROLLSPY_LOCALE } from './scrollspy'; -import { LOOKUP_CONFIG_LOCALE } from './lookup-config'; - -export const EN_US = { - locale: 'EN_US', - combo: COMBO_LOCALE, - combolist: {}, - datagrid: DATAGRID_LOCALE, - filterEditor: FILTER_EDITOR_LOCALE, - enumEditor: ENUM_EDITOR_LOCALE, - lookup: LOOKUP_LOCALE, - loading: LOADING_LOCALE, - modal: {}, - messager: MESSAGER_LOCALE, - notify: NOTIFY_LOCALE, - dialog: {}, - datatable: {}, - colorPicker: {}, - numberSpinner: NUMERIC_LOCALE, - inputGroup: {}, - treetable: TREETABLE_LOCALE, - multiSelect: MULTI_SELECT_LOCALE, - sortEditor: SORT_EDITOR_LOCALE, - tabs: TABS_LOCALE, - timePicker: {}, - wizard: {}, - tree: {}, - tooltip: {}, - listview: LISTVIEW_LOCALE, - text: TEXT_LOCALE, - switch: {}, - sidebar: SIDEBAR_LOCALE, - section: SECTION_LOCALE, - pagination: PAGINATION_LOCALE, - responseToolbar: RESPONSE_TOOLBAR_LOCALE, - queryCondition: QUERY_CONDITION_LOCALE, - querySolution: QUERY_SOLUTION_LOCALE, - collapseDirective: COLLAPSE_DIRECTIVE_LOCALE, - avatar: AVATAR_LOCALE, - listFilter: LIST_FILTER_LOCALE, - progressStep: PROGRESS_STEP_LOCALE, - languageLabel: LANGUAGE_LABEL_LOCALE, - verifyDetail: VERIFY_DETAIL_LOCALE, - batchEditDialog: BATCH_EDIT_DIALOG_LOCALE, - pageWalker: PAGE_WALKER_LOCALE, - footer: FOOTER_LOCALE, - discussionGroup: DISCUSSION_GROUP_LOCALE, - tag: TAG_LOCALE, - filterPanel: FILTER_PANEL_LOCALE, - scrollspy: SCROLLSPY_LOCALE, - lookupConfig: LOOKUP_CONFIG_LOCALE -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/language-label.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/language-label.ts deleted file mode 100644 index 7e26ec9c5b54cd8767f547919b783b354b9dbc60..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/language-label.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const LANGUAGE_LABEL_LOCALE = { - en: 'English', - "zh-cn": 'Simplified Chinese', - "zh-CHS": 'Simplified Chinese', - "zh-CHT": 'Traditional Chiness', - ok: 'OK', - cancel: 'Cancel' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/list-filter.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/list-filter.ts deleted file mode 100644 index 48401dd9cead49c29ed75b4ff218f997cce3e32a..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/list-filter.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const LIST_FILTER_LOCALE = { - filter: 'Filter', - confirm: 'OK', - cancel: 'Cancel', - reset: 'Reset' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/list-view.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/list-view.ts deleted file mode 100644 index e851261852dcfda7634332dbb758fb5473aaf46f..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/list-view.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const LISTVIEW_LOCALE = { - emptyMessage: 'Empty Data' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/loading.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/loading.ts deleted file mode 100644 index b358a3e0c85c4479bb821a124d32ba2a6a6267dc..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/loading.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const LOADING_LOCALE = { - message: 'Loading ...' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/lookup-config.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/lookup-config.ts deleted file mode 100644 index 2fff428b4442cc0dcc54cd58e1d5a7348b646614..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/lookup-config.ts +++ /dev/null @@ -1,38 +0,0 @@ -export const LOOKUP_CONFIG_LOCALE = { - placeholder: 'Select the form metadata', - code: 'Code', - name: 'Name', - select: 'Select the help metadata', - filter: 'Configuration conditions', - helpidEmpty: 'HelpID cannot be empty', - selectTitle: 'Helps with metadata selection', - lookupTitle: 'Help configuration', - sure: 'Confirm', - cancel: 'Cancel', - successSave: 'Save successfully', - helpIdError:'Please select the help metadata', - fileNamePlaceholder:'Select the help text field', - selectFileNameTitle:'Text field selector', - bindingPath:'Binding field', - fieldError:'The bound field does not exist!', - textFieldLable:'Select the textField', - loadTypeTitle: 'Select the loadType', - loadTypeList: { - all:'All load', - layer:'Layer load', - default: 'Default load' - }, - powerTitle:'Permission setting', - powerObjLabel:'Permission objects', - powerFieldLabel:'Permission field', - powerOperateLabel:'Permission operate', - linkfieldLabel:'Help associated field', - powerDataTitle:'Permission object selection', - powerFieldTitle:'Permission field selection', - powerOperateTitle:'Operation selection', - businessLable:'Business object ', - powerLable:'Permission objects', - powerError:'Select the permission objects', - operateError:'Select the Operation', - linkfieldError:'Select permission field' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/lookup.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/lookup.ts deleted file mode 100644 index edc2d0b422bbef47c5abb28cb5e830934b449766..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/lookup.ts +++ /dev/null @@ -1,48 +0,0 @@ -export const LOOKUP_LOCALE = { - placeholder: 'Please choose', - favorites: 'Favorites', - selected: 'Selected items', - okText: 'OK', - cancelText: 'Cancel', - allColumns: 'All Columns', - datalist: 'Data items', - mustWriteSomething: 'Please write a key words.', - mustChoosAdatarow: 'Please choose a data row!', - tipText: 'Are these what you are looking for?', - cascade: { - enable: 'Enable cascade selection', - disable: 'Disable cascade selection', - up: 'Cascade up selection only', - down: 'Cascade selection only' - }, - favoriteInfo: { - addFav: 'Collection success.', - cancelFav: 'Unfavorite successfully. ' - }, - getAllChilds: 'Get all children', - contextMenu: { - expandall: 'Expand all', - collapseall: 'Collapse all', - expandByLayer: 'Expand by level', - expand1: 'Expand to level 1', - expand2: 'Expand to level 2', - expand3: 'Expand to level 3', - expand4: 'Expand to level 4', - expand5: 'Expand to level 5', - expand6: 'Expand to level 6', - expand7: 'Expand to level 7', - expand8: 'Expand to level 8', - expand9: 'Expand to level 9' - }, - quick: { - notfind: 'Search content not found.', - more: 'Show more' - }, - configError: 'The help display column is not configured. Please check whether the help data source is configured correctly.', - selectedInfo: { - total: 'Selected items {0}', - clear: 'Cancel selected', - remove: 'Delete ({0})', - confirm: 'Are you sure you want to cancel all selected records?' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/messager.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/messager.ts deleted file mode 100644 index a4be98c1694b0526a8a0c2f204767b02654ed76a..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/messager.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const MESSAGER_LOCALE = { - yes: 'Yes', - no: 'No', - ok: 'OK', - cancel: 'Cancel', - title: 'System Information', - errorTitle: 'Error Information', - prompt: { - fontSize: { - name: 'Font Size', - small: 'Small', - middle: 'Middle', - big: 'Large', - large: 'Extra Large', - huge: 'Huge' - } - }, - exception: { - expand: 'Expand', - collapse: 'Collapse', - happend: 'Happened Time', - detail: 'Detail', - copy: 'Copy details', - copySuccess: 'Copy succeeded!', - copyFailed: 'Replication failed!', - roger: 'Got it.', - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/multi-select.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/multi-select.ts deleted file mode 100644 index 7c09a289502cbac5fc4cec04fb16b834a794f77a..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/multi-select.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const MULTI_SELECT_LOCALE = { - leftTitle: 'Unselected', - rightTitle: 'Selected', - noDataMoveMessage: 'Please select the data to move.', - shiftRight: 'Shift right', - shiftLeft: 'Shift left', - allShiftRight: 'Shift right all', - allShiftLeft: 'Shift left all', - top: 'Placed at the top', - bottom: 'Placed at the bottom', - shiftUp: 'Shift up', - shiftDown: 'Shift down', - emptyData: 'Empty data', - filterPlaceholder: 'Filter...' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/notify.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/notify.ts deleted file mode 100644 index a7fee07b1ae53b6c226c24f1b6a523f29566c3c5..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/notify.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const NOTIFY_LOCALE = { - title: 'System Information' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/numeric.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/numeric.ts deleted file mode 100644 index f0da3491799dcc8e7b17f7e6ae6489c1459ef255..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/numeric.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const NUMERIC_LOCALE = { - placeholder: 'Please enter the number', - range: { - begin: 'Please enter the begin number', - end: 'Please enter the end number' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/page-walker.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/page-walker.ts deleted file mode 100644 index 30b4769ee854b316512780c7128cb62140ca2b84..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/page-walker.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const PAGE_WALKER_LOCALE = { - next: 'Next', - prev: 'Prev', - skip: 'Skip', - startNow:'Start now' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/pagination.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/pagination.ts deleted file mode 100644 index a6c37bf5aa10119c0c81850d0cac62e730b4c3a5..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/pagination.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const PAGINATION_LOCALE = { - message: 'Total {1} items ', - totalinfo: { - firstText: 'Total', - lastText: 'items' - }, - pagelist: { - firstText: 'Display', - lastText: 'items' - }, - previous: 'Previous', - next: 'next', - goto: { - prefix: 'go to', - suffix: '' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/progress-step.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/progress-step.ts deleted file mode 100644 index 59e62a62f7ecd9661870e04f1ee35c2ba6bc79da..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/progress-step.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const PROGRESS_STEP_LOCALE = { - empty: 'Data is empty' -}; \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/public-api.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/public-api.ts deleted file mode 100644 index d8f7b861b42001dd056bff6b59dee0f7bacf00fb..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/public-api.ts +++ /dev/null @@ -1,23 +0,0 @@ -export * from './collapse'; -export * from './datagrid'; -export * from './filter-editor'; -export * from './loading'; -export * from './lookup'; -export * from './messager'; -export * from './multi-select'; -export * from './notify'; -export * from './pagination'; -export * from './query-condition'; -export * from './query-solution'; -export * from './response-toolbar'; -export * from './section'; -export * from './sidebar'; -export * from './sort-editor'; -export * from './tabs'; -export * from './text'; -export * from './treetable'; -export * from './avatar'; -export * from './list-filter'; -export * from './progress-step'; -export * from './language-label'; -export * from './verify-detail'; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/query-condition.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/query-condition.ts deleted file mode 100644 index 4c59bee3ce979e8cfd7d9330788a921a3bc0bce6..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/query-condition.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const QUERY_CONDITION_LOCALE = { - configDialog: { - unSelectedOptions: 'Unselected Options', - selectedOptions: 'Selected Options', - confirm: 'Confirm', - cancel: 'Cancel', - placeholder: 'Please input search keywords', - moveUp: 'Move Up', - moveAllUp: 'Move All Up', - moveDown: 'Move Down', - moveAllDown: 'Move All Down', - moveRight: 'Move Right', - moveAllRight: 'Move All Right', - moveLeft: 'Move Left', - moveAllLeft: 'Move All Left', - pleaseSelect: 'Please select options', - noOptionMove: 'No moveable options left', - selectOptionUp: 'Please select options to move up', - cannotMoveUp: 'Can\'t move up', - selectOptionTop: 'Please select options to the top', - optionIsTop: 'The option is at top', - selectOptionDown: 'Please select options to move down', - cannotMoveDown: 'Can\'t move down', - selectOptionBottom: 'Please select options to the bottom', - optionIsBottom: 'The option is at bottom' - }, - container: { - query: 'Filter', - saveAs: 'Save as', - save: 'Save', - config: 'Configurations' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/query-solution.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/query-solution.ts deleted file mode 100644 index 0bd4403b2d2468222f0cc81753df1ef75b92acb0..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/query-solution.ts +++ /dev/null @@ -1,41 +0,0 @@ -export const QUERY_SOLUTION_LOCALE = { - saveAsDialog: { - queryPlanName: 'Query Plan Name', - setAsDefault: 'Set as default', - confirm: 'Confirm', - cancel: 'Cancel', - placeholder: 'Please input query solution name', - pleaseInput: 'Please input query solution name', - title: 'New Query Solution', - maxLength: 'Query solution name exceeding nine characters' - }, - manageDialog: { - name: 'Name', - property: 'Property', - default: 'Default', - operation: 'Action', - confirm: 'Confirm', - cancel: 'Cancel', - planNameDuplicated: '{0} is duplicated', - system: 'System Plan', - personal: 'Personal Plan' - }, - container: { - default: 'Default', - manage: 'Edit', - arrowUp: 'Fold', - arrowDown: 'Unfold', - saveAs: 'Save As', - empty: 'empty', - save: 'Save', - pleaseInput: 'Property \'formId\' is required', - saveSuccess: 'Query Plan Saved', - saveFail: 'Query Plan didn\'t save', - planManage: 'Edit Query Plan', - clear: 'Clear conditions', - require: 'Please fill the {fields} before query', - defaultName: 'Default Scheme', - histroyName: 'Last Filter', - sysPresetName: 'Sys Preset Scheme' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/response-toolbar.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/response-toolbar.ts deleted file mode 100644 index 5d0c6b4609c6ada44c3c7a873ec2cf5de520500e..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/response-toolbar.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const RESPONSE_TOOLBAR_LOCALE = { - more: 'More', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/scrollspy.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/scrollspy.ts deleted file mode 100644 index 63aadaa9f65ba667356a67c246fc0c83fec9b6ee..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/scrollspy.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SCROLLSPY_LOCALE = { - guide: 'Nav' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/section.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/section.ts deleted file mode 100644 index 24773e336139b4bdd16550b490bb10e01aa29550..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/section.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const SECTION_LOCALE = { - expandLabel: 'expand', - collapseLabel: 'collapse' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/sidebar.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/sidebar.ts deleted file mode 100644 index 9498dc8e555e22ba9f2db8465aa03db5f0836039..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/sidebar.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SIDEBAR_LOCALE = { - sidebar: 'Detail', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/sort-editor.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/sort-editor.ts deleted file mode 100644 index 25ceefd63d0e730eb6448cca8022015afd197dd4..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/sort-editor.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const SORT_EDITOR_LOCALE = { - // 取消 - cancel: 'cancel', - // 确定 - ok: 'ok', - // 添加子句 - add: 'Add', - clear: 'Clear', - // 置顶 - moveTop: 'Top', - // 上移 - moveUp: 'Up', - // 下移 - moveDown: 'Down', - // 置底 - moveBottom: 'Bottom', - - // 字段 - field: 'Field Name', - // 操作符 - order: 'Order', - - asc: 'ASC', - desc: 'DESC', - title: 'Sort Editor' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/tabs.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/tabs.ts deleted file mode 100644 index 738e422a361a2747622794ede2148e8bea49ee6e..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/tabs.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const TABS_LOCALE = { - more: 'more', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/tag.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/tag.ts deleted file mode 100644 index cd0343edf0b36f55befa61b2d9ac002711567cf6..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/tag.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const TAG_LOCALE = { - addText: 'Add', - placeholder: 'Please input' -}; \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/text.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/text.ts deleted file mode 100644 index 751c8cc4853f975c06fc9ab956ac866fbd91c720..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/text.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const TEXT_LOCALE = { - yes: 'yes', - no: 'no', - zoom: 'Edit content in the dialog opened.', - comments: { - title: 'Common comments', - manager: 'Management', - empty: 'No data.' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/treetable.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/treetable.ts deleted file mode 100644 index cafb8cd769c071dcdb448047b9083c4f5c91794e..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/treetable.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const TREETABLE_LOCALE = { - emptyMessage: 'Empty Data', - pagination: { - previousLabel: 'Prev Page', - nextLabel: 'Next Page', - message: '{0} items per page, total {1} items.' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/en-us/verify-detail.ts b/packages/ui-vue/components/locale/src/lib/locales/en-us/verify-detail.ts deleted file mode 100644 index a425f65c3231859d77f0a2fd1153b75fd0ca545f..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/en-us/verify-detail.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const VERIFY_DETAIL_LOCALE = { - vertifyTypeAll:'All', - vertifyTypeError:'Error', - vertifyTypeEmpty:'Empty' -}; \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/index.ts b/packages/ui-vue/components/locale/src/lib/locales/index.ts deleted file mode 100644 index e98a9b6bd9eceeaaecd0b408b0067b6654d2731c..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/index.ts +++ /dev/null @@ -1,10 +0,0 @@ - -import { EN_US } from './en-us/index'; -import { ZH_CN } from './zh-cn/index'; -import { ZH_CHT } from './zh-CHT/index'; - -export const FARRIS_LOCALES = { - 'zh-CHS': ZH_CN, - en: EN_US, - 'zh-CHT': ZH_CHT -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/avatar.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/avatar.ts deleted file mode 100644 index ba1f597e69e9b5bce62766001f009483c89fcbe7..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/avatar.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const AVATAR_LOCALE_ZHCHT = { - imgtitle: '點擊修改', - typeError: '上傳圖片類型不正確', - sizeError: '上傳圖片不能大於', - uploadError: '圖片上傳失敗,請重試!', - loadError: '加載錯誤' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/batch-edit-dialog.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/batch-edit-dialog.ts deleted file mode 100644 index 711745e7f69e35d245e66d9d37c3a6ef1646e615..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/batch-edit-dialog.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const BATCH_EDIT_DIALOG_LOCALE_ZHCHT = { - title: '批量編輯', - appendText: '添加新編輯列', - appendTextTip: '添加更多列進行批量操作', - okText: '確定', - cancelText: '取消', - field: '請選擇要編輯的列:', - fieldValue: '請輸入要更改的值:', - appendTips: '添加更多列進行批量操作', - selected: '已選', - row: '行', - confirmTitle: '提示', - neverShow: '不再提示', - confirmText: '將修改{0}行數據,確定修改嗎?' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/collapse.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/collapse.ts deleted file mode 100644 index e5d97ba28e0a82939f26f13baa16bea4dba6d0c1..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/collapse.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const COLLAPSE_DIRECTIVE_LOCALE_ZHCHT = { - expand: '展開', - fold: '收起' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/combo.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/combo.ts deleted file mode 100644 index 1bf9a273358bee7709592f2d1688faf80da41486..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/combo.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const COMBO_LOCALE = { - placeholder: '請選擇', - emptyMsg: '暫無數據' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/datagrid.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/datagrid.ts deleted file mode 100644 index a77217abc8785cfb6d0912b833f2f3f861c323e8..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/datagrid.ts +++ /dev/null @@ -1,104 +0,0 @@ -export const DATAGRID_LOCALE_ZHCHT = { - lineNumberTitle: '序號', - emptyMessage: '暫無數據', - pagination: { - previousLabel: '上一頁', - nextLabel: '下一頁', - message: '共 {1} 條 ', - pagelist: { - firstText: '顯示', - lastText: '條' - } - }, - filter: { - title: '過濾條件', - reset: '重置', - clear: '清空條件', - clearAll: '清空所有條件', - setting: '高級設置', - nofilter: '[ 無 ]', - checkAll: '全選', - and: '並且', - or: '或者', - operators: { - equal: '等於', - notEqual: '不等於', - greater: '大於', - greaterOrEqual: '大於等於', - less: '小於', - lessOrEqual: '小於等於', - contains: '包含', - notContains: '不包含', - like: '包含', - notLike: '不包含', - in: '屬於', - notIn: '不屬於', - empty: '為空', - notEmpty: '不為空', - null: 'null', - notNull: '不為null' - }, - more: '查看更多' - }, - settings: { - visible: '顯示列', - sortting: '列排序', - title: '列配置', - canchoose: '可選列', - choosed: '已選列', - asc: '升序', - desc: '降序', - cancelSort: '取消排序', - ok: '確定', - cancel: '取消', - reset: '恢複默認', - conciseMode: '簡潔模式', - advancedMode: '高級模式', - formatSetting: '列格式', - properties: '列屬性', - groupping: '分組', - allColumns: '所有列', - visibleColumns: '可見列', - hiddenColumns: '隱藏列', - searchPlaceholder: '請輸入列名稱', - checkall: '全部顯示/隱藏', - headeralign: '表頭對齊', - dataalign: '數據對齊', - alignLeft: '左對齊', - alignCenter: '居中對齊', - alignRight: '右對齊', - summarytype: '匯總合計類型', - summarytext: '匯總合計文本', - summaryNone: '無', - summarySum: '求和', - summaryMax: '最大值', - summaryMin: '最小值', - summarCount: '計數', - summaryAverage: '平均值', - grouppingField: '分組字段', - moreGrouppingFieldWarningMessage: '最多設置3個字段進行分組', // Up to 3 fields are set for grouping - grouppingSummary: '分組合計', - addGrouppingFieldTip: '添加分組字段', - removeGrouppingFieldTip: '移除分組字段', - grouppingSummaryType: '分組合計類型', - grouppingSummaryText: '分組合計文本', - restoreDefaultSettingsText: '確認要恢複默認設置嗎?', - simple: { - title: '顯示列', - tip: '選中的字段可展示到列表中,拖拽可調整在列表中的展示順序。', - count: '已顯示 {0} 列' - } - }, - selectionData: { - clearAll: '清空', - tooltip: '點擊顯示已選記錄列錶', - currentLenth: `已選擇:{0} 條` - }, - groupRow: { - tips: '拖動列到這兒可進行數據分組', - removeColumn: '移除分組列', - clearTip: '清除所有分組字段', - clear: '清空' - } - -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/discussion-group.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/discussion-group.ts deleted file mode 100644 index 0b4158fac3f07953af0e122d794ea5d9f8f80b9d..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/discussion-group.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const DISCUSSION_GROUP_LOCALE_ZHCHT = { - submit: '提交', - cancel: '取消', - colleague: '同事', - all: '所有人員可見', - related: '僅相關人員可見', - confirm: '確定', - reply: '回複', - emptyMessage: '暫無數據', - placeholder: '請輸入姓名搜索', - notEmpty: '提交內容不能為空', - selectEmployee: '選擇員工', - next: '下級', - emptySelected: '清空已選', - emptyRight: '請在左側選擇人員', - allOrg: '全部組織', - selected: '已選', - section: '部門', - people: '人員', - viewMore: '查看更多', - per: '人', - pcs: '個', - emptyList: '聯係人為空', - advancedQuery: '高級查詢' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/filter-editor.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/filter-editor.ts deleted file mode 100644 index eddedcf8239a92dc35fa317c39aec99d22414f56..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/filter-editor.ts +++ /dev/null @@ -1,74 +0,0 @@ -export const FILTER_EDITOR_LOCALE_ZHCHT = { - // 取消 - cancelButton: '取消', - // 確定 - okButton: '確定', - // 添加子句 - addWhere: '添加子句', - clear: '清空', - // 置頂 - moveTop: '置頂', - // 上移 - moveUp: '上移', - // 下移 - moveDown: '下移', - // 置底 - moveBottom: '置底', - // 左括號 - leftBrackets: '左括號', - // 字段 - field: '字段', - // 操作符 - operator: '操作符', - // 值 - value: '值', - // 值類型 - valueType: '值類型', - expressType: { - value: '值', - express: '錶達式', - frontExpress: '表單表達式' - }, - // 右括號 - rightBrackets: '右括號', - // 關係 - relation: '關係', - relationValue: { - and: '並且', - or: '或者' - }, - designTab: '設計器', - jsonTab: '源代碼', - sqlTab: 'Sql預覽', - title: '條件編輯器', - message: '確認要清空當前所有數據嗎?', - validate: { - bracket: '左右括號不匹配,請檢查', - relation: '條件關系不完整,請檢查', - field: '條件字段未設置,請檢查' - } -}; - -export const ENUM_EDITOR_LOCALE_ZHCHT = { - // 取消 - cancelButton: '取消', - // 確定 - okButton: '確定', - // 添加子句 - addWhere: '添加', - clear: '清空', - // 置頂 - moveTop: '置頂', - // 上移 - moveUp: '上移', - // 下移 - moveDown: '下移', - // 置底 - moveBottom: '置底', - - title: '枚舉數據編輯器', - message: '確認要清空當前所有數據嗎?', - value: '值', - name: '名稱' -}; - diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/filter-panel.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/filter-panel.ts deleted file mode 100644 index 4cd032c850cc6f65685afb005bd88bce90638c47..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/filter-panel.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const FILTER_PANEL_LOCALE_ZHCHT = { - filter: '篩選', - confirm: '確定', - cancel: '取消', - reset: '清除篩選', - advancedFilter: '高級篩選', - expand: '展開', - fold: '收起', - last1Month: '近一月', - last3Month: '近三月', - last6Month: '近半年', - pleaseInput: '請先錄入', - searchHistory: '歷史搜索', - searchResult: '查詢結果', - intervalFilter: '按區間篩選', - beginPlaceHolder: '最低值', - endPlaceHolder: '最高值', - dateBeginPlaceHolder: '開始日期', - dateEndPlaceHolder: '結束日期', - empty: '清空全部', - clear: '清空已選', - today: '當天', - yesterday: '昨天', - checkall: '全選' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/footer.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/footer.ts deleted file mode 100644 index caba2cb2cf0d9a009dfc7a244119079d3d7aad4c..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/footer.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const FOOTER_LOCALE_ZHCHT = { - expandText: '查看更多信息', - collapseText: '查看更多信息' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/index.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/index.ts deleted file mode 100644 index 3cdb64284ccd710c71aa795f1c613637ece0cb0c..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { LOOKUP_LOCALE_ZHCHT } from './lookup'; -import { DATAGRID_LOCALE_ZHCHT } from './datagrid'; -import { SECTION_LOCALE_ZHCHT } from './section'; -import { LOADING_LOCALE_ZHCHT } from './loading'; -import { FILTER_EDITOR_LOCALE_ZHCHT, ENUM_EDITOR_LOCALE_ZHCHT } from './filter-editor'; -import { MESSAGER_LOCALE_ZHCHT } from './messager'; -import { NOTIFY_LOCALE_ZHCHT } from './notify'; -import { PAGINATION_LOCALE_ZHCHT } from './pagination'; -import { SORT_EDITOR_LOCALE_ZHCHT } from './sort-editor'; -import { TEXT_LOCALE_ZHCHT } from './text'; -import { SIDEBAR_LOCALE_ZHCHT } from './sidebar'; -import { TABS_LOCALE_ZHCHT } from './tabs'; -import { RESPONSE_TOOLBAR_LOCALE_ZHCHT } from './response-toolbar'; -import { MULTI_SELECT_LOCALE_ZHCHT } from './multi-select'; -import { QUERY_CONDITION_LOCALE_ZHCHT } from './query-condition'; -import { QUERY_SOLUTION_LOCALE_ZHCHT } from './query-solution'; -import { COLLAPSE_DIRECTIVE_LOCALE_ZHCHT } from './collapse'; -import { TREETABLE_LOCALE_ZHCHT } from './treetable'; -import { AVATAR_LOCALE_ZHCHT } from './avatar'; -import { LIST_FILTER_LOCALE_ZHCHT } from './list-filter'; -import { PROGRESS_STEP_LOCALE_ZHCHT } from './progress-step'; -import { LANGUAGE_LABEL_LOCALE_ZHCHT } from './language-label'; -import { VERIFY_DETAIL_ZHCHT } from './verify-detail'; -import { BATCH_EDIT_DIALOG_LOCALE_ZHCHT } from './batch-edit-dialog'; -import { PAGE_WALKER_ZHCHT } from './page-walker'; -import { FOOTER_LOCALE_ZHCHT } from './footer'; -import { DISCUSSION_GROUP_LOCALE_ZHCHT } from './discussion-group'; -import { TAG_LOCALE_ZHCHT } from './tag'; -import { NUMERIC_LOCALE_ZHCHT } from './numeric'; -import { FILTER_PANEL_LOCALE_ZHCHT } from './filter-panel'; -import { SCROLLSPY_LOCALE_ZHCHT } from './scrollspy'; -import { LOOKUP_CONFIG_LOCALE_ZHCHT } from './lookup-config'; -import { COMBO_LOCALE } from './combo'; -import { LISTVIEW_LOCALE_ZHCHT } from './list-view'; - -export const ZH_CHT = { - locale: 'ZH_CHT', - combo: COMBO_LOCALE, - combolist: {}, - datagrid: DATAGRID_LOCALE_ZHCHT, - filterEditor: FILTER_EDITOR_LOCALE_ZHCHT, - enumEditor: ENUM_EDITOR_LOCALE_ZHCHT, - lookup: LOOKUP_LOCALE_ZHCHT, - loading: LOADING_LOCALE_ZHCHT, - modal: {}, - messager: MESSAGER_LOCALE_ZHCHT, - notify: NOTIFY_LOCALE_ZHCHT, - dialog: {}, - datatable: {}, - colorPicker: {}, - numberSpinner: NUMERIC_LOCALE_ZHCHT, - inputGroup: {}, - sortEditor: SORT_EDITOR_LOCALE_ZHCHT, - treetable: TREETABLE_LOCALE_ZHCHT, - multiSelect: MULTI_SELECT_LOCALE_ZHCHT, - tabs: TABS_LOCALE_ZHCHT, - timePicker: {}, - wizard: {}, - tree: {}, - tooltip: {}, - listview: LISTVIEW_LOCALE_ZHCHT, - text: TEXT_LOCALE_ZHCHT, - switch: {}, - sidebar: SIDEBAR_LOCALE_ZHCHT, - section: SECTION_LOCALE_ZHCHT, - pagination: PAGINATION_LOCALE_ZHCHT, - responseToolbar: RESPONSE_TOOLBAR_LOCALE_ZHCHT, - queryCondition: QUERY_CONDITION_LOCALE_ZHCHT, - querySolution: QUERY_SOLUTION_LOCALE_ZHCHT, - collapseDirective: COLLAPSE_DIRECTIVE_LOCALE_ZHCHT, - avatar: AVATAR_LOCALE_ZHCHT, - listFilter: LIST_FILTER_LOCALE_ZHCHT, - progressStep: PROGRESS_STEP_LOCALE_ZHCHT, - languageLabel: LANGUAGE_LABEL_LOCALE_ZHCHT, - verifyDetail: VERIFY_DETAIL_ZHCHT, - batchEditDialog: BATCH_EDIT_DIALOG_LOCALE_ZHCHT, - pageWalker: PAGE_WALKER_ZHCHT, - footer: FOOTER_LOCALE_ZHCHT, - discussionGroup: DISCUSSION_GROUP_LOCALE_ZHCHT, - tag: TAG_LOCALE_ZHCHT, - filterPanel: FILTER_PANEL_LOCALE_ZHCHT, - scrollspy: SCROLLSPY_LOCALE_ZHCHT, - lookupConfig: LOOKUP_CONFIG_LOCALE_ZHCHT -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/language-label.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/language-label.ts deleted file mode 100644 index b2e5e04f88ce64fc1b905de500e874bed031fd5f..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/language-label.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const LANGUAGE_LABEL_LOCALE_ZHCHT = { - en: '英語', - "zh-cn": '簡體中文', - "zh-CHS": '簡體中文', - "zh-CHT": '繁體中文', - ok: '確定', - cancel: '取消' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/list-filter.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/list-filter.ts deleted file mode 100644 index 526d138d1e3fae851145e87396cafa64ef342a25..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/list-filter.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const LIST_FILTER_LOCALE_ZHCHT = { - filter: '篩選', - confirm: '確定', - cancel: '取消', - reset: '清空條件' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/list-view.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/list-view.ts deleted file mode 100644 index b66d82528c9fae9d890727d3a27ebb9a0ac85d76..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/list-view.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const LISTVIEW_LOCALE_ZHCHT = { - emptyMessage: '暫無數據' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/loading.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/loading.ts deleted file mode 100644 index 70b3ffa1f8c74d50dc9030dec3a2d57f1cbcff76..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/loading.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const LOADING_LOCALE_ZHCHT = { - message: '正在加載,請稍候...' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/lookup-config.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/lookup-config.ts deleted file mode 100644 index 814a279dbeb741cefe231ff6a945869ecfd3debd..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/lookup-config.ts +++ /dev/null @@ -1,38 +0,0 @@ -export const LOOKUP_CONFIG_LOCALE_ZHCHT = { - placeholder: '選擇表單元數據', - code: '編號', - name: '名稱', - select: '選擇幫助元數據', - filter: '配置條件', - helpidEmpty: 'helpId不能為空', - selectTitle: '幫助元數據選擇', - lookupTitle: '幫助配置', - sure: '確定', - cancel: '取消', - successSave: '保存成功', - helpIdError: '請選擇幫助元數據', - fileNamePlaceholder: '選擇幫助文本字段', - selectFileNameTitle: '文本字段選擇器', - bindingPath: '綁定字段', - fieldError: '已綁定字段不存在!', - textFieldLable: '幫助文本字段', - loadTypeTitle: '選擇加載方式', - loadTypeList: { - all: '全部加載', - layer: '分層加載', - default: '默認' - }, - powerTitle: '權限設置', - powerObjLabel: '權限對象', - powerFieldLabel: '權限字段', - powerOperateLabel: '權限操作', - linkfieldLabel: '幫助關聯字段', - powerDataTitle: '權限對象選擇', - powerFieldTitle: '權限字段選擇', - powerOperateTitle: '操作選擇', - businessLable: '業務對象', - powerLable: '權限對象', - powerError: '請選擇權限對象', - operateError: '請選擇操作', - linkfieldError: '請選擇權限字段' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/lookup.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/lookup.ts deleted file mode 100644 index 87a9a70611369329d6f078bc0f56f2843eb71671..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/lookup.ts +++ /dev/null @@ -1,48 +0,0 @@ -export const LOOKUP_LOCALE_ZHCHT = { - placeholder: '請選擇', - favorites: '收藏夾', - selected: '已選數據', - okText: '確定', - cancelText: '取消', - allColumns: '所有列', - datalist: '數據列錶', - mustWriteSomething: '請輸入關鍵字後查詢。', - mustChoosAdatarow: '請選擇一條記錄!', - tipText: '您要找的是不是這些?', - cascade: { - enable: '同步選擇', - disable: '僅選擇自身', - up: '包含上級', - down: '包含下級' - }, - favoriteInfo: { - addFav: '收藏成功。', - cancelFav: '取消收藏成功。 ' - }, - getAllChilds: '獲取所有子級數據', - contextMenu: { - expandall: '全部展開', - collapseall: '全部收起', - expandByLayer: '按層級展開', - expand1: '展開 1 級', - expand2: '展開 2 級', - expand3: '展開 3 級', - expand4: '展開 4 級', - expand5: '展開 5 級', - expand6: '展開 6 級', - expand7: '展開 7 級', - expand8: '展開 8 級', - expand9: '展開 9 級' - }, - quick: { - notfind: '未找到搜索內容', - more: '顯示更多' - }, - configError: '幫助顯示列未配置,請檢查是否已正確配置幫助數據源!', - selectedInfo: { - total: '已選 {0} 條', - clear: '取消已選', - remove: '刪除已選({0})', - confirm: '您確認要取消所有已選中的記錄嗎?' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/messager.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/messager.ts deleted file mode 100644 index 2b8b5245b7c018caaf7c1aa1a4c0d2c65ab92cd0..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/messager.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const MESSAGER_LOCALE_ZHCHT = { - yes: '是', - no: '否', - ok: '確定', - cancel: '取消', - title: '係統提示', - errorTitle: '錯誤提示', - prompt: { - fontSize: { - name: '字體大小', - small: '小', - middle: '中', - big: '大', - large: '特大', - huge: '超大' - } - }, - exception: { - expand: '展開', - collapse: '收起', - happend: '發生時間', - detail: '詳細信息', - copy: '複制詳細信息', - copySuccess: '複制成功', - copyFailed: '複制失敗', - roger: '知道了' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/multi-select.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/multi-select.ts deleted file mode 100644 index df2a2f0983dbb3794fab1b7b0117bec7ffd2ff25..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/multi-select.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const MULTI_SELECT_LOCALE_ZHCHT = { - leftTitle: '未選擇', - rightTitle: '已選擇', - noDataMoveMessage: '請選擇要移動的數據。', - shiftRight: '右移', - shiftLeft: '左移', - allShiftRight: '全部右移', - allShiftLeft: '全部左移', - top: '置頂', - bottom: '置底', - shiftUp: '上移', - shiftDown: '下移', - emptyData: '暫無數據', - filterPlaceholder: '輸入篩選項名稱搜索' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/notify.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/notify.ts deleted file mode 100644 index 815ceb7cf555c9f8fc41015e2e58bb89f6770e55..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/notify.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const NOTIFY_LOCALE_ZHCHT = { - title: '係統提示' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/numeric.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/numeric.ts deleted file mode 100644 index 5c7a05c8bad7e91161fcd09296ebbb99e8899d1f..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/numeric.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const NUMERIC_LOCALE_ZHCHT = { - placeholder: '請輸入數字', - range: { - begin: '請輸入開始數字', - end: '請輸入結束數字' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/page-walker.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/page-walker.ts deleted file mode 100644 index 5e7b697bcdd3156b00a3869904860d8c38a11ae8..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/page-walker.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const PAGE_WALKER_ZHCHT = { - next: '下一步', - prev: '上一步', - skip: '跳過', - startNow: '立即體驗' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/pagination.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/pagination.ts deleted file mode 100644 index e4178d08179a661602f93bdbd99dce5e16894918..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/pagination.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const PAGINATION_LOCALE_ZHCHT = { - message: '共 {1} 條 ', - totalinfo: { - firstText: '共', - lastText: '條' - }, - pagelist: { - firstText: '每頁', - lastText: '條' - }, - previous: '上一頁', - next: '下一頁', - goto: { - prefix: '跳至', - suffix: '頁' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/progress-step.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/progress-step.ts deleted file mode 100644 index b6e5c9d6aeea90900f148a2d863f3e7152d7f84c..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/progress-step.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const PROGRESS_STEP_LOCALE_ZHCHT = { - empty: '步驟條信息為空' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/public-api.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/public-api.ts deleted file mode 100644 index d8f7b861b42001dd056bff6b59dee0f7bacf00fb..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/public-api.ts +++ /dev/null @@ -1,23 +0,0 @@ -export * from './collapse'; -export * from './datagrid'; -export * from './filter-editor'; -export * from './loading'; -export * from './lookup'; -export * from './messager'; -export * from './multi-select'; -export * from './notify'; -export * from './pagination'; -export * from './query-condition'; -export * from './query-solution'; -export * from './response-toolbar'; -export * from './section'; -export * from './sidebar'; -export * from './sort-editor'; -export * from './tabs'; -export * from './text'; -export * from './treetable'; -export * from './avatar'; -export * from './list-filter'; -export * from './progress-step'; -export * from './language-label'; -export * from './verify-detail'; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/query-condition.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/query-condition.ts deleted file mode 100644 index 32444409b7fe08b9410083dd645824e4d2806d71..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/query-condition.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const QUERY_CONDITION_LOCALE_ZHCHT = { - configDialog: { - unSelectedOptions: '未選擇項', - selectedOptions: '已選擇項', - confirm: '確定', - cancel: '取消', - placeholder: '請輸入搜索關鍵字', - moveUp: '上移', - moveAllUp: '全部上移', - moveDown: '下移', - moveAllDown: '全部下移', - moveRight: '右移', - moveAllRight: '全部右移', - moveLeft: '左移', - moveAllLeft: '全部左移', - pleaseSelect: '請選擇字段', - noOptionMove: '冇有可移動字段', - selectOptionUp: '請選擇上移字段', - cannotMoveUp: '無法上移', - selectOptionTop: '請選擇置頂字段', - optionIsTop: '字段已置頂', - selectOptionDown: '請選擇下移字段', - cannotMoveDown: '無法下移', - selectOptionBottom: '請選擇置底字段', - optionIsBottom: '字段已置底' - }, - container: { - query: '篩選', - saveAs: '另存為', - save: '保存', - config: '配置' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/query-solution.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/query-solution.ts deleted file mode 100644 index 2a88e07a253629733142e2201653f5eee4247c05..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/query-solution.ts +++ /dev/null @@ -1,41 +0,0 @@ -export const QUERY_SOLUTION_LOCALE_ZHCHT = { - saveAsDialog: { - queryPlanName: '方案名稱', - setAsDefault: '設為默認', - confirm: '確定', - cancel: '取消', - placeholder: '請輸入方案名稱', - pleaseInput: '請輸入方案名稱', - title: '新增方案', - maxLength: '方案名稱超出九個字' - }, - manageDialog: { - name: '名稱', - property: '屬性', - default: '默認', - operation: '操作', - confirm: '確定', - cancel: '取消', - planNameDuplicated: '方案名稱{0}重複', - system: '係統', - personal: '個人' - }, - container: { - default: '默認', - manage: '管理', - arrowUp: '收起', - arrowDown: '展開', - saveAs: '另存為方案', - empty: '空', - save: '保存方案', - pleaseInput: 'formId為必傳字段,請傳入', - saveSuccess: '查詢方案保存成功', - saveFail: '查詢方案保存失敗', - planManage: '方案管理', - clear: '清空', - require: '請填寫{fields}再進行篩選', - defaultName: '默認篩選方案', - histroyName: '上次篩選', - sysPresetName: '系统预置方案' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/response-toolbar.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/response-toolbar.ts deleted file mode 100644 index abac234137560a365b090491f47aa8adec99f434..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/response-toolbar.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const RESPONSE_TOOLBAR_LOCALE_ZHCHT = { - more: '更多', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/scrollspy.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/scrollspy.ts deleted file mode 100644 index c93e3b34e899f1ca0b7a9906c59db1851f696dc6..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/scrollspy.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SCROLLSPY_LOCALE_ZHCHT = { - guide: '導航' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/section.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/section.ts deleted file mode 100644 index ea200c75fde3900af6e5aa41f0867e1fb8c49351..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/section.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const SECTION_LOCALE_ZHCHT = { - expandLabel: '展開', - collapseLabel: '收起' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/sidebar.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/sidebar.ts deleted file mode 100644 index b236d42d9c146c3dbfb2803f1e1bc533c28d242c..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/sidebar.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SIDEBAR_LOCALE_ZHCHT = { - sidebar: '詳情', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/sort-editor.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/sort-editor.ts deleted file mode 100644 index e01cba592ebaa87b1cbd22643b897508e87d1aaa..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/sort-editor.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const SORT_EDITOR_LOCALE_ZHCHT = { - // 取消 - cancel: '取消', - // 確定 - ok: '確定', - // 添加子句 - add: '添加', - clear: '清空', - // 置頂 - moveTop: '置頂', - // 上移 - moveUp: '上移', - // 下移 - moveDown: '下移', - // 置底 - moveBottom: ' 置底', - - // 字段 - field: '字段', - // 排序 - order: '排序', - asc: '升序', - desc: '降序', - title: '排序設置' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/tabs.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/tabs.ts deleted file mode 100644 index 3f6b23e7bc342df2d1dee0661ccd72aecb52f4db..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/tabs.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const TABS_LOCALE_ZHCHT = { - more: '更多', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/tag.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/tag.ts deleted file mode 100644 index 87c1c84f3c0182cc7e65057fd73e3c4083c46885..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/tag.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const TAG_LOCALE_ZHCHT = { - addText: '添加', - placeholder: '請輸入' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/text.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/text.ts deleted file mode 100644 index ba6e0b69d582876ae878a69e1157c4958f64dd8a..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/text.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const TEXT_LOCALE_ZHCHT = { - yes: '是', - no: '否', - zoom: '在打開的對話框中編輯內容', - comments: { - title: '常用意見', - manager: '意見管理', - empty: '暫無數據' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/treetable.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/treetable.ts deleted file mode 100644 index fd31f6ee13083303bf429273de5e4dccbdbc5e07..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/treetable.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const TREETABLE_LOCALE_ZHCHT = { - emptyMessage: '暫無數據', - pagination: { - previousLabel: '上一頁', - nextLabel: '下一頁', - message: '每頁 {0} 條記錄,共 {1} 條記錄。' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/verify-detail.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/verify-detail.ts deleted file mode 100644 index c0834b821888928b745984b1d21fee256d30b108..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-CHT/verify-detail.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const VERIFY_DETAIL_ZHCHT = { - vertifyTypeAll: '全部', - vertifyTypeError: '錯填', - vertifyTypeEmpty: '漏填' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/avatar.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/avatar.ts deleted file mode 100644 index 556d3f6c8a2ad8abae5092dd16109408aa05e715..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/avatar.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const AVATAR_LOCALE_ZHCHS = { - imgtitle: '点击修改', - typeError: '上传图片类型不正确', - sizeError: '上传图片不能大于', - uploadError: '图片上传失败,请重试!', - loadError: '加载错误' -}; \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/batch-edit-dialog.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/batch-edit-dialog.ts deleted file mode 100644 index 4b9bbd64136b47e4ac614c132148cc6e3d274487..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/batch-edit-dialog.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const BATCH_EDIT_DIALOG_LOCALE_ZHCHS = { - title: '批量编辑', - appendText: '添加新编辑列', - appendTextTip: '添加更多列进行批量操作', - okText: '确定', - cancelText: '取消', - field: '请选择要编辑的列:', - fieldValue: '请输入要更改的值:', - appendTips: '添加更多列进行批量操作', - selected: '已选', - row: '行', - confirmTitle: '提示', - neverShow: '不再提示', - confirmText: '将修改{0}行数据,确定修改吗?' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/collapse.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/collapse.ts deleted file mode 100644 index 7393f636e6d405a366cb0e901bd05d29cc23622e..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/collapse.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const COLLAPSE_DIRECTIVE_LOCALE_ZHCHS = { - expand: '展开', - fold: '收起' -} diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/combo.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/combo.ts deleted file mode 100644 index ff7cbda877406151a43502346988f4fe0c4c7cb4..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/combo.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const COMBO_LOCALE = { - placeholder: '请选择', - emptyMsg: '暂无数据' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/datagrid.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/datagrid.ts deleted file mode 100644 index 6e4130dd69dbfd4af601595eef621cc214543d79..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/datagrid.ts +++ /dev/null @@ -1,105 +0,0 @@ -export const DATAGRID_LOCALE_ZHCHS = { - lineNumberTitle: '序号', - emptyMessage: '暂无数据', - pagination: { - previousLabel: '上一页', - nextLabel: '下一页', - message: '共 {1} 条 ', - pagelist: { - firstText: '显示', - lastText: '条' - }, - previous: '上一页', - next: '下一页' - }, - filter: { - title: '过滤条件', - reset: '重置', - clear: '清空条件', - clearAll: '清空所有条件', - setting: '高级设置', - nofilter: '[ 无 ]', - checkAll: '全选', - and: '并且', - or: '或者', - operators: { - equal: '等于', - notEqual: '不等于', - greater: '大于', - greaterOrEqual: '大于等于', - less: '小于', - lessOrEqual: '小于等于', - contains: '包含', - notContains: '不包含', - like: '包含', - notLike: '不包含', - in: '属于', - notIn: '不属于', - empty: '为空', - notEmpty: '不为空', - null: 'null', - notNull: '不为null' - }, - more: '查看更多' - }, - settings: { - visible: '显示列', - sortting: '列排序', - title: '列配置', - canchoose: '可选列', - choosed: '已选列', - asc: '升序', - desc: '降序', - cancelSort: '取消排序', - ok: '确定', - cancel: '取消', - reset: '恢复默认', - conciseMode: '简洁模式', - advancedMode: '高级模式', - formatSetting: '列格式', - properties: '列属性', - groupping: '分组', - allColumns: '所有列', - visibleColumns: '可见列', - hiddenColumns: '隐藏列', - searchPlaceholder: '请输入列名称', - checkall: '全部显示/隐藏', - headeralign: '表头对齐', - dataalign: '数据对齐', - alignLeft: '左对齐', - alignCenter: '居中对齐', - alignRight: '右对齐', - summarytype: '汇总合计类型', - summarytext: '汇总合计文本', - summaryNone: '无', - summarySum: '求和', - summaryMax: '最大值', - summaryMin: '最小值', - summarCount: '计数', - summaryAverage: '平均值', - grouppingField: '分组字段', - moreGrouppingFieldWarningMessage: '最多设置3个字段进行分组', // Up to 3 fields are set for grouping - grouppingSummary: '分组合计', - addGrouppingFieldTip: '添加分组字段', - removeGrouppingFieldTip: '移除分组字段', - grouppingSummaryType: '分组合计类型', - grouppingSummaryText: '分组合计文本', - restoreDefaultSettingsText: '确认要恢复默认设置吗?', - simple: { - title: '显示列', - tip: '选中的字段可展示到列表中,拖拽可调整在列表中的展示顺序。', - count: '已显示 {0} 列' - } - }, - selectionData: { - clearAll: '清空', - tooltip: '点击显示已选记录列表', - currentLenth: `已选择:{0} 条` - }, - groupRow: { - tips: '拖动列到这儿可进行数据分组', - removeColumn: '移除分组列', - clearTip: '清除所有分组字段', - clear: '清空' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/discussion-group.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/discussion-group.ts deleted file mode 100644 index 6d936071af407147e002ee4807c794ca2e5c6218..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/discussion-group.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const DISCUSSION_GROUP_LOCALE_ZHCHS = { - submit: '提交', - cancel: '取消', - colleague: '同事', - all: '所有人员可见', - related: '仅相关人员可见', - confirm: '确定', - reply: '回复', - emptyMessage: '暂无数据', - placeholder: '请输入姓名搜索', - notEmpty: '提交内容不能为空', - selectEmployee: '选择员工', - next: '下级', - emptySelected: '清空已选', - emptyRight: '请在左侧选择人员', - allOrg: '全部组织', - selected: '已选', - section: '部门', - people: '人员', - viewMore: '查看更多', - per: '人', - pcs: '个', - emptyList: '联系人为空', - advancedQuery: '高级查询' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/filter-editor.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/filter-editor.ts deleted file mode 100644 index 03ea0a5476741fa5a3791d8b9ae89f8af713c5cf..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/filter-editor.ts +++ /dev/null @@ -1,74 +0,0 @@ -export const FILTER_EDITOR_LOCALE_ZHCHS = { - // 取消 - cancelButton: '取消', - // 确定 - okButton: '确定', - // 添加子句 - addWhere: '添加子句', - clear: '清空', - // 置顶 - moveTop: '置顶', - // 上移 - moveUp: '上移', - // 下移 - moveDown: '下移', - // 置底 - moveBottom: '置底', - // 左括号 - leftBrackets: '左括号', - // 字段 - field: '字段', - // 操作符 - operator: '操作符', - // 值 - value: '值', - // 值类型 - valueType: '值类型', - expressType: { - value: '值', - express: '表达式', - frontExpress: '表单表达式' - }, - // 右括号 - rightBrackets: '右括号', - // 关系 - relation: '关系', - relationValue: { - and: '并且', - or: '或者' - }, - designTab: '设计器', - jsonTab: '源代码', - sqlTab: 'Sql预览', - title: '条件编辑器', - message: '确认要清空当前所有数据吗?', - validate: { - bracket: '左右括号不匹配,请检查', - relation: '条件关系不完整,请检查', - field: '条件字段未设置,请检查' - } -}; - -export const ENUM_EDITOR_LOCALE_ZHCHS = { - // 取消 - cancelButton: '取消', - // 确定 - okButton: '确定', - // 添加子句 - addWhere: '添加', - clear: '清空', - // 置顶 - moveTop: '置顶', - // 上移 - moveUp: '上移', - // 下移 - moveDown: '下移', - // 置底 - moveBottom: '置底', - - title: '枚举数据编辑器', - message: '确认要清空当前所有数据吗?', - value: '值', - name: '名称' -}; - diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/filter-panel.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/filter-panel.ts deleted file mode 100644 index e22861a291eb0b905da21631213ac41eb4dcc413..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/filter-panel.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const FILTER_PANEL_LOCALE_ZHCHS = { - filter: '筛选', - confirm: '确定', - cancel: '取消', - reset: '清除筛选', - advancedFilter: '高级筛选', - expand: '展开', - fold: '收起', - last1Month: '近一月', - last3Month: '近三月', - last6Month: '近半年', - pleaseInput: '请先录入', - searchHistory: '历史搜索', - searchResult: '查询结果', - intervalFilter: '按区间筛选', - beginPlaceHolder: '最低值', - endPlaceHolder: '最高值', - dateBeginPlaceHolder: '开始日期', - dateEndPlaceHolder: '结束日期', - empty: '清空全部', - clear: '清空已选', - today:'当天', - yesterday: '昨天', - checkall: '全选' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/footer.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/footer.ts deleted file mode 100644 index 952526edd057c965185ef76328b4b14d38748995..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/footer.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const FOOTER_LOCALE_ZHCHS = { - expandText: '查看更多信息', - collapseText: '查看更多信息' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/index.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/index.ts deleted file mode 100644 index 31d9b0f7c66def39d149bfc4d850abbb4a4252ff..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/index.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { LISTVIEW_LOCALE_ZHCHS } from './list-view'; -import { LOOKUP_LOCALE_ZHCHS } from './lookup'; -import { DATAGRID_LOCALE_ZHCHS } from './datagrid'; -import { SECTION_LOCALE_ZHCHS } from './section'; -import { LOADING_LOCALE_ZHCHS } from './loading'; -import { FILTER_EDITOR_LOCALE_ZHCHS, ENUM_EDITOR_LOCALE_ZHCHS } from './filter-editor'; -import { MESSAGER_LOCALE_ZHCHS } from './messager'; -import { NOTIFY_LOCALE_ZHCHS } from './notify'; -import { PAGINATION_LOCALE_ZHCHS } from './pagination'; -import { SORT_EDITOR_LOCALE_ZHCHS } from './sort-editor'; -import { TEXT_LOCALE_ZHCHS } from './text'; -import { SIDEBAR_LOCALE_ZHCHS } from './sidebar'; -import { TABS_LOCALE_ZHCHS } from './tabs'; -import { RESPONSE_TOOLBAR_LOCALE_ZHCHS } from './response-toolbar'; -import { MULTI_SELECT_LOCALE_ZHCHS } from './multi-select'; -import { QUERY_CONDITION_LOCALE_ZHCHS } from './query-condition'; -import { QUERY_SOLUTION_LOCALE_ZHCHS } from './query-solution'; -import { COLLAPSE_DIRECTIVE_LOCALE_ZHCHS } from './collapse'; -import { TREETABLE_LOCALE_ZHCHS } from './treetable'; -import { AVATAR_LOCALE_ZHCHS } from './avatar'; -import { LIST_FILTER_LOCALE_ZHCHS } from './list-filter'; -import { PROGRESS_STEP_LOCALE_ZHCHS } from './progress-step'; -import { LANGUAGE_LABEL_LOCALE_ZHCHS } from './language-label'; -import { VERIFY_DETAIL_ZHCHS } from './verify-detail'; -import { BATCH_EDIT_DIALOG_LOCALE_ZHCHS } from './batch-edit-dialog'; -import { PAGE_WALKER_ZHCHS } from './page-walker'; -import { FOOTER_LOCALE_ZHCHS } from './footer'; -import { DISCUSSION_GROUP_LOCALE_ZHCHS } from './discussion-group'; -import { TAG_LOCALE_ZHCHS } from './tag'; -import { NUMERIC_LOCALE_ZHCHS } from './numeric'; -import { FILTER_PANEL_LOCALE_ZHCHS } from './filter-panel'; -import { SCROLLSPY_LOCALE_ZHCHS } from './scrollspy'; -import { LOOKUP_CONFIG_LOCALE_ZHCHS } from './lookup-config'; -import { COMBO_LOCALE } from './combo'; - -export const ZH_CN = { - locale: 'ZH_CN', - combo: COMBO_LOCALE, - combolist: {}, - datagrid: DATAGRID_LOCALE_ZHCHS, - filterEditor: FILTER_EDITOR_LOCALE_ZHCHS, - enumEditor: ENUM_EDITOR_LOCALE_ZHCHS, - lookup: LOOKUP_LOCALE_ZHCHS, - loading: LOADING_LOCALE_ZHCHS, - modal: {}, - messager: MESSAGER_LOCALE_ZHCHS, - notify: NOTIFY_LOCALE_ZHCHS, - dialog: {}, - datatable: {}, - colorPicker: {}, - numberSpinner: NUMERIC_LOCALE_ZHCHS, - inputGroup: {}, - sortEditor: SORT_EDITOR_LOCALE_ZHCHS, - treetable: TREETABLE_LOCALE_ZHCHS, - multiSelect: MULTI_SELECT_LOCALE_ZHCHS, - tabs: TABS_LOCALE_ZHCHS, - timePicker: {}, - wizard: {}, - tree: {}, - tooltip: {}, - listview: LISTVIEW_LOCALE_ZHCHS, - text: TEXT_LOCALE_ZHCHS, - switch: {}, - sidebar: SIDEBAR_LOCALE_ZHCHS, - section: SECTION_LOCALE_ZHCHS, - pagination: PAGINATION_LOCALE_ZHCHS, - responseToolbar: RESPONSE_TOOLBAR_LOCALE_ZHCHS, - queryCondition: QUERY_CONDITION_LOCALE_ZHCHS, - querySolution: QUERY_SOLUTION_LOCALE_ZHCHS, - collapseDirective: COLLAPSE_DIRECTIVE_LOCALE_ZHCHS, - avatar: AVATAR_LOCALE_ZHCHS, - listFilter: LIST_FILTER_LOCALE_ZHCHS, - progressStep: PROGRESS_STEP_LOCALE_ZHCHS, - languageLabel: LANGUAGE_LABEL_LOCALE_ZHCHS, - verifyDetail: VERIFY_DETAIL_ZHCHS, - batchEditDialog: BATCH_EDIT_DIALOG_LOCALE_ZHCHS, - pageWalker: PAGE_WALKER_ZHCHS, - footer: FOOTER_LOCALE_ZHCHS, - discussionGroup: DISCUSSION_GROUP_LOCALE_ZHCHS, - tag: TAG_LOCALE_ZHCHS, - filterPanel: FILTER_PANEL_LOCALE_ZHCHS, - scrollspy: SCROLLSPY_LOCALE_ZHCHS, - lookupConfig: LOOKUP_CONFIG_LOCALE_ZHCHS -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/language-label.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/language-label.ts deleted file mode 100644 index 2230a970836c7027c307390e51226e00f6424221..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/language-label.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const LANGUAGE_LABEL_LOCALE_ZHCHS = { - en: '英语', - "zh-cn": '简体中文', - "zh-CHS": '简体中文', - "zh-CHT": '繁体中文', - ok: '确定', - cancel: '取消' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/list-filter.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/list-filter.ts deleted file mode 100644 index 4953eeed8a31c5fc3b9935fca715f681b3536cb0..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/list-filter.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const LIST_FILTER_LOCALE_ZHCHS = { - filter: '筛选', - confirm: '确定', - cancel: '取消', - reset: '清空条件' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/list-view.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/list-view.ts deleted file mode 100644 index 609dcd4055a86b4ff2702c4c8e53371973b1f738..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/list-view.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const LISTVIEW_LOCALE_ZHCHS = { - emptyMessage: '暂无数据' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/loading.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/loading.ts deleted file mode 100644 index 3560535cb7a5274cd29d99630b9d79742e832cf7..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/loading.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const LOADING_LOCALE_ZHCHS = { - message: '正在加载,请稍候...' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/lookup-config.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/lookup-config.ts deleted file mode 100644 index 750f908e2ea07bc6d9e58369fcc19bca5f719fa2..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/lookup-config.ts +++ /dev/null @@ -1,38 +0,0 @@ -export const LOOKUP_CONFIG_LOCALE_ZHCHS = { - placeholder: '选择表单元数据', - code: '编号', - name: '名称', - select: '选择帮助元数据', - filter: '配置条件', - helpidEmpty: 'helpId不能为空', - selectTitle: '帮助元数据选择', - lookupTitle: '帮助配置', - sure: '确定', - cancel: '取消', - successSave: '保存成功', - helpIdError: '请选择帮助元数据', - fileNamePlaceholder: '选择帮助文本字段', - selectFileNameTitle: '文本字段选择器', - bindingPath: '绑定字段', - fieldError: '已绑定字段不存在!', - textFieldLable: '帮助文本字段', - loadTypeTitle: '选择加载方式', - loadTypeList: { - all:'全部加载', - layer:'分层加载', - default: '默认' - }, - powerTitle:'权限设置', - powerObjLabel:'权限对象', - powerFieldLabel:'权限字段', - powerOperateLabel:'权限操作', - linkfieldLabel:'帮助关联字段', - powerDataTitle:'权限对象选择', - powerFieldTitle:'权限字段选择', - powerOperateTitle:'操作选择', - businessLable:'业务对象', - powerLable:'权限对象', - powerError:'请选择权限对象', - operateError:'请选择操作', - linkfieldError:'请选择权限字段' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/lookup.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/lookup.ts deleted file mode 100644 index b6b57ea35c36f75e3d0d4b8d9701717e14654011..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/lookup.ts +++ /dev/null @@ -1,48 +0,0 @@ -export const LOOKUP_LOCALE_ZHCHS = { - placeholder: '请选择', - favorites: '收藏夹', - selected: '已选数据', - okText: '确定', - cancelText: '取消', - allColumns: '所有列', - datalist: '数据列表', - mustWriteSomething: '请输入关键字后查询。', - mustChoosAdatarow: '请选择一条记录!', - tipText: '您要找的是不是这些?', - cascade: { - enable: '同步选择', - disable: '仅选择自身', - up: '包含上级', - down: '包含下级' - }, - favoriteInfo: { - addFav: '收藏成功。', - cancelFav: '取消收藏成功。 ' - }, - getAllChilds: '获取所有子级数据', - contextMenu: { - expandall: '全部展开', - collapseall: '全部收起', - expandByLayer: '按层级展开', - expand1: '展开 1 级', - expand2: '展开 2 级', - expand3: '展开 3 级', - expand4: '展开 4 级', - expand5: '展开 5 级', - expand6: '展开 6 级', - expand7: '展开 7 级', - expand8: '展开 8 级', - expand9: '展开 9 级' - }, - quick: { - notfind: '未找到搜索内容', - more: '显示更多' - }, - configError: '帮助显示列未配置,请检查是否已正确配置帮助数据源! ', - selectedInfo: { - total: '已选 {0} 条', - clear: '取消已选', - remove: '删除已选({0})', - confirm: '您确认要取消所有已选中的记录吗?' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/messager.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/messager.ts deleted file mode 100644 index 845bfdfd0aa71b88c4d752fd3b683cf730a0615f..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/messager.ts +++ /dev/null @@ -1,32 +0,0 @@ -export const MESSAGER_LOCALE_ZHCHS = { - yes: '是', - no: '否', - ok: '确定', - cancel: '取消', - title: '系统提示', - errorTitle: '错误提示', - prompt: { - fontSize: { - name: '字体大小', - small: '小', - middle: '中', - big: '大', - large: '特大', - huge: '超大' - }, - tips: { - surplus: '还可以输入 {0} 个字符', - length: '已输入 {0} 个字符' - } - }, - exception: { - expand: '展开', - collapse: '收起', - happend: '发生时间', - detail: '详细信息', - copy: '复制详细信息', - copySuccess: '复制成功', - copyFailed: '复制失败', - roger: '知道了' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/multi-select.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/multi-select.ts deleted file mode 100644 index eb29553e4b213a524196db99ff124ae21d37ea3d..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/multi-select.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const MULTI_SELECT_LOCALE_ZHCHS = { - leftTitle: '未选择', - rightTitle: '已选择', - noDataMoveMessage: '请选择要移动的数据。', - shiftRight: '右移', - shiftLeft: '左移', - allShiftRight: '全部右移', - allShiftLeft: '全部左移', - top: '置顶', - bottom: '置底', - shiftUp: '上移', - shiftDown: '下移', - emptyData: '暂无数据', - filterPlaceholder: '输入筛选项名称搜索' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/notify.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/notify.ts deleted file mode 100644 index b75a810660e13f6f79cc1478b13cfce4d121f33e..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/notify.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const NOTIFY_LOCALE_ZHCHS = { - title: '系统提示' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/numeric.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/numeric.ts deleted file mode 100644 index 72811cffe824ddd14d7cedafbbd8c2bdf05602df..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/numeric.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const NUMERIC_LOCALE_ZHCHS = { - placeholder: '请输入数字', - range: { - begin: '请输入开始数字', - end: '请输入结束数字' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/page-walker.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/page-walker.ts deleted file mode 100644 index 4a9de20eea655679f0d4f8a47267ab01cd549773..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/page-walker.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const PAGE_WALKER_ZHCHS = { - next: '下一步', - prev: '上一步', - skip: '跳过', - startNow:'立即体验' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/pagination.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/pagination.ts deleted file mode 100644 index c79f2cbe1ba98f0b2688b33dd7566d4b1ba5ac2b..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/pagination.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const PAGINATION_LOCALE_ZHCHS = { - message: '共 {1} 条 ', - totalinfo: { - firstText: '共', - lastText: '条' - }, - pagelist: { - firstText: '每页', - lastText: '条' - }, - previous: '上一页', - next: '下一页', - goto: { - prefix: '跳至', - suffix: '页' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/progress-step.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/progress-step.ts deleted file mode 100644 index dc7cc96bd23b039618680ec3c65b76271c4f6d01..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/progress-step.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const PROGRESS_STEP_LOCALE_ZHCHS = { - empty: '步骤条信息为空' -}; \ No newline at end of file diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/public-api.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/public-api.ts deleted file mode 100644 index d8f7b861b42001dd056bff6b59dee0f7bacf00fb..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/public-api.ts +++ /dev/null @@ -1,23 +0,0 @@ -export * from './collapse'; -export * from './datagrid'; -export * from './filter-editor'; -export * from './loading'; -export * from './lookup'; -export * from './messager'; -export * from './multi-select'; -export * from './notify'; -export * from './pagination'; -export * from './query-condition'; -export * from './query-solution'; -export * from './response-toolbar'; -export * from './section'; -export * from './sidebar'; -export * from './sort-editor'; -export * from './tabs'; -export * from './text'; -export * from './treetable'; -export * from './avatar'; -export * from './list-filter'; -export * from './progress-step'; -export * from './language-label'; -export * from './verify-detail'; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/query-condition.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/query-condition.ts deleted file mode 100644 index 975e45620b3da74d2ff06216f458beca21c79d9a..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/query-condition.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const QUERY_CONDITION_LOCALE_ZHCHS = { - configDialog: { - unSelectedOptions: '未选择项', - selectedOptions: '已选择项', - confirm: '确定', - cancel: '取消', - placeholder: '请输入搜索关键字', - moveUp: '上移', - moveAllUp: '全部上移', - moveDown: '下移', - moveAllDown: '全部下移', - moveRight: '右移', - moveAllRight: '全部右移', - moveLeft: '左移', - moveAllLeft: '全部左移', - pleaseSelect: '请选择字段', - noOptionMove: '没有可移动字段', - selectOptionUp: '请选择上移字段', - cannotMoveUp: '无法上移', - selectOptionTop: '请选择置顶字段', - optionIsTop: '字段已置顶', - selectOptionDown: '请选择下移字段', - cannotMoveDown: '无法下移', - selectOptionBottom: '请选择置底字段', - optionIsBottom: '字段已置底' - }, - container: { - query: '筛选', - saveAs: '另存为', - save: '保存', - config: '配置' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/query-solution.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/query-solution.ts deleted file mode 100644 index 79ee51e0db5f064c16db5aec26893c3aa3816c3a..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/query-solution.ts +++ /dev/null @@ -1,42 +0,0 @@ -export const QUERY_SOLUTION_LOCALE_ZHCHS = { - saveAsDialog: { - queryPlanName: '方案名称', - setAsDefault: '设为默认', - confirm: '确定', - cancel: '取消', - placeholder: '请输入方案名称', - pleaseInput: '请输入方案名称', - title: '新增方案', - maxLength: '方案名称超出九个字' - }, - manageDialog: { - name: '名称', - property: '属性', - default: '默认', - operation: '操作', - confirm: '确定', - cancel: '取消', - planNameDuplicated: '方案名称{0}重复', - system: '系统', - personal: '个人' - }, - container: { - default: '默认', - manage: '管理', - arrowUp: '收起', - arrowDown: '展开', - saveAs: '另存为方案', - empty: '空', - save: '保存方案', - pleaseInput: 'formId为必传字段,请传入', - saveSuccess: '查询方案保存成功', - saveFail: '查询方案保存失败', - planManage: '方案管理', - clear: '清空', - require: '请填写{fields}再进行筛选', - defaultName: '默认筛选方案', - histroyName: '上次筛选', - sysPresetName: '系统预置方案' - } - -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/response-toolbar.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/response-toolbar.ts deleted file mode 100644 index 956854ac49cc557f290e4d0678a5edf1635058cc..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/response-toolbar.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const RESPONSE_TOOLBAR_LOCALE_ZHCHS = { - more: '更多', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/scrollspy.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/scrollspy.ts deleted file mode 100644 index 3bf77b4d9219d9a6b775b683ffff6507e00794d0..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/scrollspy.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SCROLLSPY_LOCALE_ZHCHS = { - guide: '导航' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/section.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/section.ts deleted file mode 100644 index eb2b4e57a1ccc44b53e2e584b2f231e362d70217..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/section.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const SECTION_LOCALE_ZHCHS = { - expandLabel: '展开', - collapseLabel: '收起' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/sidebar.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/sidebar.ts deleted file mode 100644 index e094eb866a2384cfc851b48e1d70713f9facfde9..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/sidebar.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const SIDEBAR_LOCALE_ZHCHS = { - sidebar: '详情', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/sort-editor.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/sort-editor.ts deleted file mode 100644 index 8470a5b706413f79f90bd552ae1b0fb891b6804c..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/sort-editor.ts +++ /dev/null @@ -1,25 +0,0 @@ -export const SORT_EDITOR_LOCALE_ZHCHS = { - // 取消 - cancel: '取消', - // 确定 - ok: '确定', - // 添加子句 - add: '添加', - clear: '清空', - // 置顶 - moveTop: '置顶', - // 上移 - moveUp: '上移', - // 下移 - moveDown: '下移', - // 置底 - moveBottom: ' 置底', - - // 字段 - field: '字段', - // 排序 - order: '排序', - asc: '升序', - desc: '降序', - title: '排序设置' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/tabs.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/tabs.ts deleted file mode 100644 index 56801fcc2596b04dee898659053918236fbee22b..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/tabs.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const TABS_LOCALE_ZHCHS = { - more: '更多', -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/tag.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/tag.ts deleted file mode 100644 index e17c30d0f1459979bc7ae6cdd86212f2f4be8142..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/tag.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const TAG_LOCALE_ZHCHS = { - addText: '添加', - placeholder: '请输入' -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/text.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/text.ts deleted file mode 100644 index e49656c2a581edd6f272279fb84ef73fc16cd522..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/text.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const TEXT_LOCALE_ZHCHS = { - yes: '是', - no: '否', - zoom: '在打开的对话框中编辑内容', - comments: { - title: '常用意见', - manager: '意见管理', - empty: '暂无数据' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/treetable.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/treetable.ts deleted file mode 100644 index 07c4920267148660cbb87ba777d4df6773d5bfb8..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/treetable.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const TREETABLE_LOCALE_ZHCHS = { - emptyMessage: '暂无数据', - pagination: { - previousLabel: '上一页', - nextLabel: '下一页', - message: '每页 {0} 条记录,共 {1} 条记录。' - } -}; diff --git a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/verify-detail.ts b/packages/ui-vue/components/locale/src/lib/locales/zh-cn/verify-detail.ts deleted file mode 100644 index e0fc27155be71ccd3de0d79246e48da4710490ac..0000000000000000000000000000000000000000 --- a/packages/ui-vue/components/locale/src/lib/locales/zh-cn/verify-detail.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const VERIFY_DETAIL_ZHCHS = { - vertifyTypeAll: '全部', - vertifyTypeError: '错填', - vertifyTypeEmpty: '漏填' -}; diff --git a/packages/ui-vue/components/locale/src/lib/types/index.ts b/packages/ui-vue/components/locale/src/lib/types/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..916e49387a7df441e67b76ec3db930bebececd97 --- /dev/null +++ b/packages/ui-vue/components/locale/src/lib/types/index.ts @@ -0,0 +1,20 @@ +export const LOCALE_SERVICE_INJECTION_TOKEN = Symbol('LOCALE_SERVICE'); +export const DEFAULT_LOCALE = 'zh-CHS'; +export interface LocaleConfig { + locale: string; + fallbackLocale?: string; + uri?: string; + localResources?: Record; + mapping?: Record; +} +export interface LocaleResources { + [key: string]: any; +} +export const DEFAULT_LOCALE_CONFIG: LocaleConfig = { + locale: DEFAULT_LOCALE, + fallbackLocale: DEFAULT_LOCALE, + uri: 'assets/i18n/ui' +}; +export interface UseResourceLoader{ + loadResource: (url: string)=> Promise +} \ No newline at end of file diff --git a/packages/ui-vue/components/lookup/index.ts b/packages/ui-vue/components/lookup/index.ts index f5d8d2057d1abf1700881112d2f6228873847b63..bbd2d72462938a26c2e791586ee45387e279768b 100644 --- a/packages/ui-vue/components/lookup/index.ts +++ b/packages/ui-vue/components/lookup/index.ts @@ -20,7 +20,9 @@ import FLookup from './src/lookup.component'; import { callbackResolver, propsResolver } from './src/lookup.props'; import { LookupSchemaRepositoryToken } from './src/property-config/lookup.property-config'; import './lookup-style.scss'; +import { lookupDataSourceConverter } from './src/property-config/converters/lookup-property.converter'; +export { ExternalLookupPropertyConfig } from './src/property-config/external-lookup.property-config'; export * from './src/lookup.props'; export * from './src/composition/types'; @@ -38,5 +40,5 @@ FLookup.registerDesigner = (componentMap: Record, propsResolverMap: propsResolverMap.lookup = propsResolver; }; -export { FLookup, LookupSchemaRepositoryToken }; +export { FLookup, LookupSchemaRepositoryToken,lookupDataSourceConverter }; export default FLookup as typeof FLookup & Plugin; diff --git a/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx b/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx index 9c5031095c652b4c3412dae63ca2b33f17602ac0..f50b852d1bc1f42d8cd6c566254b35075d3485f8 100644 --- a/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/cascade/tree-cascade.component.tsx @@ -1,17 +1,30 @@ -import { defineComponent, ref } from "vue"; +import { defineComponent, inject, ref, watch } from "vue"; import { FComboList } from "@farris/ui-vue/components/combo-list"; import { cascadeItems } from "../../composition/types"; +import { LOOKUP_LOCALES, LookupLocaleData } from "../../composition/use-locales"; export default defineComponent({ name: 'FLookupCascadeSelector', props: ['modelValue'], - emits: [], + emits: ['update:modelValue'], setup(props, context) { + const locales = inject(LOOKUP_LOCALES) as LookupLocaleData; const cascadeRef = ref(); const modelValue = ref(props.modelValue); + watch(() => modelValue.value, (newValue, oldValue) => { + if (newValue === oldValue) { + return; + } + context.emit('update:modelValue', newValue); + }); + + cascadeItems.forEach((item) => { + item.text = locales.tree[item.value]; + }); + return () => { return
{ return
- +
; }; } diff --git a/packages/ui-vue/components/lookup/src/components/lookup-container.component.tsx b/packages/ui-vue/components/lookup/src/components/lookup-container.component.tsx index aa8f5628c2ad15bcc991dcabb20461cb88eb50b7..be4158d2c422cd638119e17800d1b9bdd029c056 100644 --- a/packages/ui-vue/components/lookup/src/components/lookup-container.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/lookup-container.component.tsx @@ -1,4 +1,4 @@ -import { defineComponent, ref, inject, watch, onMounted, onUnmounted, Ref } from "vue"; +import { defineComponent, ref, inject, watch, onMounted, onUnmounted, Ref, onBeforeMount, nextTick, computed } from "vue"; import { FLayout, FLayoutPane } from "@farris/ui-vue/components/layout"; import { LookupProps, lookupProps } from "../lookup.props"; @@ -11,6 +11,7 @@ import { useCheckProps } from "../composition/use-check-props"; import { useNavigation } from "../composition/use-navigation"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "../composition/use-http"; import { DefaultDialogTitle, LOOKUP_ACTIVE_TAB, LookupHttpResult, LookupTabs } from "../composition/types"; +import { LOOKUP_LOCALES, LookupLocaleData } from "../composition/use-locales"; export default defineComponent({ name: "LookupContainer", @@ -21,23 +22,23 @@ export default defineComponent({ setup(props: LookupProps, context) { const currentTab = inject>(LOOKUP_ACTIVE_TAB, ref(LookupTabs.dataList)); const useHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; + const lookupLocales = inject(LOOKUP_LOCALES) as LookupLocaleData; const { lookupStates, getIdQueryParams } = useHttpComposition; const { lookupState, navigationState, queryState, lookupOptions} = lookupStates; - const { renderSearchBar } = useSearchbar(props, false, lookupStates); + const { renderSearchBar } = useSearchbar(props, false, lookupStates, lookupLocales); const { isDoubleList, getNavigationSize, isTreeList } = useCheckProps(props, lookupStates); const { renderNavigation, selectedItems:navSelectedItems, getNavigationIdField, getRelationFilter } = useNavigation(props, context); - function getRenderDataComponent() { + + const { renderTreeGrid, unwatches: unWatchTreegrid } = useTreegrid(props, context as any); + const { renderDataGrid, unwatches: unWatchDatagrid } = useDatagrid(props, context as any, { navSelectedItems, getNavigationIdField, getRelationFilter }); + + const renderDataComponent = computed(() => { if (isTreeList()) { - const { renderTreeGrid } = useTreegrid(props, context as any); return renderTreeGrid; } - - const { renderDataGrid } = useDatagrid(props, context as any, { navSelectedItems, getNavigationIdField, getRelationFilter }); return renderDataGrid; - } - - const renderDataComponent = getRenderDataComponent(); + }); const leftPanelWidth = getNavigationSize(); @@ -50,12 +51,38 @@ export default defineComponent({ context.emit('changeDialogOptions', {title}); } + function initLookupStates(result: LookupHttpResult, action: string) { + lookupState['action'] = action; + if (isTreeList()) { + unWatchDatagrid(); + } else { + unWatchTreegrid(); + } + + if (result.navigation) { + Object.keys(result.navigation).forEach(key => { + navigationState[key] = result.navigation?.[key]; + }); + + if (result.navigation.treeInfo) { + useHttpComposition?.setTreeInfo(result.navigation.treeInfo, true); + } + + delete result.navigation; + } + + if (result.treeInfo) { + useHttpComposition?.setTreeInfo(result.treeInfo); + } + Object.keys(result).forEach(key => { + lookupState[key] = result[key]; + }); + } + function initData() { const params: Record = {}; if (props.enableToSelect) { - params.selectedInfo = getIdQueryParams(); - if (queryState.value != null) { params.search = {'field': '*', value: queryState.value, type: 'like'}; params.action = 'search'; @@ -63,30 +90,13 @@ export default defineComponent({ } useHttpComposition?.loadData(params, (result: LookupHttpResult) => { - if (result.navigation) { - Object.keys(result.navigation).forEach(key => { - navigationState[key] = result.navigation?.[key]; - }); - - if (result.navigation.treeInfo) { - useHttpComposition?.setTreeInfo(result.navigation.treeInfo, true); - } - - delete result.navigation; - } - - if (result.treeInfo) { - useHttpComposition?.setTreeInfo(result.treeInfo); - } - Object.keys(result).forEach(key => { - lookupState[key] = result[key]; + nextTick(() => { + initLookupStates(result, params?.action || ''); + setDialogTitle(result.title || ''); }); - - setDialogTitle(result.title || ''); }); } - onMounted(() => { initData(); document.body.classList.add('lookup-modal-open'); @@ -107,7 +117,7 @@ export default defineComponent({
{renderSearchBar()}
- {renderDataComponent()} + {renderDataComponent.value()}
diff --git a/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx b/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx index d9f1cf6776f3c93ef8eccd588c917edbbc588bf2..f13766ef2134b810967fe3416fa127a23dccb16d 100644 --- a/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/modal-container.component.tsx @@ -10,6 +10,8 @@ import LookupIncludeChildCheckbox from './include-child-checkbox/include-child-c import CascadeControl from './cascade/tree-cascade.component'; import { LOOKUP_TREEROW_OPTIONS, useTreeRowOptions } from "../composition/use-treegrid-row-options"; +import { LOOKUP_LOCALES } from "../composition/use-locales"; + export default defineComponent({ name: 'FLookupModalContainer', props: lookupModalContainerProps, @@ -21,20 +23,25 @@ export default defineComponent({ provide(LOOKUP_HTTP_COMPOSITION, props.useHttpComposition); provide(LOOKUP_USER_DATA_SERVICE, props.userDataService); provide(LOOKUP_SELECTIONS_MANAGER, props.selectionsManager); + provide(LOOKUP_LOCALES, props.locales); - const includeChildNodesInfo = reactive(props.showIncludeChildNodes); + const {tabs: lookupTabs, buttons: lookupButtons, tree: treeLocales} = props.locales; + const includeChildNodesInfo = reactive(props.showIncludeChildNodes); const includeChildNodes = ref(includeChildNodesInfo.value); const { lookupOptions } = props.useHttpComposition.lookupStates; - const dialogOptions = lookupOptions.dialog as LookupDialogOptions; + const dialogOptions = lookupOptions.dialog as LookupDialogOptions; - const isNavigation = lookupOptions.displayType.toUpperCase() === LookupDisplayType.TreeList; + const isNavigation = computed(() => { + return lookupOptions.displayType.toUpperCase() === LookupDisplayType.TreeList; + }); - const treeRowOptions = useTreeRowOptions(lookupOptions, isNavigation); + const treeRowOptions = useTreeRowOptions(lookupOptions, isNavigation.value); provide(LOOKUP_TREEROW_OPTIONS, treeRowOptions); const isMaximized = ref(props.isMaximized); + watch(() => props.isMaximized, (newValue, oldValue) => { isMaximized.value = newValue; @@ -56,7 +63,7 @@ export default defineComponent({ const showIncludeChildNodes = computed(() => { return includeChildNodesInfo.show && activeTab.value === LookupTabs.dataList - && lookupOptions.displayType && isNavigation; + && lookupOptions.displayType && isNavigation.value; }); const popoverOffsetX = computed(() => { @@ -66,6 +73,11 @@ export default defineComponent({ watch(() => includeChildNodes.value, (newValue, oldValue) => { props.useHttpComposition.updateIncludeChilds(newValue); }); + + watch(() => props.showIncludeChildNodes, (newValue, oldValue) => { + includeChildNodesInfo.value = newValue; + props.useHttpComposition.updateIncludeChilds(newValue.value); + }); const { userDataState } = props.userDataService; @@ -151,9 +163,9 @@ export default defineComponent({
onTabClick(LookupTabs.dataList)}>数据列表
+ onClick={() => onTabClick(LookupTabs.dataList)}>{ lookupTabs.datalist }
onTabClick(LookupTabs.favorite)}>收藏夹
+ onClick={() => onTabClick(LookupTabs.favorite)}>{ lookupTabs.favorite }
} @@ -175,13 +187,13 @@ export default defineComponent({
; } diff --git a/packages/ui-vue/components/lookup/src/components/modal-container.props.ts b/packages/ui-vue/components/lookup/src/components/modal-container.props.ts index 85a6f5def0d2773f7c40dd6ce4df42c185fd340e..9b4edb1fb09357628b8133bd0ed7334d3bf56ace 100644 --- a/packages/ui-vue/components/lookup/src/components/modal-container.props.ts +++ b/packages/ui-vue/components/lookup/src/components/modal-container.props.ts @@ -3,6 +3,7 @@ import { UserDataService } from '../composition/use-user-data'; import { UseHttpComposition } from '../composition/use-http'; export const lookupModalContainerProps = { + id: { type: String }, title: { type: String }, height: { type: Number, default: 50 }, draggable: { type: Boolean, default: true }, @@ -14,7 +15,7 @@ export const lookupModalContainerProps = { showSelectedList: { type: Boolean, default: false}, showIncludeChildNodes: { type: Object, default: { show: true, value: false}}, showCascadeControl: { type: Boolean, default: false }, - cascadeValue: { type: String, default: 'both' }, isMaximized: { type: Boolean, default: false }, + locales: { type: Object, default: {} }, }; export type LookupModalContainerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/lookup/src/components/popup-container.component.tsx b/packages/ui-vue/components/lookup/src/components/popup-container.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..1a2d84642a5af49d33e67d9b0adf18ea5b8d234e --- /dev/null +++ b/packages/ui-vue/components/lookup/src/components/popup-container.component.tsx @@ -0,0 +1,96 @@ +import { computed, defineComponent, inject, nextTick, onMounted, onUnmounted, provide, watch } from "vue"; +import { LookupPopupContainerProps, lookupPopupContainerProps } from "./popup-container.props"; +import { useCheckProps } from "../composition/use-check-props"; +import { useTreegrid } from "../composition/use-treegrid"; +import { useDatagrid } from "../composition/use-datagrid"; +import { LookupHttpResult } from "../composition/types"; +import { LOOKUP_TREEROW_OPTIONS, useTreeRowOptions } from "../composition/use-treegrid-row-options"; +import { LOOKUP_LOCALES, LookupLocaleData } from "../composition/use-locales"; +import { useSearchbar } from "../composition/use-search-bar"; + +export default defineComponent({ + name: 'FLookupPopupContainer', + props: lookupPopupContainerProps, + emits: ['selectItemsChanged'], + setup(props: LookupPopupContainerProps, context) { + const lookupLocales = props.locales as LookupLocaleData; + const { lookupStates, getIdQueryParams } = props.useHttpComposition; + const { lookupState, navigationState, queryState, lookupOptions} = lookupStates; + const { isTreeList } = useCheckProps(props, lookupStates); + const { renderSearchBar } = useSearchbar(props, false, lookupStates, lookupLocales); + + const popupContainerStyle = computed(() => { + return { + width: `${props.width}px`, + height: `${props.height}px`, + padding: '5px' + }; + }); + + const { renderDataGrid } = useDatagrid(props, context as any, {}); + const { renderTreeGrid } = useTreegrid(props, context as any); + + const renderDataComponent = computed(() => { + if (isTreeList()) { + return renderTreeGrid; + } + + return renderDataGrid; + }); + + function initData() { + const params: Record = {}; + if (props.enableToSelect) { + + params.selectedInfo = getIdQueryParams(); + + if (queryState.value != null) { + params.search = {'field': '*', value: queryState.value, type: 'like'}; + params.action = 'search'; + } + } + + const http = props.useHttpComposition; + http?.loadData(params, (result: LookupHttpResult) => { + nextTick(() => { + lookupState['action'] = params.action; + if (result.navigation) { + Object.keys(result.navigation).forEach(key => { + navigationState[key] = result.navigation?.[key]; + }); + + if (result.navigation.treeInfo) { + http?.setTreeInfo(result.navigation.treeInfo, true); + } + + delete result.navigation; + } + + if (result.treeInfo) { + http?.setTreeInfo(result.treeInfo); + } + Object.keys(result).forEach(key => { + lookupState[key] = result[key]; + }); + }); + }); + } + + onMounted(() => { + initData(); + }); + + onUnmounted(() => { + props.hidePopup?.(); + }); + + + return () => { + return
+ {/* {renderSearchBar()} */} + {renderDataComponent.value()} + {/*
*/} +
; + }; + } +}); diff --git a/packages/ui-vue/components/lookup/src/components/popup-container.props.ts b/packages/ui-vue/components/lookup/src/components/popup-container.props.ts new file mode 100644 index 0000000000000000000000000000000000000000..a79511f637513f10bad48b7164e02606a80b3201 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/components/popup-container.props.ts @@ -0,0 +1,47 @@ +import { ExtractPropTypes, PropType } from "vue"; +import { UseHttpComposition } from "../composition/use-http"; +import { LoadTreeDataType, LookupDisplayType, LookupPagination } from "../composition/types"; + +export const lookupPopupContainerProps = { + height: { type: Number, default: 500 }, + width: { type: Number, default: 500 }, + useHttpComposition: { type: Object as PropType, default: {} }, + selectionsManager: { type: Object, default: {} }, + showSelectedList: { type: Boolean, default: false }, + enableToSelect: { type: Boolean, default: true }, + uri: { type: String, default: '' }, + hidePopup: { type: Function, default: null }, + fitColumns: { type: Boolean, default: true }, + openType: { type: String, default: 'Popup' }, + pagination: { + type: Object as PropType, default: { + enable: false, + showLimits: true, + sizeLimits: [10, 20, 30, 50, 100], + size: 20, + index: 1, + total: 0, + mode: 'server', + showGoto: false + } + }, + displayType: { type: String as PropType, default: LookupDisplayType.List }, + loadTreeDataType: { type: String as PropType, default: LoadTreeDataType.all }, + /** 0: 不展开; -1: 全部展开;>0: 展开到指定级数 */ + expandLevel: { type: Number, default: 0 }, + enableCascade: { type: Boolean, default: false }, + showCascadeControl: { type: Boolean, default: false }, + cascadeItems: { + type: Object, default: { + both: true, + up: true, + down: true, + disable: true + } + }, + cascadeStatus: { type: String, default: 'both' }, + enableSearchBar: { type: Boolean, default: true }, + searchAnyField: { type: Boolean, default: true }, + locales: { type: Object, default: {} } +}; +export type LookupPopupContainerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx b/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx index 95b356d8a11f40232cc593ee7eb27298e22df82d..c76ac03b52bed342b4e9d8057c59d525d7760c61 100644 --- a/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.component.tsx @@ -1,9 +1,11 @@ import { computed, defineComponent, inject, onMounted, Ref, ref, watch } from "vue"; import { FButtonEdit } from "@farris/ui-vue/components/button-edit"; import { FComboList } from "@farris/ui-vue/components/combo-list"; +// import { FSwitch } from "@farris/ui-vue/components/switch"; import { searchBarProps, SearchBarProps } from "./search-bar.props"; -import { SearchField } from "../../composition/types"; +import { SearchField, SearchInfo } from "../../composition/types"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "../../composition/use-http"; +import { debounce } from "lodash-es"; export default defineComponent({ @@ -11,7 +13,7 @@ export default defineComponent({ props: searchBarProps, emits: ["search"], setup(props: SearchBarProps, context) { - + const searchIcon = ''; const useHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; const { queryState } = useHttpComposition.lookupStates; @@ -21,7 +23,7 @@ export default defineComponent({ const isNavigation = ref(props.isNavigation); const searchFieldRef = ref(); - const eventParams = computed(() => { + const eventParams = computed(() => { return { field: searchField.value, value: searchValue.value, @@ -61,6 +63,8 @@ export default defineComponent({ context.emit('search', eventParams.value); } + const debounceRunSearch = debounce(() => onSearch(), 300); + function onClearSearchValue() { searchValue.value = ''; onSearch(); @@ -68,7 +72,7 @@ export default defineComponent({ function onEnterHandler($event: KeyboardEvent) { if ($event.key === 'Enter') { - onSearch(); + debounceRunSearch(); } } @@ -93,7 +97,7 @@ export default defineComponent({ >
- '} + + id={props.id + '-search-bar'} + > + + + {/* */}
; }; diff --git a/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.props.ts b/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.props.ts index 6aeacf3d87e4dfe08957ff17ab0e8ddde2453afd..c07406e9022c151cf030543004f879ab802129d4 100644 --- a/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.props.ts +++ b/packages/ui-vue/components/lookup/src/components/search-bar/search-bar.props.ts @@ -5,6 +5,7 @@ export const searchBarProps = { fields: { type: Array, default: () => [] }, showAllColumns: { type: Boolean, default: true }, isNavigation: { type: Boolean, default: false }, + id: { type: String, default: '' } }; export type SearchBarProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx b/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx index f3d7d2e5a798176a5d777b94de23cf5a1dc8cac1..70dd62705a6ce1f3c62bf5c2fc2f81fd397a66ef 100644 --- a/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx +++ b/packages/ui-vue/components/lookup/src/components/selected-list/lookup-selected-list.component.tsx @@ -8,6 +8,8 @@ import { LookupSelectedListProps, lookupSelectedListProps } from "./lookup-selec import { LOOKUP_SELECTIONS_MANAGER, LookupSelectionsManager } from "../../composition/use-selections"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "../../composition/use-http"; +import { LOOKUP_LOCALES, LookupLocaleData } from "../../composition/use-locales"; + export default defineComponent({ props: lookupSelectedListProps, setup(props: LookupSelectedListProps, context) { @@ -17,6 +19,8 @@ export default defineComponent({ const useHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; const { lookupState, selectionState } = useHttpComposition.lookupStates; + const lookupLocales = inject(LOOKUP_LOCALES) as LookupLocaleData; + const { clearSelections, unSelectionsByIds } = lookupSelectionsManager; const popoverRef = ref(); @@ -54,7 +58,7 @@ export default defineComponent({ formatter: (cell, row) => { return ; + style="color: #dd2438;">; } }; @@ -141,7 +145,8 @@ export default defineComponent({ }); function onClearSelections() { - messagerService?.question('您确认要取消所有选中记录吗?', '', () => { + const messageString = lookupLocales?.messages.clearSelections; + messagerService?.question(messageString, '', () => { clearSelections(); }); } @@ -165,8 +170,8 @@ export default defineComponent({ return () => { return (<>
-
已选{selectionState.value.length}
- {showClearButton.value && } +
+ {showClearButton.value && }
; + + flattenTreeNodes?: any[]; } export interface LookupHttpService { @@ -239,6 +269,7 @@ export interface SearchInfo { field?: string; value?: any; type?: 'like' | 'equal' | ''; + isNavigation?: boolean; } // eslint-disable-next-line @typescript-eslint/no-empty-object-type diff --git a/packages/ui-vue/components/lookup/src/composition/use-check-props.ts b/packages/ui-vue/components/lookup/src/composition/use-check-props.ts index aae2186bc73aa7cdfb75d9212bfe6ae507645b93..4c1225589133859d7811cd220ec1d822a70b8a87 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-check-props.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-check-props.ts @@ -1,20 +1,10 @@ import { LookupProps } from "../lookup.props"; -import { LoadTreeDataType, LookupDisplayType, LookupPagination, TreeInfo } from "./types"; +import { DEFAULT_PAGINATION_OPTIONS, LoadTreeDataType, LookupDisplayType, LookupPagination, TreeInfo } from "./types"; import { LookupStates } from "./use-state"; export function useCheckProps(props: LookupProps, lookupStates: LookupStates) { const { lookupOptions } = lookupStates; - // 定义默认分页选项常量,避免重复创建对象 - const DEFAULT_PAGINATION_OPTIONS: LookupPagination = { - enable: true, - sizeLimits: [10, 20, 30, 50, 100], - size: 20, - index: 1, - total: 0, - mode: 'server' - }; - function checkPaination(paginationOptions?: Partial) { return { ...DEFAULT_PAGINATION_OPTIONS, ...paginationOptions }; } @@ -89,7 +79,16 @@ export function useCheckProps(props: LookupProps, lookupStates: LookupStates) { return treeInfo?.layerType === "pathcode"; } + function isEnableCascadeCheck() { + return lookupOptions.enableCascade && lookupOptions.multiSelect; + } + + function isDropdownMode() { + return props.openType === 'Popup'; + } + + return { checkPaination, checkMultiSelect, checkColumnOptions, isDoubleList, getNavigationSize, isTreeList, navIsTreeList, navIsList, - isLoadAll, isPathCodeTree + isLoadAll, isPathCodeTree, isEnableCascadeCheck, isDropdownMode }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx b/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx index 5ec3c1d99dad60af5d3c6e93532cefaf4995c1a8..faf8e50ddbd56506b5c7b1db76d075db4d573ed2 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-datagrid.tsx @@ -18,14 +18,14 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio const useHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; const lookupSelectionsManager = inject(LOOKUP_SELECTIONS_MANAGER) as LookupSelectionsManager; - const {lookupStates, loadData, includeChilds, getPathCode, getChildNodes } = useHttpComposition; - const { lookupState, navigationState, pageInfoState, searchState, lookupOptions} = lookupStates; + const { lookupStates, loadData, includeChilds, getPathCode, getChildNodes } = useHttpComposition; + const { lookupState, navigationState, pageInfoState, searchState, lookupOptions } = lookupStates; const { checkPaination, checkMultiSelect, checkColumnOptions, navIsTreeList, isLoadAll, isPathCodeTree } = useCheckProps(props, lookupStates); const { updatePageInfo } = usePageInfo(props, pageInfoState); - const { updateSelections } = lookupSelectionsManager; + const { updateSelections, isSelected, getPrimaryKey } = lookupSelectionsManager; const { loadAndSelect } = useLoadData(props, lookupStates); @@ -52,41 +52,41 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio useSyncSelect(props, datagridRef, useHttpComposition.lookupStates, currentTab); function dataGridLoadData(items: any, total: number, pageInfo: Record | undefined) { - const value: any = {total}; + const value: any = { total }; if (pageInfo) { const {pageIndex: index, pageSize: size, enablePager: enable, pageList: sizeLimits } = pageInfo; - Object.assign(value, {index, size, enable, sizeLimits}); + Object.assign(value, {index, size, enable}); + } else { + value.enable = false; } updatePageInfo(value); loadAndSelect(datagridRef.value, items || []); } const relation = computed(() => { - if(navSelectedItems.value && navSelectedItems.value.length) { - let nodes = navSelectedItems.value; - if (navIsTreeList() && includeChilds.value) { - const treeInfo = navigationState.treeInfo as any; - if (isLoadAll(treeInfo)) { - nodes = getChildNodes(nodes[0]); - return {relationFilter: getRelationFilter(nodes)}; - } else if (isPathCodeTree(treeInfo)) { - return { navNodePathCode: getPathCode(nodes[0].data, treeInfo) }; - } - } - return {relationFilter: getRelationFilter(nodes)}; + if (!navSelectedItems.value?.length) { return null; } + const nodes = navSelectedItems.value; + const filterParams = { relationFilter: getRelationFilter(nodes) }; + if (!navIsTreeList() || !includeChilds.value) { + return filterParams; } - return null; + + const { treeInfo } = navigationState; + const action = { action: 'navAllChildren', ...filterParams}; + return isPathCodeTree(treeInfo)? Object.assign(action, { navNodePathCode: getPathCode(nodes[0].data, treeInfo) }): action; }); - watch([() => lookupState?.columns,() => lookupState?.items, () => lookupState?.pageInfo, () => lookupState?.total, - () => lookupState?.selectedData + const unWatchLookupState = watch([() => lookupState?.columns, () => lookupState?.items, () => lookupState?.pageInfo, () => lookupState?.total, + () => lookupState?.selectedData ], ([newColumns, newItems, newPageInfo, newTotal, selectedData]) => { - newColumns && setColumns(datagridRef.value, newColumns); - if (selectedData && selectedData.length) { - updateSelections(selectedData); + if (datagridRef.value) { + newColumns && setColumns(datagridRef.value, newColumns); + if (selectedData && selectedData.length) { + updateSelections(selectedData); + } + newItems && dataGridLoadData(newItems, newTotal || 0, newPageInfo); } - dataGridLoadData(newItems, newTotal || 0, newPageInfo); }); watch(() => props.idValue, (newValue: string) => { @@ -94,7 +94,17 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio }); function onSelectionChange(items: any[]) { + if (items && items.length) { + if (!lookupOptions.multiSelect) { + const itemId = getPrimaryKey(items[0]); + if (isSelected(itemId)) { + return; + } + } + } + updateSelections(items); + context.emit("selectionsChanged", { items }); } function onUnSelectItem(items: any) { @@ -103,7 +113,7 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio function httpRequest() { const queryParams= { search: searchState.default, action: 'list'}; - if (relation.value) { + if (props.openType === 'Modal' && relation.value) { Object.assign(queryParams, relation.value); } loadData(queryParams, (result: LookupHttpResult) => { @@ -113,12 +123,12 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio // 优化 throttle 函数的使用,同时移除未使用的变量 const throttledRequest = throttle(() => { - updatePageInfo({index: 1}); + updatePageInfo({ index: 1 }); httpRequest(); }, 200); - watch(() => navSelectedItems.value, (navSelectedRows) => { - if (props.uri) { + const unWatchNavSelectedItems = watch(() => navSelectedItems?.value, (navSelectedRows) => { + if (lookupOptions.uri) { throttledRequest(); return; } @@ -126,29 +136,44 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio context.emit("navSelectionsChanged", { items: navSelectedRows, ids: navSelectIds }); }); - watch(() => searchState.default, (newSearchInfo) => { - if (props.uri) { + const unWatchSearchState = watch(() => searchState.default, (newSearchInfo) => { + if (lookupOptions.uri) { throttledRequest(); return; } context.emit('search', newSearchInfo); }); - watch(() => includeChilds.value, () => { - if (props.uri && navSelectedItems.value && navSelectedItems.value.length) { + const unWatchIncludeChilds = watch(() => includeChilds.value, () => { + if (lookupOptions.uri && navSelectedItems.value && navSelectedItems.value.length) { throttledRequest(); return; } }); - function onPageInfoChanged({pageSize, pageIndex}) { + function unwatches() { + unWatchLookupState(); + unWatchNavSelectedItems(); + unWatchSearchState(); + unWatchIncludeChilds(); + } + + + watch(() => lookupOptions.displayType, (newDisplayType) => { + if (newDisplayType.toLowerCase() === 'treelist') { + unwatches(); + } + }); + + + function onPageInfoChanged({ pageSize, pageIndex }) { let index = pageIndex; if (currentPaginationOptions.value?.size !== pageSize) { index = 1; } - const params = {size: pageSize, index, isNavigation: false}; + const params = { size: pageSize, index, isNavigation: false }; updatePageInfo(params); - if (props.uri) { + if (lookupOptions.uri) { httpRequest(); return; } @@ -157,7 +182,7 @@ export function useDatagrid(props: LookupProps, context: SetupContext, navigatio } function renderDataGrid() { - return ; buttonEditInstance: Ref; userDataService: UserDataService; - useHttp: UseHttpComposition + useHttp: UseHttpComposition; + lookupLocales: LookupLocaleData; } export function useDialog( props: LookupProps, context: SetupContext, selectedItems: Ref, { - dictPicked, modelValue, buttonEditInstance, userDataService, useHttp }: UseDialogOptions) { + dictPicked, modelValue, buttonEditInstance, userDataService, useHttp, lookupLocales }: UseDialogOptions) { const notifyService: FNotifyService | null = inject(F_NOTIFY_SERVICE_TOKEN, null); const loadingService: any | null = inject('FLoadingService'); @@ -47,7 +49,7 @@ export function useDialog( const NOTIFY_OPTIONS = { position: 'top-center' as const }; - async function updateModelValue() { + const setModelAndIdValue = () => { const displayText = selectedItems.value.map((item: any) => { return resolveField(item, lookupOptions.textField); }).join(props.separator); @@ -61,12 +63,18 @@ export function useDialog( context.emit('update:modelValue', displayText); context.emit('update:idValue', idValues); + }; + + const runDictPickedAsync = async () => { const mappingInfo = { items: selectedItems.value, mappingFields: lookupOptions.mappingFields }; context.emit('update:dataMapping', mappingInfo); - useContext(props, mappingInfo); - return await dictPicked(mappingInfo); + }; + + async function updateModelValue() { + setModelAndIdValue(); + return await runDictPickedAsync(); } function cancelDialog() { @@ -87,6 +95,7 @@ export function useDialog( }; function destroyed() { + document.body.classList.remove('lookup-modal-open'); loadingService?.clearAll(); const targets = [lookupState, navigationState, pageInfoState, searchState, queryState]; cleanObjects(targets); @@ -97,7 +106,7 @@ export function useDialog( async function handleSubmitValidation(): Promise<{ shouldUpdate: boolean; message?: string }> { if (selectedItems.value.length === 0) { - notifyService?.warning({ message: '请选择数据!', ...NOTIFY_OPTIONS }); + notifyService?.warning({ message: lookupLocales.messages.mustChoosAdatarow, ...NOTIFY_OPTIONS }); return { shouldUpdate: false }; } @@ -234,17 +243,21 @@ export function useDialog( } }; + async function getUserSettings() { + if (props.enableUserData) { + await userDataService.getUserData(); + if (userDataService?.userDataState?.data && dialogOptions.rememberSize) { + const { size } = userDataService.userDataState.data; + if (size) { + Object.assign(modalOptions, size); + } + } + } + } + async function beforeOpenDialog($event?: any) { let shouldContinue = true; - await userDataService.getUserData(); - - if (userDataService?.userDataState?.data && props.enableUserData && dialogOptions.rememberSize) { - const { size } = userDataService.userDataState.data; - if (size) { - Object.assign(modalOptions, size); - } - } const beforeEvent = props.dictPicking; @@ -267,6 +280,10 @@ export function useDialog( } } + if (shouldContinue) { + await getUserSettings(); + } + return shouldContinue; } @@ -278,6 +295,9 @@ export function useDialog( customData, updateModelValue, cancelDialog, - submitDialog + submitDialog, + destroyed, + setModelAndIdValue, + runDictPickedAsync }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-favorite.tsx b/packages/ui-vue/components/lookup/src/composition/use-favorite.tsx index 9dce96862a7e910d3ca33fa8ecc1bfacb93383a9..a029c1c980ba59784598f09222bcd6d5a57e2f8b 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-favorite.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-favorite.tsx @@ -6,9 +6,12 @@ import { F_NOTIFY_SERVICE_TOKEN, FNotifyService } from "@farris/ui-vue/component import { LOOKUP_USER_DATA_SERVICE, UserDataService } from "./use-user-data"; import { LookupProps } from "../lookup.props"; +import { LOOKUP_LOCALES, LookupLocaleData } from "./use-locales"; + export function useFavorite(props: LookupProps, idField: string) { const notifyService = inject(F_NOTIFY_SERVICE_TOKEN) as FNotifyService; const userDataService = inject(LOOKUP_USER_DATA_SERVICE) as UserDataService; + const lookupLocales = inject(LOOKUP_LOCALES) as LookupLocaleData; const { userDataState } = userDataService; userDataState.data = userDataState.data || {}; @@ -36,7 +39,7 @@ export function useFavorite(props: LookupProps, idField: string) { } userDataService?.saveUserData(true).then((res) => { - const message = actiion === 'add' ? '已添加到收藏' : '已从收藏中移除'; + const message = actiion === 'add' ? lookupLocales.messages.addedFavSuccess : lookupLocales.messages.cancelFavSuccess; notifyService?.success({ message, position: 'top-center' }); }); @@ -59,10 +62,15 @@ export function useFavorite(props: LookupProps, idField: string) { updateFavorites({id: itemId, data: row.raw}, !isFavorite.value(row) ? 'add' : 'remove'); } + const getActionTitle = (row) => { + const title = isFavorite.value(row) ? lookupLocales.buttons.removeFav : lookupLocales.buttons.addFav; + return title; + }; + const favoriteColumnFormatter = (cell, row) => { return ; + style="color: #ffbd8c;" title={ getActionTitle(row) }>; }; const favoriteColumnFormatterForTree = (cell, row) => { diff --git a/packages/ui-vue/components/lookup/src/composition/use-http.ts b/packages/ui-vue/components/lookup/src/composition/use-http.ts index 9e4973465b928b0a485c355011ad2c0ab1b19274..100e81acaa95c47ce592a4b031bc364935e443fa 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-http.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-http.ts @@ -26,7 +26,7 @@ export interface UseHttpComposition { loadData: (event: any, callback: (data: LookupHttpResult) => void) => void; getData: (params: any) => Promise; updateSearchFieldTitle: (searchFields: any[], columns: any[]) => any[]; - getPathCode: (data: any, treeInfo: TreeInfo) => string; + getPathCode: (data: any, treeInfo?: TreeInfo) => string; getChildNodes: (node: any) => Array; lookupStates: LookupStates; httpService?: LookupHttpService; @@ -41,12 +41,11 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { const loadingService: any | null = inject('FLoadingService'); const httpService = inject(F_LOOKUP_HTTP_SERVICE_TOKEN); const lookupStates = useLookupState(props); - const { pageInfoState, lookupState, navigationState, lookupOptions } = lookupStates; + const { pageInfoState, lookupState, navigationState, lookupOptions, searchValueChanged } = lookupStates; - lookupOptions.loadTreeDataType = 'all'; + // lookupOptions.loadTreeDataType = 'all'; const { searchFieldItems, navSearchFieldItems } = useSearchFields(props, lookupState, navigationState); - const uri = ref(props.uri); const idValues = ref(props.idValue); const includeChilds = ref(false); @@ -231,7 +230,11 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { queryParams.treeToList = props.treeToList; queryParams.navTreeToList = props.navTreeToList; - queryParams.loadTreeDataType = lookupOptions.loadTreeDataType; + if (lookupOptions.loadTreeDataType === LoadTreeDataType.default) { + queryParams.loadTreeDataType = 'default'; + } else { + queryParams.loadTreeDataType = lookupOptions.loadTreeDataType === LoadTreeDataType.all ? 'loadall' : 'layerload'; + } if (event?.relationFilter) { queryParams.relationFilter = [...event.relationFilter]; @@ -290,9 +293,10 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { function getData(params: LookupRequestParams): Promise { const loader = lookupRequest(); - if (uri.value && loader) { - return loader(uri.value, params).then(res => { + if (loader) { + return loader(lookupOptions.uri, params).then(res => { initColumnsInfo(res); + searchValueChanged.value = false; return res; }); } @@ -328,7 +332,9 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { : lookupOptions.loadTreeDataType === 'all' ? !shoudExpand(getFieldValue(item.data, layerField)) : !(item.expanded ?? true); - + + item.hasChildren = !item.leaf; + if (item.children?.length) { setTreeNodeExpandStatus(item.children, expandAll, layerField); } @@ -341,9 +347,15 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { try { let httpResult = await getData(params); - + if (lookupOptions.displayType !== httpResult.displayType && event.action !== 'navsearch') { + lookupOptions.displayType = httpResult.displayType; + } + if (isTree()) { const { items: treeItems, treeInfo: treeInfoConfig } = httpResult.navigation || httpResult; + if (lookupOptions.loadDataType === LoadTreeDataType.default) { + lookupOptions.loadTreeDataType = treeInfoConfig?.loadDataType || LoadTreeDataType.all; + } if (treeItems?.length && treeInfoConfig) { const { dataField: treeInfoDataField, layerField } = treeInfoConfig; @@ -365,7 +377,10 @@ export function useHttp(props: LookupProps, context: any): UseHttpComposition { includeChilds.value = checked; } - function getPathCode(data: any, treeInfo: TreeInfo) { + function getPathCode(data: any, treeInfo?: TreeInfo) { + if (!treeInfo) { + return ''; + } const { dataField, pathField } = treeInfo; if (pathField) { if (dataField) { diff --git a/packages/ui-vue/components/lookup/src/composition/use-input-change.ts b/packages/ui-vue/components/lookup/src/composition/use-input-change.ts index 97977dce07347976a2f5d76e4733cb78dcde061b..0b84c531b724d18e1908372b972c89866307b96e 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-input-change.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-input-change.ts @@ -13,62 +13,77 @@ export type LookupInputChangeOptions = { beforeOpenDialog: BeforeOpenDialogFunction; modelValue: Ref; useHttpComposition: UseHttpComposition; - lookupOptions: any + lookupOptions: any, + usePopupComposition: any; + isPopuped: Ref; }; export function useInputChange(props: LookupProps, context: any, options: LookupInputChangeOptions) { const changeOnBlur = computed(() => props.textChangeType === 'blur' || props.textChangeType === 'any'); const changeOnEnter = computed(() => props.textChangeType === 'enter' || props.textChangeType === 'any'); - const {beforeOpenDialog, updateModelValue, selectedItems, openDialog, lookupOptions, useHttpComposition} = options; + const {beforeOpenDialog, updateModelValue, selectedItems, openDialog, lookupOptions, useHttpComposition, isPopuped} = options; const {updateSearchFieldTitle} = useHttpComposition; - const { lookupState, queryState } = useHttpComposition.lookupStates; + const { lookupState, queryState, searchValueChanged } = useHttpComposition.lookupStates; const isClear = ref(false); function isTextChange(text: string) { - const isChange = isEmpty(props.modelValue) && isEmpty(text) ? false: props.modelValue !== text; - if (isEmpty(text) && isChange) { + if (isEmpty(text) && searchValueChanged.value && !isPopuped.value) { isClear.value = true; + searchValueChanged.value = false; return false; } - if (isChange) { + if (searchValueChanged.value) { queryState.value = text; - } else { - queryState.value = ''; } - return isChange; + + return searchValueChanged.value; + } + + function getLeafNode(items: any[]) { + if (!items || items.length !== 1) {return;} + const [firstItem] = items; + return !firstItem.children?.length ? firstItem.data: getLeafNode(firstItem.children); + } + + function isOnlyOneItem(isTree: boolean, items?: any[]) { + if (!items || items.length !== 1) {return false;} + const [firstItem] = items; + if (!isTree) { + return firstItem; + } + return !firstItem.children?.length? firstItem.data: getLeafNode(firstItem.children); } async function queryDataBySearchKeys(searchText: string) { const searchParams = { - search: { field: '*', value: searchText, type: 'equal' }, + search: { field: '*', value: searchText, type: props.openType === 'Modal' ?'equal': 'like' }, action: 'search' }; - if (beforeOpenDialog) { - const shouldContinue = await beforeOpenDialog(searchParams); - if (shouldContinue === false) { - return; - } - + if (searchValueChanged.value) { document.body.classList.add("lookup-modal-open"); - useHttpComposition.loadData(searchParams, (data: LookupHttpResult) => { - const onlyOne = data.items && data.items.length === 1 && (!data.items[0].children || !data.items[0].children.length); - if (onlyOne) { - selectedItems.value = (data.items || []).map(item => item.data ? item.data: item); - updateModelValue(); - queryState.value = ''; - document.body.classList.remove('lookup-modal-open'); - return; - } - + useHttpComposition.loadData(searchParams, async (data: LookupHttpResult) => { if (data.searchFields && data.columns) { const searchFields = updateSearchFieldTitle(data.searchFields, data.columns); lookupState.searchFields = searchFields; } - - openDialog(); + searchValueChanged.value = false; + if (props.openType === 'Modal') { + const onlyOne = isOnlyOneItem(data.displayType?.toLowerCase() === 'treelist', data.items); + if (onlyOne) { + selectedItems.value = [onlyOne]; + updateModelValue(); + queryState.value = ''; + document.body.classList.remove('lookup-modal-open'); + return; + } + openDialog(); + } else { + lookupState['action'] = searchText ? 'search': 'list'; + lookupState.items = data.items; + } }); } } @@ -86,7 +101,13 @@ export function useInputChange(props: LookupProps, context: any, options: Lookup return; } - if (props.uri) { + if (props.openType !== 'Modal' && !isPopuped.value) { + openDialog(); + searchValueChanged.value = false; + return; + } + + if (lookupOptions.uri) { queryDataBySearchKeys(searchText); } else { context.emit('textChanged', {value: searchText, type: isBlur ? 'blur': 'enter'}); diff --git a/packages/ui-vue/components/lookup/src/composition/use-load-data.ts b/packages/ui-vue/components/lookup/src/composition/use-load-data.ts index 0d995031a3e612ed326c6873796e795f03248fc4..d2d74ed71fe2822189cf451bac0838136a7d95fb 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-load-data.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-load-data.ts @@ -5,6 +5,7 @@ import { LookupStates } from "./use-state"; export function useLoadData(props: LookupProps, lookupStates: LookupStates) { const { getSelectionIds } = useSelections(props, lookupStates); + const { lookupOptions } = lookupStates; function loadData(gridRef: any, data: any) { gridRef?.updateDataSource(data || []); @@ -13,7 +14,11 @@ export function useLoadData(props: LookupProps, lookupStates: LookupStates) { function selectItemByIds(gridRef: any) { const itemIds = getSelectionIds(); if (itemIds && itemIds.length) { - gridRef?.selectItemByIds(itemIds); + if(lookupOptions.multiSelect) { + gridRef?.selectItemByIds(itemIds); + return; + } + gridRef?.activeRowById(itemIds[0]); } } diff --git a/packages/ui-vue/components/lookup/src/composition/use-locales.ts b/packages/ui-vue/components/lookup/src/composition/use-locales.ts new file mode 100644 index 0000000000000000000000000000000000000000..daa33bd659b61cc9b80b892d6af2040c564c73c0 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/composition/use-locales.ts @@ -0,0 +1,98 @@ +import { LookupProps } from "../lookup.props"; +import { useI18n } from 'vue-i18n' + +export const LOOKUP_LOCALES = Symbol('FARRIS_LOOKUP_LOCALES'); + +export interface LookupLocaleData { + searchColumns: { + allColumns: string, + anyFields: string + }, + placeholder: string, + tabs: { + datalist: string, + favorite: string, + }, + tree: { + includechildren: string, + both: string, + up: string, + down: string, + disable: string + }, + buttons: { + ok: string, + cancel: string, + clear: string, + addFav: string, + removeFav: string, + selectionTotal: (total: number) => string, + }, + messages: { + clearSelections: string, + addedFavSuccess: string, + cancelFavSuccess: string, + mustChoosAdatarow: string, + mustWriteSomething: string, + } +} + +export function useLookupLocales(props: LookupProps): LookupLocaleData { + const { t: getLocaleValue } = useI18n(); + + function getValue(propertyValue, defaultValue, localeKey) { + if (propertyValue === defaultValue) { + return getLocaleValue(localeKey); + } + return propertyValue; + } + + // placeholder + const placeholder = getValue(props.placeholder, '请选择', 'lookup.placeholder'); + + const tabs = { + datalist: getLocaleValue('lookup.datalist') || '数据列表', + favorite: getLocaleValue('lookup.favorites') || '收藏夹', + }; + + const buttons = { + ok: getLocaleValue('lookup.okText') || '确定', + cancel: getLocaleValue('lookup.cancelText') || '取消', + clear: getLocaleValue('lookup.selectedInfo.clear') || '取消已选', + addFav: getLocaleValue('lookup.favoriteInfo.addFavTitle') || '收藏', + removeFav: getLocaleValue('lookup.favoriteInfo.cancelFavTitle') || '取消收藏', + selectionTotal: (total) => { + return getLocaleValue('lookup.selectedInfo.total', [total]); + } + }; + + const messages ={ + clearSelections: getLocaleValue('lookup.selectedInfo.confirm') || '您确认要取消所有选中记录吗?', + addedFavSuccess: getLocaleValue('lookup.favoriteInfo.addFav') || '已添加到收藏夹。', + cancelFavSuccess: getLocaleValue('lookup.favoriteInfo.cancelFav') || '已从收藏夹中移除。', + mustChoosAdatarow: getLocaleValue('lookup.mustChoosAdatarow') || '请选择一条数据。', + mustWriteSomething: getLocaleValue('lookup.mustWriteSomething') || '请输入关键字后查询。', + }; + + const tree = { + includechildren: getLocaleValue('lookup.includechildren') || '包含下级', + both: getLocaleValue('lookup.cascade.enable') || '同步选择', + up: getLocaleValue('lookup.cascade.up') ||'包含上级', + down: getLocaleValue('lookup.cascade.down') ||'包含下级', + disable: getLocaleValue('lookup.cascade.disable') ||'仅选择自身' + }; + + const searchColumns= { + allColumns: getLocaleValue('lookup.allColumns') || '所有列', + anyFields: getLocaleValue('lookup.anyFields') || '全部' + }; + + return { + searchColumns, + placeholder, + tree, + messages, + tabs, + buttons + }; +} diff --git a/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx b/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx index d2cb15df47ef6811e1ede38c36cb0ce284b7d0ca..a0317fa7232afca0264630f6aef3ecc1310b9cd0 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-navigation.tsx @@ -9,19 +9,28 @@ import { useSearchbar } from "./use-search-bar"; import { usePageInfo } from "./use-pageinfo"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "./use-http"; -import { LOOKUP_TREEROW_OPTIONS } from "./use-treegrid-row-options"; +import { LOOKUP_TREE_HIERARCHY, LOOKUP_TREEROW_OPTIONS } from "./use-treegrid-row-options"; +import { LookupHttpResult } from "./types"; +import { useTreeNode } from "./use-treenode"; +import { LOOKUP_LOCALES, LookupLocaleData } from "./use-locales"; export function useNavigation(props: LookupProps, context) { const treeRowOptions = inject(LOOKUP_TREEROW_OPTIONS); const useHttpComposition: UseHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; - const { loadData, lookupStates, expandAllNodes, getFieldValue } = useHttpComposition; - const { navigationState, pageInfoState, searchState } = lookupStates; - - const { renderSearchBar } = useSearchbar(props, true, lookupStates); - const { checkPaination, navIsList, navIsTreeList } = useCheckProps(props, lookupStates); + const lookupLocales = inject(LOOKUP_LOCALES) as LookupLocaleData; + + const { loadData, lookupStates, expandAllNodes, getFieldValue, getData } = useHttpComposition; + const { navigationState, pageInfoState, searchState, lookupOptions } = lookupStates; + + const { renderSearchBar } = useSearchbar(props, true, lookupStates, lookupLocales); + const useCheckPropsComposition = useCheckProps(props, lookupStates); + const { checkPaination, navIsList, navIsTreeList } = useCheckPropsComposition; const { updatePageInfo } = usePageInfo(props, pageInfoState); const selectedItems = ref([]); + const { buildGetChildrenQueryParams, loadChildNodes } = useTreeNode(lookupStates, useCheckPropsComposition); + + if (navIsList()) { const paginationOptions = checkPaination(props.navigation?.pagination); updatePageInfo(paginationOptions, true); @@ -57,7 +66,7 @@ export function useNavigation(props: LookupProps, context) { const value: any = { total }; if (pageInfo) { const { pageIndex: index, pageSize: size, enablePager: enable, pageList: sizeLimits } = pageInfo; - Object.assign(value, { index, size, enable, sizeLimits }); + Object.assign(value, { index, size, enable }); } updatePageInfo(value, true); leftDatagridRef.value?.updateDataSource(items); @@ -102,7 +111,7 @@ export function useNavigation(props: LookupProps, context) { } watch(() => searchState.navigation, (newSearchInfo) => { - if (props.uri) { + if (lookupOptions.uri) { updatePageInfo({ index: 1 }, true); httpRequest(); return; @@ -117,7 +126,7 @@ export function useNavigation(props: LookupProps, context) { } const params = { size: pageSize, index }; updatePageInfo(params, true); - if (props.uri) { + if (lookupOptions.uri) { httpRequest(); return; } @@ -125,7 +134,18 @@ export function useNavigation(props: LookupProps, context) { context.emit("pageSizeChanged", params); } - const hierarchy = { collapseField: 'collapse' }; + function loadChildren(treeNode: any) { + if (leftTreegridRef.value.hasChildren(treeNode)) { + return Promise.resolve(); + } + + // GET CHILDREN DATA + const parentId = treeNode.raw.id; + const params = buildGetChildrenQueryParams(treeNode, searchState.navigation as any, true); + return getData(params).then((result: LookupHttpResult) => { + loadChildNodes(result?.items ?? [], parentId, leftTreegridRef.value); + }); + } function renderLeftTreeGrid() { return ; + hierarchy={LOOKUP_TREE_HIERARCHY} + onSelectionChange={onTreegridSelectionChange} + loadData={loadChildren}>; } function renderLeftDataGrid() { diff --git a/packages/ui-vue/components/lookup/src/composition/use-popup.tsx b/packages/ui-vue/components/lookup/src/composition/use-popup.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0de0f25b4139cf5f174eff8b52dbaca9490def97 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/composition/use-popup.tsx @@ -0,0 +1,104 @@ +import { LookupProps } from "../lookup.props"; +import PopupContainer from '../components/popup-container.component'; +import { computed, provide, Ref, watch } from "vue"; +import { LOOKUP_SELECTIONS_MANAGER } from "./use-selections"; +import { LOOKUP_HTTP_COMPOSITION } from "./use-http"; +import { LOOKUP_USER_DATA_SERVICE } from "./use-user-data"; +import { LOOKUP_TREEROW_OPTIONS, useTreeRowOptions } from "./use-treegrid-row-options"; + +export function usePopup(props: LookupProps, context, elementRef: Ref, { + useHttpComposition, selectionsManager, userDataService, updateModelValue, lookupLocales, destroyed, + setModelAndIdValue, runDictPickedAsync +}) { + + provide(LOOKUP_USER_DATA_SERVICE, userDataService); + provide(LOOKUP_HTTP_COMPOSITION, useHttpComposition); + provide(LOOKUP_SELECTIONS_MANAGER, selectionsManager); + + const { selectionState: selectedItems, queryState, lookupOptions } = useHttpComposition.lookupStates; + const treeRowOptions = useTreeRowOptions(lookupOptions, false); + provide(LOOKUP_TREEROW_OPTIONS, treeRowOptions); + + const {idValues} = useHttpComposition; + + const isPopuped = computed(() => { + const popoverInstance = elementRef.value?.popoverRef; + if (popoverInstance) { + return popoverInstance.shown; + } + return false; + }); + + function hidePopup() { + elementRef.value?.hidePopup(); + } + + if (props.openType === 'Popup') { + watch(() => selectedItems.value, (newValue) => { + if (isPopuped.value) { + if (props.multiSelect){ + setModelAndIdValue(); + } + } + }); + } + + const getPopupSize = () => { + const { width, height } = props.dialog; + return { + width: width || 460, + height: height || 380 + }; + }; + + const popoverInstance = computed(() => { + return elementRef.value?.popoverRef; + }); + + async function onHidePopup() { + // const selectedIds = selectedItems.value.map((item: any) => { + // return item[lookupOptions.idField]; + // }).join(props.separator); + + if (!lookupOptions.multiSelect) { + await updateModelValue(); + } else { + await runDictPickedAsync(); + } + + isPopuped.value && hidePopup(); + queryState.value = ''; + destroyed(); + } + + function showPopup() { + if (!isPopuped.value) { + elementRef.value?.showPopup(); + } + } + + function onSelectionsChanged() { + if (!lookupOptions.multiSelect) { + hidePopup(); + } + } + + function renderPopup() { + const { width, height } = getPopupSize(); + return ; + } + + return { + popoverInstance, + isPopuped, + renderPopup, + showPopup + }; +} diff --git a/packages/ui-vue/components/lookup/src/composition/use-search-bar.tsx b/packages/ui-vue/components/lookup/src/composition/use-search-bar.tsx index b4fab0eb7996cd6f7932e7d155c6eb12f12c9f88..8c968c11fcc40ba6735a2572c1d7ccd227bae144 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-search-bar.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-search-bar.tsx @@ -4,9 +4,10 @@ import LookupSearchBar from "../components/search-bar/search-bar.component"; import { LookupProps } from "../lookup.props"; import { useSearchFields } from "./use-search-fields"; import { LookupStates } from "./use-state"; +import { LocaleService } from "@farris/ui-vue/components/locale"; +import { LookupLocaleData } from "./use-locales"; - -export function useSearchbar(props: LookupProps, isNavigation: boolean, lookupStates: LookupStates) { +export function useSearchbar(props: LookupProps, isNavigation: boolean, lookupStates: LookupStates, lookupLocales: LookupLocaleData) { const { lookupState, searchState, navigationState } = lookupStates; const {searchFieldItems, navSearchFieldItems} = useSearchFields(props, lookupState, navigationState); @@ -18,7 +19,7 @@ export function useSearchbar(props: LookupProps, isNavigation: boolean, lookupSt let newSearchFields = searchFields; if (props.searchAnyField) { - newSearchFields = [{label: '所有列', value: '*'}].concat(searchFields); + newSearchFields = [{label:lookupLocales.searchColumns.allColumns || '所有列', value: '*'}].concat(searchFields); } if (isNavigation) { @@ -56,6 +57,7 @@ export function useSearchbar(props: LookupProps, isNavigation: boolean, lookupSt if (props.enableSearchBar && searchFields.value.length > 0) { return
void; selectTreeNodes: (treeNodes: any[]) => void; unSelectTreeNode: (treeNode: VisualData) => void; + unSelectTreeNodes: (treeNodeIds: any[]) => void; + isSelected: (id: any) => boolean; + getPrimaryKey: (data: any) => any; } export const LOOKUP_SELECTIONS_MANAGER = Symbol('LOOKUP_SELECTIONS_MANAGER'); @@ -22,6 +25,10 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : return lookupOptions.idField; }); + function getPrimaryKey(data: any) { + return data[idField.value]; + } + function getSelectionsMap() { let selectionMap = new Map(); if (selectionState.value && selectionState.value.length) { @@ -63,6 +70,11 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : unSelectDataId.value = items; } + function isSelected(id: any) { + const selectionMap = getSelectionsMap(); + return selectionMap.has(id); + } + function updateSelections(data: any, checked = true) { if (!data) { return; @@ -85,14 +97,16 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : return; } + const itemIds = items.map(item => item[idField.value]); + if (!lookupOptions.multiSelect) { + if (itemIds.length && isSelected(itemIds[0])){ + return; + } clearSelections(); } - const itemIds = items.map(item => item[idField.value]); - const selectionMap = getSelectionsMap(); - if (checked) { const itemsMap = new Map(items.map<[string, any]>(n => [n[idField.value], n])); // Add new items and deduplicate @@ -129,6 +143,13 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : } } + function unSelectTreeNodes(treeNodeIds: any[]) { + if (!treeNodeIds || !treeNodeIds.length) { + return; + } + unSelectionsByIds(treeNodeIds); + } + return { updateSelections, clearSelections, @@ -136,6 +157,9 @@ export function useSelections(props: LookupProps, lookupStates: LookupStates) : getSelections, unSelectionsByIds, selectTreeNodes, - unSelectTreeNode + unSelectTreeNode, + unSelectTreeNodes, + isSelected, + getPrimaryKey }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-state.ts b/packages/ui-vue/components/lookup/src/composition/use-state.ts index 34e5b7c87bdcfa8ecf898f9a2e3619bf126d2c93..da2c263ae284316c388ac4c3a30774047ca0320c 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-state.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-state.ts @@ -10,7 +10,8 @@ export interface LookupStates { searchState: Reactive; selectionState: Ref; unSelectDataId: Ref; - lookupOptions: Reactive> + lookupOptions: Reactive>; + searchValueChanged: Ref; } @@ -24,6 +25,8 @@ export function useLookupState(props: LookupProps): LookupStates { const unSelectDataId = ref(); const lookupOptions = reactive>({}); + const searchValueChanged = ref(false); + const canUpdateKeys = [ 'displayType', 'treeToList', @@ -41,7 +44,12 @@ export function useLookupState(props: LookupProps): LookupStates { 'treeInfo', 'navTreeInfo', 'onlySelectLeaf', - 'loadTreeDataType' + 'loadTreeDataType', + 'uri', + 'enableFullTree', + 'cascadeValue', + 'showCascadeControl', + 'enableCascade' ]; function initAllowSetOptions() { @@ -60,6 +68,7 @@ export function useLookupState(props: LookupProps): LookupStates { searchState, selectionState, unSelectDataId, - lookupOptions + lookupOptions, + searchValueChanged }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts b/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts index 328c3877d94bf660a1c07fdf6529323006a446ee..fdf3f605c0769a2547ce5f543f9cce8a7a1b3180 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-treegrid-row-options.ts @@ -2,6 +2,11 @@ import { VisualData } from "@farris/ui-vue/components/data-view"; export const LOOKUP_TREEROW_OPTIONS = Symbol('LOOKUP_TREEROW_OPTIONS'); +export const LOOKUP_TREE_HIERARCHY = { + cascadeOption: { autoCheckChildren: false, autoCheckParent: false }, + collapseField: 'collapse', + hasChildrenField: 'hasChildren' +}; export function useTreeRowOptions(lookupOptions: any, isNavigation = false) { @@ -12,7 +17,7 @@ export function useTreeRowOptions(lookupOptions: any, isNavigation = false) { const treeinfo = isNavigation ? lookupOptions.navTreeInfo : lookupOptions.treeInfo; - if (treeinfo.onlySelectLeaf) { + if (treeinfo?.onlySelectLeaf) { visualData.disabled = !visualData.raw.leaf; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx b/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx index d90982560276a3373d4a3f25e726bb15efdf6746..a684626249a7e468dcdb3d416a14f420b8c71046 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx +++ b/packages/ui-vue/components/lookup/src/composition/use-treegrid.tsx @@ -1,31 +1,37 @@ -import { inject, Ref, ref, SetupContext, watch } from "vue"; +import { computed, inject, Ref, ref, SetupContext, watch } from "vue"; import { VisualData } from "@farris/ui-vue/components/data-view"; import { FTreeGrid } from '@farris/ui-vue/components/tree-grid'; import { LookupProps } from "../lookup.props"; import { useCheckProps } from "./use-check-props"; -import { LOOKUP_ACTIVE_TAB, LookupHttpResult, LookupTabs } from "./types"; +import { CascadeEnum, LOOKUP_ACTIVE_TAB, LookupHttpResult, LookupTabs } from "./types"; import { LOOKUP_HTTP_COMPOSITION, UseHttpComposition } from "./use-http"; -import { LOOKUP_TREEROW_OPTIONS } from "./use-treegrid-row-options"; +import { LOOKUP_TREE_HIERARCHY, LOOKUP_TREEROW_OPTIONS, useTreeRowOptions } from "./use-treegrid-row-options"; import { useFavorite } from "./use-favorite"; import { useSyncSelect } from "./use-sync-select"; import { LOOKUP_SELECTIONS_MANAGER, LookupSelectionsManager } from "./use-selections"; import { useLoadData } from "./use-load-data"; +import { useTreeNode } from "./use-treenode"; +import { get } from "lodash-es"; +import { customData } from "./use-customdata"; + export function useTreegrid(props: LookupProps, context: SetupContext) { const currentTab = inject(LOOKUP_ACTIVE_TAB, ref(LookupTabs.dataList)); const useHttpComposition = inject(LOOKUP_HTTP_COMPOSITION) as UseHttpComposition; - const treeRowOptions = inject(LOOKUP_TREEROW_OPTIONS); - + const { lookupStates } = useHttpComposition; const { lookupState, searchState, lookupOptions } = lookupStates; + // const treeRowOptions = useTreeRowOptions(lookupOptions, false); + const treeRowOptions = inject(LOOKUP_TREEROW_OPTIONS) as any; const lookupSelectionsManager = inject(LOOKUP_SELECTIONS_MANAGER) as LookupSelectionsManager; - const { checkMultiSelect, checkColumnOptions } = useCheckProps(props, lookupStates); + const useCheckPropsComposition = useCheckProps(props, lookupStates); + const { checkMultiSelect, checkColumnOptions, isEnableCascadeCheck } = useCheckPropsComposition; const { setColumns } = useFavorite(props, lookupOptions.idField); - const { selectTreeNodes, unSelectTreeNode, updateSelections } = lookupSelectionsManager; + const { selectTreeNodes, unSelectTreeNode, unSelectTreeNodes, updateSelections, getSelectionIds } = lookupSelectionsManager; const selectionOptions = checkMultiSelect(); const columnOptions = checkColumnOptions(); @@ -34,66 +40,131 @@ export function useTreegrid(props: LookupProps, context: SetupContext) { const selectionTreeValues = ref([]); const { loadAndSelect } = useLoadData(props, lookupStates); - const { loadData, expandAllNodes } = useHttpComposition; + const { loadData, expandAllNodes, getData } = useHttpComposition; - function treeGridLoadData(items: any) { + const { flattenTreeNodes, getTreeNodeWithCascadeValue, buildGetChildrenQueryParams, + needGetAllChildNodes, buildGetAllChildrenQueryParams, loadChildNodes + } = useTreeNode(lookupStates, useCheckPropsComposition); - if (searchState.default?.value) { + function treeGridLoadData(items: any, isSearched = false) { + if (isSearched) { items = expandAllNodes(items); } - + lookupState.flattenTreeNodes = flattenTreeNodes(items || []); loadAndSelect(treegridRef.value, items || []); } useSyncSelect(props, treegridRef, lookupStates, currentTab); - watch([() => lookupState?.columns, () => lookupState?.items, + const unWatchLookupState = watch([() => lookupState?.columns, () => lookupState?.items, () => lookupState?.pageInfo, () => lookupState?.total, () => lookupState?.selectedData], ([newColumns, newItems, newPageInfo, newTotal, selectedData]) => { + if (treegridRef.value) { + if (selectedData && selectedData.length) { + updateSelections(selectedData); + } - if (selectedData && selectedData.length) { - updateSelections(selectedData); + newColumns && setColumns(treegridRef.value, newColumns, true); + treeGridLoadData(newItems || [], lookupState['action'] === 'search'); } - - newColumns && setColumns(treegridRef.value, newColumns, true); - treeGridLoadData(newItems); }); + function getTreeNodeIdsWithCascadeValue(nodeId: string, isChecked = true) { + const { cascadeValue } = lookupOptions; + if (isEnableCascadeCheck() && cascadeValue !== CascadeEnum.Disable) { + const selectedNodes = getTreeNodeWithCascadeValue(nodeId, cascadeValue, isChecked); + return selectedNodes.filter(node => node.selectable || node.selectable === undefined).map(node => node.id); + } + return []; + } + function onSelectionChange(items: any[]) { selectTreeNodes(items); + context.emit("selectionsChanged", { items }); } function onUnSelectItem(item: VisualData) { unSelectTreeNode(item); + const nodeId = get(item.raw, lookupOptions.idField); + const selectedNodeIds = getTreeNodeIdsWithCascadeValue(nodeId); + + if (needGetAllChildNodes(item.raw)) { + const params = buildGetAllChildrenQueryParams(item.raw); + getData(params).then((result: LookupHttpResult) => { + const children: any[] = result.items || []; + const items = children.filter(n => !n.addtional && (n.selectable || n.selectable === undefined)); + if (items && items.length) { + selectTreeNodes(items); + } + const childs = (items || []).map(node => node.id); + unSelectTreeNodes([nodeId, ...selectedNodeIds, ...childs]); + }); + } else { + unSelectTreeNodes([nodeId,...selectedNodeIds]); + } + } + + function onSelectedItem(item: VisualData) { + const nodeId = get(item.raw, lookupOptions.idField); + const selectedNodeIds = getTreeNodeIdsWithCascadeValue(nodeId); + if (selectedNodeIds.length) { + treegridRef.value.selectItemByIds([nodeId, ...selectedNodeIds]); + } + + if (needGetAllChildNodes(item.raw)) { + const params = buildGetAllChildrenQueryParams(item.raw); + getData(params).then((result: LookupHttpResult) => { + const children: any[] = result.items || []; + const items = children.filter(n => !n.addtional && (n.selectable || n.selectable === undefined)); + if (items && items.length) { + selectTreeNodes(items); + } + }); + } } function httpRequest(action = 'list') { loadData({ search: searchState.default, action }, (result: LookupHttpResult) => { - treeGridLoadData(result.items); + treeGridLoadData(result.items, action === 'search'); }); } - watch(() => searchState.default, (newSearchInfo) => { - if (props.uri) { + const unWatchSearchState = watch(() => searchState.default, (newSearchInfo) => { + if (lookupOptions.uri) { httpRequest('search'); return; } context.emit('search', newSearchInfo); }); + function unwatches() { + unWatchLookupState(); + unWatchSearchState(); + } - const hierarchy = { - cascadeOption: { autoCheckChildren: false, autoCheckParent: false }, - collapseField: 'collapse' - }; + watch(() => lookupOptions.displayType, (newDisplayType) => { + if (newDisplayType.toLowerCase() !== 'treelist') { + unwatches(); + } + }); + + function loadChildren(treeNode: VisualData) { + if (treegridRef.value.hasChildren(treeNode)) { + return Promise.resolve(); + } - function onExpandNode(node: any) { - // return console.log(node); + // GET CHILDREN DATA + const parentId = treeNode.raw.id; + const params = buildGetChildrenQueryParams(treeNode, searchState.default as any); + return getData(params).then((result: LookupHttpResult) => { + loadChildNodes(result?.items ?? [], parentId, treegridRef.value); + }); } function renderTreeGrid() { return ; + onSelectItem={onSelectedItem} + loadData={loadChildren}>; }; return { treegridRef, selectionTreeValues, - renderTreeGrid + renderTreeGrid, + unwatches }; } diff --git a/packages/ui-vue/components/lookup/src/composition/use-treenode.ts b/packages/ui-vue/components/lookup/src/composition/use-treenode.ts new file mode 100644 index 0000000000000000000000000000000000000000..f1f459c6df1141ec2518fa12f9816b425bcc2dde --- /dev/null +++ b/packages/ui-vue/components/lookup/src/composition/use-treenode.ts @@ -0,0 +1,197 @@ +import { get } from "lodash-es"; +import { LookupStates } from "./use-state"; +import { CascadeEnum, SearchInfo } from "./types"; +import { customData } from "./use-customdata"; +import { nextTick } from "vue"; + +export interface TreeNode { + id: string; + parent?: TreeNode; + children?: TreeNode[]; + data: any; + parents?: any[], + leaf?: boolean +} + + +export function useTreeNode(lookupStates: LookupStates, useCheckPropsComposition: any) { + + const { lookupState, lookupOptions } = lookupStates; + + function flattenTreeNodes(treeNodes: any[], parentNode?: TreeNode, parentIds?: any[]): any[] { + return treeNodes.reduce((result, item) => { + item.parents = []; + if (parentNode) { + const parentID = get(parentNode.data, lookupOptions.idField); + item.parent = parentNode; + item.parents = [...(parentIds || []), parentID]; + } + item.id = get(item, lookupOptions.idField); + result.push(item); + if (item.children?.length) { + result.push(...flattenTreeNodes(item.children, item, item.parents)); + } + return result; + }, [] as any[]); + } + + function findTreeNode(nodeId: string) { + return lookupState?.flattenTreeNodes?.find(n => n.id === nodeId); + } + + function getChildren(parentNodeId: string) { + return lookupState?.flattenTreeNodes?.filter(n => n.parents?.includes(parentNodeId)) ?? []; + } + + + function getParents(nodeId: string) { + if (!nodeId) { return []; } + + const treeNode = findTreeNode(nodeId); + return treeNode?.parents?.map(parentId => findTreeNode(parentId)) ?? []; + } + + function getParentsAndChildren(nodeId: string) { + if (!nodeId) { return []; } + + const treeNode = findTreeNode(nodeId); + const parents = treeNode?.parents?.map(parentId => findTreeNode(parentId)) ?? []; + const children = getChildren(nodeId); + return [...parents, ...children]; + } + + function getUnCheckedNodes(nodeId: string, onlyParents = true) { + const parents = getParents(nodeId).filter(parent => + !parent.children?.some(child => + lookupStates.selectionState.value?.some(item => + item[lookupOptions.idField] === child.id + ) + ) + ); + + const children = onlyParents ? [] : getChildren(nodeId); + return parents.concat(children); + } + + /** 依据级联方式获取指定节点的父节点或子节点 */ + function getTreeNodeWithCascadeValue(nodeId: string, cascadeValue: CascadeEnum, isChecked = true) { + const getter = { + down: () => getChildren(nodeId), + up: () => isChecked ? getParents(nodeId) : getUnCheckedNodes(nodeId), + both: () => isChecked ? getParentsAndChildren(nodeId) : getUnCheckedNodes(nodeId, false) + }; + return getter[cascadeValue]?.() ?? []; + } + + function buildGetChildrenQueryParams(treeNode: any, searchInfo: SearchInfo = {}, isNavigation = false) { + const { field = '*', value = ''} = searchInfo as any; + + const isSearchResult = !!value; + + const { layerType, dataField, pathField, layerField } = isNavigation ? lookupOptions.navTreeInfo: lookupOptions.treeInfo; + const isParentId = layerType === 'parentId'; + const search = { category: isNavigation? 'navchildren': 'children', searchField: field , searchValue: value, searchType: 'like'}; + if (isParentId) { + search['parentId'] = treeNode.raw.id; + } else { + const treeInfoData = treeNode.raw[dataField]; + search['parentPath'] = get(treeInfoData, pathField); + search['parentLayer'] = get(treeInfoData, layerField); + } + + const queryParam: any = { + searchValue: '', + customData: customData.value, + enableFullTree: lookupOptions.enableFullTree, + loadTreeDataType: lookupOptions.loadTreeDataType === 'all'? 'loadall': 'layerload', + }; + + if (isSearchResult) { + queryParam.enableFullTree = false; + queryParam.loadTreeDataType = 'layerload'; + if (isParentId) { + search.searchValue = ''; + search.searchField = '*'; + } + } + queryParam.searchValue = JSON.stringify(search); + + return queryParam; + }; + + function needGetAllChildNodes(node?: TreeNode, isNavigation = false) { + const { isEnableCascadeCheck } = useCheckPropsComposition; + const { layerType } = isNavigation ? lookupOptions.navTreeInfo: lookupOptions.treeInfo; + const isNeed = lookupOptions.loadTreeDataType !== 'all' && layerType === "pathcode" && + isEnableCascadeCheck() && (lookupOptions.cascadeValue === "both" || lookupOptions.cascadeValue === "down"); + return node ? node.data && !node.leaf && isNeed : isNeed; + } + + function buildGetAllChildrenQueryParams(node: TreeNode) { + const { dataField, pathField } = lookupOptions.treeInfo; + + const treeInfoData = node.data[dataField]; + const pathcode = get(treeInfoData, pathField); + + + const params = { + searchValue: JSON.stringify({ category: 'allChildren' }), + parentsIds: [pathcode], + customData: customData.value + }; + return params; + } + + function findDirectParent(nodeList: TreeNode[], parentIds: any[]): any { + let currentLevel: TreeNode[] = nodeList; + let parentNode: TreeNode | null = null; + + for (const id of parentIds) { + const foundNode = currentLevel.find(node => node.data.id === id); + if (!foundNode) { + return null; // 路径不存在,返回null + } + parentNode = foundNode; + currentLevel = foundNode.children || []; + } + + return parentNode; + } + + function loadChildNodes(childItems: TreeNode[], parentId: string, gridRef: any) { + if (!gridRef) {return;} + + const parentNode = findTreeNode(parentId); + const parents = [...(parentNode?.parents ?? []), parentId]; + const children = (childItems ?? []).map(item => ({ + ...item, + hasChildren: !item.leaf, + parents: [...parents] + })); + + const treeNode = findDirectParent(lookupState.flattenTreeNodes || [], parents); + if (treeNode) { + treeNode.children = [...children]; + lookupState.flattenTreeNodes?.push(...children); + } + + const { isDropdownMode } = useCheckPropsComposition; + nextTick(() => { + gridRef.addChildrenToNode(children, parentId); + if (isDropdownMode()) { + return; + } + const itemIds = lookupStates.selectionState.value?.map(item => item[lookupOptions.idField]) ?? []; + const selectedIds = children + .filter(child => itemIds.includes(child.id)) + .map(child => child.id); + + selectedIds.length && gridRef.selectItemByIds(selectedIds); + }); + } + + + return { flattenTreeNodes, getParents, getChildren, getParentsAndChildren, getTreeNodeWithCascadeValue, buildGetChildrenQueryParams, + needGetAllChildNodes, buildGetAllChildrenQueryParams, loadChildNodes + }; +} diff --git a/packages/ui-vue/components/lookup/src/composition/use-user-data.ts b/packages/ui-vue/components/lookup/src/composition/use-user-data.ts index d0eb50ecb07f1533f1eecfec371b54d957ec6ba4..325b244b4da880472add565c8bedbd5a77ef7c53 100644 --- a/packages/ui-vue/components/lookup/src/composition/use-user-data.ts +++ b/packages/ui-vue/components/lookup/src/composition/use-user-data.ts @@ -8,7 +8,7 @@ import { UseHttpComposition } from "./use-http"; export interface LookupUserData { pageSize?: number; tabIndex?: string; - cascadeStatus?: boolean; + cascadeValue?: boolean; favorites?: Array; size?: Record; } @@ -36,7 +36,8 @@ export function useUserData(props: LookupProps, useHttpComposition: UseHttpCompo const {lookupOptions} = lookupStates; const dataKey = computed(() => { - return encrypt(lookupOptions.userDataKey || props.id); + const key = lookupOptions.userDataKey || props.id; + return key ? encrypt(key): ''; }); const userDataState = reactive({ key: dataKey.value, data: {} }); @@ -98,7 +99,7 @@ export function useUserData(props: LookupProps, useHttpComposition: UseHttpCompo const loadingInstance = loadingService?.show(); const loader = lookupRequest(); if (loader) { - return loader(props.uri, params).then((res: Partial) => { + return loader(lookupOptions.uri, params).then((res: Partial) => { loadingInstance.value?.close(); userDataState.favoriteItems = res && res.items || []; return res; diff --git a/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx b/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx index 87220d0c39fcc304881d861e50390ec23e1cb8fb..31b602eb4414f3d0605b00fbef8d9019129b923f 100644 --- a/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx +++ b/packages/ui-vue/components/lookup/src/designer/lookup.design.component.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { defineComponent, inject, onMounted, Ref, ref, SetupContext } from 'vue'; +import { computed, defineComponent, inject, onMounted, Ref, ref, SetupContext } from 'vue'; import FButtonEditDesign from '@farris/ui-vue/components/button-edit/designer'; import { useDesignerComponent, DesignerItemContext } from '@farris/ui-vue/components/designer-canvas'; import { lookupProps, LookupProps } from '../lookup.props'; @@ -30,6 +30,15 @@ export default defineComponent({ const designerRulesComposition = useLookupDesignerRules(designItemContext, designerHostService); const componentInstance = useDesignerComponent(elementRef, designItemContext, designerRulesComposition); + const modalIcon = ''; + const popupIcon = ''; + const openType = computed(() => { + return props.openType === 'Popup' ? 'Popup' : 'Modal'; + }); + const buttonIcon = computed(() => { + return openType.value === 'Popup' ? popupIcon : modalIcon; + }); + onMounted(() => { elementRef.value.componentInstance = componentInstance; }); @@ -41,6 +50,7 @@ export default defineComponent({
{0}", + "clear": "Cancel Selected", + "remove": "Delete ({0})", + "confirm": "Are you sure you want to cancel all selected records?" + }, + "clearAllConditions": "Clear All Conditions", + "anyFields": "All" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/lookup/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/lookup/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..a1a6ee1867dc311dbf689822a343916bbbacf6d4 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/locales/ui/zh-CHS.json @@ -0,0 +1,57 @@ +{ + "lookup": { + "placeholder": "请选择", + "favorites": "收藏夹", + "selected": "已选数据", + "okText": "确定", + "cancelText": "取消", + "allColumns": "所有列", + "datalist": "数据列表", + "mustWriteSomething": "请输入关键字后查询。", + "mustChoosAdatarow": "请选择一条记录!", + "tipText": "您要找的是不是这些?", + "cascade": { + "enable": "同步选择", + "disable": "仅选择自身", + "up": "包含上级", + "down": "包含下级" + }, + "includechildren":"包含下级", + "favoriteInfo": { + "addFav": "已添加到收藏夹。", + "cancelFav": "已从收藏夹中移除。", + "addFavTitle": "收藏", + "cancelFavTitle": "取消收藏" + }, + "getAllChilds": "获取所有子级数据", + "contextMenu": { + "checkChildNodes": "勾选下级数据", + "uncheckChildNodes": "取消勾选下级数据", + "expandall": "全部展开", + "collapseall": "全部收起", + "expandByLayer": "按层级展开", + "expand1": "展开 1 级", + "expand2": "展开 2 级", + "expand3": "展开 3 级", + "expand4": "展开 4 级", + "expand5": "展开 5 级", + "expand6": "展开 6 级", + "expand7": "展开 7 级", + "expand8": "展开 8 级", + "expand9": "展开 9 级" + }, + "quick": { + "notfind": "未找到搜索内容", + "more": "显示更多" + }, + "configError": "帮助显示列未配置,请检查是否已正确配置帮助数据源! ", + "selectedInfo": { + "total": "已选 {0} 条", + "clear": "取消已选", + "remove": "移除 ({0})", + "confirm": "您确认要取消所有选中记录吗?" + }, + "clearAllConditions": "清除所有查询条件", + "anyFields": "全部" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/lookup/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/lookup/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..04f898894952e4c0f4eae7d134748f4a66d62265 --- /dev/null +++ b/packages/ui-vue/components/lookup/src/locales/ui/zh-CHT.json @@ -0,0 +1,57 @@ +{ + "lookup": { + "placeholder": "請選擇", + "favorites": "收藏夾", + "selected": "已選數據", + "okText": "確定", + "cancelText": "取消", + "allColumns": "所有列", + "datalist": "數據列錶", + "mustWriteSomething": "請輸入關鍵字後查詢。", + "mustChoosAdatarow": "請選擇一條記錄!", + "tipText": "您要找的是不是這些?", + "cascade": { + "enable": "同步選擇", + "disable": "僅選擇自身", + "up": "包含上級", + "down": "包含下級" + }, + "includechildren": "包含下級", + "favoriteInfo": { + "addFav": "已添加到收藏夾。", + "cancelFav": "已從收藏夾中移除。", + "addFavTitle": "收藏", + "cancelFavTitle": "取消收藏" + }, + "getAllChilds": "獲取所有子級數據", + "contextMenu": { + "checkChildNodes": "勾選下級數據", + "uncheckChildNodes": "取消勾選下級數據", + "expandall": "全部展開", + "collapseall": "全部收起", + "expandByLayer": "按層級展開", + "expand1": "展開 1 級", + "expand2": "展開 2 級", + "expand3": "展開 3 級", + "expand4": "展開 4 級", + "expand5": "展開 5 級", + "expand6": "展開 6 級", + "expand7": "展開 7 級", + "expand8": "展開 8 級", + "expand9": "展開 9 級" + }, + "quick": { + "notfind": "未找到搜索內容", + "more": "顯示更多" + }, + "configError": "幫助顯示列未配置,請檢查是否已正確配置幫助數據源!", + "selectedInfo": { + "total": "已選 {0} 條", + "clear": "取消已選", + "remove": "移除 ({0})", + "confirm": "您確認要取消所有選中記錄嗎?" + }, + "clearAllConditions": "清除所有查詢條件", + "anyFields": "全部" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/lookup/src/lookup.component.tsx b/packages/ui-vue/components/lookup/src/lookup.component.tsx index 044401f3ec774b031d4ebd9216134991ef16a042..55372a5d7667a7fe2e99aff5e3dc80206f0ecac9 100644 --- a/packages/ui-vue/components/lookup/src/lookup.component.tsx +++ b/packages/ui-vue/components/lookup/src/lookup.component.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { computed, defineComponent, inject, onMounted, onUnmounted, reactive, ref, watch} from 'vue'; +import { computed, defineComponent, inject, onMounted, onUnmounted, reactive, ref, watch } from 'vue'; import { F_MODAL_SERVICE_TOKEN, FModalService } from '@farris/ui-vue/components/modal'; import { FButtonEdit } from '@farris/ui-vue/components/button-edit'; @@ -21,6 +21,7 @@ import { FButtonEdit } from '@farris/ui-vue/components/button-edit'; import LookupContainer from './components/lookup-container.component'; import LookupFavorite from './components/favorite/lookup-favorite.component'; import ModalContainer from './components/modal-container.component'; + import { lookupProps, LookupProps } from './lookup.props'; import { useDialog } from './composition/use-dialog'; import { LookupDisplayType, PagerChangeParams, SearchParams } from './composition/types'; @@ -31,52 +32,85 @@ import { useHttp } from './composition/use-http'; import { useUserData } from './composition/use-user-data'; import { useSelections } from './composition/use-selections'; import { useContext } from './composition/use-context'; +import { usePopup } from './composition/use-popup'; +import { debounce } from 'lodash-es'; +import { useLookupLocales } from './composition/use-locales'; export default defineComponent({ name: 'FLookup', props: lookupProps, - emits: ['update:modelValue', 'update:idValue','search', 'navSelectionsChanged', 'pageIndexChanged', 'pageSizeChanged', + emits: ['update:modelValue', 'update:idValue', 'search', 'navSelectionsChanged', 'pageIndexChanged', 'pageSizeChanged', 'resize', 'dialogClosed', 'textChanged', 'clear', 'update:dataMapping'], setup(props: LookupProps, context) { - const buttonIcon = ''; + const modalIcon = ''; + const popupIcon = ''; + const popupOpenIcon = ''; + const elementRef = ref(); const modelValue = ref(props.modelValue); const modalTitle = ref(''); customData.value = props.customData; + + const openType = computed(() => { + return props.openType === 'Popup' ? 'Popup' : 'Modal'; + }); + + const buttonIcon = computed(() => { + return openType.value === 'Popup' ? popupIcon : modalIcon; + }); + const lookupLocales = useLookupLocales(props); const useHttpComposition = useHttp(props, context); - const { lookupOptions, selectionState } = useHttpComposition.lookupStates; + const { lookupOptions, selectionState, searchValueChanged } = useHttpComposition.lookupStates; const userDataService = useUserData(props, useHttpComposition); const showSelections = computed(() => { return props.showSelections && lookupOptions.multiSelect; }); - + const editable = computed(() => { + return props.editable && (openType.value === 'Modal' || !lookupOptions.multiSelect); + }); + + const showIncludeChildNodes = computed(() => { return props.showIncludeChildNodes && props.showNavigation && !lookupOptions.navTreeToList; }); const selectionsManager = useSelections(props, useHttpComposition.lookupStates); + const { dictPicked } = useLookupCallBack(props); + const { modalOptions, beforeOpenDialog, dialogSize, customData: customQueryData, + updateModelValue, cancelDialog, submitDialog, destroyed, + setModelAndIdValue, runDictPickedAsync + } = useDialog(props, context as any, selectionState, { + dictPicked, modelValue, + buttonEditInstance: elementRef, + userDataService, + useHttp: useHttpComposition, + lookupLocales + }); + const usePopupComposition = usePopup(props, context, elementRef, { + useHttpComposition, selectionsManager, userDataService, updateModelValue, lookupLocales, destroyed, + setModelAndIdValue, runDictPickedAsync + }); + const { renderPopup, showPopup, isPopuped } = usePopupComposition; + function openDialog() { + if (props.openType === 'Popup') { + showPopup(); + return; + } elementRef.value && !elementRef.value.getModal() && elementRef.value.openDialog(); } - const { dictPicked } = useLookupCallBack(props); - const { modalOptions, beforeOpenDialog, dialogSize, customData: customQueryData, updateModelValue, - cancelDialog, submitDialog - } = - useDialog(props, context as any, selectionState, { - dictPicked, modelValue, - buttonEditInstance: elementRef, - userDataService, - useHttp: useHttpComposition + const { onInputBlur, onEnterKeyDown, isClear } = useInputChange(props, context, + { + beforeOpenDialog, updateModelValue, selectedItems: selectionState, openDialog, + modelValue, useHttpComposition, lookupOptions, usePopupComposition, isPopuped }); - const { onInputBlur, onEnterKeyDown, isClear } = useInputChange(props, context, - { beforeOpenDialog, updateModelValue, selectedItems: selectionState, openDialog, modelValue, useHttpComposition, lookupOptions }); const modalConfigs = reactive(modalOptions); @@ -86,7 +120,7 @@ export default defineComponent({ customData.value = newData; }); - watch(() => props.modelValue, (newValue) =>{ + watch(() => props.modelValue, (newValue) => { modelValue.value = newValue; }); @@ -108,7 +142,7 @@ export default defineComponent({ onUnmounted(() => { useContext(props); }); - + watch(() => dialogSize.value, (newSizeValue) => { context.emit('resize', newSizeValue); }); @@ -117,7 +151,7 @@ export default defineComponent({ context.emit('update:modelValue', ''); updateIdValue(''); - const clearParams = {items: null, mappingFields: lookupOptions.mappingFields}; + const clearParams = { items: null, mappingFields: lookupOptions.mappingFields }; context.emit('clear', clearParams); useContext(props, clearParams); } @@ -172,11 +206,11 @@ export default defineComponent({ const isMaximized = computed(() => { const modalInstance = modalService?.getCurrentModal(); - return modalInstance?.isMaximized; + return modalInstance?.isMaximized; }); const showCascadeControl = computed(() => { - return lookupOptions.multiSelect && props.showCascadeControl && props.enableCascade && + return lookupOptions.multiSelect && lookupOptions.showCascadeControl && lookupOptions.enableCascade && LookupDisplayType.Tree === lookupOptions.displayType.toUpperCase() && !lookupOptions.treeToList; }); @@ -188,13 +222,32 @@ export default defineComponent({ }); function updateLookupOptions(options: Record) { - for(const key in lookupOptions) { + for (const key in lookupOptions) { if (options[key] !== undefined) { lookupOptions[key] = options[key]; } } } + const handleChangeDebounce = debounce(($event) => { + onInputBlur($event); + }, 200); + + function onValueChange($event: any) { + let searchText = ($event.target as HTMLInputElement)?.value; + if (searchText !== '') { + searchText = searchText.trim(); + } + searchValueChanged.value = $event.target['_value'] !== searchText; + if (openType.value === 'Popup' && searchValueChanged.value) { + handleChangeDebounce($event); + } + } + + function onBeforeClearValue() { + searchValueChanged.value = false; + } + context.expose({ openDialog, updateIdValue, @@ -212,25 +265,30 @@ export default defineComponent({ v-model={modelValue.value} disable={props.disable} readonly={props.readonly} - editable={props.editable} + editable={editable.value} inputType={"text"} enableClear={props.enableClear} - buttonContent={buttonIcon} - buttonBehavior={"Modal"} + buttonContent={buttonIcon.value} + buttonBehavior={openType.value} modalOptions={modalConfigs} onClear={onClear} beforeOpen={beforeOpenDialog} onBlur={onInputBlur} onKeydown={onEnterKeyDown} - placeholder={props.placeholder} + placeholder={lookupLocales.placeholder} + keepWidthWithReference={false} + popupOnClick={!props.editable && openType.value === 'Popup'} + onInput={onValueChange} enableTitle={props.enableTitle} + placement={'auto'} + onBeforeClearValue={onBeforeClearValue} > - onMaximizeModal($event)} - useHttpComposition= {useHttpComposition} userDataService={userDataService} - useDialog={{cancelDialog, submitDialog}} showSelectedList={showSelections.value} - selectionsManager={ selectionsManager } showIncludeChildNodes={{show: showIncludeChildNodes.value, value: props.includeChildNodesValue}} - showCascadeControl={showCascadeControl.value} isMaximized={isMaximized.value}> + {openType.value === 'Modal' && onMaximizeModal($event)} + useHttpComposition={useHttpComposition} userDataService={userDataService} + useDialog={{ cancelDialog, submitDialog }} showSelectedList={showSelections.value} + selectionsManager={selectionsManager} showIncludeChildNodes={{ show: showIncludeChildNodes.value, value: props.includeChildNodesValue }} + showCascadeControl={showCascadeControl.value} isMaximized={isMaximized.value} locales={lookupLocales} id={props.id}> {{ default: () => ( ), fav: () => ( - ) }} - + } + {openType.value === 'Popup' && renderPopup()} ); }; diff --git a/packages/ui-vue/components/lookup/src/lookup.props.ts b/packages/ui-vue/components/lookup/src/lookup.props.ts index 7f7f943281fdd2db04f168ab050af0082ee1b463..9f3877e77f968393cf7ee3f9a73881dbe1172b6a 100644 --- a/packages/ui-vue/components/lookup/src/lookup.props.ts +++ b/packages/ui-vue/components/lookup/src/lookup.props.ts @@ -10,7 +10,8 @@ import { BeforeOpenDialogFunction, BeforeSubmitFunction, CallBackFunction, LoadT NavigationOptions, SearchField, TextChangedType, BeforeLoadData, CascadeOptions, - LookupDialogOptions} from "./composition/types"; + LookupDialogOptions, + DEFAULT_PAGINATION_OPTIONS} from "./composition/types"; import { createLookupCallbackResolver } from "./schema/callback-resolvers"; export const lookupProps = { @@ -36,16 +37,7 @@ export const lookupProps = { multiSelect: {type: Boolean, default: false}, showSelections: {type: Boolean, default: true}, separator: {type: String, default: ','}, - pagination: {type: Object as PropType, default: { - enable: false, - showLimits: true, - sizeLimits: [10, 20, 30,50, 100], - size: 20, - index: 1, - total: 0, - mode: 'server', - showGoto: false - }}, + pagination: {type: Object as PropType, default: DEFAULT_PAGINATION_OPTIONS}, enableClear: {type: Boolean, default: true}, inputType: {type: String, default: 'text'}, idValue: {type: String as PropType, default: ''}, @@ -64,7 +56,7 @@ export const lookupProps = { enableMultiFieldSearch: { type: Boolean, default: false }, treeToList: { type: Boolean, default: false }, navTreeToList: { type: Boolean, default: false }, - loadTreeDataType:{ type: String as PropType, default: LoadTreeDataType.all}, + loadTreeDataType:{ type: String as PropType, default: LoadTreeDataType.default }, enableToSelect: { type: Boolean, default: true }, customData: { type: Object, default: null }, modelValue: { type: String, default: '' }, @@ -78,16 +70,17 @@ export const lookupProps = { onlySelectLeaf: { type: Boolean, default: false }, enableFullTree: { type: Boolean, default: true }, enableCascade: { type: Boolean, default: false }, - showCascadeControl: { type: Boolean, default: false }, + showCascadeControl: { type: Boolean, default: true }, cascadeItems: { type: Object, default: { both: true, up: true, down: true, disable: true } }, - cascadeStatus: { type: String, default: 'both' }, + cascadeValue: { type: String, default: 'both' }, /** 0: 不展开; -1: 全部展开;>0: 展开到指定级数 */ expandLevel: { type: Number, default: 0 }, + openType: { type: String as PropType<'Modal' | 'Popup'>, default: 'Modal' }, enableTitle: { type: Boolean, default: false } } as Record; diff --git a/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts b/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts index aa394bfc1d78ca997758dd27f7991222955c9213..801eed9906c11f1825ce3a7d5986d530c706f95c 100644 --- a/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts +++ b/packages/ui-vue/components/lookup/src/property-config/converters/lookup-property.converter.ts @@ -26,17 +26,40 @@ export const lookupDefaultConverter = { } if (propertyKey === 'onlySelectLeaf') { - return schema.editor[propertyKey] == null ? false : schema.editor[propertyKey]; + return schema.editor[propertyKey] == null? false : schema.editor[propertyKey]; } if (propertyKey === 'loadTreeDataType') { - return schema.editor[propertyKey] || 'all'; + return schema.editor[propertyKey] || 'default'; } + if (propertyKey === 'placeholder') { + return schema.editor['placeholder'] || '请选择'; + } return schema.editor[propertyKey]; }, convertTo: (schema: Record, propertyKey: string, propertyValue: any) => { schema.editor[propertyKey] = propertyValue; + if(propertyKey === 'multiSelect' && !propertyValue) { + schema.editor['enableCascade'] = false; + } + + if(propertyKey === 'openType') { + if (propertyValue === 'Popup') { + schema.editor['showSelections'] = false; + schema.editor['enableCascade'] = false; + } else { + if (schema.editor.dialog) { + const { width } = schema.editor.dialog; + const { displayType } = schema.editor; + if (displayType && displayType.toUpperCase().indexOf('NAV') > -1) { + if(!width || width < 520) { + schema.editor.dialog.width = 960; + } + } + } + } + } } }; @@ -101,13 +124,22 @@ export const lookupDataSourceConverter = { if (treeInfo) { const { onlySelectLeaf, loadDataType } = treeInfo; - schema.editor.loadTreeDataType = loadDataType || 'all'; + schema.editor.loadTreeDataType = 'default'; schema.editor.onlySelectLeaf = onlySelectLeaf == null ? false : onlySelectLeaf; } if (!schema.editor.dataSource.uri) { schema.editor.dataSource.uri = generateSourceUri(schema.id); } + + if (schema.editor.dialog) { + const { width } = schema.editor.dialog; + if (displayType && displayType.toUpperCase().indexOf('NAV') > -1) { + if(!width || width < 520) { + schema.editor.dialog.width = 960; + } + } + } } } }; @@ -152,19 +184,29 @@ export const lookupDialogOptionsConverter = { return options[propertyKey] || DefaultDialogTitle; } - if (propertyKey === 'width') { - if (schema.editor.displayType?.toUpperCase().startsWith('NAV')) { - return options[propertyKey] || 960; + if (schema.editor.openType === 'Modal') { + if (propertyKey === 'width') { + if (schema.editor.displayType?.toUpperCase().startsWith('NAV')) { + return options[propertyKey] || 960; + } + return options[propertyKey] || 590; } - return options[propertyKey] || 590; - } - if (propertyKey === 'height') { - return options[propertyKey] || 620; - } + if (propertyKey === 'height') { + return options[propertyKey] || 620; + } - if (propertyKey === 'navigatorWidth') { - return options[propertyKey] || 320; + if (propertyKey === 'navigatorWidth') { + return options[propertyKey] || 320; + } + } else { + if (propertyKey === 'width') { + return options[propertyKey] || 460; + } + + if (propertyKey === 'height') { + return options[propertyKey] || 380; + } } if (propertyKey === 'draggable') { diff --git a/packages/ui-vue/components/lookup/src/property-config/external-lookup.property-config.ts b/packages/ui-vue/components/lookup/src/property-config/external-lookup.property-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..2b8e0387783a2764fdd78c5dca1a839bf71e12cf --- /dev/null +++ b/packages/ui-vue/components/lookup/src/property-config/external-lookup.property-config.ts @@ -0,0 +1,103 @@ +import { BaseControlProperty } from "../../../property-panel/src/composition/entity/base-property"; +import { ExternalLookupEvents } from "./lookup-events"; +import { LookupPropertyConfig } from "./lookup.property-config"; + +export class ExternalLookupPropertyConfig extends BaseControlProperty { + + private propertyConfigFilters = { + lookup: [ + 'dataSource', 'filterConditions', 'displayType', 'idField', 'multiSelect', 'showSelections', + 'enableSearchBar', 'searchAnyField', 'enableFavorite', 'enableUserData' + ], + pager: ['showLimits', 'sizeLimits', 'size'], + treeConfig: [ + 'cascadeStatus', 'enableCascade', 'enableFullTree', 'expandLevel', 'loadTreeDataType', + 'navTreeToList', 'onlySelectLeaf', 'showCascadeControl', 'treeToList' + ], + dialog: [ + 'title', 'width', 'height', 'showNavigation', 'navigatorWidth', 'resizeable', + 'rememberSize', 'enableEsc', 'showMaxButton', 'showCloseButton' + ], + }; + + public events = ExternalLookupEvents; + + constructor(componentId: string, designerHostService) { + super(componentId, designerHostService); + } + + /** + * 获取帮助的属性 + * @param propertyData + * @returns + */ + public getPropertyConfig(propertyData: any) { + const propertyConfigService = new LookupPropertyConfig('root-component', this.designerHostService); + + const lookup = propertyConfigService.getLookupConfig(propertyData); + const pager = propertyConfigService.getPageConfig(propertyData.editor); + const treeConfig = propertyConfigService.getTreePropConfig(propertyData.editor); + const dialog = propertyConfigService.getDialogPropertyConfig(propertyData); + const eventsEditor = this.getEventPropConfig(propertyData); + const propertyConfig = { + type: 'object', + categories: { + lookup, + treeConfig, + pager, + dialog + } + }; + + this.filterLookupPropertyConfig(propertyConfig); + + propertyConfig.categories['eventsEditor'] = eventsEditor; + return propertyConfig; + } + + /** + * 过滤帮助的属性配置 + * @param propertyConfig + */ + private filterLookupPropertyConfig(propertyConfig: any) { + Object.keys(propertyConfig.categories).forEach(categoryKey => { + const propertyFilters = this.propertyConfigFilters[categoryKey]; + const propertyCategory = propertyConfig.categories[categoryKey]; + + Object.keys(propertyCategory.properties).forEach(propertyKey => { + if (!propertyFilters.includes(propertyKey)) { + delete propertyCategory.properties[propertyKey]; + } + }); + }); + } + + + private getEventPropConfig(propertyData: any) { + const self: any = this; + const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, this.events); + const properties = self.createBaseEventProperty(initialData); + + return { + title: '事件', + hideTitle: true, + properties, + refreshPanelAfterChanged: true, + tabId: 'commands', + tabName: '交互', + setPropertyRelates(changeObject: any, newPropertyData: any) { + const propertyData = newPropertyData.editor; + + const parameters = changeObject.propertyValue; + delete propertyData[self.viewModelId]; + if (parameters) { + self.eventsEditorUtils.saveRelatedParameters(propertyData, self.viewModelId, parameters['events'], parameters); + } + self.events.forEach(event => { + newPropertyData[event.label] = propertyData[event.label]; + }); + } + }; + } + +} diff --git a/packages/ui-vue/components/lookup/src/property-config/lookup-events.ts b/packages/ui-vue/components/lookup/src/property-config/lookup-events.ts index 88e7ad2028ef2055993141e3fda8d0fcc9c19dd1..824e10d4217ac85ee9961b14bc390651893d8295 100644 --- a/packages/ui-vue/components/lookup/src/property-config/lookup-events.ts +++ b/packages/ui-vue/components/lookup/src/property-config/lookup-events.ts @@ -6,13 +6,35 @@ export const LookupEvents = [ { label: 'dictPicking', name: '帮助前事件' - }, { + }, + { label: 'beforeLoadData', name: '数据加载前事件' - }, { + }, + { label: 'beforeSelectData', name: '选择数据确认前事件' - }, { + }, + { + label: 'dictPicked', + name: '帮助后事件' + } +]; + +export const ExternalLookupEvents = [ + { + label: 'dictPicking', + name: '帮助前事件' + }, + { + label: 'beforeLoadData', + name: '数据加载前事件' + }, + { + label: 'beforeSelectData', + name: '选择数据确认前事件' + }, + { label: 'dictPicked', name: '帮助后事件' } diff --git a/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts b/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts index 1df2f5729da7b36c3b727ee86d03d8e8a2af7229..d380f9e9e469a5fccea0f7447d00aea5fcd9f7cf 100644 --- a/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts +++ b/packages/ui-vue/components/lookup/src/property-config/lookup.property-config.ts @@ -12,6 +12,7 @@ import { DesignerComponentInstance } from "@farris/ui-vue/components/designer-ca import { LookupEvents } from "./lookup-events"; import { cascadeItems } from "../composition/types"; import { ExpressionProperty } from "@farris/ui-vue/components/property-panel"; +import { FNotifyService } from "@farris/ui-vue/components/notify"; export const LookupSchemaRepositoryToken = Symbol('schema_repository_token'); @@ -41,13 +42,14 @@ export class LookupPropertyConfig extends InputBaseProperty { return new ExpressionProperty(this.formSchemaUtils).getExpressionConfig(propertyData, 'Field', suportTypes); } - getPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance | null) { - this.readonlyEditor = this.getPropertyEditorParams(propertyData, [], 'readonly'); - this.visibleEditor = this.getPropertyEditorParams(propertyData, [], 'visible'); - this.requiredEditor = this.getPropertyEditorParams(propertyData, ['Const', 'Variable', 'StateMachine', 'Expression'], 'required'); + getPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance | null, showPosition = 'Card') { + const editorTypes = propertyData.binding?.field ? [] : ['Const', 'Variable', 'StateMachine']; + this.readonlyEditor = this.getPropertyEditorParams(propertyData, editorTypes, 'readonly'); + this.visibleEditor = this.getPropertyEditorParams(propertyData, editorTypes, 'visible'); + this.requiredEditor = this.getPropertyEditorParams(propertyData, editorTypes, 'required'); this.freeInputEditor = this.getPropertyEditorParams(propertyData, ['Const', 'Variable'], 'allowFreeInput'); - const basic = this.getBasicProperties(propertyData, componentInstance); + const basic = this.getBasicProperties(propertyData, componentInstance,showPosition); const behavior = this.getBehaviorConfig(propertyData); const appearance = this.getAppearanceProperties(propertyData, componentInstance); const lookup = this.getLookupConfig(propertyData); @@ -57,7 +59,7 @@ export class LookupPropertyConfig extends InputBaseProperty { const treeConfig = this.getTreePropConfig(propertyData.editor); // 'compute', 'dependency', - const expressons = this.getExpressionConfig(propertyData, 'Field'); + const expressions = this.getExpressionConfig(propertyData, 'Field'); const categories: any = { basic, @@ -67,7 +69,7 @@ export class LookupPropertyConfig extends InputBaseProperty { treeConfig, dialog, pager, - expressons + expressions }; if (propertyData.type === 'form-group') { @@ -80,7 +82,7 @@ export class LookupPropertyConfig extends InputBaseProperty { }; } getGridFieldEdtiorPropConfig(propertyData: any) { - return this.getPropertyConfig(propertyData, null).categories; + return this.getPropertyConfig(propertyData, null,'Grid').categories; } private flattenTreeNodes(treeNodes: any[]): any[] { @@ -143,7 +145,14 @@ export class LookupPropertyConfig extends InputBaseProperty { return editorOptions.displayType ? editorOptions.displayType.toUpperCase() : ''; } - private getLookupConfig(propertyData: any) { + private mapFieldsDisplayFormatter(items: []) { + if (items && items.length) { + return items.map((item) => `${item['name']}[${item['bindingPath']}]`).join(','); + } + return ''; + } + + public getLookupConfig(propertyData: any) { const editorOptions = propertyData.editor; const getRemoteParams = () => { const viewModelId = this.formSchemaUtils.getFormSchema().module.entity[0].id; @@ -165,6 +174,7 @@ export class LookupPropertyConfig extends InputBaseProperty { break; } }, + parentPropertyID: 'editor', properties: { readonly: { description: "", @@ -199,7 +209,8 @@ export class LookupPropertyConfig extends InputBaseProperty { type: "boolean", $converter: lookupDefaultConverter, refreshPanelAfterChanged: true, - editor: this.comboListEditor + editor: this.comboListEditor, + visible: !(editorOptions.multiSelect && editorOptions.openType === 'Popup') }, allowFreeInput: { description: "", @@ -232,19 +243,30 @@ export class LookupPropertyConfig extends InputBaseProperty { } return ''; }, - viewOptions: [ - { - id: 'recommend', title: '推荐', type: 'List', - dataSource: 'Recommand', - enableGroup: true, - groupField: 'category', - groupFormatter: (value, data) => { - return `${value === 'local' ? '本地元数据' : '最近使用'}`; - } - }, - { id: 'total', title: '全部', type: 'List', dataSource: 'Total' } - ], + viewOptions: this.formSchemaUtils.designerMode === 'PC_RTC' ? + [ + { id: 'total', title: '全部', type: 'List', dataSource: 'Total' } + ] : + [ + { + id: 'recommend', title: '推荐', type: 'List', + dataSource: 'Recommand', + enableGroup: true, + groupField: 'category', + groupFormatter: (value, data) => { + return `${value === 'local' ? '本地元数据' : '最近使用'}`; + } + }, + { id: 'total', title: '全部', type: 'List', dataSource: 'Total' } + ], repositoryToken: LookupSchemaRepositoryToken, + validateFunction: async (dataSourceSchema: any) => { + if (this.formSchemaUtils.designerMode !== 'PC_RTC') { + return true; + } + return this.checkSameBESource(dataSourceSchema, propertyData); + + }, onSubmitModal: (dataSourceSchema: any) => { if (dataSourceSchema) { delete editorOptions.mappingFields; @@ -323,7 +345,7 @@ export class LookupPropertyConfig extends InputBaseProperty { if (JSON.stringify(conditions.value) === JSON.stringify(originalConditions.value)) { return true; } else { - await repository.saveFilterCondition(conditions.value, repositoryParams); + await repository.saveFilterCondition(conditions.value, repositoryParams, this.formSchemaUtils.getFormMetadataBasicInfo()); return true; } } @@ -366,6 +388,7 @@ export class LookupPropertyConfig extends InputBaseProperty { type: "field-selector", textField: 'bindingPath', idField: 'bindingPath', + disabled: true, editorParams: { propertyData: editorOptions, formBasicInfo: this.formSchemaUtils.getFormMetadataBasicInfo() @@ -378,7 +401,6 @@ export class LookupPropertyConfig extends InputBaseProperty { repositoryToken: FieldSelectorRepositoryToken }, $converter: lookupIdFieldConverter, - visible: false }, textField: { description: "显示文本字段", @@ -416,19 +438,23 @@ export class LookupPropertyConfig extends InputBaseProperty { formBasicInfo: this.formSchemaUtils.getFormMetadataBasicInfo() }, fromData: { - editable: false, + editable: true, formatter: (cell, data) => { return `${data.raw['name']} [${data.raw['bindingPath']}]`; }, idField: 'bindingPath', - textField: 'bindingPath', + textField: 'name', valueField: 'bindingPath', - repositoryToken: FieldSelectorRepositoryToken + searchFields: ['name', 'bindingPath'], + repositoryToken: FieldSelectorRepositoryToken, + displayFormatter: this.mapFieldsDisplayFormatter }, toData: { + editable: false, idField: 'bindingPath', - textField: 'bindingPath', + textField: 'name', valueField: 'bindingPath', + searchFields: ['name', 'bindingPath'], dataSource: () => { const fields = this.designViewModelUtils.getAllFields2TreeByVMId(this.viewModelId); const primaryField = this.schemaService.getPrimaryField(); @@ -439,7 +465,8 @@ export class LookupPropertyConfig extends InputBaseProperty { }, formatter: (cell, data) => { return `${data.raw['name']} [${data.raw['bindingPath']}]`; - } + }, + displayFormatter: this.mapFieldsDisplayFormatter } } }, @@ -464,7 +491,7 @@ export class LookupPropertyConfig extends InputBaseProperty { $converter: lookupDefaultConverter, title: "显示已选记录", type: "boolean", - visible: !!editorOptions.multiSelect + visible: !!editorOptions.multiSelect && editorOptions.openType!== 'Popup' }, separator: { description: "多选分隔符", @@ -500,13 +527,14 @@ export class LookupPropertyConfig extends InputBaseProperty { $converter: lookupDefaultConverter, title: "允许查询所有列", type: "boolean", - visible: editorOptions.enableSearchBar == null ? true : !!editorOptions.enableSearchBar + visible: editorOptions.openType !== 'Popup' && editorOptions.enableSearchBar == null ? true : !!editorOptions.enableSearchBar }, enableFavorite: { description: "启用收藏夹", $converter: lookupDefaultConverter, title: "启用收藏夹", type: "boolean", + visible: editorOptions.openType !== 'Popup' }, enableUserData: { description: "保存界面状态", @@ -514,7 +542,7 @@ export class LookupPropertyConfig extends InputBaseProperty { refreshPanelAfterChanged: true, title: "保存界面状态", type: "boolean", - visible: true + visible: editorOptions.openType !== 'Popup' } } }; @@ -522,42 +550,64 @@ export class LookupPropertyConfig extends InputBaseProperty { showNavigatiorWidth(editorOptions: any) { const isDoubleList = this.getDisplayType(editorOptions).startsWith('NAV'); - if (isDoubleList) { - return editorOptions.showNavigation === undefined ? true : !!editorOptions.showNavigation; + if (isDoubleList && editorOptions.openType !=='Popup') { + return (editorOptions.showNavigation === undefined ? true : !!editorOptions.showNavigation); } return false; } - private getDialogPropertyConfig(propertyData: any) { + public getDialogPropertyConfig(propertyData: any) { return { description: "帮助窗口尺寸配置", title: "帮助窗口", + parentPropertyID: 'dialog', properties: { + openType: { + description: "窗口展示方式:弹出窗口、下拉面板", + title: "打开方式", + type: "string", + $converter: lookupDefaultConverter, + refreshPanelAfterChanged: true, + editor: { + ...this.comboListEditor, + data: [ + { text: '弹出窗口', value: 'Modal' }, + { text: '下拉面板', value: 'Popup' }, + ], + valueField: 'value', + textField: 'text', + idField: 'value' + }, + visible: true + }, title: { description: "帮助标题", - title: "帮助标题", + title: "标题", type: "string", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' }, width: { - description: "窗口宽度,最小值:300px", + description: "窗口宽度,最小值:300px,最大值:3000px", title: "宽度", type: "number", refreshPanelAfterChanged: true, $converter: lookupDialogOptionsConverter, editor: { ...this.numberEditor, - min: this.showNavigatiorWidth(propertyData.editor) ? 520 : 300 + min: this.showNavigatiorWidth(propertyData.editor) ? 520 : 300, + max: 9999 } }, height: { - description: "窗口高度,最小值:200px", + description: "窗口高度,最小值:200px,最大值:2000px", title: "高度", type: "number", $converter: lookupDialogOptionsConverter, editor: { ...this.numberEditor, - min: 200 + min: 200, + max: 9999 } }, showNavigation: { @@ -566,14 +616,14 @@ export class LookupPropertyConfig extends InputBaseProperty { refreshPanelAfterChanged: true, title: "显示导航栏", type: "boolean", - visible: this.getDisplayType(propertyData.editor).includes('NAV') + visible: this.getDisplayType(propertyData.editor).includes('NAV') && propertyData.editor.openType !== 'Popup' }, navigatorWidth: { description: "导航栏宽度,最小200px, 最大为窗口宽度减去200px", title: "导航栏宽度", type: "number", $converter: lookupDialogOptionsConverter, - visible: this.showNavigatiorWidth(propertyData.editor), + visible: this.showNavigatiorWidth(propertyData.editor) && propertyData.editor.openType !== 'Popup', editor: { ...this.numberEditor, min: 200, @@ -586,31 +636,35 @@ export class LookupPropertyConfig extends InputBaseProperty { type: "boolean", $converter: lookupDialogOptionsConverter, refreshPanelAfterChanged: true, + visible: propertyData.editor.openType !== 'Popup' }, rememberSize: { description: "记录窗口尺寸", title: "记录窗口尺寸", type: "boolean", $converter: lookupDialogOptionsConverter, - visible: !!propertyData.editor.enableUserData && (propertyData.editor?.dialog?.resizeable ?? true) + visible: !!propertyData.editor.enableUserData && (propertyData.editor?.dialog?.resizeable ?? true) && propertyData.editor.openType !== 'Popup' }, enableEsc: { description: "允许ESC关闭", title: "允许ESC关闭", type: "boolean", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' }, showMaxButton: { description: "显示最大化按钮", title: "显示最大化按钮", type: "boolean", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' }, showCloseButton: { description: "显示关闭按钮", title: "显示关闭按钮", type: "boolean", - $converter: lookupDialogOptionsConverter + $converter: lookupDialogOptionsConverter, + visible: propertyData.editor.openType !== 'Popup' } } }; @@ -624,13 +678,14 @@ export class LookupPropertyConfig extends InputBaseProperty { return show; } - private getPageConfig(editorOptions: any) { + public getPageConfig(editorOptions: any) { const disablePager = !editorOptions.pagination?.enable; return { hide: this.getDisplayType(editorOptions) === 'TREELIST' || !editorOptions.helpId || disablePager, description: "分页配置", title: "分页", + parentPropertyID: 'pagination', properties: { // enable: { // description: "启用分页", @@ -711,15 +766,16 @@ export class LookupPropertyConfig extends InputBaseProperty { !editorOptions.treeToList && !editorOptions.navTreeToList; } - private showOnlySelectLeaf(editorOptions: any) { + private isTree(editorOptions: any) { return this.getDisplayType(editorOptions) === 'TREELIST' && !editorOptions.treeToList; } - private getTreePropConfig(editorOptions: any) { + public getTreePropConfig(editorOptions: any) { return { hide: this.getDisplayType(editorOptions) !== 'TREELIST' && this.getDisplayType(editorOptions) !== 'NAVTREELIST', description: "树形数据配置", title: "树形数据配置", + parentPropertyID: 'editor', properties: { treeToList: { description: "以列表的形式展示树结构数据", @@ -745,13 +801,14 @@ export class LookupPropertyConfig extends InputBaseProperty { editor: { ...this.comboListEditor, data: [ + { value: 'default', text: '默认' }, { value: 'all', text: '全部加载' }, { value: 'async', text: '分层加载' } ], textField: 'text', valueField: 'value' }, - visible: false // this.showLoadType(editorOptions) + visible: this.showLoadType(editorOptions) }, enableFullTree: { description: "启用构造完整树", @@ -765,7 +822,7 @@ export class LookupPropertyConfig extends InputBaseProperty { $converter: lookupDefaultConverter, title: "仅选择叶子节点", type: "boolean", - visible: this.showOnlySelectLeaf(editorOptions), + visible: this.isTree(editorOptions), }, enableCascade: { description: "启用级联选择", @@ -773,21 +830,21 @@ export class LookupPropertyConfig extends InputBaseProperty { refreshPanelAfterChanged: true, title: "启用级联选择", type: "boolean", - visible: false // this.showOnlySelectLeaf(editorOptions) && !!editorOptions.multiSelect + visible: this.isTree(editorOptions) && !!editorOptions.multiSelect && editorOptions.openType!== 'Popup' }, showCascadeControl: { - description: "显示级联选择控件", + description: "是否在界面中显示级联状态", $converter: lookupDefaultConverter, - title: "显示级联选择控件", + title: "显示级联状态", type: "boolean", - visible: false // !!editorOptions.enableCascade && this.showOnlySelectLeaf(editorOptions) + visible: false // !!editorOptions.enableCascade && this.isTree(editorOptions) }, - cascadeStatus: { - description: "级联选择默认状态", + cascadeValue: { + description: "缺省级联选择模式", $converter: lookupDefaultConverter, - title: "级联状态", + title: "缺省级联选择模式", type: "string", - visible: false, // !!editorOptions.enableCascade && this.showOnlySelectLeaf(editorOptions), + visible: !!editorOptions.enableCascade && this.isTree(editorOptions), editor: { ...this.comboListEditor, data: cascadeItems, @@ -813,18 +870,15 @@ export class LookupPropertyConfig extends InputBaseProperty { private getEventPropConfig(propertyData: any) { const self = this; - const events = LookupEvents; + let events = LookupEvents; - // this.appendFieldValueChangeEvents(propertyData, events); + if (propertyData.editor.openType === 'Popup') { + events = events.filter(item => item.label !== 'beforeSelectData'); + } const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { - initialData - } - }; + const properties = self.createBaseEventProperty(initialData); + return { title: '事件', hideTitle: true, @@ -849,4 +903,36 @@ export class LookupPropertyConfig extends InputBaseProperty { } }; } + + /** + * 运行时定制:校验帮助是否绑定同一be(目前不限制通过vo创建的帮助元数据) + * @param newHelpDataSource 新选的帮助 + */ + private async checkSameBESource(dataSourceSchema: any, propertyData: any) { + + if (dataSourceSchema && propertyData?.editor?.helpId && !propertyData.isRtcControl) { + if (dataSourceSchema.data.id === propertyData.editor.helpId) { + return true; + } + const newHelpDataSource = await this.metadataService.getPickMetadata('', dataSourceSchema.data).then(res => { + if (res?.metadata?.content) { return JSON.parse(res.metadata.content) }; + }); + const originalHelpDataSource = await this.metadataService.getPickMetadata('', { id: propertyData.editor.helpId }).then(res => { + if (res?.metadata?.content) { return JSON.parse(res.metadata.content) }; + }); + + const newSoureType = newHelpDataSource?.dataSource?.sourceType; + const originalSoureType = originalHelpDataSource.dataSource?.sourceType; + const newSourceId = newHelpDataSource?.dataSource?.sourceId; + const originalSourceId = originalHelpDataSource?.dataSource?.sourceId; + if (newSoureType === 'BE' && originalSoureType === 'BE' && newSourceId !== originalSourceId) { + const notifyService: any = new FNotifyService(); + notifyService.error({ message: '只允许更换同数据源的帮助元数据', position: 'top-center' }); + return false; + } + } + return true; + + } + } diff --git a/packages/ui-vue/components/lookup/src/schema/lookup.schema.json b/packages/ui-vue/components/lookup/src/schema/lookup.schema.json index de21d419e708fb31d9887fb42bfc94bbaca7fe24..f04d9f78a5d7d927bd856d4a1c4509df7ceabb4b 100644 --- a/packages/ui-vue/components/lookup/src/schema/lookup.schema.json +++ b/packages/ui-vue/components/lookup/src/schema/lookup.schema.json @@ -54,7 +54,7 @@ "placeholder": { "description": "", "type": "string", - "default": "" + "default": "请选择" }, "readonly": { "description": "", @@ -198,7 +198,7 @@ }, "loadTreeDataType": { "type": "string", - "default": "all" + "default": "default" }, "onlySelectLeaf": { "type": "boolean", @@ -222,7 +222,7 @@ }, "showCascadeControl": { "type": "boolean", - "default": false + "default": true }, "cascadeItems": { "type": "object", @@ -233,7 +233,7 @@ "disable": true } }, - "cascadeStatus": { + "cascadeValue": { "type": "string", "default": "both" }, @@ -245,16 +245,27 @@ "type": "boolean", "default": true }, + "openType": { + "type": "string", + "default": "Modal" + }, "enableTitle": { "type": "boolean", "default": false + }, + "showIncludeChildNodes": { + "type": "boolean", + "default": true + }, + "includeChildNodesValue": { + "type": "boolean", + "default": true } }, "required": [ "type" ], "ignore": [ - "id", "appearance", "binding", "visible" diff --git a/packages/ui-vue/components/mapping-editor/src/locales/designer/en.json b/packages/ui-vue/components/mapping-editor/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..410ded10c7efa94a00c2d543b06ce4efadc0bc18 --- /dev/null +++ b/packages/ui-vue/components/mapping-editor/src/locales/designer/en.json @@ -0,0 +1,22 @@ +{ + "mappgingEditor": { + "columns": { + "sourceField": "Source Field", + "targetField": "Target Field" + }, + "message": { + "noSelectItem": "Please select a field to delete!", + "confirmClear": "Are you sure you want to clear all mapping fields?", + "required": "Please fill in the mapping fields completely!", + "noDataSource": "Please set the data source!" + }, + "buttons": { + "ok": "OK", + "cancel": "Cancel", + "clear": "Clear", + "add": "Add", + "remove": "Delete" + }, + "title": "Mapping Editor" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/mapping-editor/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/mapping-editor/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..181d6af9bc16bcc566c4aa33fc82c0aa0cfd99f4 --- /dev/null +++ b/packages/ui-vue/components/mapping-editor/src/locales/designer/zh-CHS.json @@ -0,0 +1,22 @@ +{ + "mappgingEditor": { + "columns": { + "sourceField": "源字段", + "targetField": "目标字段" + }, + "message": { + "noSelectItem": "请选择要删除的字段!", + "confirmClear": "确定要清空所有映射字段吗?", + "required": "请将映射字段填写完整!", + "noDataSource": "请设置数据源!" + }, + "buttons": { + "ok": "确定", + "cancel": "取消", + "clear": "清空", + "add": "添加", + "remove": "删除" + }, + "title": "映射编辑器" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/mapping-editor/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/mapping-editor/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..fb730b32daa1efaee0c300a8b58c7cc6ffa60b2a --- /dev/null +++ b/packages/ui-vue/components/mapping-editor/src/locales/designer/zh-CHT.json @@ -0,0 +1,22 @@ +{ + "mappgingEditor": { + "columns": { + "sourceField": "源字段", + "targetField": "目標字段" + }, + "message": { + "noSelectItem": "請選擇要刪除的字段!", + "confirmClear": "確定要清空所有映射字段嗎?", + "required": "請將映射字段填寫完整!", + "noDataSource": "請設置數據源!" + }, + "buttons": { + "ok": "確定", + "cancel": "取消", + "clear": "清空", + "add": "添加", + "remove": "刪除" + }, + "title": "映射編輯器" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx b/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx index 345a932923265c0233e0a674d292dcab52a1f5e6..cb0ae9a9eaa6836712c0d4fe8ff800bac76303a9 100644 --- a/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx +++ b/packages/ui-vue/components/mapping-editor/src/mapping-editor.component.tsx @@ -10,6 +10,17 @@ export interface ComboTreeRepository { getData(params?: any): Promise; } +function flattenTreeNodes(treeNodes: any[]): any[] { + return treeNodes.reduce((result, item) => { + result.push(item.data); + if (item.children?.length) { + result.push(...flattenTreeNodes(item.children)); + } + return result; + }, [] as any[]); +} + + export default defineComponent({ name: 'FMappingEditor', props: mappingEditorProps, @@ -23,8 +34,8 @@ export default defineComponent({ const fromDataSource = ref(props.fromData.dataSource || []); const toDataSource = ref([]); - const repository = inject(props.fromData.repositoryToken); - const toDataRepository = inject(props.toData.repositoryToken); + const repository = props.fromData.repositoryToken? inject(props.fromData.repositoryToken): null; + const toDataRepository = props.toData.repositoryToken? inject(props.toData.repositoryToken): null; const notifyService: FNotifyService = inject(F_NOTIFY_SERVICE_TOKEN) as FNotifyService; const treeNodeStatus = (visualData: any) => { @@ -34,6 +45,24 @@ export default defineComponent({ return visualData; }; + const flattenFromDataSource = computed(() => { + return flattenTreeNodes(fromDataSource.value); + }); + + const flattenToDataSource = computed(() => { + return flattenTreeNodes(toDataSource.value); + }); + + const displayFieldText = (dataSource: any, id: string, textField: string, valueField: string) => { + const nameValue = dataSource.value.find(item => item[valueField] === id)?.[textField || 'name'] || ''; + const codeValue = dataSource.value.find(item => item[valueField] === id)?.[valueField || 'code'] || ''; + + if (nameValue && codeValue) { + return `${nameValue}[${codeValue}]`; + } + return ''; + }; + const gridColumns = [ { field: 'sourceField', title: '数据源字段', dataType: 'string', editor: { @@ -44,8 +73,13 @@ export default defineComponent({ valueField: props.fromData.valueField || 'id', formatter: props.fromData.formatter, editorParams: props.fromData.editorParams, - editable: false, - customRowStatus: treeNodeStatus + editable: props.fromData.editable || false, + searchFields: props.fromData.searchFields || [], + customRowStatus: treeNodeStatus, + displayFormatter: props.fromData.displayFormatter + },formatter: (cell, row) => { + // return flattenFromDataSource.value.find(item => item[props.fromData.valueField] === row.raw.sourceField)?.[props.fromData.textField || 'name'] || ''; + return displayFieldText(flattenFromDataSource, row.raw.sourceField, props.fromData.textField, props.fromData.valueField); } }, { @@ -57,8 +91,22 @@ export default defineComponent({ valueField: props.toData.valueField || 'id', formatter: props.toData.formatter, editorParams: props.toData.editorParams, + multiSelect: true, + enableSearch: true, editable: false, - customRowStatus: treeNodeStatus + searchFields: props.toData.searchFields || [], + customRowStatus: treeNodeStatus, + displayFormatter: props.toData.displayFormatter + }, formatter: (cell, row) => { + if (row.raw.targetField) { + const fields = row.raw.targetField.indexOf(',') > -1 ? row.raw.targetField.split(','): [row.raw.targetField]; + return fields.map(id => { + // return flattenToDataSource.value.find(item => item[props.toData.valueField] === id)?.[props.toData.textField || 'name'] || ''; + return displayFieldText(flattenToDataSource, id, props.toData.textField, props.toData.valueField); + }).join(','); + } else { + return ''; + } } } ]; @@ -249,10 +297,10 @@ export default defineComponent({ } function onBeforeEditCell(event: any) { - const { column } = event; + const { column, rawData } = event; const { field, editor } = column; if (field === 'sourceField' && editor) { - const disabledFields = mappingFieldList.value.map((item: any) => { + const disabledFields = mappingFieldList.value.filter((item: any) => item.sourceField !== rawData.sourceField).map((item: any) => { return item.sourceField; }); diff --git a/packages/ui-vue/components/mapping-editor/src/mapping-editor.props.ts b/packages/ui-vue/components/mapping-editor/src/mapping-editor.props.ts index 7065c9e048e9dfe8c50f184482403f7ad27c9e3c..d460491a6e843f9a51081703e6d94ff5137d2a8e 100644 --- a/packages/ui-vue/components/mapping-editor/src/mapping-editor.props.ts +++ b/packages/ui-vue/components/mapping-editor/src/mapping-editor.props.ts @@ -9,6 +9,7 @@ export interface MappingDataSource { textField: string; valueField: string; repositoryToken?: symbol; + displayFormatter?: (items: any[]) => string; } export const mappingEditorProps = { diff --git a/packages/ui-vue/components/menu-lookup/src/components/modal-container.component.tsx b/packages/ui-vue/components/menu-lookup/src/components/modal-container.component.tsx index 5d1fef04e9fe1d5086eba7c120c641a32df21f90..12c96ba24c8098ea6b62a72cf623e6d0c83941ab 100644 --- a/packages/ui-vue/components/menu-lookup/src/components/modal-container.component.tsx +++ b/packages/ui-vue/components/menu-lookup/src/components/modal-container.component.tsx @@ -125,7 +125,7 @@ export default defineComponent({ return () => { return ( - + {renderNavTree()} diff --git a/packages/ui-vue/components/menu-lookup/src/composition/types.ts b/packages/ui-vue/components/menu-lookup/src/composition/types.ts index d9648f09e93a6dc9e62c57b1544810bb3b209a32..173869eaffa6d2677ac65ccab1a32f85714b0718 100644 --- a/packages/ui-vue/components/menu-lookup/src/composition/types.ts +++ b/packages/ui-vue/components/menu-lookup/src/composition/types.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-use-before-define */ import { Ref } from 'vue'; export type TargetType = 'menu' | 'app'; diff --git a/packages/ui-vue/components/message-box/index.ts b/packages/ui-vue/components/message-box/index.ts index 5ffa28ccc25ea555872ff32c7255f6faf1a0afb1..7eadcdd94173ac69394383a1d4127397a5c85c87 100644 --- a/packages/ui-vue/components/message-box/index.ts +++ b/packages/ui-vue/components/message-box/index.ts @@ -24,6 +24,7 @@ export { FMessageBox, FMessageBoxService }; FMessageBox.install = (app: App) => { app.component(FMessageBox.name as string, FMessageBox); + FMessageBoxService.app = app; app.provide('FMessageBoxService', FMessageBoxService); }; diff --git a/packages/ui-vue/components/message-box/src/components/footer/copy-button.component.tsx b/packages/ui-vue/components/message-box/src/components/footer/copy-button.component.tsx index b3379389ae84ab69f7d1d385b45938039bb1b1b0..218153f6a59ee3514212a71da9184548a548f54c 100644 --- a/packages/ui-vue/components/message-box/src/components/footer/copy-button.component.tsx +++ b/packages/ui-vue/components/message-box/src/components/footer/copy-button.component.tsx @@ -1,17 +1,18 @@ import { computed, ref } from "vue"; import { UseCopy, UseFeedback } from "../../composition/types"; import { ExceptionInfo, MessageBoxProps } from "../../message-box.props"; +import { MessageBoxLocaleData } from "../../composition/use-locales"; export default function ( props: MessageBoxProps, useCopyComposition: UseCopy, - useFeedbackComposition: UseFeedback + useFeedbackComposition: UseFeedback, + locales: MessageBoxLocaleData ) { - const { onCopy } = useCopyComposition; const { toShowFeedback } = useFeedbackComposition; - const copyFeedback = ref('复制成功'); - const copyButtonText = ref('复制详细信息'); + const copyFeedback = ref(locales.exception.copySuccess || '复制成功'); + const copyButtonText = ref(locales.exception.copy || '复制详细信息'); const exception = ref(props.exceptionInfo || { date: '', message: '', detail: '' }); const safeExceptionMessage = computed(() => { @@ -22,7 +23,7 @@ export default function ( function onClickCopyButton($event: MouseEvent) { onCopy(safeExceptionMessage.value) .catch((reason: any) => { - copyFeedback.value = '复制失败'; + copyFeedback.value = locales.exception.copyFailed || '复制失败'; }) .finally(() => { toShowFeedback.value = true; diff --git a/packages/ui-vue/components/message-box/src/components/footer/footer.component.tsx b/packages/ui-vue/components/message-box/src/components/footer/footer.component.tsx index f7c2b432e49129a7ea7a8b24a74c35aa45ccc6a4..ef9dee09aa61c3aff6240ff11f4ce6fbf90ed3a2 100644 --- a/packages/ui-vue/components/message-box/src/components/footer/footer.component.tsx +++ b/packages/ui-vue/components/message-box/src/components/footer/footer.component.tsx @@ -3,42 +3,43 @@ import { ExceptionInfo, MessageBoxProps } from "../../message-box.props"; import getCopyButton from './copy-button.component'; import { UseCopy, UseFeedback } from "../../composition/types"; import { ModalButton } from "../../../../modal/src/composition/type"; +import { MessageBoxLocaleData } from "../../composition/use-locales"; export default function ( props: MessageBoxProps, context: SetupContext, useCopyComposition: UseCopy, - useFeedbackComposition: UseFeedback + useFeedbackComposition: UseFeedback, + locales: MessageBoxLocaleData ) { const messageType = ref(props.type); const buttons = ref(props.buttons); const exception = ref(props.exceptionInfo); - const okButtonText = ref(props.okButtonText); - const cancelButtonText = ref(props.cancelButtonText); - const showCancelButton = ref(true); - const showOkButton = ref(true); + const okButtonText = ref(locales.ok || props.okButtonText); + const cancelButtonText = ref(locales.cancel || props.cancelButtonText); + const showCancelButton = ref(props.showCancelButton); + const showOkButton = ref(props.showOkButton); const shouldShowButtons = computed(() => { return !!(buttons.value && buttons.value.length); }); const shouldShowCopyButton = computed(() => { - return exception.value && exception.value.date && exception.value.message && exception.value.detail; + return exception.value && exception.value.message && exception.value.detail; }); const shouldShowOkOrCancelButtons = computed(() => { - return !(buttons.value && buttons.value.length) && (okButtonText.value || cancelButtonText.value); + return !(buttons.value && buttons.value.length) && (showOkButton.value || showCancelButton.value); }); - const shouldShowOkButton = computed(() => { - return showOkButton.value && okButtonText.value; - }); + const renderCopyButton = getCopyButton(props, useCopyComposition, useFeedbackComposition, locales); - const shouldShowCancelButton = computed(() => { - return showCancelButton.value && cancelButtonText.value; - }); + if (okButtonText.value === '关闭') { + okButtonText.value = locales.close; + } else if (okButtonText.value === '知道了') { + okButtonText.value = locales.exception.roger; + } - const renderCopyButton = getCopyButton(props, useCopyComposition, useFeedbackComposition); function onClickCancelButton($event: MouseEvent) { if (messageType.value === 'question') { @@ -58,12 +59,12 @@ export default function ( return ( diff --git a/packages/ui-vue/components/message-box/src/components/message-detail/exception-message.component.tsx b/packages/ui-vue/components/message-box/src/components/message-detail/exception-message.component.tsx index ec68478bba0c3f56dc807dc87be1d3193ea6cb7b..72ca505dc407747c94c2e300476b00d491104dd3 100644 --- a/packages/ui-vue/components/message-box/src/components/message-detail/exception-message.component.tsx +++ b/packages/ui-vue/components/message-box/src/components/message-detail/exception-message.component.tsx @@ -1,7 +1,8 @@ import { Ref, computed, ref } from "vue"; import { ExceptionInfo } from "../../message-box.props"; +import { MessageBoxLocaleData } from "../../composition/use-locales"; -export default function (exception: Ref) { +export default function (exception: Ref, locales: MessageBoxLocaleData) { function getRealLength(value: string){ if (value == null) { @@ -52,7 +53,7 @@ export default function (exception: Ref) { const exceptionDateMessage = computed(() => { const exceptionDate = (exception.value && exception.value.date) || ''; - return `发生时间 : ${exceptionDate}`; + return `${locales.exception.happend} : ${exceptionDate}`; }); const shouldShowExceptionMessage = computed(() => { @@ -66,12 +67,12 @@ export default function (exception: Ref) { const showDetailInfo = ref(false); const toggleDetailText = computed(() => { - return showDetailInfo.value ? '收起' : '展开'; + return showDetailInfo.value ? locales.exception.collapse : locales.exception.expand; }); const safeExceptionMessage = computed(() => { - const safeMessage = '详细信息 : ' + (exception.value && exception.value.detail) || ''; + const safeMessage = locales.exception.detail + ' : ' + (exception.value && exception.value.detail) || ''; return showDetailInfo.value? safeMessage: subStrNum(safeMessage, 160); }); diff --git a/packages/ui-vue/components/message-box/src/composition/use-locales.ts b/packages/ui-vue/components/message-box/src/composition/use-locales.ts new file mode 100644 index 0000000000000000000000000000000000000000..68407c165ef5147d997cb87809e847c14a5f85aa --- /dev/null +++ b/packages/ui-vue/components/message-box/src/composition/use-locales.ts @@ -0,0 +1,55 @@ +import { useI18n } from "vue-i18n"; +import { MessageBoxProps } from "../message-box.props"; + +export interface MessageBoxLocaleData { + yes: string; + close: string; + no: string; + ok: string; + cancel: string; + title: string; + errorTitle: string; + exception: { + expand: string; + collapse: string; + happend: string; + detail: string; + copy: string; + copySuccess: string; + copyFailed: string; + roger: string; + }, + locale: string; +} + + +export function useMessagerLocales(props: MessageBoxProps): MessageBoxLocaleData{ + const { t:getLocaleValue, locale } = useI18n(); + function getValue(localeKey, propertyValue?: string, defaultValue?: string) { + if (propertyValue === defaultValue) { + return getLocaleValue(localeKey); + } + return propertyValue; + } + + return { + yes: getValue('messageBox.yes') || '是', + no: getValue('messageBox.no') ||'否', + close: getValue('messageBox.close') || '关闭', + ok: getValue('messageBox.ok', props.okButtonText, '确定') ||'确定', + cancel: getValue('messageBox.cancel', props.cancelButtonText, '取消') || '取消', + title: getValue('messageBox.title') || '系统提示', + errorTitle: getValue('messageBox.errorTitle') || '错误提示', + exception: { + expand: getValue('messageBox.exception.expand') || '展开', + collapse: getValue('messageBox.exception.collapse') || '收起', + happend: getValue('messageBox.exception.happend') || '发生时间', + detail: getValue('messageBox.exception.detail') || '详细信息', + copy: getValue('messageBox.exception.copy') || '复制详细信息', + copySuccess: getValue('messageBox.exception.copySuccess') || '复制成功', + copyFailed: getValue('messageBox.exception.copyFailed') || '复制失败', + roger: getValue('messageBox.exception.roger') || '知道了' + }, + locale: locale.value + }; +} diff --git a/packages/ui-vue/components/message-box/src/locales/ui/en.json b/packages/ui-vue/components/message-box/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..853a333bdd34cde22eb5cb45eaea23fb61505b40 --- /dev/null +++ b/packages/ui-vue/components/message-box/src/locales/ui/en.json @@ -0,0 +1,35 @@ +{ + "messageBox": { + "yes": "Yes", + "no": "No", + "ok": "OK", + "close": "Close", + "cancel": "Cancel", + "title": "System Information", + "errorTitle": "Error Information", + "prompt": { + "fontSize": { + "name": "Font Size", + "small": "Small", + "middle": "Middle", + "big": "Large", + "large": "Extra Large", + "huge": "Huge" + }, + "tips": { + "surplus": "You can also input {0} characters", + "length": "{0} characters have been entered" + } + }, + "exception": { + "expand": "Expand", + "collapse": "Collapse", + "happend": "Happened Time", + "detail": "Detail", + "copy": "Copy Details", + "copySuccess": "Copy Succeeded!", + "copyFailed": "Replication Failed!", + "roger": "OK" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/message-box/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/message-box/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..dff8681f923fc3575a388bb9bf68241315c0e900 --- /dev/null +++ b/packages/ui-vue/components/message-box/src/locales/ui/zh-CHS.json @@ -0,0 +1,35 @@ +{ + "messageBox": { + "yes": "是", + "no": "否", + "ok": "确定", + "close": "关闭", + "cancel": "取消", + "title": "系统提示", + "errorTitle": "错误提示", + "prompt": { + "fontSize": { + "name": "字体大小", + "small": "小", + "middle": "中", + "big": "大", + "large": "特大", + "huge": "超大" + }, + "tips": { + "surplus": "还可以输入 {0} 个字符", + "length": "已输入 {0} 个字符" + } + }, + "exception": { + "expand": "展开", + "collapse": "收起", + "happend": "发生时间", + "detail": "详细信息", + "copy": "复制详细信息", + "copySuccess": "复制成功", + "copyFailed": "复制失败", + "roger": "知道了" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/message-box/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/message-box/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..12ea9c1e8dccc1ebd04b46b4fc8824ec6c5aa0be --- /dev/null +++ b/packages/ui-vue/components/message-box/src/locales/ui/zh-CHT.json @@ -0,0 +1,31 @@ +{ + "messageBox": { + "yes": "是", + "no": "否", + "ok": "確定", + "close": "關閉", + "cancel": "取消", + "title": "係統提示", + "errorTitle": "錯誤提示", + "prompt": { + "fontSize": { + "name": "字體大小", + "small": "小", + "middle": "中", + "big": "大", + "large": "特大", + "huge": "超大" + } + }, + "exception": { + "expand": "展開", + "collapse": "收起", + "happend": "發生時間", + "detail": "詳細信息", + "copy": "複制詳細信息", + "copySuccess": "複制成功", + "copyFailed": "複制失敗", + "roger": "知道了" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/message-box/src/message-box.component.tsx b/packages/ui-vue/components/message-box/src/message-box.component.tsx index 7a85b42d0a6aa6d649f01bbb815f5dfcfb46e48f..eef85a7f5dbb696ece9400c625fbed80be60cb4f 100644 --- a/packages/ui-vue/components/message-box/src/message-box.component.tsx +++ b/packages/ui-vue/components/message-box/src/message-box.component.tsx @@ -21,12 +21,15 @@ import { useFeedfack } from './composition/use-feedback'; import getReactiveContentRender from './components/message-content/reactive-content.component'; import getStaticContentRender from './components/message-content/static-content.component'; import getFooter from './components/footer/footer.component'; +import { useMessagerLocales } from './composition/use-locales'; export default defineComponent({ name: 'FMessageBox', props: messageBoxProps, emits: ['accept', 'reject', 'close'] as (string[] & ThisType) | undefined, setup(props: MessageBoxProps, context: SetupContext) { + const messagerLocales = useMessagerLocales(props); + const messageType = ref(props.type); const messageTitle = ref(props.title); const messageDetail = ref(props.detail); @@ -43,7 +46,7 @@ export default defineComponent({ }); const messageBoxContainerStyle = computed(() => { - const styleObject = {} as Record; + const styleObject = {cursor: 'default'} as Record; if (messageType.value === 'prompt') { styleObject.padding = '0.5rem 0.5rem 1rem 1.5rem'; } else if (messageType.value === 'error') { @@ -57,6 +60,8 @@ export default defineComponent({ const useFeedbackComposition = useFeedfack(); const { feedbackStyle, feedbackMessage } = useFeedbackComposition; + feedbackMessage.value = messagerLocales.exception.copySuccess; + function renderFeedback() { return
{feedbackMessage.value}
; } @@ -66,16 +71,17 @@ export default defineComponent({ case 'prompt': return getReactiveContentRender(props, useEditorComposition); default: - return getStaticContentRender(props, messageType, messageTitle, messageDetail); + return getStaticContentRender(props, messageType, messageTitle, messageDetail, messagerLocales); } } const renderContent = getContentRender(); - const renderFooter = getFooter(props, context, useCopyComposition, useFeedbackComposition); + const renderFooter = getFooter(props, context, useCopyComposition, useFeedbackComposition, messagerLocales); return () => { return (
+ {!props.exceptionInfo &&
}
{renderContent()}
diff --git a/packages/ui-vue/components/message-box/src/message-box.props.ts b/packages/ui-vue/components/message-box/src/message-box.props.ts index 90e93ab3f72cb92810423af183aed13788404128..ce5c91a735dca84d450077de599b8039224a4a1f 100644 --- a/packages/ui-vue/components/message-box/src/message-box.props.ts +++ b/packages/ui-vue/components/message-box/src/message-box.props.ts @@ -31,6 +31,8 @@ export const messageBoxProps = { title: { Type: String, default: '' }, detail: { Type: String, default: '' }, okButtonText: { Type: String, default: '确定' }, + showOkButton: { Type: Boolean, default: true }, + showCancelButton: { Type: Boolean, default: true }, cancelButtonText: { Type: String, default: '取消' }, exceptionInfo: { Type: Object as PropType, default: null }, promptEditorType: { Type: String as PropType, default: 'text-area' }, diff --git a/packages/ui-vue/components/message-box/src/message-box.service.tsx b/packages/ui-vue/components/message-box/src/message-box.service.tsx index 3db86d79bb4ea0315b22805f2c1a0bfcf1c742cd..7accc169ef7cd0b0c4a8852ba1189c12da9b7ea5 100644 --- a/packages/ui-vue/components/message-box/src/message-box.service.tsx +++ b/packages/ui-vue/components/message-box/src/message-box.service.tsx @@ -32,6 +32,8 @@ export interface MessageBoxOption { } export default class MessageBoxService { + public static app: any = null; + static show(options: MessageBoxOption) { const props: MessageBoxOption = reactive({ @@ -44,7 +46,7 @@ export default class MessageBoxService { const rejectCallback = props.rejectCallback || (() => { }); let modalApp: any = null; - let modalService: FModalService | null = new FModalService(modalApp); + let modalService: FModalService | null = new FModalService(MessageBoxService.app); const onClose = () => { if (modalApp) { modalApp?.destroy(); @@ -61,6 +63,7 @@ export default class MessageBoxService { fitContent: true, showMaxButton: false, draggable: true, + dragHandle: '.messager-draggable', render: () => { return ; } @@ -75,7 +78,7 @@ export default class MessageBoxService { title: message, detail, okButtonText: '知道了', - cancelButtonText: '' + showCancelButton: false }); MessageBoxService.show(props); } @@ -86,7 +89,7 @@ export default class MessageBoxService { title: message, detail, okButtonText: '知道了', - cancelButtonText: '' + showCancelButton: false }); MessageBoxService.show(props); } @@ -97,7 +100,7 @@ export default class MessageBoxService { title: message, detail, okButtonText: '关闭', - cancelButtonText: '' + showCancelButton: false }); MessageBoxService.show(props); } @@ -107,7 +110,7 @@ export default class MessageBoxService { width: 500, type: 'error', okButtonText: '关闭', - cancelButtonText: '', + showCancelButton: false, exceptionInfo: { date, message, detail } as ExceptionInfo }); MessageBoxService.show(props); diff --git a/packages/ui-vue/components/modal/index.ts b/packages/ui-vue/components/modal/index.ts index a884c987dba86d258f54e08c4b1b7e7dd66de524..3fd1ce1beaa6cf9a9450066b21905aa0d9e64bb4 100644 --- a/packages/ui-vue/components/modal/index.ts +++ b/packages/ui-vue/components/modal/index.ts @@ -16,9 +16,13 @@ import type { App, Plugin } from 'vue'; import FModal from './src/modal.component'; import FModalService from './src/composition/modal.service'; +import { propsResolver, callbackResolver } from './src/modal.props'; +import { ModalProperty } from './src/property-config/modal.property-config'; export * from './src/composition/type'; export * from './src/modal.props'; +export * from './src/composition/use-resizeable'; +export * from './src/composition/use-draggable'; export const F_MODAL_SERVICE_TOKEN = Symbol('FModalService'); @@ -29,5 +33,11 @@ FModal.install = (app: App) => { app.provide('FModalService', modalInstance); }; -export { FModal, FModalService }; +FModal.register = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record) => { + componentMap.modal = FModal; + propsResolverMap.modal = propsResolver; + resolverMap.modal = { callbackResolver }; +}; + +export { FModal, FModalService, ModalProperty }; export default FModal as typeof FModal & Plugin; diff --git a/packages/ui-vue/components/modal/src/composition/modal.service.tsx b/packages/ui-vue/components/modal/src/composition/modal.service.tsx index cd551838aeed98156d4771bbaf00d47fbad23b06..710d76204ba36892ca579b80ef3b6e487dac1500 100644 --- a/packages/ui-vue/components/modal/src/composition/modal.service.tsx +++ b/packages/ui-vue/components/modal/src/composition/modal.service.tsx @@ -1,6 +1,7 @@ import { ref, h, render, VNode, cloneVNode, shallowRef, nextTick, App, AppContext, createApp, onUnmounted, onMounted, computed } from "vue"; import { ModalFunctions, ModalOptions } from "./type"; import FModal from '../modal.component'; +import { LocaleService } from "@farris/ui-vue/components/locale"; // import FarrisPlugin from '../../../plugin'; function getContentRender(props: ModalOptions) { @@ -82,7 +83,7 @@ function createModalInstance(options: ModalOptions) { // if (FarrisPlugin && !!FarrisPlugin.install) { // app.use(FarrisPlugin); // } - + app.use(LocaleService.i18n); app.mount(container); return app; diff --git a/packages/ui-vue/components/modal/src/composition/type.ts b/packages/ui-vue/components/modal/src/composition/type.ts index 0512b650a55889d704f5a943897d06ac06195aff..f8ad528362e65c0addb067f8d8a7403bcaf7daf5 100644 --- a/packages/ui-vue/components/modal/src/composition/type.ts +++ b/packages/ui-vue/components/modal/src/composition/type.ts @@ -28,6 +28,7 @@ export interface ModalOptions { fitContent?: boolean; buttons?: ModalButton[]; draggable?: boolean; + dragHandle?: string | HTMLElement; resizeable?: boolean; showMaxButton?: boolean; showCloseButton?: boolean; diff --git a/packages/ui-vue/components/modal/src/composition/use-resizeable.tsx b/packages/ui-vue/components/modal/src/composition/use-resizeable.tsx index 453c84c8a3c81fdc6d6b2628306580aca290f695..9648688cbedb28dc9bacbe6f10a6eaf1c68a4753 100644 --- a/packages/ui-vue/components/modal/src/composition/use-resizeable.tsx +++ b/packages/ui-vue/components/modal/src/composition/use-resizeable.tsx @@ -389,6 +389,6 @@ export function useResizeable(props: any, context: any) { const unWindowResizeHandle = onWindowResize(); return { - renderResizeBar, boundingElement, resizedEventParam, maximize, restore, allowDrag, isMaximized, unWindowResizeHandle + renderResizeBar, boundingElement, resizedEventParam, maximize, restore, allowDrag, isMaximized, unWindowResizeHandle, moveToCenter }; } diff --git a/packages/ui-vue/components/modal/src/modal.component.tsx b/packages/ui-vue/components/modal/src/modal.component.tsx index 8cf6c374250e7fd692e289d9ac3ef53c51fb3b25..7d8dc6559f504ab34a2efc1d619d7da8f3c79a80 100644 --- a/packages/ui-vue/components/modal/src/modal.component.tsx +++ b/packages/ui-vue/components/modal/src/modal.component.tsx @@ -21,6 +21,7 @@ import { useResizeable } from './composition/use-resizeable'; import { useDraggable } from './composition/use-draggable'; import './modal.scss'; import { useEnter, useEsc } from './composition/use-shortcut'; +import { useI18n } from 'vue-i18n'; export default defineComponent({ name: 'FModal', @@ -46,12 +47,19 @@ export default defineComponent({ const containment = ref(props.containment || null); const modalContainerRef = ref(); + const { t } = useI18n(); + if(title.value === '错误提示') { + title.value = t('messageBox.errorTitle'); + } + + const hasModal = ref(false); function close($event: MouseEvent, accept?: boolean) { + const closeType = accept ? 'accept' : 'cancel'; Promise.resolve() .then(() => { - return props.beforeClose?.(); + return props.beforeClose?.({closeType}); }) .then((result: boolean | undefined) => { if (result) { @@ -67,7 +75,7 @@ export default defineComponent({ const defaultButtons: ModalButton[] = [ { name: 'cancel', - text: '取消', + text: t('messageBox.cancel') || '取消', class: 'btn btn-secondary', handle: ($event: MouseEvent) => { close($event, false); @@ -75,7 +83,7 @@ export default defineComponent({ }, { name: 'accept', - text: '确定', + text: t('messageBox.ok') || '确定', class: 'btn btn-primary', handle: ($event: MouseEvent) => { close($event, true); @@ -92,7 +100,7 @@ export default defineComponent({ const maximized = ref(false); const { renderResizeBar, maximize, restore, boundingElement, - resizedEventParam, allowDrag, unWindowResizeHandle } = useResizeable(props, context); + resizedEventParam, allowDrag, unWindowResizeHandle, moveToCenter } = useResizeable(props, context); const { registerDraggle } = useDraggable(props, context, allowDrag); function hasOpenModal() { @@ -129,6 +137,7 @@ export default defineComponent({ containment.value = (modalElementRef.value as HTMLElement).parentElement; boundingElement.value = containment.value; registerDraggle(modalHeaderRef.value, modalElementRef.value, boundingElement.value); + moveToCenter(); } }); } @@ -142,6 +151,9 @@ export default defineComponent({ if (modelValue.value) { hasModal.value = hasOpenModal(); + } else { + maximized.value = false; + allowDrag.value = props.draggable; } }); // 监听是否展示标题变化 @@ -269,8 +281,8 @@ export default defineComponent({ return Object.assign(styleObject, footerSytles); }); - function maxDialog($event: MouseEvent) { - $event.stopPropagation(); + function maxDialog($event?: MouseEvent) { + $event?.stopPropagation(); if (maximized.value) { maximized.value = false; restore(); diff --git a/packages/ui-vue/components/modal/src/modal.props.ts b/packages/ui-vue/components/modal/src/modal.props.ts index 92272db84605dea7796c82f7827ccddf614c7670..281645a4273433a680a4d11f1360bf981a25d0c5 100644 --- a/packages/ui-vue/components/modal/src/modal.props.ts +++ b/packages/ui-vue/components/modal/src/modal.props.ts @@ -14,7 +14,12 @@ * limitations under the License. */ import { ExtractPropTypes, PropType } from 'vue'; +import { createPropsResolver } from "@farris/ui-vue/components/dynamic-resolver"; import { ModalButton } from './composition/type'; +import modalSchema from './schema/modal.schema.json'; +import { schemaMapper } from './schema/schema-mapper'; +import { schemaResolver } from './schema/schema-resolver'; +import { createModalCallbackResolver } from './schema/callback-resolvers'; export type DragHandleType = HTMLElement | string; @@ -101,3 +106,7 @@ export const modalProps = { }; export type ModalProps = Partial>; + +export const propsResolver = createPropsResolver(modalProps as any, modalSchema, schemaMapper, schemaResolver); + +export const callbackResolver = createModalCallbackResolver(); diff --git a/packages/ui-vue/components/modal/src/property-config/modal-events.ts b/packages/ui-vue/components/modal/src/property-config/modal-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..28bb12e1c92f0f9b36edf7f83d94da76f68b373a --- /dev/null +++ b/packages/ui-vue/components/modal/src/property-config/modal-events.ts @@ -0,0 +1,15 @@ + +export const ModalEvents = [ + { + label: 'beforeClose', + name: '关闭前回调事件' + }, + { + label: 'onAccept', + name: '确定事件' + }, + { + label: 'onCancel', + name: '取消事件' + } +]; diff --git a/packages/ui-vue/components/modal/src/property-config/modal.property-config.ts b/packages/ui-vue/components/modal/src/property-config/modal.property-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4669ba11885c8a4b7e7d96f9f41a849e315c228 --- /dev/null +++ b/packages/ui-vue/components/modal/src/property-config/modal.property-config.ts @@ -0,0 +1,110 @@ +import { BaseControlProperty } from "../../../property-panel"; +import { ModalEvents } from "./modal-events"; +export class ModalProperty extends BaseControlProperty { + private numberEditor = { + type: "number-spinner", + useThousands: false + }; + + constructor(componentId: string, designerHostService: any) { + super(componentId, designerHostService); + } + + public getPropertyConfig(propertyData: any) { + const eventsEditor = this.getEventPropConfig(propertyData); + + this.propertyConfig.categories['dialog'] = this.getDialogPropertyConfig(propertyData); + this.propertyConfig.categories['eventsEditor'] = eventsEditor; + + return this.propertyConfig; + } + + public getDialogPropertyConfig(propertyData: any) { + return { + description: "窗口尺寸配置", + title: "窗口", + parentPropertyID: 'dialog', + properties: { + title: { + description: "标题", + title: "标题", + type: "string", + visible: true + }, + width: { + description: "窗口宽度,最小值:300px,最大值:3000px", + title: "宽度", + type: "number", + editor: { + ...this.numberEditor, + min: 300, + max: 3000, + } + }, + height: { + description: "窗口高度,最小值:200px,最大值:2000px", + title: "高度", + type: "number", + editor: { + ...this.numberEditor, + min: 200, + max: 2000, + } + }, + resizeable: { + description: "允许鼠标拖拽窗口边缘调整尺寸", + title: "允许调整窗口尺寸", + type: "boolean", + visible: true + }, + enableEsc: { + description: "允许ESC关闭", + title: "允许ESC关闭", + type: "boolean", + visible: false + }, + showMaxButton: { + description: "显示最大化按钮", + title: "显示最大化按钮", + type: "boolean", + visible: true + }, + showCloseButton: { + description: "显示关闭按钮", + title: "显示关闭按钮", + type: "boolean", + visible: true + } + } + }; + } + + + private getEventPropConfig(propertyData: any) { + const self: any = this; + const events = ModalEvents; + const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, events); + const properties = self.createBaseEventProperty(initialData); + + return { + title: '事件', + hideTitle: true, + properties, + refreshPanelAfterChanged: true, + tabId: 'commands', + tabName: '交互', + setPropertyRelates(changeObject: any, newPropertyData: any) { + + const parameters = changeObject.propertyValue; + delete propertyData[self.viewModelId]; + if (parameters) { + self.eventsEditorUtils.saveRelatedParameters(propertyData, self.viewModelId, parameters['events'], parameters); + } + events.forEach(event => { + newPropertyData[event.label] = propertyData[event.label]; + }); + } + }; + } + +} diff --git a/packages/ui-vue/components/modal/src/schema/callback-resolvers.ts b/packages/ui-vue/components/modal/src/schema/callback-resolvers.ts new file mode 100644 index 0000000000000000000000000000000000000000..13dd20e54b2b8575dd964a42a05f198c352f3b9a --- /dev/null +++ b/packages/ui-vue/components/modal/src/schema/callback-resolvers.ts @@ -0,0 +1,17 @@ +import { Caller } from "@farris/ui-vue/components/dynamic-resolver"; + +export function createModalCallbackResolver() { + function resolve(viewSchema: Record, caller: Caller) { + const callbacks: Record = {}; + + // 关闭前事件 + callbacks.beforeClose = (data: any): Promise => { + return caller.call('beforeClose', [data, viewSchema]); + }; + + return callbacks; + } + return { + resolve + }; +} diff --git a/packages/ui-vue/components/modal/src/schema/modal.schema.json b/packages/ui-vue/components/modal/src/schema/modal.schema.json new file mode 100644 index 0000000000000000000000000000000000000000..151a11f5ffed1d6988d34e8ad7ce78e9708a6727 --- /dev/null +++ b/packages/ui-vue/components/modal/src/schema/modal.schema.json @@ -0,0 +1,119 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://farris-design.gitee.io/modal.schema.json", + "title": "modal", + "description": "弹窗组件", + "type": "object", + "properties": { + "id": { + "description": "组件唯一标识", + "type": "string" + }, + "type": { + "description": "组件类型", + "type": "string", + "default": "modal" + }, + "appearance": { + "description": "组件外观", + "type": "object", + "properties": { + "class": { + "type": "string" + }, + "style": { + "type": "string" + } + }, + "default": {} + }, + "name": { + "description": "名称", + "type": "string", + "default": null + }, + "title": { + "description": "标题", + "type": "string" + }, + "width": { + "description": "窗口宽度", + "type": "number", + "default": 900 + }, + "height": { + "description": "窗口高度", + "type": "number", + "default": 600 + }, + "contents": { + "description": "弹窗子组件集合", + "type": "array", + "default": [] + }, + "modelValue": { + "description": "是否显示", + "type": "boolean", + "default": false + }, + "showCloseButton": { + "description": "显示关闭按钮", + "type": "boolean", + "default": true + }, + "showMaxButton": { + "description": "显示最大化按钮", + "type": "boolean", + "default": true + }, + "enableEsc": { + "description": "允许ESC关闭", + "type": "boolean", + "default": true + }, + "resizeable": { + "description": "允许调整窗口尺寸", + "type": "boolean", + "default": true + }, + "fitContent": { + "description": "是否自适应", + "type": "boolean", + "default": false + }, + "showButtons": { + "description": "是否显示底部按钮", + "type": "boolean", + "default": true + }, + "mask": { + "description": "是否模态", + "type": "boolean", + "default": true + }, + "draggable": { + "description": "是否允许拖拽调整位置", + "type": "boolean", + "default": true + }, + "onAccept": { + "description": "确定事件", + "type": "object", + "default": null + }, + "onCancel": { + "description": "取消事件", + "type": "object", + "default": null + }, + "beforeClose": { + "description": "关闭前回调", + "type": "object", + "default": null + } + }, + "required": [ + "id", + "type" + ] +} \ No newline at end of file diff --git a/packages/ui-vue/components/modal/src/schema/schema-mapper.ts b/packages/ui-vue/components/modal/src/schema/schema-mapper.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ca33ca080647b9edc66a133396282d6a3b1698f --- /dev/null +++ b/packages/ui-vue/components/modal/src/schema/schema-mapper.ts @@ -0,0 +1,5 @@ +import { MapperFunction, resolveAppearance } from '@farris/ui-vue/components/dynamic-resolver'; + +export const schemaMapper = new Map([ + ['appearance', resolveAppearance] +]); diff --git a/packages/ui-vue/components/modal/src/schema/schema-resolver.ts b/packages/ui-vue/components/modal/src/schema/schema-resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..d36ae7457434b7381b796814e9926d352feacff6 --- /dev/null +++ b/packages/ui-vue/components/modal/src/schema/schema-resolver.ts @@ -0,0 +1,5 @@ +import { DynamicResolver } from "@farris/ui-vue/components/dynamic-resolver"; + +export function schemaResolver(resolver: DynamicResolver, schema: Record, context: Record): Record { + return schema; +} diff --git a/packages/ui-vue/components/notify/src/components/toast.component.tsx b/packages/ui-vue/components/notify/src/components/toast.component.tsx index 590f8f486eab1fad1b6d923ead7954fe15efd494..cc465f0b7ee0fcfc7cd9201f917ae15e2a0c1efd 100644 --- a/packages/ui-vue/components/notify/src/components/toast.component.tsx +++ b/packages/ui-vue/components/notify/src/components/toast.component.tsx @@ -16,6 +16,7 @@ import { computed, defineComponent, ref, SetupContext, watch } from 'vue'; import { NotifyButton, NotifyData } from '../notify.props'; import { ToastProps, toastProps } from './toast.props'; +import { useI18n } from 'vue-i18n'; export default defineComponent({ @@ -23,6 +24,7 @@ export default defineComponent({ props: toastProps, emits: ['close', 'click'] as (string[] & ThisType) | undefined, setup: (props: ToastProps, context: SetupContext) => { + const {locale, t} = useI18n(); const animateIn = ref(props.animate); const animateEnd = 'fadeOut'; const toast = computed(() => { @@ -65,6 +67,13 @@ export default defineComponent({ const shouldShowButtonsInTitle = computed(() => !!toast.value.buttons || !!context.slots.default); + const wrapStyles = computed(() => { + if (locale.value === 'en') { + return { wordBreak: 'keep-all', overflowWrap: 'break-word' }; + } + return {}; + }); + function onCloseToast($event: Event) { $event.stopPropagation(); $event.preventDefault(); @@ -109,7 +118,7 @@ export default defineComponent({ return (
{shouldShowCloseButton.value && ( - )} @@ -122,18 +131,18 @@ export default defineComponent({ {shouldShowTitle.value && ( <> -

+

{shouldShowButtonsInTitle.value && renderNotifyButtons()} )} {shouldShowMessageOnly.value && (toast.value.buttons ? (
- +
{renderNotifyButtons()}
) : ( - + ))}
diff --git a/packages/ui-vue/components/notify/src/notify.service.tsx b/packages/ui-vue/components/notify/src/notify.service.tsx index 9b23449aed13a1f039fa802e1539f4f175688375..2adaa70fcf8e6f689997d2738e511fe09a60f6a6 100644 --- a/packages/ui-vue/components/notify/src/notify.service.tsx +++ b/packages/ui-vue/components/notify/src/notify.service.tsx @@ -1,6 +1,7 @@ import { createApp, inject, onMounted, onUnmounted, reactive, ref, Transition, type App } from "vue"; import { NotifyGlobalConfig, NotifyProps, ShowNotifyParams } from "./notify.props"; import Notify from './notify.component'; +import { LocaleService } from '@farris/ui-vue/components/locale'; export default class NotifyService { @@ -22,7 +23,7 @@ export default class NotifyService { // 获取完全转义后的内容(含 < > & 等字符转义) const escapedContent = container.innerHTML; return escapedContent - .replace(/ /g, ' ') // 普通空格转义 + // .replace(/ /g, ' ') // 普通空格转义 .replace(/\\n/g, '
') // 保留换行 .replace(/\\t/g, '    ') // 保留制表符 .replace(/\\r/g, ''); // 移除回车(通常不需要单独处理) @@ -82,6 +83,7 @@ export default class NotifyService { }); app.provide('NotifyService', this); document.body.appendChild(container); + app.use(LocaleService.i18n); app.mount(container); return app; }; diff --git a/packages/ui-vue/components/number-range/src/components/text-box.component.tsx b/packages/ui-vue/components/number-range/src/components/text-box.component.tsx index 8615a11ce564abcd4570bfcee098019f1db7aad0..3316948ce03bdbec8e5d86d6aeb95b826627cf05 100644 --- a/packages/ui-vue/components/number-range/src/components/text-box.component.tsx +++ b/packages/ui-vue/components/number-range/src/components/text-box.component.tsx @@ -1,11 +1,13 @@ - import { SetupContext, computed } from 'vue'; import { UseTextBox } from '../composition/types'; import { NumberRangeProps } from '../number-range.props'; -export default function (props: NumberRangeProps, context: SetupContext, useTextBoxComposition: UseTextBox, isBeginTextBox: boolean) { +export default function (props: NumberRangeProps, context: SetupContext, useTextBoxComposition: UseTextBox, isBeginTextBox: boolean, locales: any) { const { onBlurTextBox, onFocusTextBox, onInput, onKeyDown, textBoxValue } = useTextBoxComposition; - const placeholder = computed(() => isBeginTextBox ? props.beginPlaceHolder : props.endPlaceHolder); + /** 禁用、只读情况下不显示文本提示 */ + const placeholder = computed(() =>{ + return (props.disabled||props.readonly || !props.editable)?'':isBeginTextBox ? locales.range.begin : locales.range.end; + }); const numberTextBoxClass = computed(() => ({ 'form-control': true, diff --git a/packages/ui-vue/components/number-range/src/designer/number-range.design.component.tsx b/packages/ui-vue/components/number-range/src/designer/number-range.design.component.tsx index c0ebf177382286d1fc5b06ccea8d9e14afdedc9e..e6e514043c0fbd0561e6c5f16a904cc39657a6e8 100644 --- a/packages/ui-vue/components/number-range/src/designer/number-range.design.component.tsx +++ b/packages/ui-vue/components/number-range/src/designer/number-range.design.component.tsx @@ -20,7 +20,7 @@ import { useNumber } from '../composition/use-number'; import { useFormat } from '../composition/use-format'; import { useSpinner } from '../composition/use-spinner'; import { useTextBox } from '../composition/use-text-box'; -import getNumberTextBoxRender from '../components/text-box.component'; +import getNumberTextBoxDesignRender from './text-box.design.component'; import getSpinnerRender from '../components/spinner.component'; import { useDesignerComponent } from '../../../designer-canvas/src/composition/function/use-designer-component'; import { DesignerItemContext } from '../../../designer-canvas/src/types'; @@ -79,7 +79,7 @@ export default defineComponent({ true ); const renderBeginValueSpinner = getSpinnerRender(props, context, useBeginValueSpinnerComposition); - const renderBeginValueNumberTextBox = getNumberTextBoxRender(props, context, useBeginValueTextBoxComposition, true); + const renderBeginValueNumberTextBox = getNumberTextBoxDesignRender(props, context, useBeginValueTextBoxComposition, true); const endValueChangedCallback = (numberValue: string | number) => { context.emit('endValueChange', numberValue); }; @@ -109,7 +109,7 @@ export default defineComponent({ false ); const renderEndValueSpinner = getSpinnerRender(props, context, useEndValueSpinnerComposition); - const renderEndValueNumberTextBox = getNumberTextBoxRender(props, context, useEndValueTextBoxComposition, false); + const renderEndValueNumberTextBox = getNumberTextBoxDesignRender(props, context, useEndValueTextBoxComposition, false); const shouldShowSpinner = computed(() =>props.showButton); diff --git a/packages/ui-vue/components/number-range/src/designer/text-box.design.component.tsx b/packages/ui-vue/components/number-range/src/designer/text-box.design.component.tsx new file mode 100644 index 0000000000000000000000000000000000000000..02c8d575693127fd74736a08f30dd809ce607844 --- /dev/null +++ b/packages/ui-vue/components/number-range/src/designer/text-box.design.component.tsx @@ -0,0 +1,43 @@ + import { SetupContext, computed } from 'vue'; +import { UseTextBox } from '../composition/types'; +import { NumberRangeProps } from '../number-range.props'; + +export default function (props: NumberRangeProps, context: SetupContext, useTextBoxComposition: UseTextBox, isBeginTextBox: boolean) { + const { onBlurTextBox, onFocusTextBox, onInput, onKeyDown, textBoxValue } = useTextBoxComposition; + const placeholder = computed(() =>isBeginTextBox ? props.beginPlaceHolder : props.endPlaceHolder); + + const numberTextBoxClass = computed(() => ({ + 'form-control': true, + 'sub-input': true + })); + + const numberTextBoxStyle = computed(() => { + const styleObject = { + 'text-align': props.textAlign + } as Record; + return styleObject; + }); + + function onChange($event: Event) { + $event.stopPropagation(); + } + + return () => { + return ( + + ); + }; +} diff --git a/packages/ui-vue/components/number-range/src/number-range.component.tsx b/packages/ui-vue/components/number-range/src/number-range.component.tsx index 2b38a5a511a5c0765e6dd1d942175f212a61f246..074a9573dfd1110593f5288b6ab6ce88e0501750 100644 --- a/packages/ui-vue/components/number-range/src/number-range.component.tsx +++ b/packages/ui-vue/components/number-range/src/number-range.component.tsx @@ -23,6 +23,9 @@ import { useTextBox } from './composition/use-text-box'; import getNumberTextBoxRender from './components/text-box.component'; import getSpinnerRender from './components/spinner.component'; +import { useNumberLocales } from '@farris/ui-vue/components/number-spinner'; + + export default defineComponent({ name: 'FNumberRange', props: numberRangeProps, @@ -36,6 +39,7 @@ export default defineComponent({ const endDisplayValue = ref(''); const useNumberComposition = useNumber(props, context); const useFormatComposition = useFormat(props, context, useNumberComposition); + const numberLocales = useNumberLocales(props); const benginValueChangedCallback = (numberValue: string | number) => { context.emit('beginValueChange', numberValue); }; @@ -65,7 +69,7 @@ export default defineComponent({ true ); const renderBeginValueSpinner = getSpinnerRender(props, context, useBeginValueSpinnerComposition); - const renderBeginValueNumberTextBox = getNumberTextBoxRender(props, context, useBeginValueTextBoxComposition, true); + const renderBeginValueNumberTextBox = getNumberTextBoxRender(props, context, useBeginValueTextBoxComposition, true, numberLocales); const endValueChangedCallback = (numberValue: string | number) => { context.emit('endValueChange', numberValue); }; @@ -95,7 +99,7 @@ export default defineComponent({ false ); const renderEndValueSpinner = getSpinnerRender(props, context, useEndValueSpinnerComposition); - const renderEndValueNumberTextBox = getNumberTextBoxRender(props, context, useEndValueTextBoxComposition, false); + const renderEndValueNumberTextBox = getNumberTextBoxRender(props, context, useEndValueTextBoxComposition, false, numberLocales); const { getRealValue } = useNumberComposition; const { format } = useFormatComposition; diff --git a/packages/ui-vue/components/number-range/src/property-config/number-range.property-config.json b/packages/ui-vue/components/number-range/src/property-config/number-range.property-config.json index 89975d2467a2768ed46df7b3a2a081ec413952c5..6c1ea9af744250f5b763ef1755f3311d22c43d13 100644 --- a/packages/ui-vue/components/number-range/src/property-config/number-range.property-config.json +++ b/packages/ui-vue/components/number-range/src/property-config/number-range.property-config.json @@ -41,8 +41,9 @@ "editor": { "description": "Basic Infomation", "title": "组件信息", - "type":"number-range", - "$converter":"/converter/property-editor.converter", + "type": "number-range", + "$converter": "/converter/property-editor.converter", + "parentPropertyID": "editor", "properties": { "readonly": { "description": "", @@ -53,7 +54,7 @@ "description": "", "title": "禁用", "type": "boolean" - }, + }, "editable": { "description": "", "title": "可编辑", @@ -67,4 +68,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/ui-vue/components/number-spinner/index.ts b/packages/ui-vue/components/number-spinner/index.ts index f2a047541749bad7b356c08cbe8c497eb4b5b026..cc7a97a168753e28a95f9b657fab384b9bdbe266 100644 --- a/packages/ui-vue/components/number-spinner/index.ts +++ b/packages/ui-vue/components/number-spinner/index.ts @@ -26,6 +26,7 @@ export * from './src/composition/use-spinner'; export * from './src/composition/use-text-box'; export { default as getNumberTextBoxRender } from './src/components/text-box.component'; export { default as getSpinnerRender } from './src/components/spinner.component'; +export { useNumberLocales } from './src/composition/use-locales'; FNumberSpinner.register = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record) => { componentMap['number-spinner'] = FNumberSpinner; diff --git a/packages/ui-vue/components/number-spinner/src/components/text-box.component.tsx b/packages/ui-vue/components/number-spinner/src/components/text-box.component.tsx index 04709207831ab543a775cbd84f0c92dff3927678..e62d4da0e622d6fba41d84006f966a6e80910e37 100644 --- a/packages/ui-vue/components/number-spinner/src/components/text-box.component.tsx +++ b/packages/ui-vue/components/number-spinner/src/components/text-box.component.tsx @@ -1,15 +1,18 @@ import { SetupContext, computed, nextTick, onMounted, ref } from 'vue'; import { NumberSpinnerProps } from '../number-spinner.props'; import { UseFormat, UseNumber, UseTextBox } from '../composition/types'; +import { useNumberLocales } from '../composition/use-locales'; + export default function ( props: NumberSpinnerProps, context: SetupContext, useTextBoxComposition: UseTextBox ) { + const numberLocales = useNumberLocales(props); const inputElementRef = ref(); const { onBlurTextBox, onFocusTextBox, onInput, onKeyDown, textBoxValue } = useTextBoxComposition; - const placeholder = computed(() => ((props.disabled || props.readonly || !props.editable) && !props.forcePlaceholder ? '' : props.placeholder)); + const placeholder = computed(() => ((props.disabled || props.readonly || !props.editable) && !props.forcePlaceholder ? '' : numberLocales.placeholder)); const numberTextBoxClass = computed(() => ({ 'form-control': true, diff --git a/packages/ui-vue/components/number-spinner/src/composition/use-locales.ts b/packages/ui-vue/components/number-spinner/src/composition/use-locales.ts new file mode 100644 index 0000000000000000000000000000000000000000..d429a8360d158adb67334accc5dce4630e59035d --- /dev/null +++ b/packages/ui-vue/components/number-spinner/src/composition/use-locales.ts @@ -0,0 +1,26 @@ +import { NumberSpinnerProps } from "../number-spinner.props"; +import { useI18n } from 'vue-i18n'; + +export interface NumberLocaleData { + placeholder: string; + range: { + begin: string; + end: string; + }; +} + +export function useNumberLocales(props: NumberSpinnerProps): NumberLocaleData { + const {t: getLocaleValue} = useI18n(); + function getValue(localeKey, propertyValue?: string, defaultValue?: string): any { + if (propertyValue === defaultValue) { + return getLocaleValue(localeKey); + } + return propertyValue || ''; + } + const placeholder = getValue('numberSpinner.placeholder', props.placeholder, '请输入数字'); + const range = { + begin: getValue('numberSpinner.range.begin', props.beginPlaceHolder, '请输入开始数字'), + end: getValue('numberSpinner.range.end', props.endPlaceHolder, '请输入结束数字'), + }; + return { placeholder, range }; +} diff --git a/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts b/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts index 81f2714d4a1f65c75b9c93c79c6b5c4d2f4391f0..108e059aa795b6ab2fbfb5e9c67fdf0e45faea24 100644 --- a/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts +++ b/packages/ui-vue/components/number-spinner/src/composition/use-text-box.ts @@ -29,7 +29,9 @@ export function useTextBox( } const textValue = cleanFormat(inputValue); displayValue.value = format(getRealValue(textValue)); - onNumberValueChanged(getRealValue(textValue)); + if (props.updateOn === 'blur') { + onNumberValueChanged(getRealValue(textValue)); + } context.emit('blur', { event: $event, formatted: displayValue.value, value: modelValue.value }); } @@ -56,8 +58,11 @@ export function useTextBox( inputValue = inputValue || 0; } const textValue = cleanFormat(inputValue); - displayValue.value = textValue; - onNumberValueChanged(getRealValue(textValue)); + if (props.updateOn === 'change') { + displayValue.value = textValue; + onNumberValueChanged(getRealValue(textValue)); + } + context.emit('input', getRealValue(textValue)); } diff --git a/packages/ui-vue/components/number-spinner/src/locales/ui/en.json b/packages/ui-vue/components/number-spinner/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..2c12cbe74599dd1d66fbe63c504269180ce5fac7 --- /dev/null +++ b/packages/ui-vue/components/number-spinner/src/locales/ui/en.json @@ -0,0 +1,9 @@ +{ + "numberSpinner": { + "placeholder": "Please enter the number", + "range": { + "begin": "Please enter the begin number", + "end": "Please enter the end number" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/number-spinner/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/number-spinner/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..50e0c647e7d805de6cee06d517375ca2d59cf133 --- /dev/null +++ b/packages/ui-vue/components/number-spinner/src/locales/ui/zh-CHS.json @@ -0,0 +1,9 @@ +{ + "numberSpinner": { + "placeholder": "请输入数字", + "range": { + "begin": "请输入开始数字", + "end": "请输入结束数字" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/number-spinner/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/number-spinner/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..5713af7825a21196cd0d0553790c0301790f2863 --- /dev/null +++ b/packages/ui-vue/components/number-spinner/src/locales/ui/zh-CHT.json @@ -0,0 +1,9 @@ +{ + "numberSpinner": { + "placeholder": "請輸入數字", + "range": { + "begin": "請輸入開始數字", + "end": "請輸入結束數字" + } + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/number-spinner/src/number-spinner.component.tsx b/packages/ui-vue/components/number-spinner/src/number-spinner.component.tsx index 7da961aebb75c66ceee4acfe65b358ab8b199a64..b5c3b809114899da0074fa4906b4601e639a80f9 100644 --- a/packages/ui-vue/components/number-spinner/src/number-spinner.component.tsx +++ b/packages/ui-vue/components/number-spinner/src/number-spinner.component.tsx @@ -44,7 +44,8 @@ export default defineComponent({ 'f-cmp-number-spinner':true, 'f-state-disabled': props.disable, 'f-state-readonly': props.readonly && !props.disable, - 'f-state-focus': isFocus.value + 'f-state-focus': isFocus.value, + 'spinner-hidden':!shouldShowSpinner.value }; return classObject; diff --git a/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts b/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts index 8bf68c6fcddd80658937871f2e8eac57cd3a19aa..5a434617cfdc6556c754b5ae7e1d4d50a89b9cd6 100644 --- a/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts +++ b/packages/ui-vue/components/number-spinner/src/number-spinner.props.ts @@ -134,7 +134,10 @@ export const numberSpinnerProps = { /** * 是否启用大数 */ - bigNumber: { type: Boolean, default: false } + bigNumber: { type: Boolean, default: false }, + + /** 更新方式 blur | change */ + updateOn: { type: String, default: 'change' }, } as Record; export type NumberSpinnerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/number-spinner/src/property-config/number-spinner.property-config.ts b/packages/ui-vue/components/number-spinner/src/property-config/number-spinner.property-config.ts index d47b9d55945c358cc6fd2d16f3709366869d5247..8e5560678c2c9bcd85062d49fa55229829780e38 100644 --- a/packages/ui-vue/components/number-spinner/src/property-config/number-spinner.property-config.ts +++ b/packages/ui-vue/components/number-spinner/src/property-config/number-spinner.property-config.ts @@ -37,24 +37,24 @@ export class NumberSpinnerProperty extends InputBaseProperty { precision: propertyData?.editor?.precision } }, - max: { + min: { description: "", - title: "最大值", + title: "最小值", type: "number", editor: { nullable: true, - min: propertyData?.editor?.min === undefined ? undefined : propertyData?.editor?.min, + max: propertyData?.editor?.max, precision: propertyData?.editor?.precision }, refreshPanelAfterChanged: true }, - min: { + max: { description: "", - title: "最小值", + title: "最大值", type: "number", editor: { nullable: true, - max: propertyData?.editor?.max, + min: propertyData?.editor?.min === undefined ? undefined : propertyData?.editor?.min, precision: propertyData?.editor?.precision }, refreshPanelAfterChanged: true diff --git a/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json b/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json index b1e4dfdf30d69c01c273bf5853528c2ef21b6119..a76e4e50ab16b65db14611ed186e62b1010dfc9d 100644 --- a/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json +++ b/packages/ui-vue/components/number-spinner/src/schema/number-spinner.schema.json @@ -124,6 +124,16 @@ "description": "", "type": "boolean", "default": false + }, + "updateOn": { + "description": "", + "type": "string", + "default": "blur" + }, + "showButton":{ + "description": "", + "type": "boolean", + "default": true } }, "required": [ diff --git a/packages/ui-vue/components/page-header/index.ts b/packages/ui-vue/components/page-header/index.ts index cb3e8d0805d33e6597d5b98fea5ba5ac405c5514..670b3084cf0997a7931f547006279e3cda83f556 100644 --- a/packages/ui-vue/components/page-header/index.ts +++ b/packages/ui-vue/components/page-header/index.ts @@ -2,7 +2,7 @@ import { App } from 'vue'; import PageHeader from './src/page-header.component'; import PageHeaderDesign from './src/designer/page-header.design.component'; -import { eventHandlerResolver, propsResolver } from './src/page-header.props'; +import { eventHandlerResolver, propsDesignResolver, propsResolver } from './src/page-header.props'; export * from './src/page-header.props'; export { PageHeader }; @@ -18,6 +18,6 @@ export default { }, registerDesigner(componentMap: Record, propsResolverMap: Record, configResolverMap: Record): void { componentMap['page-header'] = PageHeaderDesign; - propsResolverMap['page-header'] = propsResolver; + propsResolverMap['page-header'] = propsDesignResolver; } }; diff --git a/packages/ui-vue/components/page-header/src/designer/page-header.design.component.tsx b/packages/ui-vue/components/page-header/src/designer/page-header.design.component.tsx index e1a3acda36113905c3d31a7fe9393459a256cc34..0e9428c39ee698565939519ccae70fe2cd1eb78e 100644 --- a/packages/ui-vue/components/page-header/src/designer/page-header.design.component.tsx +++ b/packages/ui-vue/components/page-header/src/designer/page-header.design.component.tsx @@ -6,6 +6,7 @@ import { useDesignerRules } from './use-designer-rules'; import { responseToolbarResolver } from '../../../response-toolbar'; import FResponseToolbarDesignComponent from '../../../response-toolbar/src/designer/response-toolbar.design.component'; import FDesignerInnerItem from '../../../designer-canvas/src/components/designer-inner-item.component'; +import { getCustomClass } from "@farris/ui-vue/components/common"; export default defineComponent({ name: 'FPageHeaderDesign', @@ -71,19 +72,60 @@ export default defineComponent({ classConverter(classObject, props.buttonClass || ''); return classObject; }); + const titleContentClass = computed(() => { + const classObject = { + 'f-title': true + } as Record; + return getCustomClass(classObject, props.titleContentClass); + }); + const contentClass = computed(() => { + const classObject = { + 'f-content': true + } as Record; + return getCustomClass(classObject, props.contentClass); + }); + const downContentClass = computed(() => { + const classObject = { + 'f-page-header-extend': true + } as Record; + + return getCustomClass(classObject, props.downContentClass); + }); + function renderTitleArea() { + // 模板HTML + if (props.titleContentHtml) { + return
; + } + return
+ {props.showIcon && props.icon ? + + + + : ''} +

{props.title}

+
+ } + function renderContentArea() { + // 模板HTML + if (props.contentHtml) { + return
; + } + return null; + } + + function renderDownArea() { + // 模板HTML + if (props.downContentHtml) { + return
; + } + return null; + } return () => { return (
+ {renderDownArea()}
); }; diff --git a/packages/ui-vue/components/page-header/src/page-header.component.tsx b/packages/ui-vue/components/page-header/src/page-header.component.tsx index f809da9f724b2d63f67ab8eb32715077509df471..c1d6ee1d913359e632fc2d00fb8595868f1ee4e7 100644 --- a/packages/ui-vue/components/page-header/src/page-header.component.tsx +++ b/packages/ui-vue/components/page-header/src/page-header.component.tsx @@ -2,6 +2,7 @@ import { defineComponent, ref, computed, watch } from "vue"; import { PageHeaderProps, pageHeaderProps } from "./page-header.props"; import FResponseToolbar from '@farris/ui-vue/components/response-toolbar'; +import { getCustomClass } from "@farris/ui-vue/components/common"; export default defineComponent({ name: 'FPageHeader', @@ -13,59 +14,43 @@ export default defineComponent({ const onClickToolbarItem = (payload: any, itemId: string) => { context.emit('click', payload, itemId); }; - - function classConverter(currentClass: object, customClass: string) { - if (currentClass && customClass) { - const customClassArray = customClass.split(' '); - customClassArray.reduce((result: any, className: any) => { - result[className] = true; - return result; - }, currentClass); - } - } const headerClass = computed(() => { const classObject = { 'f-page-header': true, 'd-none': !props.visible } as Record; - classConverter(classObject, props.customClass); - return classObject; + return getCustomClass(classObject, props.customClass); }); const iconClass = computed(() => { const classObject = { 'f-title-icon': true } as Record; - classConverter(classObject, props.iconClass); - return classObject; + return getCustomClass(classObject, props.iconClass); }); const icon = computed(() => { const classObject = { 'f-icon': true } as Record; - classConverter(classObject, props.icon); - return classObject; + return getCustomClass(classObject, props.icon); }); const titleContentClass = computed(() => { const classObject = { 'f-title': true } as Record; - classConverter(classObject, props.titleContentClass); - return classObject; + return getCustomClass(classObject, props.titleContentClass); }); const contentClass = computed(() => { const classObject = { 'f-content': true } as Record; - classConverter(classObject, props.contentClass); - return classObject; + return getCustomClass(classObject, props.contentClass); }); const downContentClass = computed(() => { const classObject = { - 'f-content': true + 'f-page-header-extend': true } as Record; - classConverter(classObject, props.downContentClass); - return classObject; + return getCustomClass(classObject, props.downContentClass); }); const prePaginationIconClass = computed(() => { @@ -91,28 +76,41 @@ export default defineComponent({ context.emit('nextPaginationClick', payload); } } - function renderLeftArea() { - return context.slots.titleContent ? -
{context.slots.titleContent()}
- : -
- {props.showIcon && props.icon ? : ''} -

{props.title}

- {props.subTitle ?
{props.subTitle}
: ''} + function renderTitleArea() { + // 模板 + if (context.slots.titleContent) { + return
{context.slots.titleContent()}
; + } + // 模板HTML + if (props.titleContentRenderFunction) { + return
{props.headerContentRenderFunction()}
; + } + return
+ {props.showIcon && props.icon ? : ''} +

{props.title}

+ {props.subTitle ?
{props.subTitle}
: ''} - {props.showPagination ? -
- - -
- : '' - } -
; + {props.showPagination ? +
+ + +
+ : '' + } +
; } function renderContentArea() { - return context.slots.content ? < div class={contentClass.value} > {context.slots.content()}
: ''; + // 模板 + if (context.slots.content) { + return
{context.slots.content()}
; + } + // 模板HTML + if (props.contentRenderFunction) { + return
{props.contentRenderFunction()}
; + } + return null; } function renderResponseToolbar() { @@ -122,7 +120,15 @@ export default defineComponent({ } function renderDownArea() { - return context.slots.downContent ?
{context.slots.downContent()}
: ''; + // 模板 + if (context.slots.downContent) { + return
{context.slots.downContent()}
; + } + // 模板HTML + if (props.downContentRenderFunction) { + return
{props.downContentRenderFunction()}
; + } + return null; } watch(() => props.buttons, (newValue) => { @@ -135,7 +141,7 @@ export default defineComponent({ return props.visible ?
diff --git a/packages/ui-vue/components/page-header/src/page-header.props.ts b/packages/ui-vue/components/page-header/src/page-header.props.ts index 8aa61ab03145408667e3ba109e82507e42195ba6..dcd002afb001bfeef4c9871fbdce11c4c1baa7a5 100644 --- a/packages/ui-vue/components/page-header/src/page-header.props.ts +++ b/packages/ui-vue/components/page-header/src/page-header.props.ts @@ -1,5 +1,5 @@ -import { ExtractPropTypes } from "vue"; +import { ExtractPropTypes, PropType, RenderFunction } from "vue"; import { createPageHeaderEventHandlerResolver, createPropsResolver } from "../../dynamic-resolver"; import { schemaMapper } from "./schema/schema-mapper"; import pageHeaderSchema from './schema/page-header.schema.json'; @@ -55,14 +55,27 @@ export const pageHeaderProps = { visible: { type: Boolean, default: true }, /** 控制按钮是否可见 */ - buttonsVisible: { type: Boolean, default: true } + buttonsVisible: { type: Boolean, default: true }, + /** 标题区域 */ + titleRenderFunction: { type: Function as PropType }, + /** 内容区域 */ + contentRenderFunction: { type: Function as PropType }, + /** 下方扩展区域 */ + downContentRenderFunction: { type: Function as PropType }, + } as Record; export type PageHeaderProps = ExtractPropTypes; export const pageHeaderDesignerProps = Object.assign({}, pageHeaderProps, { - componentId: { type: String, default: '' } + componentId: { type: String, default: '' }, + titleContentHtml: { type: String, default: '' }, + contentHtml: { type: String, default: '' }, + downContentHtml: { type: String, default: '' }, }); export type PageHeaderDesignerProps = ExtractPropTypes; export const propsResolver = createPropsResolver(pageHeaderProps, pageHeaderSchema, schemaMapper, schemaResolver); + +export const propsDesignResolver = createPropsResolver(pageHeaderDesignerProps, pageHeaderSchema, schemaMapper, schemaResolver); + export const eventHandlerResolver = createPageHeaderEventHandlerResolver(); diff --git a/packages/ui-vue/components/page-header/src/property-config/page-header.property-config.ts b/packages/ui-vue/components/page-header/src/property-config/page-header.property-config.ts index f7ef320510b665efbf232bf259efae4c566d8a4e..88f107097488d5dd42e07c7a02f75f918282d574 100644 --- a/packages/ui-vue/components/page-header/src/property-config/page-header.property-config.ts +++ b/packages/ui-vue/components/page-header/src/property-config/page-header.property-config.ts @@ -11,8 +11,10 @@ export class PageHeaderProperty extends BaseControlProperty { this.propertyConfig.categories['basic'] = this.getBasicPropConfig(propertyData); // 外观 this.propertyConfig.categories['appearance'] = this.getAppearanceProperties(propertyData); + // 模板配置 + // this.propertyConfig.categories['template'] = this.getTemplateConfig(propertyData); // 行为 - this.propertyConfig.categories['behavior']=this.getBehaviorConfig(propertyData); + this.propertyConfig.categories['behavior'] = this.getBehaviorConfig(propertyData); return this.propertyConfig; } @@ -52,5 +54,58 @@ export class PageHeaderProperty extends BaseControlProperty { } }; } + getTemplateConfig(propertyData) { + return { + title: '模板配置', + description: '', + properties: { + titleContentClass: { + title: '标题模板样式', + type: 'string', + description: '标题模板外层容器的自定义样式' + }, + titleContentHtml: { + title: '标题模板', + type: 'string', + description: '设置标题HTML模板,替代图标和标题文字区域', + refreshPanelAfterChanged: true, + editor: { + type: "code-editor", + language: "html", + }, + }, + contentClass: { + title: '内容模板样式', + type: 'string', + description: '内容模板外层容器的自定义样式' + }, + contentHtml: { + title: '内容模板', + type: 'string', + description: '设置页头中间区域的模板', + refreshPanelAfterChanged: true, + editor: { + type: "code-editor", + language: "html", + }, + }, + downContentClass: { + title: '扩展模板样式', + type: 'string', + description: '扩展模板外层容器的自定义样式' + }, + downContentHtml: { + title: '扩展模板', + type: 'string', + description: '设置页头下方区域的模板', + refreshPanelAfterChanged: true, + editor: { + type: "code-editor", + language: "html", + }, + } + } + }; + } }; diff --git a/packages/ui-vue/components/page-header/src/schema/page-header.schema.json b/packages/ui-vue/components/page-header/src/schema/page-header.schema.json index 56faf8b4e198c9b338e3a49269e5bab1c1d75a3a..6c8c8f822600bbb121cf454dd5070601d6ba2a92 100644 --- a/packages/ui-vue/components/page-header/src/schema/page-header.schema.json +++ b/packages/ui-vue/components/page-header/src/schema/page-header.schema.json @@ -136,6 +136,41 @@ "description": "", "type": "boolean", "default": true + }, + "buttonsVisible": { + "description": "", + "type": "boolean", + "default": true + }, + "titleContentHtml": { + "description": "", + "type": "string", + "default": "" + }, + "titleContentRenderFunction": { + "description": "", + "type": "function", + "default": null + }, + "contentHtml": { + "description": "", + "type": "string", + "default": "" + }, + "contentRenderFunction": { + "description": "", + "type": "function", + "default": null + }, + "downContentHtml": { + "description": "", + "type": "string", + "default": "" + }, + "downContentRenderFunction": { + "description": "", + "type": "function", + "default": null } }, "required": [ diff --git a/packages/ui-vue/components/page-header/src/schema/schema-mapper.ts b/packages/ui-vue/components/page-header/src/schema/schema-mapper.ts index 47a7733d2ea4acfd0bfd96d2d0c5248ede9ed0e5..e801e9a2ebf801fc120fda3c12274c6061909f18 100644 --- a/packages/ui-vue/components/page-header/src/schema/schema-mapper.ts +++ b/packages/ui-vue/components/page-header/src/schema/schema-mapper.ts @@ -4,3 +4,4 @@ export const schemaMapper = new Map([ ['appearance', resolveAppearance], ['toolbar', resolveToolbar] // 内部处理可见、Id、样式、按钮 ]); + diff --git a/packages/ui-vue/components/pagination/src/components/buttons/goto-buttons.component.tsx b/packages/ui-vue/components/pagination/src/components/buttons/goto-buttons.component.tsx index 46461e2b045f2bd5c3a429b18ad445c81483fd05..c5bf1dc533a3ed20b42ba9a7067bfbeaadec40e6 100644 --- a/packages/ui-vue/components/pagination/src/components/buttons/goto-buttons.component.tsx +++ b/packages/ui-vue/components/pagination/src/components/buttons/goto-buttons.component.tsx @@ -14,14 +14,15 @@ * limitations under the License. */ import { ComputedRef, ref, Ref, SetupContext, watch } from 'vue'; - +import { useI18n } from 'vue-i18n'; export default function ( currentPage: Ref, lastPage: ComputedRef, currentPageSize: Ref, context: SetupContext ) { - const gotoPrefix = ref('跳转至'); + const { t:getLocaleValue } = useI18n(); + const gotoPrefix = ref(getLocaleValue('pagination.goto.prefix')); const gotoSuffix = ref(''); const pageNumber = ref(currentPage.value); watch(pageNumber, (value: number, previousValue: number) => { diff --git a/packages/ui-vue/components/pagination/src/components/pages/page-info.component.tsx b/packages/ui-vue/components/pagination/src/components/pages/page-info.component.tsx index 0d082d3854bf2681b751afee816302665d92f5a0..b10e26dd077c9b8c3ae1370f84e7fe15b8082170 100644 --- a/packages/ui-vue/components/pagination/src/components/pages/page-info.component.tsx +++ b/packages/ui-vue/components/pagination/src/components/pages/page-info.component.tsx @@ -14,10 +14,11 @@ * limitations under the License. */ import { computed, ref, Ref } from 'vue'; - +import { useI18n } from 'vue-i18n'; export default function (position: Ref, totalItems: Ref) { - const prefixTotalItems = ref('共'); - const suffixTotalItems = ref('条'); + const { t: getLocaleValue } = useI18n(); + const prefixTotalItems = ref(getLocaleValue('pagination.totalInfo.firstText')); + const suffixTotalItems = ref(getLocaleValue('pagination.totalInfo.lastText')); const pageInfoClass = computed(() => { const classObject = { diff --git a/packages/ui-vue/components/pagination/src/components/pages/page-list.component.tsx b/packages/ui-vue/components/pagination/src/components/pages/page-list.component.tsx index cf28ee9ba0fb0bb5f58d57089b15c0b57ffef93b..bba2119309a3aec717dc4e6ca33ac4e3a813c803 100644 --- a/packages/ui-vue/components/pagination/src/components/pages/page-list.component.tsx +++ b/packages/ui-vue/components/pagination/src/components/pages/page-list.component.tsx @@ -14,16 +14,17 @@ * limitations under the License. */ import { computed, ref, Ref, SetupContext } from 'vue'; - +import { useI18n } from 'vue-i18n'; export default function ( currentPage: Ref, currentPageSize: Ref, pageList: Ref, totalItems: Ref, context: SetupContext) { + const { t: getLocaleValue } = useI18n(); const shouldShowPagePanel = ref(false); - const prefixPageSize = ref('显示'); - const suffixPageSize = ref('条'); + const prefixPageSize = ref(getLocaleValue('pagination.show')); + const suffixPageSize = ref(getLocaleValue('pagination.totalInfo.lastText')); const pageListClass = computed(() => { const classObject = { diff --git a/packages/ui-vue/components/pagination/src/locales/designer/en.json b/packages/ui-vue/components/pagination/src/locales/designer/en.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/pagination/src/locales/designer/zh-CHS.json b/packages/ui-vue/components/pagination/src/locales/designer/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/pagination/src/locales/designer/zh-CHT.json b/packages/ui-vue/components/pagination/src/locales/designer/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/packages/ui-vue/components/pagination/src/locales/ui/en.json b/packages/ui-vue/components/pagination/src/locales/ui/en.json new file mode 100644 index 0000000000000000000000000000000000000000..00c1e158d28bd79d7d49deb1bc77fd5a7db9fbdc --- /dev/null +++ b/packages/ui-vue/components/pagination/src/locales/ui/en.json @@ -0,0 +1,20 @@ +{ + "pagination": { + "message": "Total {1} Items ", + "totalInfo": { + "firstText": "Total", + "lastText": "Items" + }, + "pageList": { + "firstText": "Display", + "lastText": "Items" + }, + "previous": "Previous", + "next": "Next", + "goto": { + "prefix": "Go to", + "suffix": "" + }, + "show": "Display" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/pagination/src/locales/ui/zh-CHS.json b/packages/ui-vue/components/pagination/src/locales/ui/zh-CHS.json new file mode 100644 index 0000000000000000000000000000000000000000..f3b8081e5870d46e9022133c4c63caf7d6a10025 --- /dev/null +++ b/packages/ui-vue/components/pagination/src/locales/ui/zh-CHS.json @@ -0,0 +1,20 @@ +{ + "pagination": { + "message": "共 {1} 条", + "totalInfo": { + "firstText": "共", + "lastText": "条" + }, + "pageList": { + "firstText": "每页", + "lastText": "条" + }, + "previous": "上一页", + "next": "下一页", + "goto": { + "prefix": "跳转至", + "suffix": "页" + }, + "show": "显示" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/pagination/src/locales/ui/zh-CHT.json b/packages/ui-vue/components/pagination/src/locales/ui/zh-CHT.json new file mode 100644 index 0000000000000000000000000000000000000000..972e29061efc643b4371fa89047fbe2bb3c9e081 --- /dev/null +++ b/packages/ui-vue/components/pagination/src/locales/ui/zh-CHT.json @@ -0,0 +1,20 @@ +{ + "pagination": { + "message": "共 {1} 條 ", + "totalInfo": { + "firstText": "共", + "lastText": "條" + }, + "pageList": { + "firstText": "每頁", + "lastText": "條" + }, + "previous": "上一頁", + "next": "下一頁", + "goto": { + "prefix": "跳轉至", + "suffix": "頁" + }, + "show": "顯示" + } +} \ No newline at end of file diff --git a/packages/ui-vue/components/popover/src/composition/use-position.ts b/packages/ui-vue/components/popover/src/composition/use-position.ts index 1a77275fe0b0f1f115e1683e13c374b45446cf30..31020294c399d0bcc53d404d22b3e563248494a4 100644 --- a/packages/ui-vue/components/popover/src/composition/use-position.ts +++ b/packages/ui-vue/components/popover/src/composition/use-position.ts @@ -269,7 +269,7 @@ export function usePosition( const referenceRect = referenceElement.getBoundingClientRect(); originalReferenceTop.value = referenceRect.top; originalReferenceLeft.value = referenceRect.left; - const arrowRect = arrowRef.value?.getBoundingClientRect(); + const arrowRect = arrowRef.value?arrowRef.value.getBoundingClientRect():{height:4,width:4}; const popoverRect = popoverRef.value?.getBoundingClientRect(); popoverWidth.value = popoverRect.width; diff --git a/packages/ui-vue/components/popover/src/popover.component.tsx b/packages/ui-vue/components/popover/src/popover.component.tsx index 40d0a0d71df109feea392cdaf6cff6ca25b98ba6..683b78317fd6796be13b31f349b289f2f7629a65 100644 --- a/packages/ui-vue/components/popover/src/popover.component.tsx +++ b/packages/ui-vue/components/popover/src/popover.component.tsx @@ -60,6 +60,10 @@ export default defineComponent({ popoverClassObject[originPopover] = true; popoverClassObject[bsPopover] = true; const customClassArray = (props.class || '').split(' '); + if(props.isSimpleTips){ + // 如果是提示,并且没有自定义样式,使用提示的自定义样式 + customClassArray.unshift('popover-tips'); + } customClassArray.reduce>((classObject, classString) => { classObject[classString] = true; return classObject; @@ -79,9 +83,19 @@ export default defineComponent({ styles.visibility = showPopover.value ? 'visible' : 'hidden'; return styles; }); + /** + * 如果是提示,默认带箭头 + */ + const showArrow=computed(()=>{ + if(props.isSimpleTips){ + return true; + } + return props.showArrow; + }); const { delayedValue } = useDelayedRef(popupStyles, 50); + onMounted(() => { if (shouldFitWidthToReference.value) { fitToReference(reference.value); @@ -103,7 +117,7 @@ export default defineComponent({
{ payload.stopPropagation(); }} onClick={(payload: MouseEvent) => { payload.stopPropagation(); }}> - {props.showArrow &&
} + {showArrow.value &&
} {shouldShowTitle.value &&

{props.title}

}
{context.slots.default && context.slots?.default()}
diff --git a/packages/ui-vue/components/popover/src/popover.props.ts b/packages/ui-vue/components/popover/src/popover.props.ts index 1dadd1250e246bf26204ad1857d3298854fea50a..dd00ae2371136d07499e8ad4c91b01286a7d2cd1 100644 --- a/packages/ui-vue/components/popover/src/popover.props.ts +++ b/packages/ui-vue/components/popover/src/popover.props.ts @@ -20,8 +20,13 @@ export type PopoverPlacement = 'top' | 'bottom' | 'left' | 'right' | 'auto' | 'b export const popoverProps = { id: { type: String }, arrowOffsetX: { type: Number, default: 0 }, - class: { type: String }, + class: { type: String, default: '' }, customStyles: { type: Object as PropType>, default: null }, + /** + * popover应用场景有提示、展示其他内容,可能在界面呈现上会有区别。 + * 如果做提示使用,需要特殊样式,通过此属性控制在没有自定义样式的情况下,指定特殊样式 + */ + isSimpleTips: { type: Boolean, default: false }, fitContent: { type: Boolean, default: false }, /** * 指定要弹出的内容插入到哪个元素 @@ -29,27 +34,41 @@ export const popoverProps = { host: { type: Object as PropType }, + /** + * 未被使用 + */ leftBoundary: { type: Object as PropType }, keepWidthWithReference: { type: Boolean, default: false }, minWidth: { type: Number, default: -1 }, + /** + * 未被使用 + */ offsetX: { type: Object as PropType, default: ref(0) }, placement: { type: String as PropType, default: 'bottom' }, reference: { type: Object as PropType }, + /** + * 未被使用 + */ rightBoundary: { type: Object as PropType }, - showArrow: { type: Boolean, default: true }, + /** + * popover应用场景有提示、展示其他内容。 + * 通常展示其他内容,不显示箭头。 + * 提示通常展示箭头。 + */ + showArrow: { type: Boolean, default: false}, title: { type: String }, visible: { type: Boolean, default: false }, zIndex: { type: Number, default: -1 }, /** * 根据空间大小重新调整,原下拉面板内容指定的高度 */ - limitContentBySpace:{ type: Boolean, default: false } + limitContentBySpace: { type: Boolean, default: false } }; export type PopoverProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/popover/src/popover.service.tsx b/packages/ui-vue/components/popover/src/popover.service.tsx index acbe0eafedbf29f2518869db2e3440b31fd794c4..9a50fafd24e42bc1a17ca2ff1417bde11d61582c 100644 --- a/packages/ui-vue/components/popover/src/popover.service.tsx +++ b/packages/ui-vue/components/popover/src/popover.service.tsx @@ -1,6 +1,7 @@ import { App, createApp, onUnmounted } from 'vue'; import FPopover from './popover.component'; import { JSX } from 'vue/jsx-runtime'; +import { LocaleService } from '@farris/ui-vue/components/locale'; export interface PopoverServiceOptions { reference: any; @@ -52,6 +53,7 @@ function createPopoverInstance(options: PopoverServiceOptions): App { } }); document.body.appendChild(container); + popoverApp.use(LocaleService.i18n); popoverApp.mount(container); return popoverApp; } diff --git a/packages/ui-vue/components/property-editor/index.ts b/packages/ui-vue/components/property-editor/index.ts index 4b58d5c871c264f7e92ef62b707c80efbbadc9bc..2f4d755190886dd562bd45362212554648b62d17 100644 --- a/packages/ui-vue/components/property-editor/index.ts +++ b/packages/ui-vue/components/property-editor/index.ts @@ -14,20 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { App } from 'vue'; -import PropertyEditor from './src/property-editor.component'; +import FPropertyEditor from './src/property-editor.component'; import { propsResolver } from './src/property-editor.props'; +import { withInstall } from '@farris/ui-vue/components/common'; export * from './src/property-editor.props'; -export { PropertyEditor }; -export default { - install(app: App): void { - app.component(PropertyEditor.name as string, PropertyEditor); - }, - register(componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record): void { - componentMap['property-editor'] = PropertyEditor; - propsResolverMap['property-editor'] = propsResolver; - } +FPropertyEditor.register = (componentMap: Record, propsResolverMap: Record, configResolverMap: Record, resolverMap: Record) => { + componentMap['property-editor'] = FPropertyEditor; + propsResolverMap['property-editor'] = propsResolver; }; +export { FPropertyEditor }; +export default withInstall(FPropertyEditor); diff --git a/packages/ui-vue/components/property-editor/src/components/const/const-property.css b/packages/ui-vue/components/property-editor/src/components/const/const-property.css index 4796a723fb2e9183594e81537c58dc03b734d42f..8739fd510ca4ad5f5db171d0f08f5d1b6b9725a8 100644 --- a/packages/ui-vue/components/property-editor/src/components/const/const-property.css +++ b/packages/ui-vue/components/property-editor/src/components/const/const-property.css @@ -1,6 +1,4 @@ .f-property-editor-const-container { - float: left; - width: 65%; margin-left: 3%; background: #FFFFFF; } \ No newline at end of file diff --git a/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css b/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css index ffa645de94e0d71efd2759e5e7a918b9eac81b8c..a9de211a06bdc4987d8bd508f01974a5b027b1ce 100644 --- a/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css +++ b/packages/ui-vue/components/property-editor/src/components/custom/custom-property.css @@ -1,6 +1,5 @@ .f-property-editor-customize-container { - width: 65%; - float: left; - margin-left: 3%; + width: 100%; + padding-left: 3%; background: #FFFFFF; } \ No newline at end of file diff --git a/packages/ui-vue/components/property-editor/src/components/expression/expression-property.component.tsx b/packages/ui-vue/components/property-editor/src/components/expression/expression-property.component.tsx index 95b1e1939541f80a9a3767104a35df07baf9125d..c675623ad679469ecfca715a8ec77348d3ce0ab3 100644 --- a/packages/ui-vue/components/property-editor/src/components/expression/expression-property.component.tsx +++ b/packages/ui-vue/components/property-editor/src/components/expression/expression-property.component.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ref, SetupContext } from 'vue'; +import { ref, SetupContext, watch } from 'vue'; import { ExpressionPropertyProps } from './expression-property.props'; import './expression-property.css'; import { UsePropertyValue } from '../../composition/types'; @@ -32,7 +32,7 @@ export default function ( /** 表达式相关方法 */ const { getExpressionValue } = useExpression(props, context, usePropertyValueComposition); /** 表达式属性值 */ - const expressionValue = getExpressionValue(); + let expressionValue = getExpressionValue(); const { expressionInfo } = expressionValue.value; /** 表达式的规则值 */ const expressionRule = ref(expressionInfo.value); @@ -41,14 +41,20 @@ export default function ( /** 表达式编辑器所需的配置 */ const expressionConfig = ref(props.expressionConfig); + watch(() => [props.expressionConfig], () => { + expressionConfig.value = props.expressionConfig; + expressionValue = getExpressionValue(); + expressionRule.value = expressionValue.value.expressionInfo.value; + }); + function onSubmitModal({ expression, message }) { if (!expression) { return; } expressionValue.value.expressionInfo.value = expression; - if(message){ + if (message) { expressionValue.value.expressionInfo.message = message; - }else{ + } else { delete expressionValue.value.expressionInfo.message; } triggerValueChange(expressionValue.value); diff --git a/packages/ui-vue/components/property-editor/src/components/expression/expression-property.css b/packages/ui-vue/components/property-editor/src/components/expression/expression-property.css index fa335d0e915d6deff2c7c1575ccc8c04791768f7..2667fec45ca693e86c84e38982cba430f0ac1e2e 100644 --- a/packages/ui-vue/components/property-editor/src/components/expression/expression-property.css +++ b/packages/ui-vue/components/property-editor/src/components/expression/expression-property.css @@ -1,5 +1,3 @@ .f-property-editor-expression-container { - width: 65%; - float: left; margin-left: 3%; } \ No newline at end of file diff --git a/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.component.tsx b/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.component.tsx index fbacd752008c44aa743fd93cd018cf6191799988..5b98e38003af20fca826255a1909642fe3344e0f 100644 --- a/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.component.tsx +++ b/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.component.tsx @@ -16,7 +16,7 @@ import { Ref, ref, SetupContext } from 'vue'; import { StateMachinePropertyProps } from './state-machine-property.props'; import { FComboList } from '@farris/ui-vue/components/combo-list'; -import './state-machine-property.css'; +import './state-machine-property.scss'; import { useStateMachine } from '../../composition/use-state-machine'; import { StateMachineItem, UsePropertyValue } from '../../composition/types'; diff --git a/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.css b/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.scss similarity index 97% rename from packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.css rename to packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.scss index 70d83cd6adc7c30edd88e21af6ef6c19267bb59e..4985fd433d71b89f55d486821a7677eff3ac6412 100644 --- a/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.css +++ b/packages/ui-vue/components/property-editor/src/components/state-machine/state-machine-property.scss @@ -1,6 +1,4 @@ .f-property-editor-stateMachine-container { - width: 65%; - float: left; margin-left: 3%; background: #FFFFFF; border: 1px solid #D8DCE6; diff --git a/packages/ui-vue/components/property-editor/src/components/variable/variable-property.component.tsx b/packages/ui-vue/components/property-editor/src/components/variable/variable-property.component.tsx index 328a0bd5080a22265fa7ae41f8a270381657e841..b2446cbee129913f12ccaeb5d1a16da7644494df 100644 --- a/packages/ui-vue/components/property-editor/src/components/variable/variable-property.component.tsx +++ b/packages/ui-vue/components/property-editor/src/components/variable/variable-property.component.tsx @@ -16,7 +16,7 @@ import { computed, Ref, ref, SetupContext } from 'vue'; import { VariablePropertyProps } from './variable-property.props'; import { FComboList } from '@farris/ui-vue/components/combo-list'; -import './variable-property.css'; +import './variable-property.scss'; import { UsePropertyValue, VariableItem, VariableValue } from '../../composition/types'; import { useVariable } from '../../composition/use-variable'; diff --git a/packages/ui-vue/components/property-editor/src/components/variable/variable-property.css b/packages/ui-vue/components/property-editor/src/components/variable/variable-property.scss similarity index 96% rename from packages/ui-vue/components/property-editor/src/components/variable/variable-property.css rename to packages/ui-vue/components/property-editor/src/components/variable/variable-property.scss index 5e30182cddfdcdf7f975d660b9f04c7ac0c0f696..afae774db1b0fbce1180f2ac307afc6337ac6c9d 100644 --- a/packages/ui-vue/components/property-editor/src/components/variable/variable-property.css +++ b/packages/ui-vue/components/property-editor/src/components/variable/variable-property.scss @@ -1,6 +1,4 @@ .f-property-editor-variable-container { - float: left; - width: 65%; margin-left: 3%; .f-property-editor-variable-add-button { margin-left: 2%; diff --git a/packages/ui-vue/components/property-editor/src/composition/use-expression.ts b/packages/ui-vue/components/property-editor/src/composition/use-expression.ts index 106226409835669ffc79077abaeb05b4a411a68c..43707cb684c0bdba24bcc60b823e572504ae5c85 100644 --- a/packages/ui-vue/components/property-editor/src/composition/use-expression.ts +++ b/packages/ui-vue/components/property-editor/src/composition/use-expression.ts @@ -10,15 +10,12 @@ export function useExpression( const { getPropertyValue } = usePropertyValueComposition; const expressionType = props.id; - const { expressionConfig = {} } = props; - const { expressionInfo = { value: '', targetId: '', targetType: '', expressionType: '' } } = expressionConfig; - const { targetId } = expressionInfo; /** * 生成表达式内码 * @returns */ - function generateExpressionId(): string { + function generateExpressionId(targetId: string): string { return targetId + '_' + expressionType; } @@ -26,11 +23,11 @@ export function useExpression( * 生成一个空的表达式值 * @returns */ - function generateExpressionValue(): ExpressionValue { + function generateExpressionValue(expressionInfo: any): ExpressionValue { return { type: 'Expression', - expressionId: generateExpressionId(), - expressionInfo: expressionInfo + expressionId: generateExpressionId(expressionInfo?.targetId || ''), + expressionInfo }; } @@ -38,15 +35,18 @@ export function useExpression( * 获取表达式的值 */ function getExpressionValue(): Ref { + const { expressionInfo = { value: '', targetId: '', targetType: '', expressionType: '' } } = props.expressionConfig; + const propertyValue = getPropertyValue('Expression'); // 1、表达式为空,生成一个空的表达式值 if (!propertyValue.value) { - propertyValue.value = generateExpressionValue(); + propertyValue.value = generateExpressionValue(expressionInfo); return propertyValue; } // 2、记录expressionInfo的值 propertyValue.value.expressionInfo = expressionInfo; + propertyValue.value.expressionId = generateExpressionId(expressionInfo?.targetId); return propertyValue; } diff --git a/packages/ui-vue/components/property-editor/src/composition/use-property-type.ts b/packages/ui-vue/components/property-editor/src/composition/use-property-type.ts index 402132cce73290770daa6797eb8ff93794e8902b..25fbf9f4c7327ac44cc73b48996ccf16f836daf8 100644 --- a/packages/ui-vue/components/property-editor/src/composition/use-property-type.ts +++ b/packages/ui-vue/components/property-editor/src/composition/use-property-type.ts @@ -1,6 +1,6 @@ -import { Ref, SetupContext, ref } from "vue"; +import { Ref, SetupContext, ref, watch } from "vue"; import { PropertyEditorProps, PropertyType } from "../property-editor.props"; import { PROPERTY_TYPE_ENUMS } from "./datas"; import { ConstEnumItem, PropertyTypeItem, UsePropertyType } from "./types"; @@ -20,7 +20,6 @@ export function usePropertyType( /** 当前选择的属性类型 */ const currentPropertyType: Ref = ref(); - /** * 获取允许选择的属性类型数据 */ @@ -30,6 +29,11 @@ export function usePropertyType( return allowedPropertyTypeItems; } + watch(() => [props.propertyTypes], () => { + propertyTypes.value = props.propertyTypes; + getAllowedPropertyTypeItems(); + }); + /** * 判断是否是状态机(旧的状态机格式) * @param propertyValue diff --git a/packages/ui-vue/components/property-editor/src/composition/use-property-value.ts b/packages/ui-vue/components/property-editor/src/composition/use-property-value.ts index 904a95e963779dd7cc915f01209453c59d4cf806..b9af6f6d7a669dd00b94cc90d2581c2c45643bac 100644 --- a/packages/ui-vue/components/property-editor/src/composition/use-property-value.ts +++ b/packages/ui-vue/components/property-editor/src/composition/use-property-value.ts @@ -52,7 +52,7 @@ export function usePropertyValue( case 'Variable': return propertyValue && propertyValue.field; case 'Custom': - return propertyValue !== undefined; + return true; case 'Expression': return propertyValue && propertyValue.expressionInfo && propertyValue.expressionInfo.value; case 'StateMachine': diff --git a/packages/ui-vue/components/property-editor/src/property-editor.component.tsx b/packages/ui-vue/components/property-editor/src/property-editor.component.tsx index 604a13bed8f7ee485a05e6e73ad1ac72b712e8c9..591614e7dd1c94782c2e42e84fd5eb117efbe856 100644 --- a/packages/ui-vue/components/property-editor/src/property-editor.component.tsx +++ b/packages/ui-vue/components/property-editor/src/property-editor.component.tsx @@ -1,4 +1,4 @@ - + /** * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd. * @@ -30,7 +30,7 @@ import './property-editor.css'; export default defineComponent({ name: 'FPropertyEditor', props: propertyEditorProps, - emits: ['valueChange','beforeOpenVariables'] as (string[] & ThisType) | undefined, + emits: ['valueChange', 'beforeOpenVariables'] as (string[] & ThisType) | undefined, setup(props: PropertyEditorProps, context: SetupContext) { const readOnly = ref(false); @@ -55,6 +55,7 @@ export default defineComponent({ return selectedPropertyType === currentPropertyType.value; }); + context.expose({ currentPropertyType }); /** * 左侧属性类型变化 */ @@ -63,7 +64,7 @@ export default defineComponent({ const propertyValue = getPropertyValue(currentPropertyType.value); // 2、如果属性值不为空,触发属性值变更 - if (isPropertyValueValid(currentPropertyType.value,propertyValue.value)) { + if (isPropertyValueValid(currentPropertyType.value, propertyValue.value)) { triggerValueChange(propertyValue.value); } } diff --git a/packages/ui-vue/components/property-editor/src/property-editor.css b/packages/ui-vue/components/property-editor/src/property-editor.css index cd5079843e552878a7454c90d2151061edcbbe5d..090163f3bd97a4055a42b6d919da0b66493846cc 100644 --- a/packages/ui-vue/components/property-editor/src/property-editor.css +++ b/packages/ui-vue/components/property-editor/src/property-editor.css @@ -1,13 +1,15 @@ .f-property-editor-container { min-width: 230px; + display: flex; } .f-property-editor-right { height: 28px; + flex:1 1 0; } .f-property-editor-left { - float: left; height: 28px; width: 32%; + min-width: 90px; } \ No newline at end of file diff --git a/packages/ui-vue/components/property-editor/src/property-editor.props.ts b/packages/ui-vue/components/property-editor/src/property-editor.props.ts index 9ba44286bef8fb49191591af4f16e49b262e704e..8655d54befc054ab6ca601c401245cbd28142fcd 100644 --- a/packages/ui-vue/components/property-editor/src/property-editor.props.ts +++ b/packages/ui-vue/components/property-editor/src/property-editor.props.ts @@ -40,6 +40,7 @@ export const propertyEditorProps = { /** 属性类型列表 */ propertyTypes: { type: Array, default: [] }, + } as Record; export type PropertyEditorProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/base-property.ts b/packages/ui-vue/components/property-panel/src/composition/entity/base-property.ts index 82749a19d040275817d52d4f84e119976d6f9827..f3c1bb4ed9f0d3ab01658a0a13c8e081f7f5c3d9 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/base-property.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/base-property.ts @@ -23,7 +23,7 @@ export class BaseControlProperty { public designViewModelField: any; public controlCreatorUtils: any; public designerHostService: any; - + public designerContext: any; schemaService: any = null; metadataService: any = null; @@ -33,6 +33,7 @@ export class BaseControlProperty { categories: {} }; + constructor(componentId: string, designerHostService: any) { this.componentId = componentId; this.designerHostService = designerHostService; @@ -44,8 +45,11 @@ export class BaseControlProperty { this.controlCreatorUtils = designerHostService['controlCreatorUtils']; this.metadataService = designerHostService['metadataService']; this.schemaService = designerHostService['schemaService']; + this.designerContext = designerHostService['designerContext']; + } + getFormDesignerInstance() { + return this.designerContext?.instances?.formDesigner.value; } - getTableInfo() { return this.schemaService?.getTableInfoByViewModelId(this.viewModelId); } @@ -83,7 +87,7 @@ export class BaseControlProperty { valueField: 'value', idField: 'value', editable: false, - data: [{ value: propertyData.type, name: DgControl[propertyData.type].name }] + data: [{ value: propertyData.type, name: DgControl[propertyData.type] && DgControl[propertyData.type].name }] } } } @@ -102,13 +106,15 @@ export class BaseControlProperty { title: "class样式", type: "string", description: "组件的CSS样式", - $converter: "/converter/appearance.converter" + $converter: "/converter/appearance.converter", + parentPropertyID: 'appearance' }, style: { title: "style样式", type: "string", description: "组件的样式", - $converter: "/converter/appearance.converter" + $converter: "/converter/appearance.converter", + parentPropertyID: 'appearance' } }; @@ -173,7 +179,13 @@ export class BaseControlProperty { } protected getVisibleProperty(propertyData, position = '') { - const editor = this.getPropertyEditorParams(propertyData, position === 'gridFieldEditor' ? ['Const', 'Expression'] : ['Const', 'Variable', 'StateMachine', 'Expression'], 'visible'); + let editorTypes = ['Const', 'Variable', 'StateMachine', 'Expression']; + if (position === 'gridFieldEditor') { + editorTypes = ['Const', 'Expression']; + } else if (position === 'form-group' && !propertyData.binding?.field) { + editorTypes = ['Const', 'Variable', 'StateMachine']; + } + const editor = this.getPropertyEditorParams(propertyData, editorTypes, 'visible'); return { visible: { title: "是否可见", @@ -310,7 +322,7 @@ export class BaseControlProperty { const { expressionId, expressionInfo } = newPropertyValue; const { targetId, targetType, expressionType, value, message } = expressionInfo; const module = this.formSchemaUtils.getModule(); - module.expressions ??= []; + module.expressions = module.expressions || []; const { expressions } = module; // 2、获取目标表达式,如果不存在,则创建一个空的目标表达式 @@ -371,4 +383,21 @@ export class BaseControlProperty { } return 'Container'; } + + createBaseEventProperty(initialData: any) { + const properties = {}; + properties[this.viewModelId] = { + type: 'events-editor', + editor: { + initialData: initialData, + viewSourceHandle: (commandInfo: any) => { + if (commandInfo.controller?.label.indexOf(this.formSchemaUtils.getModule().code) > -1) { + this.eventsEditorUtils.jumpToMethod(commandInfo); + } + } + } + }; + + return properties; + } } diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/expression-property.ts b/packages/ui-vue/components/property-panel/src/composition/entity/expression-property.ts index d976887149d4d74b734e1d164061c691eee538e3..442ad4bf870e4d1a4f019ae63f42b8b638b225ac 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/expression-property.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/expression-property.ts @@ -332,6 +332,7 @@ export class ExpressionProperty { title: this.expressionNames[name], type: "string", $converter: this.getExpressionConverter(expressionId), + refreshPanelAfterChanged: true, editor: { type: "expression-editor", singleExpand: false, @@ -377,6 +378,7 @@ export class ExpressionProperty { return { description: "表达式", title: "表达式", + hide: !propertyData.binding, properties: { ...this.getExpressionEditorOptions(propertyData, type, expressionTypes, callback) } diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts b/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts index 527b57264302f1d1fffb14c28e419ece825ed1c9..15d34717d17c8563da36ec9ec1f439a5254c2cab 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/input-base-property.ts @@ -12,6 +12,7 @@ import { useResponseLayoutEditorSetting } from "../../../../response-layout-edit import { FormSchemaEntityField$Type, FormSchemaEntityFieldType$Type, FormVariable } from "@farris/ui-vue/components/common"; import { DgControl } from "../../../../designer-canvas"; import { ExpressionProperty } from "./expression-property"; +import { nextTick } from "vue"; export class InputBaseProperty extends BaseControlProperty { public responseLayoutEditorFunction: UseResponseLayoutEditorSetting; @@ -22,26 +23,26 @@ export class InputBaseProperty extends BaseControlProperty { super(componentId, designerHostService); this.responseLayoutEditorFunction = useResponseLayoutEditorSetting(this.formSchemaUtils); } - private getCommonPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance | null) { + private getCommonPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance | null, showPosition = 'Card') { // 基本信息 - this.propertyConfig.categories['basic'] = this.getBasicProperties(propertyData, componentInstance); + this.propertyConfig.categories['basic'] = this.getBasicProperties(propertyData, componentInstance, showPosition); // 外观 this.propertyConfig.categories['appearance'] = this.getAppearanceProperties(propertyData, componentInstance); - this.propertyConfig.categories['behavior'] = this.getBehaviorConfig(propertyData); + this.propertyConfig.categories['behavior'] = this.getBehaviorConfig(propertyData, 'form-group'); } public getPropertyConfig(propertyData: any, componentInstance: DesignerComponentInstance) { - this.getCommonPropertyConfig(propertyData, componentInstance); + this.getCommonPropertyConfig(propertyData, componentInstance, 'Card'); // 编辑器 this.propertyConfig.categories['editor'] = this.getEditorProperties(propertyData); // 表达式编辑器 - this.propertyConfig.categories['expressons'] = this.getExpressionConfig(propertyData, 'Field'); + this.propertyConfig.categories['expressions'] = this.getExpressionConfig(propertyData, 'Field'); // 事件 暂不支持 // this.propertyConfig.categories['eventsEditor'] = this.getEventPropertyConfig(propertyData); return this.propertyConfig; } public getGridFieldEdtiorPropConfig(propertyData: any, componentInstance: DesignerComponentInstance | null) { this.propertyConfig.categories = {}; - this.getCommonPropertyConfig(propertyData, componentInstance); + this.getCommonPropertyConfig(propertyData, componentInstance, 'Grid'); if (this['getGridFieldEdtiorProperties']) { // 编辑器 this.propertyConfig.categories['editor'] = this['getGridFieldEdtiorProperties'](propertyData); @@ -55,11 +56,11 @@ export class InputBaseProperty extends BaseControlProperty { // this.propertyConfig.categories['eventsEditor'] = this.getEventPropertyConfig(propertyData, 'gridFieldEditor'); return this.propertyConfig.categories; } - public getBasicProperties(propertyData, componentInstance): any { + public getBasicProperties(propertyData, componentInstance, showPosition = 'Card'): any { const self = this; this.setDesignViewModelField(propertyData); const { canChangeControlType, editorTypeList } = this.getAvailableEditorType(propertyData); - return { + const basicProperties = { description: 'Basic Information', title: '基本信息', properties: { @@ -74,6 +75,7 @@ export class InputBaseProperty extends BaseControlProperty { title: '编辑器类型', type: 'string', $converter: '/converter/change-editor.converter', + parentPropertyID: 'editor', editor: { type: 'combo-list', textField: 'value', @@ -89,6 +91,14 @@ export class InputBaseProperty extends BaseControlProperty { type: "string", $converter: '/converter/form-group-label.converter' }, + showLabelType: { + description: '标签显示方式:1、显示:显示标签 2、占位:保留标签空间,但不显示文本 3、不显示:不显示标签', + title: "标签显示", + type: "enum", + editor: { + data: [{ "id": "visible", "name": "显示" }, { "id": "reserve-space", "name": "占位" }, { "id": "none", "name": "不显示" }] + } + }, binding: { description: "绑定的表单字段", title: "绑定", @@ -104,7 +114,8 @@ export class InputBaseProperty extends BaseControlProperty { }, textField: 'bindingField' }, - refreshPanelAfterChanged: true + refreshPanelAfterChanged: true, + readonly: this.formSchemaUtils.designerMode === 'PC_RTC' && !propertyData.isRtcControl } }, setPropertyRelates(changeObject, prop, paramters: any) { @@ -126,7 +137,11 @@ export class InputBaseProperty extends BaseControlProperty { } } } - }; + } as any; + if (showPosition.toLocaleLowerCase() !== 'card') { + delete basicProperties.properties.showLabelType; + } + return basicProperties; } /** @@ -172,10 +187,11 @@ export class InputBaseProperty extends BaseControlProperty { let editorTypeList: any = []; if (this.designViewModelField && this.designViewModelField.$type === FormSchemaEntityField$Type.SimpleField) { // 绑定字段 - editorTypeList = SchemaDOMMapping.getEditorTypesByMDataType(this.designViewModelField.type.name); + editorTypeList = SchemaDOMMapping.getEditorTypesByMDataType(this.designViewModelField.type.name, this.designViewModelField.multiLanguage); + } else if (this.bindingVarible) { // 绑定变量 - editorTypeList = SchemaDOMMapping.getEditorTypesByMDataType(this.bindingVarible.type); + editorTypeList = SchemaDOMMapping.getEditorTypesByMDataType(this.bindingVarible.type, this.designViewModelField.multiLanguage); } return { canChangeControlType, editorTypeList }; } @@ -197,13 +213,15 @@ export class InputBaseProperty extends BaseControlProperty { title: "class样式", type: "string", description: "组件的CSS样式", - $converter: "/converter/appearance.converter" + $converter: "/converter/appearance.converter", + parentPropertyID: 'appearance' }, style: { title: "style样式", type: "string", description: "组件的内联样式", - $converter: "/converter/appearance.converter" + $converter: "/converter/appearance.converter", + parentPropertyID: 'appearance' }, responseLayout: { description: "响应式列宽", @@ -306,13 +324,15 @@ export class InputBaseProperty extends BaseControlProperty { } }); } - // 5、替换控件 - parentContainer.contents.splice(index, 1); - parentContainer.contents.splice(index, 0, newControl); - componentInstance.schema = Object.assign(oldControl, newControl); + if (componentInstance?.updateContextSchema) { + // 使用这种方式能把新控件属性更新到schema,而且使得parent、designItemContext.schema、componentInstance.schema的关联 + componentInstance.updateContextSchema(newControl); + } else { + componentInstance.schema = Object.assign(oldControl, newControl); + Object.assign(propertyData, newControl); + } // 重组VM // this.designViewModelUtils.assembleDesignViewModel(); - Object.assign(propertyData, newControl); // 6、暂时移除旧控件的选中样式(后续考虑更好的方式) Array.from(document.getElementsByClassName('dgComponentSelected') as HTMLCollectionOf).forEach( @@ -324,7 +344,10 @@ export class InputBaseProperty extends BaseControlProperty { ); // 7、触发刷新 canvasChanged.value++; - + nextTick(() => { + // 刷新属性面板, 放在此处更新,如果时机获取的editor.type可能是旧编辑器 + this.getFormDesignerInstance().reloadPropertyPanel(); + }); } public getComponentConfig(propertyData, info = {}, properties = {}, setPropertyRelates?: (changeObject, propertyData, parameters) => any) { @@ -332,11 +355,12 @@ export class InputBaseProperty extends BaseControlProperty { description: "编辑器", title: "编辑器", type: "input-group", - $converter: "/converter/property-editor.converter" + $converter: "/converter/property-editor.converter", + parentPropertyID: 'editor' }, info); - - const readonlyEditor = this.getPropertyEditorParams(propertyData, [], 'readonly'); - const requiredEditor = this.getPropertyEditorParams(propertyData, [], 'required'); + const editorTypes = propertyData.binding?.field ? [] : ['Const', 'Variable', 'StateMachine']; + const readonlyEditor = this.getPropertyEditorParams(propertyData, editorTypes, 'readonly'); + const requiredEditor = this.getPropertyEditorParams(propertyData, editorTypes, 'required'); const editorProperties = { readonly: { description: "", @@ -573,13 +597,8 @@ export class InputBaseProperty extends BaseControlProperty { this.appendFieldValueChangeEvents(propertyData, eventList); const initialData = self.eventsEditorUtils['formProperties'](propertyData, self.viewModelId, eventList); - const properties = {}; - properties[self.viewModelId] = { - type: 'events-editor', - editor: { - initialData - } - }; + const properties = self.createBaseEventProperty(initialData); + const eventsEditorConfig = { title: '事件', hideTitle: true, @@ -619,4 +638,54 @@ export class InputBaseProperty extends BaseControlProperty { propertyData, type, expressionTypes, associationCallBack); } + + protected getBindingDataType() { + if (!this.designViewModelField) { + return 'boolean'; + } + return this.designViewModelField.type.name === 'Number' ? 'number' : this.designViewModelField.type.name === 'String' ? 'string' : 'boolean'; + } + + private numberEditorOptions = { + type: "number-spinner", + useThousands: false, + keyboard: false, + showButton: false + }; + + protected getEditor() { + return this.getBindingDataType() === 'number' ? this.numberEditorOptions : {}; + } + + updatePropertyValue(schema: Record, propertyKey: string, propertyValue: any) { + if (schema.editor[propertyKey] !== propertyValue) { + schema.editor[propertyKey] = propertyValue; + } else { + this.getFormDesignerInstance()?.reloadPropertyPanel(); + } + } + + private setBooleanValue(schema: Record, propertyKey: string, propertyValue: any) { + let newValue = propertyValue; + const dataType = this.getBindingDataType(); + if (dataType === 'string') { + newValue = (newValue || '').trim() || (propertyKey === 'trueValue' ? 'true' : 'false'); + } + const targetKey = propertyKey === 'trueValue' ? 'falseValue' : 'trueValue'; + if (schema.editor[targetKey] === newValue) { + newValue = newValue + 1; + } + this.updatePropertyValue(schema, propertyKey, newValue); + } + + protected getBooleanValueConverter() { + return { + convertFrom: (schema: Record, propertyKey: string) => { + return schema.editor[propertyKey]; + }, + convertTo: (schema: Record, propertyKey: string, propertyValue: any) => { + this.setBooleanValue(schema, propertyKey, propertyValue); + } + }; + } } diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/property-entity.ts b/packages/ui-vue/components/property-panel/src/composition/entity/property-entity.ts index a4a543fdd335bd652c180073ff77cd120b7d1b5d..9eed2a146f0c8ab4d69e399c13cb3c454da24b4a 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/property-entity.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/property-entity.ts @@ -118,6 +118,8 @@ export interface PropertyEntity { questionMessage?: string; editor?: EditorConfig; + + parentPropertyID?: string; } export interface ElementPropertyConfig { diff --git a/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts b/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts index 7900e2ea6a161cb7b2ea5233d9765414a53ab46a..67edb2740229f8c99521089d07d2a02a5d4935a0 100644 --- a/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts +++ b/packages/ui-vue/components/property-panel/src/composition/entity/schema-dom-mapping.ts @@ -12,11 +12,13 @@ export class SchemaDOMMapping { { key: DgControl['input-group'].type, value: DgControl['input-group'].name }, { key: DgControl['lookup'].type, value: DgControl['lookup'].name }, { key: DgControl['date-picker'].type, value: DgControl['date-picker'].name }, + { key: DgControl['switch'].type, value: DgControl['switch'].name }, + { key: DgControl['check-box'].type, value: DgControl['check-box'].name }, { key: DgControl['check-group'].type, value: DgControl['check-group'].name }, { key: DgControl['radio-group'].type, value: DgControl['radio-group'].name }, { key: DgControl['combo-list'].type, value: DgControl['combo-list'].name }, { key: DgControl['textarea'].type, value: DgControl['textarea'].name }, - { key: DgControl['time-picker'].type, value: DgControl['time-picker'].name }, + { key: DgControl['time-picker'].type, value: DgControl['time-picker'].name } ], Text: [ { key: DgControl['textarea'].type, value: DgControl['textarea'].name }, @@ -29,7 +31,9 @@ export class SchemaDOMMapping { { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name } ], Number: [ - { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name } + { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name }, + { key: DgControl['switch'].type, value: DgControl['switch'].name }, + { key: DgControl['check-box'].type, value: DgControl['check-box'].name } ], BigNumber: [ { key: DgControl['number-spinner'].type, value: DgControl['number-spinner'].name } @@ -57,7 +61,10 @@ export class SchemaDOMMapping { /** * 根据绑定字段类型获取可用的输入类控件 */ - static getEditorTypesByMDataType(fieldType: string, controlType = '') { + static getEditorTypesByMDataType(fieldType: string, multiLanguage = false, controlType = '') { + if (multiLanguage) { + return [{ key: DgControl['language-textbox'].type, value: DgControl['language-textbox'].name }]; + } let editorTypes = SchemaDOMMapping.fieldControlTypeMapping[fieldType]; // 列表列编辑器支持的控件类型有限 diff --git a/packages/ui-vue/components/property-panel/src/property-panel.component.tsx b/packages/ui-vue/components/property-panel/src/property-panel.component.tsx index 9b4131f398b2492a5935b9303b986ce587ead5ea..c9481645f3f63d5bc325d015053b3512ee769317 100644 --- a/packages/ui-vue/components/property-panel/src/property-panel.component.tsx +++ b/packages/ui-vue/components/property-panel/src/property-panel.component.tsx @@ -15,9 +15,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { defineComponent, SetupContext, ref, watch, onMounted, onBeforeMount, inject, nextTick } from 'vue'; +import { defineComponent, SetupContext, ref, watch, onMounted, onBeforeMount, inject, nextTick, computed } from 'vue'; import { PropertyPanelProps, propertyPanelProps } from './composition/props/property-panel.props'; -import { getPropertyConfigBySchema } from '../../dynamic-resolver/src/property-config-resolver'; +import { getPropertyConfigBySchemaForDesigner } from '../../dynamic-resolver/src/resolver/property-config/property-config-resolver-design'; import { SchemaService } from '../../dynamic-resolver'; import FPropertyPanelItemList from '../src/component/property-panel-item-list.component'; @@ -73,6 +73,13 @@ export default defineComponent({ /** 给分类组件配置唯一id,用于切换控件时强制刷新分类 */ const categoryReload = ref(0); + /** 当前是否只有一个标签页 */ + const isOnlyTab = computed(() => { + return categoryTabs.map(category => !category.hide).length === 1; + }); + /** 设计器上下文 */ + const designerContext = inject('designerContext') as any; + function collectProperties() { properties = []; if (selectedTab.value && selectedTab.value.categoryList && Array.isArray(selectedTab.value.categoryList) && selectedTab.value.categoryList.length > 0) { @@ -308,7 +315,10 @@ export default defineComponent({ if (!props.schema || !props.schema.type) { propertyConfig.value = []; } else { - propertyConfig.value = getPropertyConfigBySchema(propertyData.value, schemaService as SchemaService, designerItem, componentId); + propertyConfig.value = getPropertyConfigBySchemaForDesigner(propertyData.value, schemaService as SchemaService, designerItem, componentId); + if (designerContext.filterPropertyEntity) { + propertyConfig.value = designerContext.filterPropertyEntity(propertyConfig.value, propertyData.value); + } } checkShowTabCategory(); onClearEvent(keyword.value); @@ -324,7 +334,10 @@ export default defineComponent({ if (!propertyData.value || newSchema) { propertyData.value = newSchema || props.schema; } - propertyConfig.value = getPropertyConfigBySchema(propertyData.value, schemaService as SchemaService, designerItem, componentId, newPropertyConfig); + propertyConfig.value = getPropertyConfigBySchemaForDesigner(propertyData.value, schemaService as SchemaService, designerItem, componentId, newPropertyConfig); + if (designerContext.filterPropertyEntity) { + propertyConfig.value = designerContext.filterPropertyEntity(propertyConfig.value, propertyData.value); + } checkShowTabCategory(); enableSearch.value && onClearEvent(keyword.value); refreshFlag && categoryReload.value++; @@ -369,7 +382,10 @@ export default defineComponent({ } } function onRefreshPanel() { - propertyConfig.value = getPropertyConfigBySchema(propertyData.value, schemaService as SchemaService, designerItem, componentId); + propertyConfig.value = getPropertyConfigBySchemaForDesigner(propertyData.value, schemaService as SchemaService, designerItem, componentId); + if (designerContext.filterPropertyEntity) { + propertyConfig.value = designerContext.filterPropertyEntity(propertyConfig.value, propertyData.value); + } checkShowTabCategory(); } /** 搜索框 */ @@ -486,7 +502,7 @@ export default defineComponent({ return categoryTabs.map((tab: any) => { return (
onChangeSelectedTab(tab)}> {tab.tabName}
@@ -525,7 +541,7 @@ export default defineComponent({ class={['property-panel', { 'white-theme': isWhiteTheme.value }]} style={handlePropertyPanelStyleObject()}>
{weekTitle.value}
{week.numberInTheYear}
onClick(payload, item)} onKeydown={(payload: KeyboardEvent) => onKeyDown(payload, item)} onMouseenter={() => onMouseEnter(item)} onMouseleave={() => onMouseLeave()}> @@ -238,7 +249,7 @@ export default defineComponent({ ) } -
+
onClick(payload, item)}> {item.date.day}
onClick(payload, monthViewItem)} onKeydown={(payload: KeyboardEvent) => onKeyDown(payload, monthViewItem)} onMouseenter={() => onMouseEnter(monthViewItem)} onMouseleave={() => onMouseLeave()} tabindex="0" style="width: 33.3%"> -
+
onClick(payload, monthViewItem)}> {monthViewItem.displayText}
onClickYear(payload, yearViewItem)} onKeydown={(payload: KeyboardEvent) => onKeyDown(payload, yearViewItem)} onMouseenter={() => onMouseEnter(yearViewItem)} onMouseleave={() => onMouseLeave()} tabindex="0" > -
- {yearViewItem.year} +
onClickYear(payload, yearViewItem)}> + {yearViewItem.year}