diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 645af30ce..5f0829773 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -22,6 +22,7 @@ importers: '@visactor/vtable': workspace:* '@visactor/vtable-editors': workspace:* '@visactor/vtable-export': workspace:* + '@visactor/vtable-gantt': workspace:* '@visactor/vtable-search': workspace:* '@visactor/vue-vtable': workspace:* '@visactor/vutils': ~0.18.14 @@ -55,6 +56,7 @@ importers: '@visactor/vtable': link:../packages/vtable '@visactor/vtable-editors': link:../packages/vtable-editors '@visactor/vtable-export': link:../packages/vtable-export + '@visactor/vtable-gantt': link:../packages/vtable-gantt '@visactor/vtable-search': link:../packages/vtable-search '@visactor/vue-vtable': link:../packages/vue-vtable '@visactor/vutils': 0.18.18 @@ -143,7 +145,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@visactor/vchart': 1.12.12 axios: 1.7.8 @@ -165,12 +167,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/react-vtable: @@ -240,7 +242,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -268,12 +270,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable: @@ -360,7 +362,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -393,12 +395,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-calendar: @@ -463,7 +465,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -490,12 +492,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-editors: @@ -527,7 +529,7 @@ importers: '@internal/ts-config': link:../../share/ts-config '@rushstack/eslint-patch': 1.1.4 '@types/jest': 26.0.24 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 eslint: 8.18.0 husky: 7.0.4 @@ -538,11 +540,11 @@ importers: react-device-detect: 2.2.3 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 tslint: 5.12.1_typescript@4.9.5 typescript: 4.9.5 - vite: 3.2.6_@types+node@22.10.0 + vite: 3.2.6_@types+node@22.10.1 ../../packages/vtable-export: specifiers: @@ -611,7 +613,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -639,12 +641,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-gantt: @@ -722,7 +724,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -754,12 +756,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-plugins: @@ -780,6 +782,7 @@ importers: '@types/react-is': ^17.0.3 '@visactor/vchart': 1.12.12 '@visactor/vtable': workspace:* + '@visactor/vtable-editors': workspace:* '@visactor/vutils': ~0.18.14 '@vitejs/plugin-react': 3.1.0 axios: ^1.4.0 @@ -822,13 +825,14 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 '@types/react-is': 17.0.7 '@visactor/vchart': 1.12.12 '@visactor/vtable': link:../vtable + '@visactor/vtable-editors': link:../vtable-editors '@vitejs/plugin-react': 3.1.0_vite@3.2.6 axios: 1.7.8 chai: 4.3.4 @@ -851,12 +855,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vtable-search: @@ -920,7 +924,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@types/react': 18.3.12 '@types/react-dom': 18.3.1 @@ -948,12 +952,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 ../../packages/vue-vtable: @@ -1017,7 +1021,7 @@ importers: '@types/chai': 4.2.22 '@types/jest': 26.0.24 '@types/mocha': 9.0.0 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/offscreencanvas': 2019.6.4 '@visactor/vchart': 1.12.12 '@vitejs/plugin-vue': 5.2.1_vite@3.2.6+vue@3.5.13 @@ -1025,7 +1029,7 @@ importers: chai: 4.3.4 cross-env: 7.0.3 eslint: 8.18.0 - eslint-plugin-vue: 9.31.0_eslint@8.18.0 + eslint-plugin-vue: 9.32.0_eslint@8.18.0 form-data: 4.0.1 inversify: 6.0.1 jest: 26.6.3_ts-node@10.9.0 @@ -1041,12 +1045,12 @@ importers: sass: 1.43.5 ts-jest: 26.5.6_xuote2qreek47x2di7kesslrai ts-loader: 9.2.6_typescript@4.9.5 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde tslib: 2.3.1 ttypescript: 1.5.13_fxi2xlggroal5l3a4znftvxz2m typescript: 4.9.5 typescript-transform-paths: 3.3.1_typescript@4.9.5 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vite-plugin-markdown: 2.2.0_vite@3.2.6 vue: 3.5.13_typescript@4.9.5 vue-eslint-parser: 9.4.3_eslint@8.18.0 @@ -1072,7 +1076,7 @@ importers: eslint-plugin-promise: 6.0.0_eslint@8.18.0 eslint-plugin-react: 7.30.1_eslint@8.18.0 eslint-plugin-react-hooks: 4.6.0_eslint@8.18.0 - eslint-plugin-vue: 9.31.0_eslint@8.18.0 + eslint-plugin-vue: 9.32.0_eslint@8.18.0 prettier: 2.8.8 devDependencies: eslint: 8.18.0 @@ -1107,13 +1111,13 @@ importers: '@internal/eslint-config': link:../../share/eslint-config '@internal/ts-config': link:../../share/ts-config '@rushstack/eslint-patch': 1.1.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/node-fetch': 2.6.4 cross-env: 7.0.3 eslint: 8.18.0 form-data: 4.0.1 node-fetch: 2.6.7 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde typescript: 4.9.5 ../../tools/bundler: @@ -1262,7 +1266,7 @@ importers: '@types/merge2': 1.4.0 '@types/minimist': 1.2.2 '@types/ms': 0.7.31 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/semver': 7.3.12 '@types/terser': 3.12.0 '@types/through2': 2.0.38 @@ -1271,7 +1275,7 @@ importers: '@types/yargs-parser': 21.0.0 eslint: 8.18.0 rimraf: 3.0.2 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde typescript: 4.9.5 vitest: 0.30.1_less@4.1.3+terser@5.17.1 @@ -2757,7 +2761,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 jest-message-util: 26.6.2 jest-util: 26.6.2 @@ -2773,7 +2777,7 @@ packages: '@jest/test-result': 26.6.2 '@jest/transform': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 ansi-escapes: 4.3.2 chalk: 4.1.2 exit: 0.1.2 @@ -2786,7 +2790,7 @@ packages: jest-resolve: 26.6.2 jest-resolve-dependencies: 26.6.3 jest-runner: 26.6.3_ts-node@10.9.0 - jest-runtime: 26.6.3_ts-node@10.9.0 + jest-runtime: 26.6.3 jest-snapshot: 26.6.2 jest-util: 26.6.2 jest-validate: 26.6.2 @@ -2822,7 +2826,7 @@ packages: dependencies: '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-mock: 26.6.2 dev: true @@ -2841,7 +2845,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@sinonjs/fake-timers': 6.0.1 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-message-util: 26.6.2 jest-mock: 26.6.2 jest-util: 26.6.2 @@ -2947,7 +2951,7 @@ packages: graceful-fs: 4.2.11 jest-haste-map: 26.6.2 jest-runner: 26.6.3_ts-node@10.9.0 - jest-runtime: 26.6.3_ts-node@10.9.0 + jest-runtime: 26.6.3 transitivePeerDependencies: - bufferutil - canvas @@ -3018,7 +3022,7 @@ packages: dependencies: '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/yargs': 15.0.19 chalk: 4.1.2 dev: true @@ -3663,7 +3667,7 @@ packages: /@types/clean-css/4.2.6: resolution: {integrity: sha512-Ze1tf+LnGPmG6hBFMi0B4TEB0mhF7EiMM5oyjLDNPE9hxrPU0W+5+bHvO+eFPA+bt0iC1zkQMoU/iGdRVjcRbw==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 source-map: 0.6.1 dev: true @@ -3691,13 +3695,13 @@ packages: /@types/fs-extra/9.0.13: resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/glob-stream/8.0.2: resolution: {integrity: sha512-kyuRfGE+yiSJWzSO3t74rXxdZNdYfLcllO0IUha4eX1fl40pm9L02Q/TEc3mykTLjoWz4STBNwYnUWdFu3I0DA==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/picomatch': 3.0.1 '@types/streamx': 2.9.5 dev: true @@ -3705,19 +3709,19 @@ packages: /@types/glob-watcher/5.0.2: resolution: {integrity: sha512-MZeh2nIzibl/euv5UV0femkGzcKTSE4G2+zv48d6ymeitWwCx52+4X+FqzML9oH2mQnPs+N/JHp3CsBPj1x1Ug==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/graceful-fs/4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/gulp-if/0.0.34: resolution: {integrity: sha512-r2A04hHDC+ZWMRAm+3q6/UeC3ggvl+TZm9P1+2umnp4q9bOlBmUQnR178Io3c0DkZPQAwup8VNtOvmvaWCpP5w==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 dev: true @@ -3731,7 +3735,7 @@ packages: /@types/gulp-sourcemaps/0.0.35: resolution: {integrity: sha512-vUBuizwA4CAV3Mke0DJYHQxyN4YOB1aAql284qAO7Et7fe0hmnPi/R9Fhu2UhxMuSxAwFktsJUOQk5dJHOU1eA==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 dev: true @@ -3807,7 +3811,7 @@ packages: /@types/merge2/1.4.0: resolution: {integrity: sha512-MRHDvln2ldZELrUC8n1PGaQzZ33aNh8uDcsGehREW0zR1Fr818a4/JTZjO9eloHPPxnpUp8fz/YFTRc5CWm7Xw==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/minimatch/5.1.2: @@ -3829,7 +3833,7 @@ packages: /@types/node-fetch/2.6.4: resolution: {integrity: sha512-1ZX9fcN4Rvkvgv4E6PAY5WXUFWFcRWxZa3EW83UjycOB9ljJCedb2CupIP4RZMEwF/M3eTcCihbBRgwtGbg5Rg==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 form-data: 3.0.2 dev: true @@ -3841,8 +3845,8 @@ packages: resolution: {integrity: sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==} dev: false - /@types/node/22.10.0: - resolution: {integrity: sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==} + /@types/node/22.10.1: + resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==} dependencies: undici-types: 6.20.0 @@ -3900,7 +3904,7 @@ packages: /@types/resolve/0.0.8: resolution: {integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/resolve/1.20.2: @@ -3926,7 +3930,7 @@ packages: /@types/streamx/2.9.5: resolution: {integrity: sha512-IHYsa6jYrck8VEdSwpY141FTTf6D7boPeMq9jy4qazNrFMA4VbRz/sw5LSsfR7jwdDcx0QKWkUexZvsWBC2eIQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/terser/3.12.0: @@ -3939,7 +3943,7 @@ packages: /@types/through2/2.0.38: resolution: {integrity: sha512-YFu+nHmjxMurkH1BSzA0Z1WrKDAY8jUKPZctNQn7mc+/KKtp2XxnclHFXxdB1m7Iqnzb5aywgP8TMK283LezGQ==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /@types/undertaker-registry/1.0.4: @@ -3949,7 +3953,7 @@ packages: /@types/undertaker/1.2.8: resolution: {integrity: sha512-gW3PRqCHYpo45XFQHJBhch7L6hytPsIe0QeLujlnFsjHPnXLhJcPdN6a9368d7aIQgH2I/dUTPFBlGeSNA3qOg==} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/undertaker-registry': 1.0.4 async-done: 1.3.2 dev: true @@ -3958,7 +3962,7 @@ packages: resolution: {integrity: sha512-ckYz9giHgV6U10RFuf9WsDQ3X86EFougapxHmmoxLK7e6ICQqO8CE+4V/3lBN148V5N1pb4nQMmMjyScleVsig==} dependencies: '@types/glob-stream': 8.0.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 dev: true @@ -3966,7 +3970,7 @@ packages: resolution: {integrity: sha512-4UqPv+2567NhMQuMLdKAyK4yzrfCqwaTt6bLhHEs8PFcxbHILsrxaY63n4wgE/BRLDWDQeI+WcTmkXKExh9hQg==} dependencies: '@types/expect': 1.20.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 /@types/yargs-parser/21.0.0: resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==} @@ -4336,7 +4340,7 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 dependencies: - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq vue: 3.5.13_typescript@4.9.5 dev: true @@ -4794,7 +4798,7 @@ packages: es-abstract: 1.23.5 es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 - is-string: 1.0.7 + is-string: 1.1.0 dev: false /array-initial/1.1.0: @@ -4872,7 +4876,7 @@ packages: es-array-method-boxes-properly: 1.0.0 es-errors: 1.3.0 es-object-atoms: 1.0.0 - is-string: 1.0.7 + is-string: 1.1.0 dev: true /arraybuffer.prototype.slice/1.0.3: @@ -4956,7 +4960,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001684 + caniuse-lite: 1.0.30001685 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -5316,8 +5320,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001684 - electron-to-chromium: 1.5.65 + caniuse-lite: 1.0.30001685 + electron-to-chromium: 1.5.68 node-releases: 2.0.18 update-browserslist-db: 1.1.1_browserslist@4.24.2 @@ -5449,13 +5453,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001684 + caniuse-lite: 1.0.30001685 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false - /caniuse-lite/1.0.30001684: - resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==} + /caniuse-lite/1.0.30001685: + resolution: {integrity: sha512-e/kJN1EMyHQzgcMEEgoo+YTCO1NGCmIYHk5Qk8jT6AazWemS5QFKJ5ShCJlH3GZrNIdZofcNCEwZqbMjjKzmnA==} /capture-exit/2.0.0: resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==} @@ -6340,7 +6344,7 @@ packages: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.1.0 /define-properties/1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} @@ -6535,8 +6539,8 @@ packages: safer-buffer: 2.1.2 dev: true - /electron-to-chromium/1.5.65: - resolution: {integrity: sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==} + /electron-to-chromium/1.5.68: + resolution: {integrity: sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==} /electron/11.5.0: resolution: {integrity: sha512-WjNDd6lGpxyiNjE3LhnFCAk/D9GIj1rU3GSDealVShhkkkPR3Vh4q8ErXGDl1OAO/faomVa10KoFPUN/pLbNxg==} @@ -6635,24 +6639,24 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 + es-to-primitive: 1.3.0 function.prototype.name: 1.1.6 get-intrinsic: 1.2.4 get-symbol-description: 1.0.2 globalthis: 1.0.4 - gopd: 1.0.1 + gopd: 1.1.0 has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.1.0 + has-symbols: 1.1.0 hasown: 2.0.2 internal-slot: 1.0.7 is-array-buffer: 3.0.4 is-callable: 1.2.7 is-data-view: 1.0.1 is-negative-zero: 2.0.3 - is-regex: 1.1.4 + is-regex: 1.2.0 is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 + is-string: 1.1.0 is-typed-array: 1.1.13 is-weakref: 1.0.2 object-inspect: 1.13.3 @@ -6669,7 +6673,7 @@ packages: typed-array-byte-offset: 1.0.3 typed-array-length: 1.0.7 unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 + which-typed-array: 1.1.16 /es-array-method-boxes-properly/1.0.0: resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} @@ -6705,13 +6709,13 @@ packages: hasown: 2.0.2 dev: false - /es-to-primitive/1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + /es-to-primitive/1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 - is-symbol: 1.0.4 + is-symbol: 1.1.0 /es5-ext/0.10.64: resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} @@ -7073,8 +7077,8 @@ packages: string.prototype.matchall: 4.0.11 dev: false - /eslint-plugin-vue/9.31.0_eslint@8.18.0: - resolution: {integrity: sha512-aYMUCgivhz1o4tLkRHj5oq9YgYPM4/EJc0M7TAKRLCUA5OYxRLAhYEVD2nLtTwLyixEFI+/QXSvKU9ESZFgqjQ==} + /eslint-plugin-vue/9.32.0_eslint@8.18.0: + resolution: {integrity: sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==} engines: {node: ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 @@ -7811,8 +7815,8 @@ packages: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.1.0 + has-symbols: 1.1.0 hasown: 2.0.2 /get-package-type/0.1.0: @@ -8011,7 +8015,7 @@ packages: engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.1 - gopd: 1.0.1 + gopd: 1.1.0 /globby/11.1.0: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} @@ -8031,8 +8035,9 @@ packages: sparkles: 1.0.1 dev: false - /gopd/1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + /gopd/1.1.0: + resolution: {integrity: sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==} + engines: {node: '>= 0.4'} dependencies: get-intrinsic: 1.2.4 @@ -8120,7 +8125,7 @@ packages: resolution: {integrity: sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==} engines: {node: '>=10'} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@types/vinyl': 2.0.7 istextorbinary: 3.3.0 replacestream: 4.0.3 @@ -8214,19 +8219,21 @@ packages: dependencies: es-define-property: 1.0.0 - /has-proto/1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + /has-proto/1.1.0: + resolution: {integrity: sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==} engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 - /has-symbols/1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + /has-symbols/1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} /has-tostringtag/1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 /has-value/0.3.1: resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} @@ -8527,8 +8534,9 @@ packages: dependencies: has-tostringtag: 1.0.2 - /is-bigint/1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + /is-bigint/1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} dependencies: has-bigints: 1.0.2 @@ -8546,8 +8554,8 @@ packages: binary-extensions: 2.3.0 dev: true - /is-boolean-object/1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + /is-boolean-object/1.2.0: + resolution: {integrity: sha512-kR5g0+dXf/+kXnqI+lu0URKYPKgICtHGGNCDSB10AaUFj3o/HkB3u7WfpRBJGFopxxY0oH3ux7ZsDjLtK7xqvw==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 @@ -8701,10 +8709,11 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - /is-number-object/1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + /is-number-object/1.1.0: + resolution: {integrity: sha512-KVSZV0Dunv9DTPkhXwcZ3Q+tUc9TsaE1ZwX5J2WMvsSGS6Md8TFPun5uwh0yRdrNerI6vf/tbJxqSx4c1ZI1Lw==} engines: {node: '>= 0.4'} dependencies: + call-bind: 1.0.7 has-tostringtag: 1.0.2 /is-number/3.0.0: @@ -8752,12 +8761,14 @@ packages: '@types/estree': 1.0.6 dev: false - /is-regex/1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + /is-regex/1.2.0: + resolution: {integrity: sha512-B6ohK4ZmoftlUe+uvenXSbPJFo6U37BH7oO1B3nQH8f/7h27N56s85MhUtbFJAziz5dcmuR3i8ovUl35zp8pFA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 + gopd: 1.1.0 has-tostringtag: 1.0.2 + hasown: 2.0.2 /is-relative/1.0.0: resolution: {integrity: sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==} @@ -8786,23 +8797,26 @@ packages: engines: {node: '>=8'} dev: true - /is-string/1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + /is-string/1.1.0: + resolution: {integrity: sha512-PlfzajuF9vSo5wErv3MJAKD/nqf9ngAs1NFQYm16nUYFO2IzxJ2hcm+IOCg+EEopdykNNUhVq5cz35cAUxU8+g==} engines: {node: '>= 0.4'} dependencies: + call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-symbol/1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + /is-symbol/1.1.0: + resolution: {integrity: sha512-qS8KkNNXUZ/I+nX6QT8ZS1/Yx0A444yhzdTKxCzKkNjQ9sHErBxJnJAgh+f5YhusYECEcjo4XcyH87hn6+ks0A==} engines: {node: '>= 0.4'} dependencies: - has-symbols: 1.0.3 + call-bind: 1.0.7 + has-symbols: 1.1.0 + safe-regex-test: 1.0.3 /is-typed-array/1.1.13: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: - which-typed-array: 1.1.15 + which-typed-array: 1.1.16 /is-typedarray/1.0.0: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} @@ -9059,7 +9073,7 @@ packages: jest-validate: 26.6.2 micromatch: 4.0.8 pretty-format: 26.6.2 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde transitivePeerDependencies: - bufferutil - canvas @@ -9164,7 +9178,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-mock: 26.6.2 jest-util: 26.6.2 jsdom: 16.7.0 @@ -9195,7 +9209,7 @@ packages: '@jest/environment': 26.6.2 '@jest/fake-timers': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 jest-mock: 26.6.2 jest-util: 26.6.2 dev: true @@ -9235,7 +9249,7 @@ packages: dependencies: '@jest/types': 26.6.2 '@types/graceful-fs': 4.1.9 - '@types/node': 22.10.0 + '@types/node': 22.10.1 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -9283,7 +9297,7 @@ packages: '@jest/source-map': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 co: 4.6.0 expect: 26.6.2 @@ -9381,7 +9395,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 dev: true /jest-pnp-resolver/1.2.3_jest-resolve@24.9.0: @@ -9487,7 +9501,7 @@ packages: '@jest/environment': 26.6.2 '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 emittery: 0.7.2 exit: 0.1.2 @@ -9498,7 +9512,7 @@ packages: jest-leak-detector: 26.6.2 jest-message-util: 26.6.2 jest-resolve: 26.6.2 - jest-runtime: 26.6.3_ts-node@10.9.0 + jest-runtime: 26.6.3 jest-util: 26.6.2 jest-worker: 26.6.2 source-map-support: 0.5.21 @@ -9543,6 +9557,42 @@ packages: - supports-color dev: true + /jest-runtime/26.6.3: + resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==} + engines: {node: '>= 10.14.2'} + hasBin: true + dependencies: + '@jest/console': 26.6.2 + '@jest/environment': 26.6.2 + '@jest/fake-timers': 26.6.2 + '@jest/globals': 26.6.2 + '@jest/source-map': 26.6.2 + '@jest/test-result': 26.6.2 + '@jest/transform': 26.6.2 + '@jest/types': 26.6.2 + '@types/yargs': 15.0.19 + chalk: 4.1.2 + cjs-module-lexer: 0.6.0 + collect-v8-coverage: 1.0.2 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-config: 26.6.3_ts-node@10.9.0 + jest-haste-map: 26.6.2 + jest-message-util: 26.6.2 + jest-mock: 26.6.2 + jest-regex-util: 26.0.0 + jest-resolve: 26.6.2 + jest-snapshot: 26.6.2 + jest-util: 26.6.2 + jest-validate: 26.6.2 + slash: 3.0.0 + strip-bom: 4.0.0 + yargs: 15.4.1 + transitivePeerDependencies: + - supports-color + dev: true + /jest-runtime/26.6.3_ts-node@10.9.0: resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==} engines: {node: '>= 10.14.2'} @@ -9592,7 +9642,7 @@ packages: resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==} engines: {node: '>= 10.14.2'} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 graceful-fs: 4.2.11 dev: true @@ -9664,7 +9714,7 @@ packages: engines: {node: '>= 10.14.2'} dependencies: '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 chalk: 4.1.2 graceful-fs: 4.2.11 is-ci: 2.0.0 @@ -9701,7 +9751,7 @@ packages: dependencies: '@jest/test-result': 26.6.2 '@jest/types': 26.6.2 - '@types/node': 22.10.0 + '@types/node': 22.10.1 ansi-escapes: 4.3.2 chalk: 4.1.2 jest-util: 26.6.2 @@ -9720,7 +9770,7 @@ packages: resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 merge-stream: 2.0.0 supports-color: 7.2.0 dev: true @@ -9789,7 +9839,7 @@ packages: escodegen: 1.14.3 html-encoding-sniffer: 1.0.2 left-pad: 1.3.0 - nwsapi: 2.2.13 + nwsapi: 2.2.16 parse5: 4.0.0 pn: 1.1.0 request: 2.88.2 @@ -9829,7 +9879,7 @@ packages: http-proxy-agent: 4.0.1 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.13 + nwsapi: 2.2.16 parse5: 6.0.1 saxes: 5.0.1 symbol-tree: 3.2.4 @@ -10800,8 +10850,8 @@ packages: /number-precision/1.6.0: resolution: {integrity: sha512-05OLPgbgmnixJw+VvEh18yNPUo3iyp4BEWJcrLu4X9W05KmMifN7Mu5exYvQXqxxeNWhvIF+j3Rij+HmddM/hQ==} - /nwsapi/2.2.13: - resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==} + /nwsapi/2.2.16: + resolution: {integrity: sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==} dev: true /oauth-sign/0.9.0: @@ -10840,7 +10890,7 @@ packages: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - has-symbols: 1.0.3 + has-symbols: 1.1.0 object-keys: 1.1.1 /object.defaults/1.1.0: @@ -10881,7 +10931,7 @@ packages: define-properties: 1.2.1 es-abstract: 1.23.5 es-object-atoms: 1.0.0 - gopd: 1.0.1 + gopd: 1.1.0 safe-array-concat: 1.1.2 dev: true @@ -11437,7 +11487,7 @@ packages: dependencies: lilconfig: 2.1.0 postcss: 8.4.21 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde yaml: 1.10.2 dev: false @@ -11857,8 +11907,8 @@ packages: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} optional: true - /psl/1.13.0: - resolution: {integrity: sha512-BFwmFXiJoFqlUpZ5Qssolv15DMyc84gTBds1BjsV1BfXEo1UyyD7GsmN67n7J77uRhoSNW1AXtXKPLcBFQn9Aw==} + /psl/1.15.0: + resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} dependencies: punycode: 2.3.1 dev: true @@ -12143,7 +12193,7 @@ packages: es-abstract: 1.23.5 es-errors: 1.3.0 get-intrinsic: 1.2.4 - gopd: 1.0.1 + gopd: 1.1.0 which-builtin-type: 1.2.0 /regenerate-unicode-properties/10.2.0: @@ -12570,7 +12620,7 @@ packages: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - has-symbols: 1.0.3 + has-symbols: 1.1.0 isarray: 2.0.5 /safe-buffer/5.1.2: @@ -12589,7 +12639,7 @@ packages: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 - is-regex: 1.1.4 + is-regex: 1.2.0 /safe-regex/1.1.0: resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} @@ -12711,7 +12761,7 @@ packages: es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.2.4 - gopd: 1.0.1 + gopd: 1.1.0 has-property-descriptors: 1.0.2 /set-function-name/2.0.2: @@ -13108,8 +13158,8 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-symbols: 1.0.3 + gopd: 1.1.0 + has-symbols: 1.1.0 internal-slot: 1.0.7 regexp.prototype.flags: 1.5.3 set-function-name: 2.0.2 @@ -13536,7 +13586,7 @@ packages: resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==} engines: {node: '>=0.8'} dependencies: - psl: 1.13.0 + psl: 1.15.0 punycode: 2.3.1 dev: true @@ -13544,7 +13594,7 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} dependencies: - psl: 1.13.0 + psl: 1.15.0 punycode: 2.3.1 universalify: 0.2.0 url-parse: 1.5.10 @@ -13590,7 +13640,7 @@ packages: mkdirp: 1.0.4 semver: 7.3.4 typescript: 4.9.5 - yargs-parser: 20.2.4 + yargs-parser: 20.2.9 dev: true /ts-loader/9.2.6_typescript@4.9.5: @@ -13607,7 +13657,7 @@ packages: typescript: 4.9.5 dev: true - /ts-node/10.9.0_gvyh6auciohtph3635nyjnikui: + /ts-node/10.9.0_c27w6cdkixrdosqhzsswatltde: resolution: {integrity: sha512-bunW18GUyaCSYRev4DPf4SQpom3pWH29wKl0sDk5zE7ze19RImEVhCW7K4v3hHKkUyfWotU08ToE2RS+Y49aug==} hasBin: true peerDependencies: @@ -13626,7 +13676,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.10.0 + '@types/node': 22.10.1 acorn: 8.14.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -13696,7 +13746,7 @@ packages: typescript: '>=3.2.2' dependencies: resolve: 1.22.8 - ts-node: 10.9.0_gvyh6auciohtph3635nyjnikui + ts-node: 10.9.0_c27w6cdkixrdosqhzsswatltde typescript: 4.9.5 dev: true @@ -13782,8 +13832,8 @@ packages: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 + gopd: 1.1.0 + has-proto: 1.1.0 is-typed-array: 1.1.13 /typed-array-byte-offset/1.0.3: @@ -13793,8 +13843,8 @@ packages: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 + gopd: 1.1.0 + has-proto: 1.1.0 is-typed-array: 1.1.13 reflect.getprototypeof: 1.0.7 @@ -13804,7 +13854,7 @@ packages: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.1.0 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 reflect.getprototypeof: 1.0.7 @@ -13852,8 +13902,8 @@ packages: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.0 /unc-path-regex/0.1.2: resolution: {integrity: sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==} @@ -14058,8 +14108,8 @@ packages: call-bind: 1.0.7 define-properties: 1.2.1 for-each: 0.3.3 - has-proto: 1.0.3 - has-symbols: 1.0.3 + has-proto: 1.1.0 + has-symbols: 1.1.0 object.getownpropertydescriptors: 2.1.8 safe-array-concat: 1.1.2 dev: true @@ -14164,7 +14214,7 @@ packages: replace-ext: 1.0.1 dev: false - /vite-node/0.30.1_bynzxunm26yds2qzned2henjoq: + /vite-node/0.30.1_umpqsa3rsshwmingabuhbdi534: resolution: {integrity: sha512-vTikpU/J7e6LU/8iM3dzBo8ZhEiKZEKRznEMm+mJh95XhWaPrJQraT/QsT2NWmuEf+zgAoMe64PKT7hfZ1Njmg==} engines: {node: '>=v14.18.0'} hasBin: true @@ -14174,7 +14224,7 @@ packages: mlly: 1.7.3 pathe: 1.1.2 picocolors: 1.1.1 - vite: 3.2.6_bynzxunm26yds2qzned2henjoq + vite: 3.2.6_umpqsa3rsshwmingabuhbdi534 transitivePeerDependencies: - '@types/node' - less @@ -14194,7 +14244,7 @@ packages: front-matter: 4.0.2 htmlparser2: 6.1.0 markdown-it: 12.3.2 - vite: 3.2.6_3fcrixghlk5j7efp7olgstdjb4 + vite: 3.2.6_girnrryua4ny3ya6dfs4crajjq dev: true /vite/3.2.6: @@ -14230,7 +14280,7 @@ packages: fsevents: 2.3.3 dev: true - /vite/3.2.6_3fcrixghlk5j7efp7olgstdjb4: + /vite/3.2.6_@types+node@22.10.1: resolution: {integrity: sha512-nTXTxYVvaQNLoW5BQ8PNNQ3lPia57gzsQU/Khv+JvzKPku8kNZL6NMUR/qwXhMG6E+g1idqEPanomJ+VZgixEg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -14255,17 +14305,16 @@ packages: terser: optional: true dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 esbuild: 0.15.18 postcss: 8.4.21 resolve: 1.22.8 rollup: 2.79.2 - sass: 1.43.5 optionalDependencies: fsevents: 2.3.3 dev: true - /vite/3.2.6_@types+node@22.10.0: + /vite/3.2.6_girnrryua4ny3ya6dfs4crajjq: resolution: {integrity: sha512-nTXTxYVvaQNLoW5BQ8PNNQ3lPia57gzsQU/Khv+JvzKPku8kNZL6NMUR/qwXhMG6E+g1idqEPanomJ+VZgixEg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -14290,16 +14339,17 @@ packages: terser: optional: true dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 esbuild: 0.15.18 postcss: 8.4.21 resolve: 1.22.8 rollup: 2.79.2 + sass: 1.43.5 optionalDependencies: fsevents: 2.3.3 dev: true - /vite/3.2.6_bynzxunm26yds2qzned2henjoq: + /vite/3.2.6_umpqsa3rsshwmingabuhbdi534: resolution: {integrity: sha512-nTXTxYVvaQNLoW5BQ8PNNQ3lPia57gzsQU/Khv+JvzKPku8kNZL6NMUR/qwXhMG6E+g1idqEPanomJ+VZgixEg==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true @@ -14324,7 +14374,7 @@ packages: terser: optional: true dependencies: - '@types/node': 22.10.0 + '@types/node': 22.10.1 esbuild: 0.15.18 less: 4.1.3 postcss: 8.4.21 @@ -14368,7 +14418,7 @@ packages: dependencies: '@types/chai': 4.3.20 '@types/chai-subset': 1.3.5 - '@types/node': 22.10.0 + '@types/node': 22.10.1 '@vitest/expect': 0.30.1 '@vitest/runner': 0.30.1 '@vitest/snapshot': 0.30.1 @@ -14389,8 +14439,8 @@ packages: strip-literal: 1.3.0 tinybench: 2.9.0 tinypool: 0.4.0 - vite: 3.2.6_bynzxunm26yds2qzned2henjoq - vite-node: 0.30.1_bynzxunm26yds2qzned2henjoq + vite: 3.2.6_umpqsa3rsshwmingabuhbdi534 + vite-node: 0.30.1_umpqsa3rsshwmingabuhbdi534 why-is-node-running: 2.3.0 transitivePeerDependencies: - less @@ -14518,14 +14568,15 @@ packages: webidl-conversions: 6.1.0 dev: true - /which-boxed-primitive/1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + /which-boxed-primitive/1.1.0: + resolution: {integrity: sha512-Ei7Miu/AXe2JJ4iNF5j/UphAgRoma4trE6PtisM09bPygb3egMH3YLW/befsWb1A1AxvNSFidOFTB18XtnIIng==} + engines: {node: '>= 0.4'} dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 + is-bigint: 1.1.0 + is-boolean-object: 1.2.0 + is-number-object: 1.1.0 + is-string: 1.1.0 + is-symbol: 1.1.0 /which-builtin-type/1.2.0: resolution: {integrity: sha512-I+qLGQ/vucCby4tf5HsLmGueEla4ZhwTBSqaooS+Y0BuxN4Cp+okmGuV+8mXZ84KDI9BA+oklo+RzKg0ONdSUA==} @@ -14538,12 +14589,12 @@ packages: is-date-object: 1.0.5 is-finalizationregistry: 1.1.0 is-generator-function: 1.0.10 - is-regex: 1.1.4 + is-regex: 1.2.0 is-weakref: 1.0.2 isarray: 2.0.5 - which-boxed-primitive: 1.0.2 + which-boxed-primitive: 1.1.0 which-collection: 1.0.2 - which-typed-array: 1.1.15 + which-typed-array: 1.1.16 /which-collection/1.0.2: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} @@ -14562,14 +14613,14 @@ packages: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: true - /which-typed-array/1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + /which-typed-array/1.1.16: + resolution: {integrity: sha512-g+N+GAWiRj66DngFwHvISJd+ITsyphZvD1vChfVg6cEdnzy53GzB3oy0fUNlvhz7H7+MiqhYr26qxQShCpKTTQ==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.1.0 has-tostringtag: 1.0.2 /which/1.3.1: @@ -14735,6 +14786,11 @@ packages: engines: {node: '>=10'} dev: true + /yargs-parser/20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + dev: true + /yargs-parser/21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -14798,7 +14854,7 @@ packages: require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 - yargs-parser: 20.2.4 + yargs-parser: 20.2.9 dev: true /yargs/17.7.2: diff --git a/common/config/rush/version-policies.json b/common/config/rush/version-policies.json index c32d6d378..64d3345a6 100644 --- a/common/config/rush/version-policies.json +++ b/common/config/rush/version-policies.json @@ -1 +1 @@ -[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.11.5","mainProject":"@visactor/vtable","nextBump":"patch"}] +[{"definitionName":"lockStepVersion","policyName":"vtableMain","version":"1.12.0","mainProject":"@visactor/vtable","nextBump":"minor"}] diff --git a/docs/assets/api/en/GanttAPI.md b/docs/assets/api/en/GanttAPI.md index 8a33a122b..bce82791b 100644 --- a/docs/assets/api/en/GanttAPI.md +++ b/docs/assets/api/en/GanttAPI.md @@ -71,6 +71,27 @@ deleteLink: (link: ITaskLink) => void ``` +### scrollTop + +Get or set the vertical scroll value to a specified position. + +### scrollLeft + +Get or set the horizontal scroll value to a specified position. + +### getTaskBarRelativeRect(Function) + +Get the position of the task bar. The position relative to the top-left corner of the Gantt chart. + +``` + getTaskBarRelativeRect:(index: number) =>{ + left: number; + top: number; + width: number; + height: number; + } +``` + ## Events The Gantt chart event list allows you to listen to the required events and implement custom business logic as needed. diff --git a/docs/assets/api/en/methods.md b/docs/assets/api/en/methods.md index 47dd8c353..579dd1143 100644 --- a/docs/assets/api/en/methods.md +++ b/docs/assets/api/en/methods.md @@ -140,12 +140,12 @@ Set the table data interface, which can be called as an update interface. Basic table updates: -The basic table can also set the sorting status to sort the table data. Set sortState to null to clear the sorting status. If not set, the incoming data will be sorted according to the current sorting status. +The basic table can also set the sorting status to sort the table data. Set sortState to null to clear the sorting status. If not set, the incoming data will be sorted according to the current sorting status.In a scenario where internal sorting is disabled, be sure to clear the current sorting state before invoking the interface. ``` setRecords( records: Array, - option?: { sortState?: SortState | SortState[]} + option?: { sortState?: SortState | SortState[] | null } ): void; ``` @@ -1322,6 +1322,7 @@ setCanvasSize: (width: number, height: number) => void; ## setLoadingHierarchyState(Function) Set the loading state of the tree expansion and collapse of the cell + ``` /** Set the loading state of the tree expansion and collapse of the cell */ setLoadingHierarchyState: (col: number, row: number) => void; diff --git a/docs/assets/api/zh/GanttAPI.md b/docs/assets/api/zh/GanttAPI.md index 5f79e51e9..a8c5592be 100644 --- a/docs/assets/api/zh/GanttAPI.md +++ b/docs/assets/api/zh/GanttAPI.md @@ -71,6 +71,27 @@ ``` +### scrollTop + +竖向滚动到指定位置的滚动值获取或者设置 + +### scrollLeft + +横向滚动到指定位置的滚动值获取或者设置 + +### getTaskBarRelativeRect(Function) + +获取任务条的位置。相对应甘特图表左上角的位置。 + +``` + getTaskBarRelativeRect:(index: number) =>{ + left: number; + top: number; + width: number; + height: number; + } +``` + ## Events 甘特图事件列表,可以根据实际需要,监听所需事件,实现自定义业务。 diff --git a/docs/assets/api/zh/methods.md b/docs/assets/api/zh/methods.md index 285cf781d..3df1a4027 100644 --- a/docs/assets/api/zh/methods.md +++ b/docs/assets/api/zh/methods.md @@ -140,12 +140,12 @@ tableInstance.renderWithRecreateCells(); 基本表格更新: -基本表格可同时设置排序状态对表格数据排序,sortState 设置为 null 清空排序状态,如果不设置则按当前排序状态对传入数据排序。 +基本表格可同时设置排序状态对表格数据排序,sortState 设置为 null 清空当前的排序状态,如果不设置则按当前排序状态对传入数据排序。如果是禁用内部排序的场景,请务必在调用该接口前清空当前的排序状态。 ``` setRecords( records: Array, - option?: { sortState?: SortState | SortState[] } + option?: { sortState?: SortState | SortState[] | null } ): void; ``` @@ -1319,7 +1319,7 @@ interface ISortedMapItem { ## setLoadingHierarchyState(Function) -设置单元格的树形展开收起状态为 loading +设置单元格的树形展开收起状态为 loading ``` /** 设置单元格的树形展开收起状态为 loading */ diff --git a/docs/assets/changelog/en/release.md b/docs/assets/changelog/en/release.md index a578a8c00..c8572ae8c 100644 --- a/docs/assets/changelog/en/release.md +++ b/docs/assets/changelog/en/release.md @@ -1,3 +1,19 @@ +# v1.11.5 + +2024-11-29 + + +**🆕 New feature** + +- **@visactor/vtable**: add @visactor/vtable-plugins package + +**📖 Site / documentation update** + +- **@visactor/vtable**: 更新进入或离开节点时的事件文档 +- **@visactor/vtable**: 更新甘特图事件文档,暴露整个e的信息 + +[more detail about v1.11.5](https://github.com/VisActor/VTable/releases/tag/v1.11.5) + # v1.11.3 2024-11-28 diff --git a/docs/assets/changelog/zh/release.md b/docs/assets/changelog/zh/release.md index bfd89064a..b0d235661 100644 --- a/docs/assets/changelog/zh/release.md +++ b/docs/assets/changelog/zh/release.md @@ -1,3 +1,19 @@ +# v1.11.5 + +2024-11-29 + + +**🆕 新增功能** + +- **@visactor/vtable**: add @visactor/vtable-plugins package + +**📖 文档更新** + +- **@visactor/vtable**: 更新进入或离开节点时的事件文档 +- **@visactor/vtable**: 更新甘特图事件文档,暴露整个e的信息 + +[更多详情请查看 v1.11.5](https://github.com/VisActor/VTable/releases/tag/v1.11.5) + # v1.11.3 2024-11-28 diff --git a/docs/assets/demo/en/gantt/gantt-table-mergeCell.md b/docs/assets/demo/en/gantt/gantt-table-mergeCell.md new file mode 100644 index 000000000..622b4971d --- /dev/null +++ b/docs/assets/demo/en/gantt/gantt-table-mergeCell.md @@ -0,0 +1,453 @@ +--- +category: examples +group: gantt +title: cellMerge In Gantt +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-table-mergeCell.png +link: '../guide/basic_function/merge_cell' +option: ListTable-columns-text#mergeCell +--- + +# cellMerge In Gantt + +In Gantt, if the task names are the same, the visual effect of parent tasks containing subtasks can be achieved through cell merging. This can be done by setting the `mergeCell` property in `ListTable#columns`. + +## Key Configuration + +- `Gantt` +- `VTable#ListTable#Column#mergeCell` + +## Code Demo + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +// 使用时需要引入插件包@visactor/vtable-editors +// import * as VTable_editors from '@visactor/vtable-editors'; +// 正常使用方式 const input_editor = new VTable.editors.InputEditor(); +// 官网编辑器中将 VTable.editors重命名成了VTable_editors +const input_editor = new VTable_editors.InputEditor(); +const date_input_editor = new VTable_editors.DateInputEditor(); +VTableGantt.VTable.register.editor('input', input_editor); +VTableGantt.VTable.register.editor('date-input', date_input_editor); +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; + +let ganttInstance; +const records = [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + parentTask: 'Research', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + parentTask: 'Reporting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } +]; + +const columns = [ + { + field: 'parentTask', + title: 'Task', + width: 100, + mergeCell: true, + editor: 'input', + style: { + bgColor: '#EEF1F5', + color: '#141414', + fontWeight: 'bold', + fontSize: 16, + autoWrapText: true + } + }, + { + field: 'name', + title: 'Master', + width: 100, + editor: 'input' + }, + { + field: 'start', + title: 'start', + width: 100, + sort: true, + editor: 'date-input' + }, + { + field: 'end', + title: 'end', + width: 100, + sort: true, + editor: 'date-input' + } +]; +const option = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + }, + theme: { + bodyStyle: { + padding: 5, + bgColor: 'white' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: args => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new VRender.Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new VRender.Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new VRender.Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new VRender.Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new VRender.Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new VRender.Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + colWidth: 100, + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; + +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); + +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/demo/en/gantt/gantt-taskShowMode.md b/docs/assets/demo/en/gantt/gantt-taskShowMode.md new file mode 100644 index 000000000..a1c63c684 --- /dev/null +++ b/docs/assets/demo/en/gantt/gantt-taskShowMode.md @@ -0,0 +1,358 @@ +--- +category: examples +group: gantt +title: Gantt Chart Sub-task Layout Mode +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-taskShowMode.gif +link: '../guide/gantt/subtask_layout' +option: Gantt#tasksShowMode +--- + +# Gantt Chart Sub-task Layout Mode + +In Gantt, the task bar layout mode determines the display effect of the task bars. Gantt provides the following several task bar layout modes: + +- `Tasks_Separate`: Each task node is displayed in a separate row, with the parent task occupying one row and the subtasks occupying one row respectively. This is the default display effect. +- `Sub_Tasks_Separate`: The parent task node is omitted and not displayed, and all subtask nodes are displayed in separate rows. +- `Sub_Tasks_Inline`: The parent task node is omitted and not displayed, and all subtask nodes are placed in the same row. +- `Sub_Tasks_Arrange`: The parent task node is omitted and not displayed, and all subtasks will maintain the data sequence in the records for layout, ensuring that the nodes do not overlap. +- `Sub_Tasks_Compact`: The parent task node is omitted and not displayed, and all subtasks will be laid out according to the date order attribute, ensuring a compact display without node overlap. + +## Key Configuration + +- `Gantt` +- `Gantt#tasksShowMode` + +## Code Demo + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +let ganttInstance; +const records = [ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + name: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + name: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + name: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + name: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } +]; + +const columns = [ + { + field: 'name', + title: 'PROCESS', + width: 150, + tree: true + } +]; +const option = { + records, + taskListTable: { + columns: columns, + theme: { + bodyStyle: { + bgColor: 'white', + color: 'rgb(115 115 115)' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 14, + textAlign: 'center', + color: 'white' + }, + barStyle: { + width: 22, + /** 任务条的颜色 */ + barColor: 'rgb(68 99 244)', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 15, + borderColor: 'black', + borderWidth: 1 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/demo/menu.json b/docs/assets/demo/menu.json index e4fe1b18a..f5b0c5c94 100644 --- a/docs/assets/demo/menu.json +++ b/docs/assets/demo/menu.json @@ -302,6 +302,20 @@ "zh": "甘特图交互——创建排期", "en": "Gantt Interaction —— Create Schedule" } + }, + { + "path": "gantt-taskShowMode", + "title": { + "zh": "甘特图子任务布局模式", + "en": "Gantt Subtask Show Mode" + } + }, + { + "path": "gantt-table-mergeCell", + "title": { + "zh": "任务名称单元格合并", + "en": "Gantt Task Merge Cell" + } } ] }, diff --git a/docs/assets/demo/zh/gantt/gantt-table-mergeCell.md b/docs/assets/demo/zh/gantt/gantt-table-mergeCell.md new file mode 100644 index 000000000..1485c3db8 --- /dev/null +++ b/docs/assets/demo/zh/gantt/gantt-table-mergeCell.md @@ -0,0 +1,452 @@ +--- +category: examples +group: gantt +title: 任务名称单元格合并 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-table-mergeCell.png +link: '../guide/basic_function/merge_cell' +option: ListTable-columns-text#mergeCell +--- + +# 任务名称单元格合并 + +在 Gantt 中,任务名称如果是一样的,可以通过单元格合来实现父级任务包含子任务的视觉效果。可以通过设置 `ListTable#columns` 中的 `mergeCell` 属性来实现。 + +## 关键配置 + +- `Gantt` +- `VTable#ListTable#Column#mergeCell` + +## 代码演示 + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +// 使用时需要引入插件包@visactor/vtable-editors +// import * as VTable_editors from '@visactor/vtable-editors'; +// 正常使用方式 const input_editor = new VTable.editors.InputEditor(); +// 官网编辑器中将 VTable.editors重命名成了VTable_editors +const input_editor = new VTable_editors.InputEditor(); +const date_input_editor = new VTable_editors.DateInputEditor(); +VTableGantt.VTable.register.editor('input', input_editor); +VTableGantt.VTable.register.editor('date-input', date_input_editor); +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; + +let ganttInstance; +const records = [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + parentTask: 'Research', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + parentTask: 'Reporting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } +]; + +const columns = [ + { + field: 'parentTask', + title: 'Task', + width: 100, + mergeCell: true, + editor: 'input', + style: { + bgColor: '#EEF1F5', + color: '#141414', + fontWeight: 'bold', + fontSize: 16, + autoWrapText: true + } + }, + { + field: 'name', + title: 'Master', + width: 100, + editor: 'input' + }, + { + field: 'start', + title: 'start', + width: 100, + sort: true, + editor: 'date-input' + }, + { + field: 'end', + title: 'end', + width: 100, + sort: true, + editor: 'date-input' + } +]; +const option = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + }, + theme: { + bodyStyle: { + padding: 5, + bgColor: 'white' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: args => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new VRender.Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new VRender.Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new VRender.Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new VRender.Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new VRender.Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new VRender.Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + colWidth: 100, + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; + +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/demo/zh/gantt/gantt-taskShowMode.md b/docs/assets/demo/zh/gantt/gantt-taskShowMode.md new file mode 100644 index 000000000..a1a5ac32e --- /dev/null +++ b/docs/assets/demo/zh/gantt/gantt-taskShowMode.md @@ -0,0 +1,358 @@ +--- +category: examples +group: gantt +title: 甘特图子任务布局模式 +cover: https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-taskShowMode.gif +link: '../guide/gantt/subtask_layout' +option: Gantt#tasksShowMode +--- + +# 甘特图子任务布局模式 + +在 Gantt 中,任务条布局模式决定了任务条的显示效果。Gantt 提供了以下几种任务条布局模式: + +- `Tasks_Separate`: 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果 +- `Sub_Tasks_Separate`: 省去父任务节点不展示,且所有子任务的节点分别用一行展示。 +- `Sub_Tasks_Inline`: 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 +- `Sub_Tasks_Arrange`: 省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 +- `Sub_Tasks_Compact`: 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + +## 关键配置 + +- `Gantt` +- `Gantt#tasksShowMode` + +## 代码演示 + +```javascript livedemo template=vtable +// import * as VTableGantt from '@visactor/vtable-gantt'; +let ganttInstance; +const records = [ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + name: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + name: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + name: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + name: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } +]; + +const columns = [ + { + field: 'name', + title: 'PROCESS', + width: 150, + tree: true + } +]; +const option = { + records, + taskListTable: { + columns: columns, + theme: { + bodyStyle: { + bgColor: 'white', + color: 'rgb(115 115 115)' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 14, + textAlign: 'center', + color: 'white' + }, + barStyle: { + width: 22, + /** 任务条的颜色 */ + barColor: 'rgb(68 99 244)', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 15, + borderColor: 'black', + borderWidth: 1 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: VTableGantt.TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: VTableGantt.TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } +}; +ganttInstance = new VTableGantt.Gantt(document.getElementById(CONTAINER_ID), option); +window['ganttInstance'] = ganttInstance; +``` diff --git a/docs/assets/guide/en/basic_function/row_height_column_width.md b/docs/assets/guide/en/basic_function/row_height_column_width.md index fd8e889fc..5a53fbab2 100644 --- a/docs/assets/guide/en/basic_function/row_height_column_width.md +++ b/docs/assets/guide/en/basic_function/row_height_column_width.md @@ -61,6 +61,10 @@ const table = new VTable.ListTable({ }); ``` +## Custom calculated row height + +If you need to customize the logic for calculating row height, you can configure the `customComputeRowHeight` function to proxy the logic for calculating row height inside VTable. + # Column width related configuration ## Column width width diff --git a/docs/assets/guide/en/gantt/subtask_layout.md b/docs/assets/guide/en/gantt/subtask_layout.md new file mode 100644 index 000000000..49d7e8795 --- /dev/null +++ b/docs/assets/guide/en/gantt/subtask_layout.md @@ -0,0 +1,115 @@ +# Gantt Chart Task Bar Layout Modes + +In the Gantt chart, the layout mode of the task bar determines the display effect of the task bar. The Gantt chart provides the following several task bar layout modes: + +- `Tasks_Separate`: Each task node is displayed in a separate row, with the parent task occupying one row and the subtasks occupying separate rows respectively. This is the default display effect. +- `Sub_Tasks_Separate`: The parent task node is omitted and not displayed, and all subtask nodes are displayed in separate rows. +- `Sub_Tasks_Inline`: The parent task node is omitted and not displayed, and all subtask nodes are placed in the same row. +- `Sub_Tasks_Arrange`: The parent task node is omitted and not displayed, and all subtasks will maintain the data order in the records for layout, ensuring that the nodes do not overlap. +- `Sub_Tasks_Compact`: The parent task node is omitted and not displayed, and all subtasks will be laid out according to the date attribute from earliest to latest, ensuring a compact display without node overlap. + +Among these layout modes, except for the default `Tasks_Separate` mode, the other modes only display the task bars of subtasks. + +# Configuration Method + +The configuration method for the Gantt chart task bar layout mode is as follows: + +``` +import * as VTableGantt from '@visactor/vtable-gantt'; +const options={ + ... + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Compact, + ... +} +``` + +# Example Display + +The following shows the effects of different task bar layout patterns, such as the following data: + +``` +const options={ + ... + records:[ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + ... + ] +} +``` + +## Tasks_Separate + +`Tasks_Separate` is the default display effect, that is, each task node is displayed in a separate row. + +The display effect using the above data is: the parent task `id: 0` occupies one row, and the subtasks `id: 1~4` occupy one row respectively. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-task-separate.png) + +## Sub_Tasks_Separate + +`Sub_Tasks_Separate` omits the parent task node and does not display it, and places all subtask nodes in separate rows. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` occupy one row respectively (note that there is no row separator here). The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-separate.png) + +## Sub_Tasks_Inline + +`Sub_Tasks_Inline` omits the parent task node and does not display it, and places all subtask nodes in the same row. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` are all displayed in the same row. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-inline.png) + +## Sub_Tasks_Arrange + +`Sub_Tasks_Arrange` omits the parent task node and does not display it, and all subtasks will maintain the data order in the records for layout, ensuring that the nodes do not overlap. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` will maintain the data order in the records for layout, ensuring that the nodes do not overlap. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-arrange.png) + +## Sub_Tasks_Compact + +`Sub_Tasks_Compact` omits the parent task node and does not display it, and all subtasks will be laid out according to the date attribute from earliest to latest, ensuring a compact display without node overlap. + +The display effect using the above data is: the parent task `id: 0` only shows the task name in the first column, and the subtasks `id: 1~4` will be laid out according to the `start` date from earliest to latest, ensuring a compact display without node overlap. The layout effect is as follows: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-compact.png) + +**Note: The difference between `Sub_Tasks_Compact` and `Sub_Tasks_Arrange` is that for `Arrange`, the data order in the records will not be changed, but the layout is not the most compact. For `Compact`, the data order in the records will be changed, but the layout is the most compact, and when the position of the task is moved, re-layout will be triggered.** diff --git a/docs/assets/guide/menu.json b/docs/assets/guide/menu.json index cca7e45af..174b845c5 100644 --- a/docs/assets/guide/menu.json +++ b/docs/assets/guide/menu.json @@ -176,6 +176,13 @@ "zh": "甘特图编辑", "en": "Gantt Edit" } + }, + { + "path": "subtask_layout", + "title": { + "zh": "子任务布局", + "en": "Subtask Layout" + } } ] }, diff --git a/docs/assets/guide/zh/basic_function/row_height_column_width.md b/docs/assets/guide/zh/basic_function/row_height_column_width.md index bd340cc53..4cb91109c 100644 --- a/docs/assets/guide/zh/basic_function/row_height_column_width.md +++ b/docs/assets/guide/zh/basic_function/row_height_column_width.md @@ -61,6 +61,10 @@ const table = new VTable.ListTable({ }); ``` +## 自定义计算行高 + +如果需要自定义计算行高的逻辑,可以配置`customComputeRowHeight`函数来代理 VTable 内部计算行高的逻辑。 + # 列宽相关配置 ## 列宽 width diff --git a/docs/assets/guide/zh/gantt/subtask_layout.md b/docs/assets/guide/zh/gantt/subtask_layout.md new file mode 100644 index 000000000..b926fa44b --- /dev/null +++ b/docs/assets/guide/zh/gantt/subtask_layout.md @@ -0,0 +1,115 @@ +# 甘特图任务条布局模式 + +在 Gantt 中,任务条的布局模式决定了任务条的显示效果。Gantt 提供了以下几种任务条布局模式: + +- `Tasks_Separate`: 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果 +- `Sub_Tasks_Separate`: 省去父任务节点不展示,且所有子任务的节点分别用一行展示。 +- `Sub_Tasks_Inline`: 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 +- `Sub_Tasks_Arrange`: 省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 +- `Sub_Tasks_Compact`: 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + +在这几种布局模式中,除了默认的`Tasks_Separate`模式以外,其他几种模式都是只显示子任务的任务条。 + +# 配置方式 + +Gantt 任务条布局模式的配置方式如下: + +``` +import * as VTableGantt from '@visactor/vtable-gantt'; +const options={ + ... + tasksShowMode: VTableGantt.TYPES.TasksShowMode.Sub_Tasks_Compact, + ... +} +``` + +# 示例展示 + +下面分别展示不同的任务条布局模式的效果,以下面的一份数据为例: + +``` +const options={ + ... + records:[ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + ... + ] +} +``` + +## Tasks_Separate + +`Tasks_Separate`是默认的显示效果,即每一个任务节点用单独一行来展示。 + +利用上面数据的展示效果为:父任务`id: 0`占用一行,子任务`id: 1~4`分别占用一行。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-task-separate.png) + +## Sub_Tasks_Separate + +`Sub_Tasks_Separate`省去父任务节点不展示,并把所有子任务的节点分别放置到单独的一行来展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`分别占用一行(注意这里没有行分割线)。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-separate.png) + +## Sub_Tasks_Inline + +`Sub_Tasks_Inline`省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`都在同一行展示。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-inline.png) + +## Sub_Tasks_Arrange + +`Sub_Tasks_Arrange`省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`会按照 records 中的数据顺序布局,并保证节点不重叠展示。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-arrange.png) + +## Sub_Tasks_Compact + +`Sub_Tasks_Compact`省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + +利用上面数据的展示效果为:父任务`id: 0`只在第一列展示了任务名称,子任务`id: 1~4`会按照日期`start`早晚的属性来布局,并保证节点不重叠的紧凑型展示。布局效果如下: + +![image](https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/gantt/gantt-sub-task-compact.png) + +**注意:`Sub_Tasks_Compact`和`Sub_Tasks_Arrange`的区别在于`Arrange`的话,不会改变 records 中的数据顺序,但是布局不是最紧密的。而`Compact`的话,会改变 records 中的数据顺序,但是布局是最紧密的,且当移动任务的位置时都会触发重新布局** diff --git a/docs/assets/option/en/common/option-secondary.md b/docs/assets/option/en/common/option-secondary.md index ad007d706..50562c9c1 100644 --- a/docs/assets/option/en/common/option-secondary.md +++ b/docs/assets/option/en/common/option-secondary.md @@ -88,21 +88,37 @@ Allow the number of frozen columns, indicating how many columns will show the fr Whether to show the fixed column pin icon, effective for basic tables -#${prefix} defaultRowHeight(number) = 40 +#${prefix} defaultRowHeight(number|'auto') = 40 -Default row height +Default row height. + +- 'auto': The default row height calculated based on the row height. Combined with defaultHeaderRowHeight, it can achieve the effect of automatic row height calculation for the header or body part. +- Specific value: Set a specific row height. #${prefix} defaultHeaderRowHeight(Array|number) -Default row height for list header, can be set row by row. If not set, defaultRowHeight is used. +The default row height of the column header can be set row by row. If not set, the defaultRowHeight value will be used as the row height of the table header. + +Specific definition: + +``` +defaultHeaderRowHeight?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} defaultColWidth(number) = 80 -Default column width value +Column width default value #${prefix} defaultHeaderColWidth(Array|number) -Default column width for row headers, can be set column by column. If not set, defaultColWidth is used. +The default column width of the row header can be set column by column. If not set, the value of defaultColWidth will be used as the column width and height of the header. + +Specific definition: + +``` +/** The default column width of the row header can be set column by column. If not, defaultColWidth is used */ +defaultHeaderColWidth?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} keyboardOptions(Object) diff --git a/docs/assets/option/en/table/gantt.md b/docs/assets/option/en/table/gantt.md index aa11f36c0..c1635cd1e 100644 --- a/docs/assets/option/en/table/gantt.md +++ b/docs/assets/option/en/table/gantt.md @@ -68,6 +68,18 @@ Optional {{ use: common-gantt-task-bar(prefix = '###')}} +## taskShowMode(TaskShowMode) + +Task bar display mode. It is configured using the enumeration type `TaskShowMode`. + +Optional + +- `TaskShowMode.Tasks_Separate`: Each task node is displayed in a separate row, with the parent task occupying one row and child tasks occupying one row each. This is the default display effect! +- `TaskShowMode.Sub_Tasks_Separate`: The parent task node is omitted and not displayed, and all child task nodes are displayed in separate rows. +- `TaskShowMode.Sub_Tasks_Inline`: The parent task node is omitted and not displayed, and all child task nodes are placed in the same row for display. +- `TaskShowMode.Sub_Tasks_Arrange`: The parent task node is omitted and not displayed, and all child tasks will maintain the data order in the records and ensure that the nodes are displayed without overlapping. +- `TaskShowMode.Sub_Tasks_Compact`: The parent task node is omitted and not displayed, and all child tasks will be arranged according to the date attribute and ensure a compact display without overlapping nodes. + ## taskKeyField(string) The field name that uniquely identifies the data entry, default is 'id' diff --git a/docs/assets/option/en/table/listTable.md b/docs/assets/option/en/table/listTable.md index 3e50395a3..32f4b90b4 100644 --- a/docs/assets/option/en/table/listTable.md +++ b/docs/assets/option/en/table/listTable.md @@ -145,4 +145,10 @@ Enable the group title sticking function. Customize the group title layout. - groupTitleStyle(CustomLayoutStyle) \ No newline at end of file +## customComputeRowHeight(Function) + +Code VTable internally calculates the row height. Users can customize the method for calculating row height. + +``` +customComputeRowHeight?: (computeArgs: { row: number; table: ListTableAPI }) => number; +``` diff --git a/docs/assets/option/zh/common/option-secondary.md b/docs/assets/option/zh/common/option-secondary.md index 96c33d6ec..8946cfb8d 100644 --- a/docs/assets/option/zh/common/option-secondary.md +++ b/docs/assets/option/zh/common/option-secondary.md @@ -88,13 +88,22 @@ adaptive 模式下高度的适应策略,默认为 'only-body'。 是否显示固定列图钉(基本表格生效) -#${prefix} defaultRowHeight(number) = 40 +#${prefix} defaultRowHeight(number|'auto') = 40 -默认行高 +默认行高。 + +- 'auto':根据行高计算出的默认行高。结合 defaultHeaderRowHeight 使用可以实现表头或者 body 部分的行自动行高计算的效果。 +- 具体数值:设置具体的行高。 #${prefix} defaultHeaderRowHeight(Array|number) -列表头默认行高 可以按逐行设置 如果没有就取 defaultRowHeight +列表头默认行高 可以按逐行设置, 如果没有设置的话会取 defaultRowHeight 的值作为表头的行高。 + +具体定义: + +``` + defaultHeaderRowHeight?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} defaultColWidth(number) = 80 @@ -102,7 +111,14 @@ adaptive 模式下高度的适应策略,默认为 'only-body'。 #${prefix} defaultHeaderColWidth(Array|number) -行表头默认列宽 可以按逐列设置 如果没有就取 defaultColWidth +行表头默认列宽 可以按逐列设置, 如果没有设置的话会取 defaultColWidth 的值作为表头的列宽高。 + +具体定义: + +``` + /** 行表头默认列宽 可以按逐列设置 如果没有就取defaultColWidth */ + defaultHeaderColWidth?: (number | 'auto') | (number | 'auto')[]; +``` #${prefix} keyboardOptions(Object) diff --git a/docs/assets/option/zh/table/gantt.md b/docs/assets/option/zh/table/gantt.md index 14632b609..7d5c2295f 100644 --- a/docs/assets/option/zh/table/gantt.md +++ b/docs/assets/option/zh/table/gantt.md @@ -68,6 +68,18 @@ {{ use: common-gantt-task-bar(prefix = '###')}} +## taskShowMode(TaskShowMode) + +任务条展示模式。使用枚举类型`TaskShowMode`进行配置 + +非必填 + +- `TaskShowMode.Tasks_Separate`: 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果! +- `TaskShowMode.Sub_Tasks_Separate`: 省去父任务节点不展示,且所有子任务的节点分别用一行展示。 +- `TaskShowMode.Sub_Tasks_Inline`: 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 +- `TaskShowMode.Sub_Tasks_Arrange`: 省去父任务节点不展示,且所有子任务会维持 records 中的数据顺序布局,并保证节点不重叠展示。 +- `TaskShowMode.Sub_Tasks_Compact`: 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示。 + ## taskKeyField(string) 数据条目可唯一标识的字段名, 默认为'id' diff --git a/docs/assets/option/zh/table/listTable.md b/docs/assets/option/zh/table/listTable.md index 79e027a42..026a24862 100644 --- a/docs/assets/option/zh/table/listTable.md +++ b/docs/assets/option/zh/table/listTable.md @@ -141,3 +141,11 @@ type CustomAggregation = { ## groupTitleCustomLayout(CustomLayout) 分组标题自定义布局。 + +## customComputeRowHeight(Function) + +代码 VTable 内部计算行高的方法,用户可以自定义计算行高的方法。 + +``` +customComputeRowHeight?: (computeArgs: { row: number; table: ListTableAPI }) => number; +``` diff --git a/docs/package.json b/docs/package.json index c003e9e41..616ce4e6d 100644 --- a/docs/package.json +++ b/docs/package.json @@ -13,6 +13,7 @@ "@arco-design/web-react": "2.60.2", "@arco-design/web-vue": "^2.11.0", "@visactor/vtable": "workspace:*", + "@visactor/vtable-gantt": "workspace:*", "@visactor/react-vtable": "workspace:*", "@visactor/vue-vtable": "workspace:*", "@visactor/openinula-vtable": "workspace:*", diff --git a/packages/openinula-vtable/package.json b/packages/openinula-vtable/package.json index 6d06cae46..89423b482 100644 --- a/packages/openinula-vtable/package.json +++ b/packages/openinula-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/openinula-vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "The openinula version of VTable", "keywords": [ "openinula", diff --git a/packages/react-vtable/package.json b/packages/react-vtable/package.json index 76dd1dba8..b66669dfe 100644 --- a/packages/react-vtable/package.json +++ b/packages/react-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "The react version of VTable", "keywords": [ "react", diff --git a/packages/vtable-calendar/package.json b/packages/vtable-calendar/package.json index 58688a381..abd18bb91 100644 --- a/packages/vtable-calendar/package.json +++ b/packages/vtable-calendar/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-calendar", - "version": "1.11.5", + "version": "1.12.0", "description": "The calendar component of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-editors/package.json b/packages/vtable-editors/package.json index 28b47420d..9c5fcfbee 100644 --- a/packages/vtable-editors/package.json +++ b/packages/vtable-editors/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-editors", - "version": "1.11.5", + "version": "1.12.0", "description": "", "sideEffects": false, "main": "cjs/index.js", diff --git a/packages/vtable-export/package.json b/packages/vtable-export/package.json index c0493ad38..7555877ed 100644 --- a/packages/vtable-export/package.json +++ b/packages/vtable-export/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-export", - "version": "1.11.5", + "version": "1.12.0", "description": "The export util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts new file mode 100644 index 000000000..449eb13ff --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Arrange-customLayout.ts @@ -0,0 +1,354 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + id: 0, + name: 'Planning', + start: '2024-11-15', + end: '2024-11-21', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + name: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + name: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + name: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + name: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + name: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'name', + title: 'PROCESS', + width: 150, + tree: true + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + theme: { + bodyStyle: { + bgColor: 'white', + color: 'rgb(115 115 115)' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 14, + textAlign: 'center', + color: 'white' + }, + barStyle: { + width: 22, + /** 任务条的颜色 */ + barColor: 'rgb(68 99 244)', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 15, + borderColor: 'black', + borderWidth: 1 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts new file mode 100644 index 000000000..a0c2594ca --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Compact-customLayout.ts @@ -0,0 +1,427 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + title: 'Planning', + children: [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + title: 'Research', + children: [ + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Goal Setting', + children: [ + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + title: 'Strategy', + children: [ + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + } + ] + }, + { + title: 'Execution', + children: [ + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + title: 'Monitoring', + children: [ + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Process review', + children: [ + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'PROCESS', + width: 150 + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Compact, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 80, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Inline-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Inline-customLayout.ts new file mode 100644 index 000000000..91884c589 --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Inline-customLayout.ts @@ -0,0 +1,407 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + title: 'Planning', + children: [ + { + id: 1, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 2, + name: 'Michael Smith', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + title: 'Research', + children: [ + { + id: 3, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Goal Setting', + children: [ + { + id: 4, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 5, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + title: 'Strategy', + children: [ + { + id: 6, + name: 'Sarah Miller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 7, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Execution', + children: [ + { + id: 8, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 9, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 10, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + title: 'Monitoring', + children: [ + { + id: 11, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 12, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 13, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Process review', + children: [ + { + id: 14, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'PROCESS', + width: 150 + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Inline, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 80, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-11-17', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Separate-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-Separate-customLayout.ts new file mode 100644 index 000000000..c2b95b62a --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Separate-customLayout.ts @@ -0,0 +1,391 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + title: 'Planning', + children: [ + { + id: 1, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 2, + name: 'Michael Smith', + start: '2024-11-19', + end: '2024-11-20', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + id: 300, + title: 'Research', + children: [ + { + id: 3, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Goal Setting', + children: [ + { + id: 4, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 5, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + + { + title: 'Strategy', + children: [ + { + id: 6, + name: 'Sarah Miller', + start: '2024-11-20', + end: '2024-11-21', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 7, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Execution', + children: [ + { + id: 8, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 9, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 10, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + }, + { + title: 'Monitoring', + children: [ + { + id: 11, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 12, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + } + ] + }, + { + title: 'Reporting', + children: [ + { + id: 13, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + } + ] + }, + { + title: 'Process review', + children: [ + { + id: 14, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'PROCESS', + width: 150 + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 80, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + ] + }, + minDate: '2024-11-17', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Arrange.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Arrange.ts new file mode 100644 index 000000000..5cd18105f --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Arrange.ts @@ -0,0 +1,461 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +register.editor('input', input_editor); +register.editor('date-input', date_input_editor); +export function createTable() { + const records = [ + { + id: 100, + title: 'Software Development', + children: [ + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-07-27', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/08/01', + end: '2024/08/04', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 200, + title: 'Scope' + }, + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + + { + id: 4, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 5, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.02.2024', + progress: 31, + priority: 'P0' + }, + { + id: 6, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 7, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 100, + title: 'Software Development' + }, + { + id: 200, + title: 'Scope', + children: [ + { + id: 8, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 9, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 10, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 11, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 12, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 13, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-01', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 14, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-03', + end: '2024-08-05', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 15, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 16, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 17, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 18, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 19, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + } + ] + }, + { + id: 100, + title: 'Software Development', + children: [ + { + id: 20, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 21, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 22, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'title', + width: 200, + tree: true, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + rowHeight: 30, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Arrange, + frame: { + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + }, + verticalSplitLineMoveable: true + }, + grid: { + // backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title}_{id} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 8 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 3, + linkedToTaskKey: 2 + }, + { + type: DependencyType.StartToFinish, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 4 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 6, + linkedToTaskKey: 5 + } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-01', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts new file mode 100644 index 000000000..d72400e97 --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Inline.ts @@ -0,0 +1,453 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +register.editor('input', input_editor); +register.editor('date-input', date_input_editor); +export function createTable() { + const records = [ + { + id: 100, + title: 'Software Development', + children: [ + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-07-27', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/08/01', + end: '2024/08/04', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 200, + title: 'Scope' + }, + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + + { + id: 4, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 5, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.02.2024', + progress: 31, + priority: 'P0' + }, + { + id: 6, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 7, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 100, + title: 'Software Development' + }, + { + id: 200, + title: 'Scope', + children: [ + { + id: 8, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 9, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 10, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 11, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 12, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 13, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-01', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 14, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-03', + end: '2024-08-05', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 15, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 16, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 17, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 18, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 19, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + } + ] + }, + { + id: 100, + title: 'Software Development', + children: [ + { + id: 20, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 21, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 22, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'title', + width: 200, + tree: true, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Sub_Tasks_Inline, + frame: { + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + }, + verticalSplitLineMoveable: true + }, + grid: { + backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 8 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 3, + linkedToTaskKey: 2 + } + // { + // type: DependencyType.StartToFinish, + // linkedFromTaskKey: 2, + // linkedToTaskKey: 3 + // }, + // { + // type: DependencyType.StartToStart, + // linkedFromTaskKey: 3, + // linkedToTaskKey: 4 + // }, + // { + // type: DependencyType.FinishToFinish, + // linkedFromTaskKey: 4, + // linkedToTaskKey: 5 + // } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-07', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts new file mode 100644 index 000000000..760d0b73e --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-Sub_Tasks_Separate.ts @@ -0,0 +1,461 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const CONTAINER_ID = 'vTable'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +register.editor('input', input_editor); +register.editor('date-input', date_input_editor); +export function createTable() { + const records = [ + { + id: 100, + title: 'Software Development', + children: [ + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-07-27', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/08/01', + end: '2024/08/04', + progress: 100, + priority: 'P1' + } + ] + }, + { + id: 200, + title: 'Scope' + }, + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + + { + id: 4, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 5, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.02.2024', + progress: 31, + priority: 'P0' + }, + { + id: 6, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 7, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 100, + title: 'Software Development' + }, + { + id: 200, + title: 'Scope', + children: [ + { + id: 8, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 9, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 10, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 11, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 12, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 13, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-01', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 14, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-03', + end: '2024-08-05', + progress: 100, + priority: 'P1' + } + ] + }, + + { + id: 300, + title: 'Determine project scope', + children: [ + { + id: 15, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 16, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 17, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 18, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 19, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + } + ] + }, + { + id: 100, + title: 'Software Development', + children: [ + { + id: 20, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 21, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 22, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + } + ] + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'title', + title: 'title', + width: 200, + tree: true, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + rowHeight: 30, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } + }, + + tasksShowMode: TasksShowMode.Sub_Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + }, + verticalSplitLineMoveable: true + }, + grid: { + // backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 8 + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 3, + linkedToTaskKey: 2 + } + // { + // type: DependencyType.StartToFinish, + // linkedFromTaskKey: 2, + // linkedToTaskKey: 3 + // }, + // { + // type: DependencyType.StartToStart, + // linkedFromTaskKey: 3, + // linkedToTaskKey: 4 + // }, + // { + // type: DependencyType.FinishToFinish, + // linkedFromTaskKey: 4, + // linkedToTaskKey: 5 + // } + ] + }, + timelineHeader: { + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red', + backgroundColor: '#EEF1F5' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-07', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-editor.ts b/packages/vtable-gantt/examples/gantt/gantt-editor.ts index 23b7bf7ba..a6caa8eb2 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-editor.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-editor.ts @@ -1,7 +1,7 @@ import type { ColumnsDefine } from '@visactor/vtable'; import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; -import type { GanttConstructorOptions, TYPES } from '../../src/index'; -import { Gantt, VTable } from '../../src/index'; +import type { GanttConstructorOptions } from '../../src/index'; +import { Gantt, VTable, TYPES } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; const CONTAINER_ID = 'vTable'; const date_input_editor = new DateInputEditor({}); @@ -38,7 +38,7 @@ export function createTable() { priority: 'P1' }, { - id: 1, + id: 4, title: 'Software Development', developer: 'liufangfang.jane@bytedance.com', start: '2024-08-04', @@ -47,7 +47,7 @@ export function createTable() { priority: 'P0' }, { - id: 2, + id: 5, title: 'Scope', developer: 'liufangfang.jane@bytedance.com', start: '07/24/2024', @@ -944,6 +944,39 @@ export function createTable() { } } ], + + dependency: { + links: [ + { + type: TYPES.DependencyType.FinishToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: TYPES.DependencyType.StartToFinish, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: TYPES.DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 4 + }, + { + type: TYPES.DependencyType.FinishToFinish, + linkedFromTaskKey: 4, + linkedToTaskKey: 5 + } + ], + // linkLineSelectable: false, + linkSelectedLineStyle: { + shadowBlur: 5, //阴影宽度 + shadowColor: 'red', + lineColor: 'red', + lineWidth: 1 + }, + linkCreatable: true + }, rowSeriesNumber: { title: '行号', dragOrder: true, diff --git a/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts b/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts index 357d557cd..a7926168f 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-noLeftListTable.ts @@ -931,18 +931,18 @@ export function createTable() { } } ], - rowSeriesNumber: { - title: '行号', - dragOrder: true, - headerStyle: { - bgColor: '#EEF1F5', + // rowSeriesNumber: { + // title: '行号', + // dragOrder: true, + // headerStyle: { + // bgColor: '#EEF1F5', - borderColor: '#e1e4e8' - }, - style: { - borderColor: '#e1e4e8' - } - }, + // borderColor: '#e1e4e8' + // }, + // style: { + // borderColor: '#e1e4e8' + // } + // }, scrollStyle: { visible: 'scrolling' }, diff --git a/packages/vtable-gantt/examples/gantt/gantt-table-groupBy.ts b/packages/vtable-gantt/examples/gantt/gantt-table-groupBy.ts new file mode 100644 index 000000000..31e6de5ec --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-table-groupBy.ts @@ -0,0 +1,986 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Gantt } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +const CONTAINER_ID = 'vTable'; + +export function createTable() { + const records = [ + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/09', + end: '2024/07/11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.04.2024', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-30', + end: '2024-08-14', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/24', + end: '2024/08/04', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-04', + end: '2024-08-04', + progress: 90, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '07/24/2024', + end: '08/04/2024', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024.07.06', + end: '2024.07.08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024/07/09', + end: '2024/07/11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '07.24.2024', + end: '08.04.2024', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-08-09', + end: '2024-09-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + }, + { + id: 1, + title: 'Software Development', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-24', + end: '2024-08-04', + progress: 31, + priority: 'P0' + }, + { + id: 2, + title: 'Scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-06', + end: '2024-07-08', + progress: 60, + priority: 'P0' + }, + { + id: 3, + title: 'Determine project scope', + developer: 'liufangfang.jane@bytedance.com', + start: '2024-07-09', + end: '2024-07-11', + progress: 100, + priority: 'P1' + } + ]; + + const columns: ColumnsDefine = [ + // { + // field: 'id', + // title: 'ID', + // width: 80, + // sort: true + // }, + { + field: 'title', + title: 'title', + width: 200, + sort: true + }, + { + field: 'start', + title: 'start', + width: 150, + sort: true + }, + { + field: 'end', + title: 'end', + width: 150, + sort: true + }, + { + field: 'priority', + title: 'priority', + width: 100, + sort: true + }, + + { + field: 'progress', + title: 'progress', + width: 200, + sort: true + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + tableWidth: 400, + minTableWidth: 100, + maxTableWidth: 600, + groupBy: 'priority' + }, + + frame: { + verticalSplitLineMoveable: true, + outerFrameStyle: { + borderLineWidth: 2, + borderColor: 'red', + cornerRadius: 8 + }, + verticalSplitLine: { + lineWidth: 3, + lineColor: '#e1e4e8' + }, + verticalSplitLineHighlight: { + lineColor: 'green', + lineWidth: 3 + } + }, + grid: { + // backgroundColor: 'gray', + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 40, + + taskBar: { + selectable: false, + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{title} {progress}%', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 20, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 10 + } + }, + timelineHeader: { + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + backgroundColor: '#EEF1F5', + colWidth: 60, + scales: [ + { + unit: 'week', + step: 1, + startOfWeek: 'sunday', + format(date: TYPES.DateFormatArgumentType) { + return `Week ${date.dateIndex}`; + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + }, + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'red' + } + } + // { + // unit: 'quarter', + // step: 1, + // format(date: TYPES.DateFormatArgumentType) { + // return '第' + date.index + '季度'; + // } + // } + ] + }, + minDate: '2024-07-01', + maxDate: '2024-10-15', + markLine: [ + { + date: '2024-07-17', + style: { + lineWidth: 1, + lineColor: 'blue', + lineDash: [8, 4] + } + }, + { + date: '2024-08-17', + position: 'middle', + scrollToMarkLine: true, + style: { + lineWidth: 2, + lineColor: 'red', + lineDash: [8, 4] + } + } + ], + rowSeriesNumber: { + title: '行号', + dragOrder: true, + headerStyle: { + bgColor: '#EEF1F5', + + borderColor: '#e1e4e8' + }, + style: { + borderColor: '#e1e4e8' + } + }, + scrollStyle: { + visible: 'scrolling' + }, + overscrollBehavior: 'none' + }; + // columns:[ + // { + // title:'2024-07', + // columns:[ + // { + // title:'01' + // }, + // { + // title:'02' + // }, + // ... + // ] + // }, + // ... + // ] + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + ganttInstance.on('change_date_range', e => { + console.log('change_date_range', e); + }); + ganttInstance.on('mouseenter_task_bar', e => { + console.log('mouseenter_taskbar', e); + }); + ganttInstance.on('mouseleave_task_bar', e => { + console.log('mouseleave_taskbar', e); + }); + ganttInstance.on('click_task_bar', e => { + console.log('click_task_bar', e); + }); + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + bindDebugTool(ganttInstance.scenegraph.stage as any, { + customGrapicKeys: ['role', '_updateTag'] + }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-taskMerge-customLayout.ts b/packages/vtable-gantt/examples/gantt/gantt-taskMerge-customLayout.ts new file mode 100644 index 000000000..36c6945b2 --- /dev/null +++ b/packages/vtable-gantt/examples/gantt/gantt-taskMerge-customLayout.ts @@ -0,0 +1,448 @@ +import type { ColumnsDefine } from '@visactor/vtable'; +import { register } from '@visactor/vtable'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import type { GanttConstructorOptions, TYPES } from '../../src/index'; +import { Group, Image, Text } from '@visactor/vtable/es/vrender'; +import { Gantt, VTable } from '../../src/index'; +import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; +import { scale } from '@visactor/vutils'; +import { DependencyType, TasksShowMode } from '../../src/ts-types'; +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +VTable.register.editor('input', input_editor); +VTable.register.editor('date-input', date_input_editor); +const CONTAINER_ID = 'vTable'; + +const barColors0 = ['#aecde6', '#c6a49a', '#ffb582', '#eec1de', '#b3d9b3', '#d9d1a5', '#cccccc', '#e59a9c', '#c9bede']; +const barColors = ['#1f77b4', '#8c564b', '#ff7f0e', '#e377c2', '#2ca02c', '#bcbd22', '#7f7f7f', '#d62728', '#9467bd']; +export function createTable() { + const records = [ + { + id: 1, + name: 'Michael Smith', + start: '2024-11-15', + end: '2024-11-17', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 2, + name: 'Emily', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + + { + id: 3, + name: 'Rramily', + start: '2024-11-19', + end: '2024-11-20', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 4, + name: 'Lichael Join', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Planning', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + + { + id: 5, + name: 'Ryan', + start: '2024-11-18', + end: '2024-11-21', + parentTask: 'Research', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 6, + name: 'Daniel Davis', + start: '2024-11-21', + end: '2024-11-22', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 7, + name: 'Lauren', + start: '2024-11-18', + end: '2024-11-19', + parentTask: 'Goal Setting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 8, + name: 'Tacarah Siller', + start: '2024-11-20', + end: '2024-11-21', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 9, + name: 'Camentew Olision', + start: '2024-11-25', + end: '2024-11-26', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 10, + name: 'Sarah Miller', + start: '2024-11-17', + end: '2024-11-18', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 11, + name: 'Matthew Wilson', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 12, + name: 'Grarah Poliller', + start: '2024-11-23', + end: '2024-11-24', + parentTask: 'Strategy', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 13, + name: 'Ashley Taylor', + start: '2024-11-22', + end: '2024-11-25', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 14, + name: 'Megan', + start: '2024-11-27', + end: '2024-11-30', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 15, + name: 'David', + start: '2024-12-10', + end: '2024-12-18', + parentTask: 'Execution', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + }, + { + id: 16, + name: 'Hannah', + start: '2024-11-20', + end: '2024-11-30', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/bear.jpg' + }, + { + id: 17, + name: 'Andrew', + start: '2024-12-02', + end: '2024-12-18', + parentTask: 'Monitoring', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/wolf.jpg' + }, + { + id: 18, + name: 'Joshua Anderson', + start: '2024-12-22', + end: '2024-12-28', + parentTask: 'Reporting', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/rabbit.jpg' + }, + { + id: 19, + name: 'Christopher Moore', + start: '2024-11-25', + end: '2024-11-30', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/cat.jpg' + }, + { + id: 20, + name: 'Emma', + start: '2024-12-01', + end: '2024-12-18', + parentTask: 'Process review', + avatar: 'https://lf9-dp-fe-cms-tos.byteorg.com/obj/bit-cloud/VTable/custom-render/flower.jpg' + } + ]; + + const columns: ColumnsDefine = [ + { + field: 'parentTask', + title: 'Task', + width: 100, + mergeCell: true, + editor: 'input', + style: { + bgColor: '#EEF1F5', + color: '#141414', + fontWeight: 'bold', + fontSize: 16, + autoWrapText: true + } + }, + { + field: 'name', + title: 'Master', + width: 100, + editor: 'input' + }, + { + field: 'start', + title: 'start', + width: 100, + sort: true, + editor: 'date-input' + }, + { + field: 'end', + title: 'end', + width: 100, + sort: true, + editor: 'date-input' + } + ]; + const option: GanttConstructorOptions = { + records, + taskListTable: { + columns: columns, + minTableWidth: 100, + hierarchyExpandLevel: 5, + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + }, + theme: { + bodyStyle: { + padding: 5, + bgColor: 'white' + }, + headerStyle: { + color: 'white' + } + } + }, + groupBy: true, + tasksShowMode: TasksShowMode.Tasks_Separate, + frame: { + outerFrameStyle: { + borderLineWidth: 1, + borderColor: '#e1e4e8', + cornerRadius: 8 + }, + verticalSplitLineMoveable: false + }, + grid: { + horizontalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + }, + verticalLine: { + lineWidth: 1, + lineColor: '#e1e4e8' + } + }, + headerRowHeight: 60, + rowHeight: 60, + taskBar: { + startDateField: 'start', + endDateField: 'end', + progressField: 'progress', + labelText: '{name}', + labelTextStyle: { + fontFamily: 'Arial', + fontSize: 16, + textAlign: 'left' + }, + barStyle: { + width: 50, + /** 任务条的颜色 */ + barColor: '#ee8800', + /** 已完成部分任务条的颜色 */ + completedBarColor: '#91e8e0', + /** 任务条的圆角 */ + cornerRadius: 25 + }, + customLayout: (args: any) => { + const colorLength = barColors.length; + const { width, height, index, startDate, endDate, taskDays, progress, taskRecord, ganttInstance } = args; + const container = new Group({ + width, + height, + fill: { + gradient: 'linear', + x0: 0, + y0: 0, + x1: 1, + y1: 0, + stops: [ + { + offset: 0, + color: barColors0[index % colorLength] + }, + { + offset: 0.5, + color: barColors[index % colorLength] + }, + { + offset: 1, + color: barColors0[index % colorLength] + } + ] + }, + display: 'flex', + flexDirection: 'row', + flexWrap: 'nowrap' + }); + const containerLeft = new Group({ + height, + width: 60, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'space-around' + // fill: 'red' + }); + container.add(containerLeft); + + const icon0 = new Image({ + width: 40, + height: 40, + image: taskRecord.avatar, + cornerRadius: 20 + }); + containerLeft.add(icon0); + + const containerRight = new Group({ + height, + width: width - 60, + display: 'flex', + flexDirection: 'column' + // alignItems: 'left' + }); + container.add(containerRight); + + const bloggerName = new Text({ + text: taskRecord.name + ' ' + taskRecord.id, + fontSize: 16, + fontFamily: 'sans-serif', + fill: 'white', + maxLineWidth: width - 60, + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(bloggerName); + + const days = new Text({ + text: `${taskDays}天`, + fontSize: 13, + fontFamily: 'sans-serif', + fill: 'white', + boundsPadding: [10, 0, 0, 0] + }); + containerRight.add(days); + return { + rootContainer: container + // renderDefaultBar: true + // renderDefaultText: true + }; + } + }, + dependency: { + linkCreatable: true, + links: [ + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 1, + linkedToTaskKey: 2 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 2, + linkedToTaskKey: 3 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 3, + linkedToTaskKey: 5 + }, + { + type: DependencyType.FinishToFinish, + linkedFromTaskKey: 5, + linkedToTaskKey: 4 + }, + { + type: DependencyType.StartToStart, + linkedFromTaskKey: 8, + linkedToTaskKey: 9 + }, + { + type: DependencyType.FinishToStart, + linkedFromTaskKey: 9, + linkedToTaskKey: 10 + } + ] + }, + timelineHeader: { + colWidth: 100, + verticalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + horizontalLine: { + lineColor: '#e1e4e8', + lineWidth: 1 + }, + backgroundColor: '#63a8ff', + scales: [ + { + unit: 'day', + step: 1, + format(date: TYPES.DateFormatArgumentType) { + return date.dateIndex.toString(); + }, + style: { + fontSize: 20, + fontWeight: 'bold', + color: 'white' + } + } + ] + }, + minDate: '2024-11-14', + maxDate: '2024-12-31', + scrollStyle: { + scrollRailColor: 'RGBA(246,246,246,0.5)', + visible: 'none', + width: 6, + scrollSliderCornerRadius: 2, + scrollSliderColor: '#5cb85c' + } + }; + + const ganttInstance = new Gantt(document.getElementById(CONTAINER_ID)!, option); + window.ganttInstance = ganttInstance; + ganttInstance.on('scroll', e => { + console.log('scroll', e); + }); + + ganttInstance.listTableInstance?.on('scroll', e => { + console.log('listTable scroll', e); + }); + // bindDebugTool(ganttInstance.scenegraph.stage as any, { + // customGrapicKeys: ['role', '_updateTag'] + // }); +} diff --git a/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts b/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts index 1d0a2534b..92f7d8cdc 100644 --- a/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts +++ b/packages/vtable-gantt/examples/gantt/gantt-tree-dependency.ts @@ -1,11 +1,15 @@ import type { ColumnsDefine } from '@visactor/vtable'; import type { GanttConstructorOptions, TYPES } from '../../src/index'; -import { Gantt } from '../../src/index'; +import { DateInputEditor, InputEditor } from '@visactor/vtable-editors'; +import { Gantt, VTable } from '../../src/index'; import { bindDebugTool } from '../../../vtable/src/scenegraph/debug-tool'; import { DependencyType } from '../../src/ts-types'; import { HierarchyState } from '../../../vtable/src/ts-types'; const CONTAINER_ID = 'vTable'; - +const date_input_editor = new DateInputEditor({}); +const input_editor = new InputEditor({}); +VTable.register.editor('input', input_editor); +VTable.register.editor('date-input', date_input_editor); export function createTable() { const records = [ { @@ -179,12 +183,14 @@ export function createTable() { field: 'start', title: 'start', width: 150, + editor: 'date-input', sort: true }, { field: 'end', title: 'end', width: 150, + editor: 'date-input', sort: true }, { diff --git a/packages/vtable-gantt/examples/menu.ts b/packages/vtable-gantt/examples/menu.ts index 35a4780e0..912210bd0 100644 --- a/packages/vtable-gantt/examples/menu.ts +++ b/packages/vtable-gantt/examples/menu.ts @@ -65,6 +65,43 @@ export const menus = [ { path: 'gantt', name: 'gantt-listTable' + }, + { + path: 'gantt', + name: 'gantt-table-groupBy' + }, + { + path: 'gantt', + name: 'gantt-Sub_Tasks_Inline' + }, + + { + path: 'gantt', + name: 'gantt-Sub_Tasks_Separate' + }, + { + path: 'gantt', + name: 'gantt-Sub_Tasks_Arrange' + }, + { + path: 'gantt', + name: 'gantt-Inline-customLayout' + }, + { + path: 'gantt', + name: 'gantt-Separate-customLayout' + }, + { + path: 'gantt', + name: 'gantt-Arrange-customLayout' + }, + { + path: 'gantt', + name: 'gantt-Compact-customLayout' + }, + { + path: 'gantt', + name: 'gantt-taskMerge-customLayout' } // ] // } diff --git a/packages/vtable-gantt/package.json b/packages/vtable-gantt/package.json index 68d98c209..cd2554a0a 100644 --- a/packages/vtable-gantt/package.json +++ b/packages/vtable-gantt/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-gantt", - "version": "1.11.5", + "version": "1.12.0", "description": "canvas table width high performance", "keywords": [ "vtable-gantt", diff --git a/packages/vtable-gantt/src/Gantt.ts b/packages/vtable-gantt/src/Gantt.ts index 57de3dd37..7eecda0be 100644 --- a/packages/vtable-gantt/src/Gantt.ts +++ b/packages/vtable-gantt/src/Gantt.ts @@ -24,11 +24,14 @@ import type { ITaskLinkSelectedStyle, IPointStyle } from './ts-types'; +import { TasksShowMode } from './ts-types'; import type { ListTableConstructorOptions } from '@visactor/vtable'; import { themes, registerCheckboxCell, registerProgressBarCell, registerRadioCell, ListTable } from '@visactor/vtable'; import { EventManager } from './event/event-manager'; import { StateManager } from './state/state-manager'; import { + computeRowsCountByRecordDate, + computeRowsCountByRecordDateForCompact, convertProgress, createSplitLineAndResizeLine, DayTimes, @@ -42,6 +45,7 @@ import { import { EventTarget } from './event/EventTarget'; import { createDateAtMidnight, formatDate, isPropertyWritable, parseDateFormat } from './tools/util'; import { DataSource } from './data/DataSource'; +import { isValid } from '@visactor/vutils'; // import { generateGanttChartColumns } from './gantt-helper'; export function createRootElement(padding: any, className: string = 'vtable-gantt'): HTMLElement { const element = document.createElement('div'); @@ -111,6 +115,7 @@ export class Gantt extends EventTarget { taskBarLabelText: ITaskBarLabelText; taskBarMoveable: boolean; taskBarResizable: boolean; + taskBarDragOrder: boolean; taskBarLabelStyle: ITaskBarLabelTextStyle; taskBarCustomLayout: ITaskBarCustomLayout; taskBarCreatable: boolean; @@ -122,6 +127,7 @@ export class Gantt extends EventTarget { outerFrameStyle: IFrameStyle; pixelRatio: number; + tasksShowMode: TasksShowMode; startDateField: string; endDateField: string; @@ -323,8 +329,30 @@ export class Gantt extends EventTarget { listTable_options[key] = this.options.taskListTable[key]; if (key === 'columns') { listTable_options[key][listTable_options[key].length - 1].disableColumnResize = true; + if ( + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + for (let i = 0; i < listTable_options.columns.length; i++) { + if (listTable_options.columns[i].tree) { + listTable_options.columns[i].tree = false; + } + } + } + } + if ( + key === 'hierarchyExpandLevel' && + (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact) + ) { + delete listTable_options[key]; } } + // lineWidthArr[1] = 0; //Object.assign浅拷贝 会直接覆盖第一层属性 。theme.ARCO.extends 其中extends不能连续调用,且赋值也只是第一层 if (this.options.taskListTable?.theme) { @@ -496,7 +524,39 @@ export class Gantt extends EventTarget { listTable_options.canvasWidth = this.taskTableWidth as number; listTable_options.canvasHeight = this.canvas.height; listTable_options.defaultHeaderRowHeight = this.getAllHeaderRowsHeight(); - listTable_options.defaultRowHeight = this.parsedOptions.rowHeight; + if (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { + const { row, table } = args; + const record = table.getRecordByRowCol(0, row); + return (record.children?.length || 1) * this.parsedOptions.rowHeight; + }; + listTable_options.defaultRowHeight = 'auto'; + } else if (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact) { + listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { + const { row, table } = args; + const record = table.getRecordByRowCol(0, row); + return ( + computeRowsCountByRecordDateForCompact( + record, + this.parsedOptions.startDateField, + this.parsedOptions.endDateField + ) * this.parsedOptions.rowHeight + ); + }; + listTable_options.defaultRowHeight = 'auto'; + } else if (this.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange) { + listTable_options.customComputeRowHeight = (args: { row: number; table: ListTable }) => { + const { row, table } = args; + const record = table.getRecordByRowCol(0, row); + return ( + computeRowsCountByRecordDate(record, this.parsedOptions.startDateField, this.parsedOptions.endDateField) * + this.parsedOptions.rowHeight + ); + }; + listTable_options.defaultRowHeight = 'auto'; + } else { + listTable_options.defaultRowHeight = this.options.rowHeight ?? 40; + } listTable_options.clearDOM = false; return listTable_options; } @@ -577,7 +637,25 @@ export class Gantt extends EventTarget { this.parsedOptions.colWidthPerDay = 0; } } + getRowHeightByIndex(index: number) { + if (this.taskListTableInstance) { + return this.taskListTableInstance.getRowHeight(index + this.taskListTableInstance.columnHeaderLevelCount); + } + return this.parsedOptions.rowHeight; + } + getRowsHeightByIndex(startIndex: number, endIndex: number) { + if (this.taskListTableInstance) { + return this.taskListTableInstance.getRowsHeight( + startIndex + this.taskListTableInstance.columnHeaderLevelCount, + endIndex + this.taskListTableInstance.columnHeaderLevelCount + ); + } + return this.parsedOptions.rowHeight * (endIndex - startIndex + 1); + } getAllRowsHeight() { + if (this.taskListTableInstance) { + return this.taskListTableInstance.getAllRowsHeight(); + } return this.getAllHeaderRowsHeight() + this.itemCount * this.parsedOptions.rowHeight; } getAllHeaderRowsHeight() { @@ -603,12 +681,21 @@ export class Gantt extends EventTarget { } getAllTaskBarsHeight() { + if (this.taskListTableInstance) { + return this.taskListTableInstance.getRowsHeight( + this.taskListTableInstance.columnHeaderLevelCount, + this.taskListTableInstance.rowCount - 1 + ); + } return this.itemCount * this.parsedOptions.rowHeight; } getTaskShowIndexByRecordIndex(index: number | number[]) { return this.taskListTableInstance.getBodyRowIndexByRecordIndex(index); } - getRecordByIndex(taskShowIndex: number) { + getRecordByIndex(taskShowIndex: number, sub_task_index?: number) { + if (isValid(sub_task_index)) { + return this.records[taskShowIndex]?.children?.[sub_task_index]; + } if (this.taskListTableInstance) { return this.taskListTableInstance.getRecordByRowCol( 0, @@ -621,6 +708,11 @@ export class Gantt extends EventTarget { _refreshTaskBar(taskShowIndex: number) { // this.taskListTableInstance.updateRecords([record], [index]); this.scenegraph.taskBar.updateTaskBarNode(taskShowIndex); + this.scenegraph.refreshRecordLinkNodes( + taskShowIndex, + undefined, + this.scenegraph.taskBar.getTaskBarNodeByIndex(taskShowIndex) + ); this.scenegraph.updateNextFrame(); } _updateRecordToListTable(record: any, index: number) { @@ -635,14 +727,17 @@ export class Gantt extends EventTarget { * @param index * @returns 当前任务信息 */ - getTaskInfoByTaskListIndex(taskShowIndex: number): { + getTaskInfoByTaskListIndex( + taskShowIndex: number, + sub_task_index?: number + ): { taskRecord: any; taskDays: number; startDate: Date; endDate: Date; progress: number; } { - const taskRecord = this.getRecordByIndex(taskShowIndex); + const taskRecord = this.getRecordByIndex(taskShowIndex, sub_task_index); const startDateField = this.parsedOptions.startDateField; const endDateField = this.parsedOptions.endDateField; const progressField = this.parsedOptions.progressField; @@ -678,17 +773,83 @@ export class Gantt extends EventTarget { progress }; } + // /** + // * 获取指定index处任务数据的具体信息 + // * @param index + // * @returns 当前任务信息 + // */ + // getTaskInfoByTaskListIndexs( + // taskShowIndex: number, + // subTaskIndex: number + // ): { + // taskRecord: any; + // taskDays: number; + // startDate: Date; + // endDate: Date; + // progress: number; + // } { + // const taskParentRecord = this.getRecordByIndex(taskShowIndex); + // if (taskParentRecord.children?.length) { + // const taskRecord = taskParentRecord.children[subTaskIndex]; + // const startDateField = this.parsedOptions.startDateField; + // const endDateField = this.parsedOptions.endDateField; + // const progressField = this.parsedOptions.progressField; + // const rawDateStartDateTime = createDateAtMidnight(taskRecord?.[startDateField]).getTime(); + // const rawDateEndDateTime = createDateAtMidnight(taskRecord?.[endDateField]).getTime(); + // if ( + // rawDateEndDateTime < this.parsedOptions._minDateTime || + // rawDateStartDateTime > this.parsedOptions._maxDateTime || + // !taskRecord?.[startDateField] || + // !taskRecord?.[endDateField] + // ) { + // return { + // taskDays: 0, + // progress: 0, + // startDate: null, + // endDate: null, + // taskRecord + // }; + // } + // const startDate = createDateAtMidnight( + // Math.min(Math.max(this.parsedOptions._minDateTime, rawDateStartDateTime), this.parsedOptions._maxDateTime) + // ); + // const endDate = createDateAtMidnight( + // Math.max(Math.min(this.parsedOptions._maxDateTime, rawDateEndDateTime), this.parsedOptions._minDateTime) + // ); + // const progress = convertProgress(taskRecord[progressField]); + // const taskDays = Math.ceil(Math.abs(endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1; + // return { + // taskRecord, + // taskDays, + // startDate, + // endDate, + // progress + // }; + // } + // return { + // taskDays: 0, + // progress: 0, + // startDate: null, + // endDate: null, + // taskRecord: null + // }; + // } /** * 拖拽任务条或者调整任务条尺寸修改日期更新到数据中 * @param updateDateType * @param days * @param index */ - _updateDateToTaskRecord(updateDateType: 'move' | 'start-move' | 'end-move', days: number, index: number) { - const taskRecord = this.getRecordByIndex(index); + _updateDateToTaskRecord( + updateDateType: 'move' | 'start-move' | 'end-move', + days: number, + index: number, + sub_task_index?: number + ) { + const taskRecord = this.getRecordByIndex(index, sub_task_index); const startDateField = this.parsedOptions.startDateField; const endDateField = this.parsedOptions.endDateField; - const dateFormat = this.parsedOptions.dateFormat ?? parseDateFormat(taskRecord[startDateField]); + const dateFormat = this.parsedOptions.dateFormat ?? parseDateFormat(taskRecord[endDateField]); const startDate = createDateAtMidnight(taskRecord[startDateField]); const endDate = createDateAtMidnight(taskRecord[endDateField]); if (updateDateType === 'move') { @@ -703,7 +864,26 @@ export class Gantt extends EventTarget { const newEndDate = formatDate(createDateAtMidnight(days * DayTimes + endDate.getTime()), dateFormat); taskRecord[endDateField] = newEndDate; } - this._updateRecordToListTable(taskRecord, index); + if (!isValid(sub_task_index)) { + //子任务不是独占左侧表格一行的情况 + this._updateRecordToListTable(taskRecord, index); + } + } + + /** + * 拖拽任务条或者调整任务条尺寸修改日期更新到数据中 + * @param updateDateType + * @param days + * @param index + */ + _dragOrderTaskRecord( + source_index: number, + source_sub_task_index: number, + target_index: number, + target_sub_task_index: number + ) { + // const source_taskRecord = this.getRecordByIndex(source_index, source_sub_task_index); + this.data.adjustOrder(source_index, source_sub_task_index, target_index, target_sub_task_index); } /** 目前不支持树形tree的情况更新单条数据 需要的话目前可以setRecords。 */ updateTaskRecord(record: any, index: number) { @@ -737,10 +917,7 @@ export class Gantt extends EventTarget { ? this.taskListTableInstance.rowCount - this.taskListTableInstance.columnHeaderLevelCount : this.records.length; this.headerHeight = this.getAllHeaderRowsHeight(); - this.drawHeight = Math.min( - this.headerHeight + this.parsedOptions.rowHeight * this.itemCount, - this.tableNoFrameHeight - ); + this.drawHeight = Math.min(this.getAllRowsHeight(), this.tableNoFrameHeight); this.gridHeight = this.drawHeight - this.headerHeight; } /** 获取绘制画布的canvas上下文 */ @@ -828,4 +1005,35 @@ export class Gantt extends EventTarget { this.scenegraph.updateNextFrame(); } } + get scrollTop(): number { + return this.stateManager.scrollTop; + } + set scrollTop(value: number) { + this.stateManager.setScrollTop(value); + } + get scrollLeft(): number { + return this.stateManager.scrollLeft; + } + set scrollLeft(value: number) { + this.stateManager.setScrollLeft(value); + } + /** 获取任务条的位置。相对应甘特图表左上角的位置。 */ + getTaskBarRelativeRect(index: number) { + const taskBarNode = this.scenegraph.taskBar.getTaskBarNodeByIndex(index); + const left = + taskBarNode.attribute.x + + this.taskListTableInstance.tableNoFrameWidth + + this.taskListTableInstance.tableX + + this.tableX - + this.scrollLeft; + const top = taskBarNode.attribute.y + this.tableY + this.headerHeight - this.scrollTop; + const width = taskBarNode.attribute.width; + const height = taskBarNode.attribute.height; + return { + left, + top, + width, + height + }; + } } diff --git a/packages/vtable-gantt/src/data/DataSource.ts b/packages/vtable-gantt/src/data/DataSource.ts index 8314a3d23..e16cf87ec 100644 --- a/packages/vtable-gantt/src/data/DataSource.ts +++ b/packages/vtable-gantt/src/data/DataSource.ts @@ -1,6 +1,7 @@ import type { Gantt } from '../Gantt'; import { createDateAtMidnight } from '../tools/util'; - +import { TasksShowMode } from '../ts-types'; +import { isValid } from '@visactor/vutils'; export class DataSource { records: any[]; minDate: Date; @@ -19,7 +20,13 @@ export class DataSource { let minDate = Number.MAX_SAFE_INTEGER; let maxDate = Number.MIN_SAFE_INTEGER; - if ((needMinDate || needMaxDate) && this.records.length) { + if ( + (needMinDate || + needMaxDate || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact) && + this.records.length + ) { for (let i = 0; i < this.records.length; i++) { const record = this.records[i]; if (needMinDate) { @@ -30,6 +37,20 @@ export class DataSource { const recordMaxDate = createDateAtMidnight(record[this._gantt.parsedOptions.endDateField]); maxDate = Math.max(maxDate, recordMaxDate.getTime()); } + + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + // 将子任务按开始时间升序排列 + record.children && + record.children.sort((a: any, b: any) => { + return ( + createDateAtMidnight(a[this._gantt.parsedOptions.startDateField]).getTime() - + createDateAtMidnight(b[this._gantt.parsedOptions.startDateField]).getTime() + ); + }); + } } needMinDate && (this.minDate = createDateAtMidnight(minDate)); @@ -40,6 +61,55 @@ export class DataSource { this._gantt.parsedOptions._maxDateTime = this._gantt.parsedOptions.maxDate.getTime(); } } + adjustOrder( + source_index: number, + source_sub_task_index: number, + target_index: number, + target_sub_task_index: number + ) { + if ( + (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact) && + source_index === target_index + ) { + return; + } + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { + if ( + isValid(source_sub_task_index) && + isValid(target_sub_task_index) && + isValid(source_index) && + isValid(target_index) + ) { + const sub_task_record = this.records[source_index].children[source_sub_task_index]; + this.records[source_index].children.splice(source_sub_task_index, 1); + if (!this.records[target_index].children) { + this.records[target_index].children = []; + } + this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); + } + this.records[target_index]?.children?.sort((a: any, b: any) => { + return ( + createDateAtMidnight(a[this._gantt.parsedOptions.startDateField]).getTime() - + createDateAtMidnight(b[this._gantt.parsedOptions.startDateField]).getTime() + ); + }); + } else { + if ( + isValid(source_sub_task_index) && + isValid(target_sub_task_index) && + isValid(source_index) && + isValid(target_index) + ) { + const sub_task_record = this.records[source_index].children[source_sub_task_index]; + this.records[source_index].children.splice(source_sub_task_index, 1); + if (!this.records[target_index].children) { + this.records[target_index].children = []; + } + this.records[target_index].children.splice(target_sub_task_index, 0, sub_task_record); + } + } + } setRecords(records: any[]) { this.records = records; this.processRecords(); diff --git a/packages/vtable-gantt/src/event/event-manager.ts b/packages/vtable-gantt/src/event/event-manager.ts index ef91fc412..0befaae3e 100644 --- a/packages/vtable-gantt/src/event/event-manager.ts +++ b/packages/vtable-gantt/src/event/event-manager.ts @@ -4,7 +4,7 @@ import type { Gantt } from '../Gantt'; import { EventHandler } from '../event/EventHandler'; import { handleWhell } from '../event/scroll'; import { formatDate, parseDateFormat, throttle } from '../tools/util'; -import { GANTT_EVENT_TYPE, InteractionState } from '../ts-types'; +import { GANTT_EVENT_TYPE, InteractionState, TasksShowMode } from '../ts-types'; import { isValid } from '@visactor/vutils'; import { getPixelRatio } from '../tools/pixel-ratio'; import { DayTimes, getDateIndexByX, getTaskIndexByY } from '../gantt-helper'; @@ -140,35 +140,47 @@ function bindTableGroupListener(event: EventManager) { scene._gantt.stateManager.hoverTaskBar.target = taskBarTarget as any as GanttTaskBarNode; stateManager.showTaskBarHover(); if (scene._gantt.hasListeners(GANTT_EVENT_TYPE.MOUSEENTER_TASK_BAR)) { - const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); - const record = scene._gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); + const taskIndex = taskBarTarget.task_index; + const sub_task_index = taskBarTarget.sub_task_index; + const record = scene._gantt.getRecordByIndex(taskIndex, sub_task_index); scene._gantt.fireListeners(GANTT_EVENT_TYPE.MOUSEENTER_TASK_BAR, { federatedEvent: e, event: e.nativeEvent, index: taskIndex, + sub_task_index, record }); } } } else { if (scene._gantt.stateManager.hoverTaskBar.target) { - stateManager.hideTaskBarHover(e); if (scene._gantt.hasListeners(GANTT_EVENT_TYPE.MOUSELEAVE_TASK_BAR)) { - const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); - const record = scene._gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(e.offset.y, scene._gantt); + const taskIndex = scene._gantt.stateManager.hoverTaskBar.target.task_index; + const sub_task_index = scene._gantt.stateManager.hoverTaskBar.target.sub_task_index; + const record = scene._gantt.getRecordByIndex(taskIndex, sub_task_index); scene._gantt.fireListeners(GANTT_EVENT_TYPE.MOUSELEAVE_TASK_BAR, { federatedEvent: e, event: e.nativeEvent, index: taskIndex, + sub_task_index, record }); } + stateManager.hideTaskBarHover(e); } //#region hover到某一个任务 检查有没有日期安排,没有的话显示创建按钮 - if (gantt.parsedOptions.taskBarCreatable) { + if ( + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Inline && + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Separate && + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Arrange && + gantt.parsedOptions.tasksShowMode !== TasksShowMode.Sub_Tasks_Compact && + gantt.parsedOptions.taskBarCreatable + ) { const taskIndex = getTaskIndexByY(e.offset.y, gantt); const recordTaskInfo = gantt.getTaskInfoByTaskListIndex(taskIndex); - if (!recordTaskInfo.taskDays && recordTaskInfo.taskRecord) { + if (!recordTaskInfo.taskDays && recordTaskInfo.taskRecord && !recordTaskInfo.taskRecord.vtableMerge) { const dateIndex = getDateIndexByX(e.offset.x, gantt); const showX = dateIndex * gantt.parsedOptions.timelineColWidth - gantt.stateManager.scroll.horizontalBarPos; const showY = taskIndex * gantt.parsedOptions.rowHeight - gantt.stateManager.scroll.verticalBarPos; @@ -297,15 +309,17 @@ function bindTableGroupListener(event: EventManager) { }); if (isClickBar && scene._gantt.parsedOptions.taskBarSelectable && event.poniterState === 'down') { stateManager.hideDependencyLinkSelectedLine(); - scene._gantt.stateManager.selectedTaskBar.target = taskBarTarget as any as GanttTaskBarNode; - stateManager.showTaskBarSelectedBorder(); + stateManager.showTaskBarSelectedBorder(taskBarTarget); if (gantt.hasListeners(GANTT_EVENT_TYPE.CLICK_TASK_BAR)) { - const taskIndex = getTaskIndexByY(e.offset.y, gantt); - const record = gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(e.offset.y, gantt); + const taskIndex = taskBarTarget.task_index; + const sub_task_index = taskBarTarget.sub_task_index; + const record = gantt.getRecordByIndex(taskIndex, sub_task_index); gantt.fireListeners(GANTT_EVENT_TYPE.CLICK_TASK_BAR, { federatedEvent: e, event: e.nativeEvent, index: taskIndex, + sub_task_index, record }); } @@ -493,8 +507,8 @@ function bindContainerDomListener(eventManager: EventManager) { } if (stateManager.interactionState === InteractionState.grabing && gantt.eventManager.poniterState === 'draging') { const lastX = gantt.eventManager.lastDragPointerXYOnWindow?.x ?? e.x; - // const lastY = gantt.eventManager.lastDragPointerXYOnWindow?.y ?? e.y; - if (Math.abs(lastX - e.x) >= 1) { + const lastY = gantt.eventManager.lastDragPointerXYOnWindow?.y ?? e.y; + if (Math.abs(lastX - e.x) >= 1 || Math.abs(lastY - e.y) >= 1) { if (stateManager.isResizingTableWidth()) { stateManager.hideDependencyLinkSelectedLine(); stateManager.hideTaskBarSelectedBorder(); @@ -527,7 +541,7 @@ function bindContainerDomListener(eventManager: EventManager) { if (stateManager.isResizingTableWidth()) { stateManager.endResizeTableWidth(); } else if (stateManager.isMoveingTaskBar()) { - stateManager.endMoveTaskBar(e.x); + stateManager.endMoveTaskBar(); } else if (stateManager.isResizingTaskBar()) { stateManager.endResizeTaskBar(e.x); } diff --git a/packages/vtable-gantt/src/gantt-helper.ts b/packages/vtable-gantt/src/gantt-helper.ts index 005e6af66..5af878165 100644 --- a/packages/vtable-gantt/src/gantt-helper.ts +++ b/packages/vtable-gantt/src/gantt-helper.ts @@ -1,6 +1,12 @@ import { text } from 'stream/consumers'; import type { Gantt } from './Gantt'; -import type { IMarkLine, IScrollStyle, ITimelineDateInfo, ITimelineScale } from './ts-types'; +import { + TasksShowMode, + type IMarkLine, + type IScrollStyle, + type ITimelineDateInfo, + type ITimelineScale +} from './ts-types'; import { createDateAtMidnight, getWeekNumber } from './tools/util'; const isNode = typeof window === 'undefined' || typeof window.window === 'undefined'; @@ -88,6 +94,7 @@ export { isNode }; export function initOptions(gantt: Gantt) { const options = gantt.options; + gantt.parsedOptions.tasksShowMode = options?.tasksShowMode ?? TasksShowMode.Tasks_Separate; gantt.parsedOptions.pixelRatio = options?.pixelRatio ?? 1; gantt.parsedOptions.rowHeight = options?.rowHeight ?? 40; gantt.parsedOptions.timelineColWidth = options?.timelineHeader?.colWidth ?? 60; @@ -166,9 +173,9 @@ export function initOptions(gantt: Gantt) { /** 任务条的圆角 */ cornerRadius: 3, /** 任务条的边框 */ - borderWidth: 1, + borderWidth: 0, /** 边框颜色 */ - borderColor: 'red', + // borderColor: 'red', fontFamily: 'Arial', fontSize: 14 }, @@ -197,11 +204,13 @@ export function initOptions(gantt: Gantt) { gantt.parsedOptions.taskBarLabelText = options?.taskBar?.labelText ?? ''; gantt.parsedOptions.taskBarMoveable = options?.taskBar?.moveable ?? true; gantt.parsedOptions.taskBarResizable = options?.taskBar?.resizable ?? true; + gantt.parsedOptions.taskBarDragOrder = options?.taskBar?.dragOrder ?? true; + // gantt.parsedOptions.taskBarHoverColor = // options?.taskBar?.hoverColor === null ? 'rgba(0,0,0,0)' : options?.taskBar?.hoverColor ?? 'rgba(0,0,0,0.1)'; gantt.parsedOptions.taskBarLabelStyle = { fontFamily: options?.taskBar?.labelTextStyle?.fontFamily ?? 'Arial', - fontSize: options?.taskBar?.labelTextStyle?.fontSize ?? gantt.parsedOptions.rowHeight / 2, + fontSize: options?.taskBar?.labelTextStyle?.fontSize ?? 20, color: options?.taskBar?.labelTextStyle?.color ?? '#F01', textAlign: options?.taskBar?.labelTextStyle?.textAlign ?? 'left', textBaseline: options?.taskBar?.labelTextStyle?.textBaseline ?? 'middle', @@ -597,7 +606,8 @@ export function updateSplitLineAndResizeLine(gantt: Gantt) { export function findRecordByTaskKey( records: any[], taskKeyField: string, - taskKey: string | number | (string | number)[] + taskKey: string | number | (string | number)[], + childrenField: string = 'children' ): { record: any; index: number[] } | undefined { for (let i = 0; i < records.length; i++) { if ( @@ -605,10 +615,10 @@ export function findRecordByTaskKey( records[i][taskKeyField] === taskKey ) { return { record: records[i], index: [i] }; - } else if (records[i].children?.length) { + } else if (records[i][childrenField]?.length) { if (Array.isArray(taskKey) && taskKey[0] === records[i][taskKeyField]) { const result: { record: any; index: number[] } | undefined = findRecordByTaskKey( - records[i].children, + records[i][childrenField], taskKeyField, taskKey.slice(1) ); @@ -618,7 +628,7 @@ export function findRecordByTaskKey( } } else if (!Array.isArray(taskKey)) { const result: { record: any; index: number[] } | undefined = findRecordByTaskKey( - records[i].children, + records[i][childrenField], taskKeyField, taskKey ); @@ -630,3 +640,180 @@ export function findRecordByTaskKey( } } } + +export function clearRecordLinkInfos(records: any[], childrenField: string = 'children') { + for (let i = 0; i < records.length; i++) { + if (records[i][childrenField]?.length) { + clearRecordLinkInfos(records[i][childrenField], childrenField); + } else { + delete records[i].vtable_gantt_linkedTo; + delete records[i].vtable_gantt_linkedFrom; + } + } +} + +export function clearRecordShowIndex(records: any[], childrenField: string = 'children') { + for (let i = 0; i < records.length; i++) { + if (records[i][childrenField]?.length) { + clearRecordShowIndex(records[i][childrenField], childrenField); + } else { + delete records[i].vtable_gantt_showIndex; + } + } +} +export function getTaskIndexsByTaskY(y: number, gantt: Gantt) { + let task_index; + let sub_task_index; + if (gantt.taskListTableInstance) { + const rowInfo = gantt.taskListTableInstance.getTargetRowAt(y + gantt.headerHeight); + if (rowInfo) { + const { row } = rowInfo; + task_index = row - gantt.taskListTableInstance.columnHeaderLevelCount; + const beforeRowsHeight = gantt.getRowsHeightByIndex(0, task_index - 1); // 耦合了listTableOption的customComputeRowHeight + sub_task_index = Math.floor((y - beforeRowsHeight) / gantt.parsedOptions.rowHeight); + } + } else { + task_index = Math.floor(y / gantt.parsedOptions.rowHeight); + } + return { task_index, sub_task_index }; +} + +export function computeRowsCountByRecordDateForCompact(record: any, startDateField: string, endDateField: string) { + if (!record.children || record.children.length === 1) { + if (record.children?.length === 1) { + record.children[0].vtable_gantt_showIndex = 0; + } + return 1; + } + // 创建一个浅拷贝并排序子任务,根据开始日期排序 + const sortedChildren = record.children.slice().sort((a: any, b: any) => { + return createDateAtMidnight(a[startDateField]).getTime() - createDateAtMidnight(b[startDateField]).getTime(); + }); + const count = 0; + // 用于存储每一行的结束日期 + const rows = []; + for (let i = 0; i <= sortedChildren.length - 1; i++) { + const newRecord = sortedChildren[i]; + const startDate = createDateAtMidnight(newRecord[startDateField]).getTime(); + const endDate = createDateAtMidnight(newRecord[endDateField]).getTime(); + + let placed = false; + + // 尝试将当前任务放入已有的行中 + for (let j = 0; j < rows.length; j++) { + if (startDate > rows[j]) { + // 如果当前任务的开始日期在该行的结束日期之后,则可以放在这一行 + rows[j] = endDate; + placed = true; + newRecord.vtable_gantt_showIndex = j; + break; + } + } + + // 如果不能放在已有的行中,则需要新开一行 + if (!placed) { + rows.push(endDate); + newRecord.vtable_gantt_showIndex = rows.length - 1; + } + } + + return rows.length; +} +// 检查两个日期范围是否重叠 +function isOverlapping(task: any, rowTasks: any[], startDateField: string, endDateField: string) { + const start1 = createDateAtMidnight(task[startDateField]).getTime(); + const end1 = createDateAtMidnight(task[endDateField]).getTime(); + return rowTasks.some(rowTask => { + const start2 = createDateAtMidnight(rowTask[startDateField]).getTime(); + const end2 = createDateAtMidnight(rowTask[endDateField]).getTime(); + return start1 <= end2 && start2 <= end1; + }); +} +export function computeRowsCountByRecordDate(record: any, startDateField: string, endDateField: string) { + if (!record.children || record.children.length === 1) { + if (record.children?.length === 1) { + record.children[0].vtable_gantt_showIndex = 0; + } + return 1; + } + + const count = 0; + // 用于存储每一行的结束日期 + const rows = []; + for (let i = 0; i <= record.children.length - 1; i++) { + const newRecord = record.children[i]; + const startDate = createDateAtMidnight(newRecord[startDateField]).getTime(); + const endDate = createDateAtMidnight(newRecord[endDateField]).getTime(); + + let placed = false; + + // 尝试将当前任务放入已有的行中 + for (let j = 0; j < rows.length; j++) { + const rowTasks = record.children.filter((t: any) => t !== newRecord && t.vtable_gantt_showIndex === j); + if (!isOverlapping(newRecord, rowTasks, startDateField, endDateField)) { + // 如果当前任务的开始日期在该行的结束日期之后,则可以放在这一行 + rows[j] = endDate; + placed = true; + newRecord.vtable_gantt_showIndex = j; + break; + } + } + + // 如果不能放在已有的行中,则需要新开一行 + if (!placed) { + rows.push(endDate); + newRecord.vtable_gantt_showIndex = rows.length - 1; + } + } + + return rows.length; +} +export function getSubTaskRowIndexByRecordDate( + record: any, + childIndex: number, + startDateField: string, + endDateField: string +) { + if (childIndex === 0) { + return 0; + } + // 排序在datasource中已经排过了 + // 创建一个浅拷贝并排序子任务,根据开始日期排序 + // const sortedChildren = record.children.slice().sort((a: any, b: any) => { + // return createDateAtMidnight(a[startDateField]).getTime() - createDateAtMidnight(b[startDateField]).getTime(); + // }); + + // 用于存储每一行的结束日期 + const rows = []; + if (record?.children) { + for (let i = 0; i <= record.children.length - 1; i++) { + const newRecord = record.children[i]; + const startDate = createDateAtMidnight(newRecord[startDateField]).getTime(); + const endDate = createDateAtMidnight(newRecord[endDateField]).getTime(); + + let placed = false; + + // 尝试将当前任务放入已有的行中 + for (let j = 0; j < rows.length; j++) { + if (startDate > rows[j]) { + // 如果当前任务的开始日期在该行的结束日期之后,则可以放在这一行 + rows[j] = endDate; + placed = true; + if (i === childIndex) { + return j; + } + break; + } + } + // 如果不能放在已有的行中,则需要新开一行 + if (!placed) { + rows.push(endDate); + } + if (i === childIndex) { + return rows.length - 1; + } + } + } + + return 0; +} diff --git a/packages/vtable-gantt/src/scenegraph/dependency-link.ts b/packages/vtable-gantt/src/scenegraph/dependency-link.ts index d8822ca43..a47cd247f 100644 --- a/packages/vtable-gantt/src/scenegraph/dependency-link.ts +++ b/packages/vtable-gantt/src/scenegraph/dependency-link.ts @@ -4,10 +4,10 @@ import type { Scenegraph } from './scenegraph'; // import { Icon } from './icon'; import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/util'; import { isValid } from '@visactor/vutils'; -import { findRecordByTaskKey, getTextPos } from '../gantt-helper'; +import { clearRecordLinkInfos, findRecordByTaskKey, getSubTaskRowIndexByRecordDate, getTextPos } from '../gantt-helper'; import type { GanttTaskBarNode } from './gantt-node'; import type { ITaskLink } from '../ts-types'; -import { DependencyType } from '../ts-types'; +import { DependencyType, TasksShowMode } from '../ts-types'; export class DependencyLink { group: Group; @@ -36,6 +36,7 @@ export class DependencyLink { } initLinkLines() { + clearRecordLinkInfos(this._scene._gantt.records); this.linkLinesContainer = new Group({ x: 0, y: 0, @@ -66,19 +67,89 @@ export class DependencyLink { } linkedFromTaskRecord.record.vtable_gantt_linkedFrom.push(link); - const linkedFromTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); - const linkedToTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + let linkedToTaskStartDate; + let linkedToTaskEndDate; + let linkedToTaskTaskDays; + let linkedFromTaskStartDate; + let linkedFromTaskEndDate; + let linkedFromTaskTaskDays; - const { - startDate: linkedToTaskStartDate, - endDate: linkedToTaskEndDate, - taskDays: linkedToTaskTaskDays - } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex); - const { - startDate: linkedFromTaskStartDate, - endDate: linkedFromTaskEndDate, - taskDays: linkedFromTaskTaskDays - } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex); + let linkedToTaskShowIndex; + let linkedFromTaskShowIndex; + + if (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { + linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; + linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else if ( + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + const beforeRowCountLinkedFrom = + this._scene._gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / + this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = + beforeRowCountLinkedFrom + + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // getSubTaskRowIndexByRecordDate( + // this._scene._gantt.records[linkedFromTaskRecord.index[0]], + // linkedFromTaskRecord.index[1], + // this._scene._gantt.parsedOptions.startDateField, + // this._scene._gantt.parsedOptions.endDateField + // ) + linkedFromTaskRecord.record.vtable_gantt_showIndex + : linkedFromTaskRecord.index[1]); + const beforeRowCountLinkedTo = + this._scene._gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / + this._scene._gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = + beforeRowCountLinkedTo + + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? //getSubTaskRowIndexByRecordDate( + // this._scene._gantt.records[linkedToTaskRecord.index[0]], + // linkedToTaskRecord.index[1], + // this._scene._gantt.parsedOptions.startDateField, + // this._scene._gantt.parsedOptions.endDateField + // ) + linkedToTaskRecord.record.vtable_gantt_showIndex + : linkedToTaskRecord.index[1]); + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + } else { + linkedFromTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkedToTaskShowIndex = this._scene._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex)); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = this._scene._gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex)); + } if (!linkedFromTaskTaskDays || !linkedToTaskTaskDays) { return; } @@ -353,11 +424,32 @@ export function generateLinkLinePoints( y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX - distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkToPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) }, { @@ -397,11 +489,29 @@ export function generateLinkLinePoints( y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX + distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + }, + { + x: linkToPointX + distanceToTaskBar, y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) }, { @@ -432,14 +542,34 @@ export function generateLinkLinePoints( return { linePoints, arrowPoints }; } +/** + * + * @param type 依赖关联类型 + * @param linkedFromTaskStartDate 关联源任务的开始时间 + * @param linkedFromTaskEndDate 关联源任务的结束时间 + * @param linkedFromTaskRecordRowIndex 关联源任务所在的行索引 + * @param fromNodeDiffY 关联源任务的偏移量,如果在拖拽过程中,会有偏移量 + * @param linkedToTaskStartDate 关联目标任务的开始时间 + * @param linkedToTaskEndDate 关联目标任务的结束时间 + * @param linkedToTaskRecordRowIndex 关联目标任务所在的行索引 + * @param toNodeDiffY 关联目标任务的偏移量,如果在拖拽过程中,会有偏移量 + * @param minDate 甘特图设置的最开始时间 + * @param rowHeight 单个任务条占用的行高 + * @param colWidthPerDay 单个日期占用的列宽 + * @param linkedFromMovedTaskBarNode 关联源任务的任务场景树节点 + * @param linkedToMovedTaskBarNode 关联目标任务的任务场景树节点 + * @returns + */ export function updateLinkLinePoints( type: DependencyType, linkedFromTaskStartDate: Date, linkedFromTaskEndDate: Date, linkedFromTaskRecordRowIndex: number, + fromNodeDiffY: number, linkedToTaskStartDate: Date, linkedToTaskEndDate: Date, linkedToTaskRecordRowIndex: number, + toNodeDiffY: number, minDate: Date, rowHeight: number, colWidthPerDay: number, @@ -466,37 +596,39 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX + distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX + distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX - distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX - distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; if (linkFromPointX + distanceToTaskBar <= linkToPointX - distanceToTaskBar) { linePoints.splice(2, 3, { x: linkFromPointX + distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }); } const lastPoint = linePoints[linePoints.length - 1]; @@ -531,37 +663,39 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX - distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { x: linkFromPointX - distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX + distanceToTaskBar, y: rowHeight * - (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + (linkedFromTaskRecordRowIndex + (linkedFromTaskRecordRowIndex > linkedToTaskRecordRowIndex ? 0 : 1)) + + fromNodeDiffY }, { x: linkToPointX + distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; if (linkFromPointX - distanceToTaskBar >= linkToPointX + distanceToTaskBar) { linePoints.splice(2, 3, { x: linkFromPointX - distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }); } const lastPoint = linePoints[linePoints.length - 1]; @@ -595,19 +729,42 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { - x: Math.min(linkFromPointX, linkToPointX) - distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY + }, + { + x: linkToPointX - distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY + }, + { + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkToPointX + : Math.min(linkFromPointX, linkToPointX)) - distanceToTaskBar, + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; const lastPoint = linePoints[linePoints.length - 1]; @@ -641,19 +798,39 @@ export function updateLinkLinePoints( linePoints = [ { x: linkFromPointX, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, - y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, + y: rowHeight * (linkedFromTaskRecordRowIndex + 0.5) + fromNodeDiffY }, { - x: Math.max(linkFromPointX, linkToPointX) + distanceToTaskBar, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + x: + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex + ? linkFromPointX + : Math.max(linkFromPointX, linkToPointX)) + distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY + }, + { + x: linkToPointX + distanceToTaskBar, + y: + rowHeight * + (linkedToTaskRecordRowIndex + (linkedFromTaskRecordRowIndex === linkedToTaskRecordRowIndex ? 1 : 0.5)) + + toNodeDiffY + }, + { + x: linkToPointX + distanceToTaskBar, + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY }, { x: linkToPointX, - y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + y: rowHeight * (linkedToTaskRecordRowIndex + 0.5) + toNodeDiffY } ]; const lastPoint = linePoints[linePoints.length - 1]; diff --git a/packages/vtable-gantt/src/scenegraph/gantt-node.ts b/packages/vtable-gantt/src/scenegraph/gantt-node.ts index 45575b4e9..166da1c3f 100644 --- a/packages/vtable-gantt/src/scenegraph/gantt-node.ts +++ b/packages/vtable-gantt/src/scenegraph/gantt-node.ts @@ -2,9 +2,14 @@ import type { IRect, IText, IGroupGraphicAttribute } from '@visactor/vtable/es/v import { Group } from '@visactor/vtable/es/vrender'; export class GanttTaskBarNode extends Group { + clipGroupBox: Group; barRect?: IRect; progressRect?: IRect; textLabel?: IText; + name: string; + task_index: number; + sub_task_index?: number; + record?: any; constructor(attrs: IGroupGraphicAttribute) { super(attrs); } diff --git a/packages/vtable-gantt/src/scenegraph/grid.ts b/packages/vtable-gantt/src/scenegraph/grid.ts index 1d1635599..0909ebca3 100644 --- a/packages/vtable-gantt/src/scenegraph/grid.ts +++ b/packages/vtable-gantt/src/scenegraph/grid.ts @@ -13,8 +13,6 @@ export class Grid { y: number; width: number; height: number; - timelineDates: any; - colWidthPerDay: number; rowHeight: number; rowCount: number; group: Group; @@ -34,8 +32,6 @@ export class Grid { this.y = scene._gantt.getAllHeaderRowsHeight(); this.width = scene.tableGroup.attribute.width; this.height = scene.tableGroup.attribute.height - scene.timelineHeader.group.attribute.height; - this.timelineDates = scene._gantt.parsedOptions.reverseSortedTimelineScales[0].timelineDates; - this.colWidthPerDay = scene._gantt.parsedOptions.colWidthPerDay; this.rowHeight = scene._gantt.parsedOptions.rowHeight; this.rowCount = scene._gantt.itemCount; this.allGridWidth = scene._gantt._getAllColsWidth(); @@ -93,9 +89,11 @@ export class Grid { if (this.gridStyle?.verticalLine.lineWidth & 1) { x = 0.5; } - for (let i = 0; i < this.timelineDates?.length - 1; i++) { - const dateline = this.timelineDates[i]; - x = x + Math.floor(this.colWidthPerDay * dateline.days); + const timelineDates = this._scene._gantt.parsedOptions.reverseSortedTimelineScales[0].timelineDates; + const colWidthPerDay = this._scene._gantt.parsedOptions.colWidthPerDay; + for (let i = 0; i < timelineDates?.length - 1; i++) { + const dateline = timelineDates[i]; + x = x + Math.floor(colWidthPerDay * dateline.days); const line = createLine({ pickable: false, stroke: this.gridStyle?.verticalLine.lineColor, @@ -127,7 +125,7 @@ export class Grid { y += 0.5; } for (let i = 0; i < this.rowCount - 1; i++) { - y = y + Math.floor(this.rowHeight); + y = y + this._scene._gantt.getRowHeightByIndex(i); // Math.floor(this.rowHeight); const line = createLine({ pickable: false, stroke: this.gridStyle?.horizontalLine.lineColor, diff --git a/packages/vtable-gantt/src/scenegraph/scenegraph.ts b/packages/vtable-gantt/src/scenegraph/scenegraph.ts index 162973caa..802514253 100644 --- a/packages/vtable-gantt/src/scenegraph/scenegraph.ts +++ b/packages/vtable-gantt/src/scenegraph/scenegraph.ts @@ -9,11 +9,13 @@ import { TimelineHeader } from './timeline-header'; import { TaskBar } from './task-bar'; import { MarkLine } from './mark-line'; import { FrameBorder } from './frame-border'; -import { getTaskIndexByY } from '../gantt-helper'; +import { findRecordByTaskKey, getTaskIndexByY, getTaskIndexsByTaskY } from '../gantt-helper'; import graphicContribution from './graphic'; import { TaskCreationButton } from './task-creation-button'; -import { DependencyLink } from './dependency-link'; +import { DependencyLink, updateLinkLinePoints } from './dependency-link'; import { DragOrderLine } from './drag-order-line'; +import type { GanttTaskBarNode } from './gantt-node'; +import { TasksShowMode } from '../ts-types'; container.load(graphicContribution); export class Scenegraph { dateStepWidth: number; @@ -131,6 +133,7 @@ export class Scenegraph { this.timelineHeader.refresh(); this.grid.refresh(); this.taskBar.refresh(); + this.dependencyLink.refresh(); this.markLine.refresh(); this.dependencyLink.refresh(); this.frameBorder.resize(); @@ -149,6 +152,7 @@ export class Scenegraph { this.updateNextFrame(); } refreshTaskBarsAndGrid() { + this._gantt.verticalSplitResizeLine.style.height = this._gantt.drawHeight + 'px'; //'100%'; this.tableGroupHeight = Math.min(this._gantt.tableNoFrameHeight, this._gantt.drawHeight); this.tableGroup.setAttribute('height', this.tableGroupHeight); // this.timelineHeader.refresh(); @@ -256,9 +260,6 @@ export class Scenegraph { release() { this.stage.release(); } - getTaskBarNodeByY(y: number) { - const taskIndex = getTaskIndexByY(y, this._gantt); - } showTaskCreationButton(x: number, y: number, taskIndex: number, record: any) { if (!this.taskCreationButton) { @@ -274,4 +275,279 @@ export class Scenegraph { this.updateNextFrame(); } } + refreshRecordLinkNodes(taskIndex: number, sub_task_index: number, target: GanttTaskBarNode, dy: number = 0) { + const gantt: Gantt = this._gantt; + const record = gantt.getRecordByIndex(taskIndex, sub_task_index); + const vtable_gantt_linkedTo = record.vtable_gantt_linkedTo; + const vtable_gantt_linkedFrom = record.vtable_gantt_linkedFrom; + for (let i = 0; i < vtable_gantt_linkedTo?.length; i++) { + const link = vtable_gantt_linkedTo[i]; + const linkLineNode = link.vtable_gantt_linkLineNode; + const lineArrowNode = link.vtable_gantt_linkArrowNode; + + const { linkedToTaskKey, linkedFromTaskKey, type } = link; + const { taskKeyField, minDate } = gantt.parsedOptions; + const linkedFromTaskRecord = findRecordByTaskKey(gantt.records, taskKeyField, linkedFromTaskKey); + const linkedToTaskRecord = findRecordByTaskKey(gantt.records, taskKeyField, linkedToTaskKey); + // const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = + // gantt.getTaskInfoByTaskListIndex(taskIndex); + // const taskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + // const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = + // gantt.getTaskInfoByTaskListIndex(taskShowIndex); + + let linkedToTaskStartDate; + let linkedToTaskEndDate; + let linkedToTaskTaskDays; + // let linkedToTaskTaskRecord; + let linkedFromTaskStartDate; + let linkedFromTaskEndDate; + let linkedFromTaskTaskDays; + // let linkedFromTaskTaskRecord; + + let linkedToTaskShowIndex; + let linkedFromTaskShowIndex; + let diffY: number; + if (gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, gantt); + linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; + // linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + const beforeRowCountLinkedTo = + gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = beforeRowCountLinkedTo; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + + const taskbarHeight = gantt.parsedOptions.taskBarStyle.width; + diffY = target.attribute.y + taskbarHeight / 2 - (linkedToTaskShowIndex + 0.5) * gantt.parsedOptions.rowHeight; + } else if ( + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, gantt); + const beforeRowCountLinkedFrom = + gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = + beforeRowCountLinkedFrom + + (gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // getSubTaskRowIndexByRecordDate( + // gantt.records[linkedFromTaskRecord.index[0]], + // linkedFromTaskRecord.index[1], + // gantt.parsedOptions.startDateField, + // gantt.parsedOptions.endDateField + // ) + linkedFromTaskRecord.record.vtable_gantt_showIndex + : linkedFromTaskRecord.index[1]); + // const beforeRowCountLinkedTo = + // gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + // linkedToTaskShowIndex = beforeRowCountLinkedTo + linkedToTaskRecord.index[1]; + const beforeRowCountLinkedTo = + gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = + beforeRowCountLinkedTo + + (gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // ? getSubTaskRowIndexByRecordDate( + // gantt.records[linkedToTaskRecord.index[0]], + // linkedToTaskRecord.index[1], + // gantt.parsedOptions.startDateField, + // gantt.parsedOptions.endDateField + // ) + linkedToTaskRecord.record.vtable_gantt_showIndex + : new_indexs.sub_task_index); + + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + + const taskbarHeight = gantt.parsedOptions.taskBarStyle.width; + diffY = target.attribute.y + taskbarHeight / 2 - (linkedToTaskShowIndex + 0.5) * gantt.parsedOptions.rowHeight; + } else { + linkedFromTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkedToTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + if (linkedFromTaskShowIndex === -1 || linkedToTaskShowIndex === -1) { + continue; + } + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex)); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex)); + } + const { linePoints, arrowPoints } = updateLinkLinePoints( + type, + linkedFromTaskStartDate, + linkedFromTaskEndDate, + linkedFromTaskShowIndex, + 0, + linkedToTaskStartDate, + linkedToTaskEndDate, + linkedToTaskShowIndex, + diffY ?? 0, + minDate, + gantt.parsedOptions.rowHeight, + gantt.parsedOptions.colWidthPerDay, + null, + target + ); + linkLineNode.setAttribute('points', linePoints); + lineArrowNode.setAttribute('points', arrowPoints); + } + + for (let i = 0; i < vtable_gantt_linkedFrom?.length; i++) { + const link = vtable_gantt_linkedFrom[i]; + const linkLineNode = link.vtable_gantt_linkLineNode; + const lineArrowNode = link.vtable_gantt_linkArrowNode; + + const { linkedToTaskKey, linkedFromTaskKey, type } = link; + const { taskKeyField, minDate } = gantt.parsedOptions; + const linkedToTaskRecord = findRecordByTaskKey(gantt.records, taskKeyField, linkedToTaskKey); + const linkedFromTaskRecord = findRecordByTaskKey(gantt.records, taskKeyField, linkedFromTaskKey); + // const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = + // gantt.getTaskInfoByTaskListIndex(taskIndex); + // const taskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + // const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = + // gantt.getTaskInfoByTaskListIndex(taskShowIndex); + + let linkedToTaskStartDate; + let linkedToTaskEndDate; + let linkedToTaskTaskDays; + let linkedFromTaskStartDate; + let linkedFromTaskEndDate; + let linkedFromTaskTaskDays; + + let linkedToTaskShowIndex; + let linkedFromTaskShowIndex; + let diffY: number; + if (gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline) { + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, gantt); + const beforeRowCountLinkedFrom = + gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = beforeRowCountLinkedFrom; + + // linkedFromTaskShowIndex = linkedFromTaskRecord.index[0]; + linkedToTaskShowIndex = linkedToTaskRecord.index[0]; + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + const taskbarHeight = gantt.parsedOptions.taskBarStyle.width; + diffY = + target.attribute.y + taskbarHeight / 2 - (linkedFromTaskShowIndex + 0.5) * gantt.parsedOptions.rowHeight; + } else if ( + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + const new_indexs = getTaskIndexsByTaskY(target.attribute.y + dy, gantt); + // const beforeRowCountLinkedFrom = + // gantt.getRowsHeightByIndex(0, linkedFromTaskRecord.index[0] - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + // linkedFromTaskShowIndex = beforeRowCountLinkedFrom + linkedFromTaskRecord.index[1]; + const beforeRowCountLinkedFrom = + gantt.getRowsHeightByIndex(0, new_indexs.task_index - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedFromTaskShowIndex = + beforeRowCountLinkedFrom + + (gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // ? getSubTaskRowIndexByRecordDate( + // gantt.records[linkedFromTaskRecord.index[0]], + // linkedFromTaskRecord.index[1], + // gantt.parsedOptions.startDateField, + // gantt.parsedOptions.endDateField + // ) + linkedFromTaskRecord.record.vtable_gantt_showIndex + : new_indexs.sub_task_index); + + const beforeRowCountLinkedTo = + gantt.getRowsHeightByIndex(0, linkedToTaskRecord.index[0] - 1) / gantt.parsedOptions.rowHeight; // 耦合了listTableOption的customComputeRowHeight + linkedToTaskShowIndex = + beforeRowCountLinkedTo + + (gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? // ? getSubTaskRowIndexByRecordDate( + // gantt.records[linkedToTaskRecord.index[0]], + // linkedToTaskRecord.index[1], + // gantt.parsedOptions.startDateField, + // gantt.parsedOptions.endDateField + // ) + linkedToTaskRecord.record.vtable_gantt_showIndex + : linkedToTaskRecord.index[1]); + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedToTaskRecord.index[0], linkedToTaskRecord.index[1])); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedFromTaskRecord.index[0], linkedFromTaskRecord.index[1])); + const taskbarHeight = gantt.parsedOptions.taskBarStyle.width; + diffY = + target.attribute.y + taskbarHeight / 2 - (linkedFromTaskShowIndex + 0.5) * gantt.parsedOptions.rowHeight; + } else { + linkedFromTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkedToTaskShowIndex = gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + if (linkedFromTaskShowIndex === -1 || linkedToTaskShowIndex === -1) { + continue; + } + ({ + startDate: linkedToTaskStartDate, + endDate: linkedToTaskEndDate, + taskDays: linkedToTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedToTaskShowIndex)); + ({ + startDate: linkedFromTaskStartDate, + endDate: linkedFromTaskEndDate, + taskDays: linkedFromTaskTaskDays + } = gantt.getTaskInfoByTaskListIndex(linkedFromTaskShowIndex)); + } + const { linePoints, arrowPoints } = updateLinkLinePoints( + type, + linkedFromTaskStartDate, + linkedFromTaskEndDate, + linkedFromTaskShowIndex, + diffY ?? 0, + linkedToTaskStartDate, + linkedToTaskEndDate, + linkedToTaskShowIndex, + 0, + minDate, + gantt.parsedOptions.rowHeight, + gantt.parsedOptions.colWidthPerDay, + target, + null + ); + + linkLineNode.setAttribute('points', linePoints); + lineArrowNode.setAttribute('points', arrowPoints); + } + } } diff --git a/packages/vtable-gantt/src/scenegraph/task-bar.ts b/packages/vtable-gantt/src/scenegraph/task-bar.ts index b73cd44cd..ed4b2c82a 100644 --- a/packages/vtable-gantt/src/scenegraph/task-bar.ts +++ b/packages/vtable-gantt/src/scenegraph/task-bar.ts @@ -3,8 +3,14 @@ import type { Scenegraph } from './scenegraph'; // import { Icon } from './icon'; import { createDateAtMidnight, parseStringTemplate, toBoxArray } from '../tools/util'; import { isValid } from '@visactor/vutils'; -import { getTextPos } from '../gantt-helper'; +import { + computeRowsCountByRecordDate, + computeRowsCountByRecordDateForCompact, + getSubTaskRowIndexByRecordDate, + getTextPos +} from '../gantt-helper'; import { GanttTaskBarNode } from './gantt-node'; +import { TasksShowMode } from '../ts-types'; const TASKBAR_HOVER_ICON = ` @@ -54,38 +60,99 @@ export class TaskBar { this.group.appendChild(this.barContainer); for (let i = 0; i < this._scene._gantt.itemCount; i++) { - const barGroup = this.initBar(i); - if (barGroup) { - this.barContainer.appendChild(barGroup); + if ( + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + const record = this._scene._gantt.getRecordByIndex(i); + if (record.children?.length > 0) { + for (let j = 0; j < record.children.length; j++) { + const barGroup = this.initBar(i, j, record.children.length); + if (barGroup) { + this.barContainer.appendChild(barGroup); + } + } + } + continue; + } else { + const barGroup = this.initBar(i); + if (barGroup) { + this.barContainer.appendChild(barGroup); + } } } } - initBar(index: number) { + initBar(index: number, childIndex?: number, childrenLength?: number) { const taskBarCustomLayout = this._scene._gantt.parsedOptions.taskBarCustomLayout; - const { startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex(index); - if (taskDays <= 0 || !startDate || !endDate) { + + const { startDate, endDate, taskDays, progress, taskRecord } = this._scene._gantt.getTaskInfoByTaskListIndex( + index, + childIndex + ); + + if (taskDays <= 0 || !startDate || !endDate || startDate.getTime() > endDate.getTime()) { return null; } const taskBarSize = this._scene._gantt.parsedOptions.colWidthPerDay * taskDays; const taskbarHeight = this._scene._gantt.parsedOptions.taskBarStyle.width; const minDate = createDateAtMidnight(this._scene._gantt.parsedOptions.minDate); - const barGroup = new GanttTaskBarNode({ + + const subTaskShowRowCount = + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + ? childrenLength + : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange + ? computeRowsCountByRecordDate( + this._scene._gantt.records[index], + this._scene._gantt.parsedOptions.startDateField, + this._scene._gantt.parsedOptions.endDateField + ) + : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? computeRowsCountByRecordDateForCompact( + this._scene._gantt.records[index], + this._scene._gantt.parsedOptions.startDateField, + this._scene._gantt.parsedOptions.endDateField + ) + : 1; + const oneTaskHeigth = this._scene._gantt.getRowHeightByIndex(index) / subTaskShowRowCount; + const barGroupBox = new GanttTaskBarNode({ x: this._scene._gantt.parsedOptions.colWidthPerDay * Math.ceil(Math.abs(startDate.getTime() - minDate.getTime()) / (1000 * 60 * 60 * 24)), // y: this._scene._gantt.parsedOptions.rowHeight * i, y: - this._scene._gantt.parsedOptions.rowHeight * index + - (this._scene._gantt.parsedOptions.rowHeight - taskbarHeight) / 2, + this._scene._gantt.getRowsHeightByIndex(0, index - 1) + + (this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + ? childIndex * oneTaskHeigth + : this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._scene._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ? taskRecord.vtable_gantt_showIndex * oneTaskHeigth + : 0) + + (oneTaskHeigth - taskbarHeight) / 2, width: taskBarSize, // height: this._scene._gantt.parsedOptions.rowHeight, height: taskbarHeight, cornerRadius: this._scene._gantt.parsedOptions.taskBarStyle.cornerRadius, + lineWidth: this._scene._gantt.parsedOptions.taskBarStyle.borderWidth * 2, + stroke: this._scene._gantt.parsedOptions.taskBarStyle.borderColor + // clip: true + }); + barGroupBox.name = 'task-bar'; + barGroupBox.task_index = index; + barGroupBox.sub_task_index = childIndex; + barGroupBox.record = taskRecord; + + const barGroup = new Group({ + x: 0, + y: 0, + width: taskBarSize, + height: taskbarHeight, + cornerRadius: this._scene._gantt.parsedOptions.taskBarStyle.cornerRadius, clip: true }); - barGroup.name = 'task-bar'; - barGroup.id = index; - // this.barContainer.appendChild(barGroup); + barGroupBox.appendChild(barGroup); + barGroupBox.clipGroupBox = barGroup; let rootContainer; let renderDefaultBar = true; let renderDefaultText = true; @@ -131,7 +198,7 @@ export class TaskBar { }); rect.name = 'task-bar-rect'; barGroup.appendChild(rect); - barGroup.barRect = rect; + barGroupBox.barRect = rect; // 创建已完成部分任务条rect const progress_rect = createRect({ x: 0, @@ -143,7 +210,7 @@ export class TaskBar { }); progress_rect.name = 'task-bar-progress-rect'; barGroup.appendChild(progress_rect); - barGroup.progressRect = progress_rect; + barGroupBox.progressRect = progress_rect; } rootContainer && barGroup.appendChild(rootContainer); @@ -180,9 +247,9 @@ export class TaskBar { // dy: this._scene._gantt.barLabelStyle.fontSize / 2 }); barGroup.appendChild(label); - barGroup.textLabel = label; + barGroupBox.textLabel = label; } - return barGroup; + return barGroupBox; } updateTaskBarNode(index: number) { const taskbarGroup = this.getTaskBarNodeByIndex(index); @@ -317,7 +384,8 @@ export class TaskBar { shadowOffsetX: this._scene._gantt.parsedOptions.taskBarSelectedStyle.shadowOffsetX, shadowOffsetY: this._scene._gantt.parsedOptions.taskBarSelectedStyle.shadowOffsetY, shadowBlur: this._scene._gantt.parsedOptions.taskBarSelectedStyle.shadowBlur, - attachedToTaskBarNode: attachedToTaskBarNode + attachedToTaskBarNode: attachedToTaskBarNode, + zIndex: 10000 }); selectedBorder.name = 'task-bar-select-border'; this.barContainer.appendChild(selectedBorder); @@ -397,13 +465,16 @@ export class TaskBar { this.selectedBorders[0].appendChild(line); } - getTaskBarNodeByIndex(index: number) { + getTaskBarNodeByIndex(index: number, sub_task_index?: number) { let c = this.barContainer.firstChild as Group; if (!c) { return null; } for (let i = 0; i < this.barContainer.childrenCount; i++) { - if (c.id === index) { + if ( + c.task_index === index && + (!isValid(sub_task_index) || (isValid(sub_task_index) && c.sub_task_index === sub_task_index)) + ) { return c; } c = c._next as Group; diff --git a/packages/vtable-gantt/src/state/gantt-table-sync.ts b/packages/vtable-gantt/src/state/gantt-table-sync.ts index ed600107f..77da9c7fd 100644 --- a/packages/vtable-gantt/src/state/gantt-table-sync.ts +++ b/packages/vtable-gantt/src/state/gantt-table-sync.ts @@ -1,4 +1,5 @@ import type { Gantt } from '../Gantt'; +import { TasksShowMode } from '../ts-types'; export function syncScrollStateToTable(gantt: Gantt) { const { scroll } = gantt.stateManager; @@ -29,7 +30,7 @@ export function syncEditCellFromTable(gantt: Gantt) { export function syncTreeChangeFromTable(gantt: Gantt) { gantt.taskListTableInstance?.on('tree_hierarchy_state_change', (args: any) => { gantt._syncPropsFromTable(); - gantt.verticalSplitResizeLine.style.height = gantt.drawHeight + 'px'; //'100%'; + gantt.scenegraph.refreshTaskBarsAndGrid(); const left = gantt.stateManager.scroll.horizontalBarPos; const top = gantt.stateManager.scroll.verticalBarPos; @@ -48,7 +49,15 @@ export function syncSortFromTable(gantt: Gantt) { } export function syncDragOrderFromTable(gantt: Gantt) { gantt.taskListTableInstance?.on('change_header_position', (args: any) => { - gantt.scenegraph.refreshTaskBars(); + if ( + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact || + gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate + ) { + gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + gantt.scenegraph.refreshTaskBars(); + } gantt.scenegraph.dragOrderLine.hideDragLine(); const left = gantt.stateManager.scroll.horizontalBarPos; const top = gantt.stateManager.scroll.verticalBarPos; diff --git a/packages/vtable-gantt/src/state/state-manager.ts b/packages/vtable-gantt/src/state/state-manager.ts index f3722ca6e..27a6104fb 100644 --- a/packages/vtable-gantt/src/state/state-manager.ts +++ b/packages/vtable-gantt/src/state/state-manager.ts @@ -1,7 +1,7 @@ import { clone, cloneDeep, isValid } from '@visactor/vutils'; import type { Gantt } from '../Gantt'; import type { ITaskLink } from '../ts-types'; -import { InteractionState, GANTT_EVENT_TYPE, DependencyType } from '../ts-types'; +import { InteractionState, GANTT_EVENT_TYPE, DependencyType, TasksShowMode } from '../ts-types'; import type { Group, FederatedPointerEvent, Polygon, Line, Circle } from '@visactor/vtable/es/vrender'; import { syncEditCellFromTable, @@ -11,12 +11,17 @@ import { syncTreeChangeFromTable, syncSortFromTable } from './gantt-table-sync'; -import { findRecordByTaskKey, getTaskIndexByY } from '../gantt-helper'; +import { + clearRecordShowIndex, + findRecordByTaskKey, + getSubTaskRowIndexByRecordDate, + getTaskIndexsByTaskY +} from '../gantt-helper'; import { debounce } from '../tools/debounce'; import type { GanttTaskBarNode } from '../scenegraph/gantt-node'; import { TASKBAR_HOVER_ICON_WIDTH } from '../scenegraph/task-bar'; import { Inertia } from '../tools/inertia'; -import { generateLinkLinePoints, updateLinkLinePoints } from '../scenegraph/dependency-link'; +import { updateLinkLinePoints } from '../scenegraph/dependency-link'; export class StateManager { _gantt: Gantt; @@ -40,7 +45,9 @@ export class StateManager { startX: number; startY: number; deltaX: number; + deltaY: number; targetStartX: number; + targetStartY: number; startOffsetY: number; moving: boolean; target: GanttTaskBarNode; @@ -102,7 +109,9 @@ export class StateManager { }; this.moveTaskBar = { targetStartX: null, + targetStartY: null, deltaX: 0, + deltaY: 0, startOffsetY: null, startX: null, startY: null, @@ -311,67 +320,153 @@ export class StateManager { this.moveTaskBar.moving = true; this.moveTaskBar.target = target; this.moveTaskBar.targetStartX = target.attribute.x; + this.moveTaskBar.targetStartY = target.attribute.y; this.moveTaskBar.startX = x; this.moveTaskBar.startY = y; this.moveTaskBar.startOffsetY = offsetY; + target.setAttribute('zIndex', 10000); } isMoveingTaskBar() { return this.moveTaskBar.moving; } - endMoveTaskBar(x: number) { + endMoveTaskBar() { if (this.moveTaskBar.moveTaskBarXInertia.isInertiaScrolling()) { this.moveTaskBar.moveTaskBarXInertia.endInertia(); } - const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); - // const deltaX = x - this.moveTaskBar.startX; - const deltaX = this.moveTaskBar.deltaX; - const days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); - - const correctX = days * this._gantt.parsedOptions.colWidthPerDay; - const targetEndX = this.moveTaskBar.targetStartX + correctX; - const target = this._gantt.stateManager.moveTaskBar.target; - // target.setAttribute('x', targetEndX); - resizeOrMoveTaskBar(taskIndex, target, targetEndX - target.attribute.x, null, this); - // if (target.attribute.x < this._gantt.stateManager.scrollLeft - 2) { - // this._gantt.stateManager.setScrollLeft(target.attribute.x); - // } - // if(this._gantt.stateManager.scrollLeft===0){ - // } - if (Math.abs(days) >= 1) { - const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); - const oldRecord = this._gantt.getRecordByIndex(taskIndex); - const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; - const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; - this._gantt._updateDateToTaskRecord('move', days, taskIndex); - if (this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { - const newRecord = this._gantt.getRecordByIndex(taskIndex); - this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { - startDate: newRecord[this._gantt.parsedOptions.startDateField], - endDate: newRecord[this._gantt.parsedOptions.endDateField], - oldStartDate, - oldEndDate, - index: taskIndex, - record: newRecord - }); + const deltaX = this.moveTaskBar.deltaX; + const deltaY = this.moveTaskBar.deltaY; + const target = this.moveTaskBar.target; + if (Math.abs(deltaX) >= 1 || Math.abs(deltaY) >= 1) { + const days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); + + const correctX = days * this._gantt.parsedOptions.colWidthPerDay; + const targetEndX = this.moveTaskBar.targetStartX + correctX; + const targetEndY = + this.moveTaskBar.targetStartY + + this._gantt.parsedOptions.rowHeight * Math.round(deltaY / this._gantt.parsedOptions.rowHeight); + // 判断横向拖动 更新数据的date + if (Math.abs(days) >= 1) { + const taskIndex = target.task_index; + const sub_task_index = target.sub_task_index; + const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); + const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; + const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; + this._gantt._updateDateToTaskRecord('move', days, taskIndex, sub_task_index); + const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); + if (this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { + this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { + startDate: newRecord[this._gantt.parsedOptions.startDateField], + endDate: newRecord[this._gantt.parsedOptions.endDateField], + oldStartDate, + oldEndDate, + index: taskIndex, + record: newRecord + }); + } + } + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); + this._gantt._dragOrderTaskRecord( + target.task_index, + target.sub_task_index, + indexs.task_index, + indexs.sub_task_index + ); + clearRecordShowIndex(this._gantt.records); + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt._syncPropsFromTable(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + // 判断纵向拖动 处理数据的位置 + if ( + this._gantt.parsedOptions.tasksShowMode !== TasksShowMode.Tasks_Separate && + Math.abs(Math.round(deltaY / this._gantt.parsedOptions.rowHeight)) >= 1 + ) { + const indexs = getTaskIndexsByTaskY(targetEndY, this._gantt); + this._gantt._dragOrderTaskRecord( + target.task_index, + target.sub_task_index, + indexs.task_index, + indexs.sub_task_index + ); + if (this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate) { + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + this._gantt.scenegraph.taskBar.refresh(); + this._gantt.scenegraph.dependencyLink.refresh(); + } + // target = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(indexs.task_index, indexs.sub_task_index); + } else { + resizeOrMoveTaskBar( + target, + targetEndX - (target as Group).attribute.x, + targetEndY - (target as Group).attribute.y, + null, + this + ); + + // 为了确保拖拽后 保持startDate日期晚的显示在上层不被盖住 这里需要重新排序一下 + if (days > 0) { + let insertAfterNode = target; + while ( + (insertAfterNode as Group).nextSibling && + (insertAfterNode as Group).nextSibling.attribute.y === (target as Group).attribute.y && + (insertAfterNode as Group).nextSibling.record[this._gantt.parsedOptions.startDateField] <= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertAfterNode = (insertAfterNode as Group).nextSibling; + } + if (insertAfterNode !== target) { + (insertAfterNode as Group).parent.insertAfter(target, insertAfterNode); + } + } else if (days < 0) { + let insertBeforeNode = target; + while ( + (insertBeforeNode as Group).previousSibling && + (insertBeforeNode as Group).previousSibling.attribute.y === (target as Group).attribute.y && + (insertBeforeNode as Group).previousSibling.record[this._gantt.parsedOptions.startDateField] >= + target.record[this._gantt.parsedOptions.startDateField] + ) { + insertBeforeNode = (insertBeforeNode as Group).previousSibling; + } + if (insertBeforeNode !== target) { + (insertBeforeNode as Group).parent.insertBefore(target, insertBeforeNode); + } + } + } } + this._gantt.scenegraph.updateNextFrame(); } - this._gantt.scenegraph.updateNextFrame(); this.moveTaskBar.moving = false; + if (this.selectedTaskBar.target !== target) { + target.setAttribute('zIndex', 0); + } this.moveTaskBar.target = null; this.moveTaskBar.deltaX = 0; + this.moveTaskBar.deltaY = 0; this.moveTaskBar.moveTaskBarXSpeed = 0; } dealTaskBarMove(e: FederatedPointerEvent) { const target = this.moveTaskBar.target; - const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); + target.setAttribute('zIndex', 10000); + // const taskIndex = getTaskIndexByY(this.moveTaskBar.startOffsetY, this._gantt); const x1 = this._gantt.eventManager.lastDragPointerXYOnWindow.x; const x2 = e.x; const dx = x2 - x1; + const y1 = this._gantt.eventManager.lastDragPointerXYOnWindow.y; + const y2 = e.y; + const dy = y2 - y1; + this.moveTaskBar.deltaX += dx; + this.moveTaskBar.deltaY += dy; // target.setAttribute('x', target.attribute.x + dx); - resizeOrMoveTaskBar(taskIndex, target, dx, null, this); + resizeOrMoveTaskBar(target, dx, dy, null, this); // 处理向左拖拽任务条时,整体向左滚动 if (target.attribute.x <= this._gantt.stateManager.scrollLeft && dx < 0) { @@ -380,7 +475,8 @@ export class StateManager { this.moveTaskBar.moveTaskBarXInertia.startInertia(this.moveTaskBar.moveTaskBarXSpeed, 0, 1); this.moveTaskBar.moveTaskBarXInertia.setScrollHandle((dx: number, dy: number) => { this.moveTaskBar.deltaX += dx; - resizeOrMoveTaskBar(taskIndex, target, dx, null, this); + this.moveTaskBar.deltaY += dy; + resizeOrMoveTaskBar(target, dx, dy, null, this); this._gantt.stateManager.setScrollLeft(target.attribute.x); if (this._gantt.stateManager.scrollLeft === 0) { @@ -398,7 +494,8 @@ export class StateManager { this.moveTaskBar.moveTaskBarXInertia.startInertia(this.moveTaskBar.moveTaskBarXSpeed, 0, 1); this.moveTaskBar.moveTaskBarXInertia.setScrollHandle((dx: number, dy: number) => { this.moveTaskBar.deltaX += dx; - resizeOrMoveTaskBar(taskIndex, target, dx, null, this); + this.moveTaskBar.deltaY += dy; + resizeOrMoveTaskBar(target, dx, dy, null, this); this._gantt.stateManager.setScrollLeft( target.attribute.x + target.attribute.width - this._gantt.tableNoFrameWidth @@ -440,42 +537,59 @@ export class StateManager { let diff_days = Math.round(deltaX / this._gantt.parsedOptions.colWidthPerDay); diff_days = direction === 'left' ? -diff_days : diff_days; - const taskBarGroup = this._gantt.stateManager.resizeTaskBar.target; - const rect = this._gantt.stateManager.resizeTaskBar.target.barRect; - const progressRect = this._gantt.stateManager.resizeTaskBar.target.progressRect; - const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); - const oldRecord = this._gantt.getRecordByIndex(taskIndex); + const taskBarGroup = this.resizeTaskBar.target; + const clipGroupBox = taskBarGroup.clipGroupBox; + const rect = this.resizeTaskBar.target.barRect; + const progressRect = this.resizeTaskBar.target.progressRect; + // const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); + const taskIndex = this.resizeTaskBar.target.task_index; + const sub_task_index = this.resizeTaskBar.target.sub_task_index; + const oldRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); const oldStartDate = oldRecord[this._gantt.parsedOptions.startDateField]; const oldEndDate = oldRecord[this._gantt.parsedOptions.endDateField]; - const { taskDays, progress } = this._gantt.getTaskInfoByTaskListIndex(taskIndex); + const { taskDays, progress } = this._gantt.getTaskInfoByTaskListIndex(taskIndex, sub_task_index); if (diff_days < 0 && taskDays + diff_days <= 0) { diff_days = 1 - taskDays; } - const correctX = (direction === 'left' ? -diff_days : diff_days) * this._gantt.parsedOptions.colWidthPerDay; - const targetEndX = this.resizeTaskBar.targetStartX + correctX; - - const taskBarSize = this._gantt.parsedOptions.colWidthPerDay * (taskDays + diff_days); - if (direction === 'left') { - // taskBarGroup.setAttribute('x', targetEndX); - // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskIndex, taskBarGroup, targetEndX - taskBarGroup.attribute.x, taskBarSize, this); - rect?.setAttribute('width', taskBarGroup.attribute.width); - progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); - this._gantt._updateDateToTaskRecord('start-move', -diff_days, taskIndex); - } else if (direction === 'right') { - // taskBarGroup.setAttribute('width', taskBarSize); - resizeOrMoveTaskBar(taskIndex, taskBarGroup, 0, taskBarSize, this); - rect?.setAttribute('width', taskBarGroup.attribute.width); - progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); - this._gantt._updateDateToTaskRecord('end-move', diff_days, taskIndex); + this._gantt._updateDateToTaskRecord( + direction === 'left' ? 'start-move' : 'end-move', + direction === 'left' ? -diff_days : diff_days, + taskIndex, + sub_task_index + ); + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + this._gantt.taskListTableInstance.renderWithRecreateCells(); + this._gantt._syncPropsFromTable(); + this._gantt.scenegraph.refreshTaskBarsAndGrid(); + } else { + const correctX = (direction === 'left' ? -diff_days : diff_days) * this._gantt.parsedOptions.colWidthPerDay; + const targetEndX = this.resizeTaskBar.targetStartX + correctX; + + const taskBarSize = this._gantt.parsedOptions.colWidthPerDay * (taskDays + diff_days); + if (direction === 'left') { + resizeOrMoveTaskBar(taskBarGroup, targetEndX - taskBarGroup.attribute.x, 0, taskBarSize, this); + clipGroupBox.setAttribute('width', taskBarGroup.attribute.width); + rect?.setAttribute('width', taskBarGroup.attribute.width); + progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); + } else if (direction === 'right') { + resizeOrMoveTaskBar(taskBarGroup, 0, 0, taskBarSize, this); + clipGroupBox.setAttribute('width', taskBarGroup.attribute.width); + rect?.setAttribute('width', taskBarGroup.attribute.width); + progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); + } + this.showTaskBarHover(); + reCreateCustomNode(this._gantt, taskBarGroup, taskIndex, sub_task_index); + + taskBarGroup.setAttribute('zIndex', 0); } - this.showTaskBarHover(); - reCreateCustomNode(this._gantt, taskBarGroup, taskIndex); this.resizeTaskBar.resizing = false; this.resizeTaskBar.target = null; if (Math.abs(diff_days) >= 1 && this._gantt.hasListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE)) { - const newRecord = this._gantt.getRecordByIndex(taskIndex); + const newRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); this._gantt.fireListeners(GANTT_EVENT_TYPE.CHANGE_DATE_RANGE, { startDate: newRecord[this._gantt.parsedOptions.startDateField], endDate: newRecord[this._gantt.parsedOptions.endDateField], @@ -494,13 +608,17 @@ export class StateManager { const dx = x2 - x1; // debugger; const taskBarGroup = this._gantt.stateManager.resizeTaskBar.target; + taskBarGroup.setAttribute('zIndex', 10000); + const clipGroupBox = taskBarGroup.clipGroupBox; const rect = taskBarGroup.barRect; const progressRect = taskBarGroup.progressRect; const textLabel = taskBarGroup.textLabel; const progressField = this._gantt.parsedOptions.progressField; - const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); - const taskRecord = this._gantt.getRecordByIndex(taskIndex); + // const taskIndex = getTaskIndexByY(this.resizeTaskBar.startOffsetY, this._gantt); + const taskIndex = taskBarGroup.task_index; + const sub_task_index = taskBarGroup.sub_task_index; + const taskRecord = this._gantt.getRecordByIndex(taskIndex, sub_task_index); const progress = taskRecord[progressField]; let diffWidth = this._gantt.stateManager.resizeTaskBar.onIconName === 'left' ? -dx : dx; @@ -515,12 +633,13 @@ export class StateManager { // taskBarGroup.setAttribute('x', taskBarGroup.attribute.x - diffWidth); // } resizeOrMoveTaskBar( - taskIndex, taskBarGroup, this._gantt.stateManager.resizeTaskBar.onIconName === 'left' ? -diffWidth : 0, + 0, taskBarSize, this ); + clipGroupBox.setAttribute('width', taskBarGroup.attribute.width); rect?.setAttribute('width', taskBarGroup.attribute.width); progressRect?.setAttribute('width', (progress / 100) * taskBarGroup.attribute.width); @@ -528,7 +647,7 @@ export class StateManager { this.showTaskBarHover(); - reCreateCustomNode(this._gantt, taskBarGroup, taskIndex); + reCreateCustomNode(this._gantt, taskBarGroup, taskIndex, sub_task_index); this._gantt.scenegraph.updateNextFrame(); // } @@ -551,11 +670,15 @@ export class StateManager { } endCreateDependencyLine(offsetY: number) { const taskKeyField = this._gantt.parsedOptions.taskKeyField; - const fromTaskIndex = getTaskIndexByY(this.creatingDenpendencyLink.startOffsetY, this._gantt); - const toTaskIndex = getTaskIndexByY(offsetY, this._gantt); - const fromRecord = this._gantt.getRecordByIndex(fromTaskIndex); + // const fromTaskIndex = getTaskIndexByY(this.creatingDenpendencyLink.startOffsetY, this._gantt); + const fromTaskIndex = this.selectedTaskBar.target.task_index; + const from_sub_task_id = this.selectedTaskBar.target.sub_task_index; + // const toTaskIndex = getTaskIndexByY(offsetY, this._gantt); + const toTaskIndex = this.creatingDenpendencyLink.secondTaskBarNode.task_index; + const to_sub_task_id = this.creatingDenpendencyLink.secondTaskBarNode.sub_task_index; + const fromRecord = this._gantt.getRecordByIndex(fromTaskIndex, from_sub_task_id); const linkedFromTaskKey = fromRecord[taskKeyField]; - const toRecord = this._gantt.getRecordByIndex(toTaskIndex); + const toRecord = this._gantt.getRecordByIndex(toTaskIndex, to_sub_task_id); const linkedToTaskKey = toRecord[taskKeyField]; const link = { linkedFromTaskKey, @@ -643,12 +766,14 @@ export class StateManager { showTaskBarHover() { const target = this._gantt.stateManager.hoverTaskBar.target; - const x = target.attribute.x; - const y = target.attribute.y; - const width = target.attribute.width; - const height = target.attribute.height; - this._gantt.scenegraph.taskBar.showHoverBar(x, y, width, height, target); - this._gantt.scenegraph.updateNextFrame(); + if (target) { + const x = target.attribute.x; + const y = target.attribute.y; + const width = target.attribute.width; + const height = target.attribute.height; + this._gantt.scenegraph.taskBar.showHoverBar(x, y, width, height, target); + this._gantt.scenegraph.updateNextFrame(); + } } hideTaskBarHover(e: FederatedPointerEvent) { this._gantt.stateManager.hoverTaskBar.target = null; @@ -656,9 +781,11 @@ export class StateManager { this._gantt.scenegraph.updateNextFrame(); } - showTaskBarSelectedBorder() { + showTaskBarSelectedBorder(target: GanttTaskBarNode) { + this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', 0); + this._gantt.stateManager.selectedTaskBar.target = target as any as GanttTaskBarNode; const linkCreatable = this._gantt.parsedOptions.dependencyLinkCreatable; - const target = this._gantt.stateManager.selectedTaskBar.target; + target.setAttribute('zIndex', 10000); const x = target.attribute.x; const y = target.attribute.y; const width = target.attribute.width; @@ -668,6 +795,7 @@ export class StateManager { } hideTaskBarSelectedBorder() { + this._gantt.stateManager.selectedTaskBar.target?.setAttribute('zIndex', 0); this._gantt.stateManager.selectedTaskBar.target = null; this._gantt.scenegraph.taskBar.removeSelectedBorder(); this._gantt.scenegraph.updateNextFrame(); @@ -692,12 +820,29 @@ export class StateManager { const { taskKeyField, dependencyLinks } = this._gantt.parsedOptions; const { linkedToTaskKey, linkedFromTaskKey, type } = link; + let linkFrom_index; + let linkFrom_sub_task_index; + let linkTo_index; + let linkTo_sub_task_index; const linkedToTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedToTaskKey); const linkedFromTaskRecord = findRecordByTaskKey(this._gantt.records, taskKeyField, linkedFromTaskKey); - const linkedFromTaskShowIndex = this._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); - const linkedToTaskShowIndex = this._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + if ( + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Inline || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Separate || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Arrange || + this._gantt.parsedOptions.tasksShowMode === TasksShowMode.Sub_Tasks_Compact + ) { + linkFrom_index = linkedFromTaskRecord.index[0]; + linkFrom_sub_task_index = linkedFromTaskRecord.index[1]; + linkTo_index = linkedToTaskRecord.index[0]; + linkTo_sub_task_index = linkedToTaskRecord.index[1]; + } else { + linkFrom_index = this._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); + linkTo_index = this._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); + } const fromTaskNode = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex( - linkedFromTaskShowIndex + linkFrom_index, + linkFrom_sub_task_index ) as GanttTaskBarNode; this._gantt.scenegraph.taskBar.createSelectedBorder( fromTaskNode.attribute.x, @@ -707,7 +852,10 @@ export class StateManager { fromTaskNode, false ); - const toTaskNode = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex(linkedToTaskShowIndex) as GanttTaskBarNode; + const toTaskNode = this._gantt.scenegraph.taskBar.getTaskBarNodeByIndex( + linkTo_index, + linkTo_sub_task_index + ) as GanttTaskBarNode; this._gantt.scenegraph.taskBar.createSelectedBorder( toTaskNode.attribute.x, toTaskNode.attribute.y, @@ -747,12 +895,15 @@ export class StateManager { } } -function reCreateCustomNode(gantt: Gantt, taskBarGroup: Group, taskIndex: number) { +function reCreateCustomNode(gantt: Gantt, taskBarGroup: Group, taskIndex: number, sub_task_index?: number) { const taskBarCustomLayout = gantt.parsedOptions.taskBarCustomLayout; if (taskBarCustomLayout) { let customLayoutObj; if (typeof taskBarCustomLayout === 'function') { - const { startDate, endDate, taskDays, progress, taskRecord } = gantt.getTaskInfoByTaskListIndex(taskIndex); + const { startDate, endDate, taskDays, progress, taskRecord } = gantt.getTaskInfoByTaskListIndex( + taskIndex, + sub_task_index + ); const arg = { width: taskBarGroup.attribute.width, height: taskBarGroup.attribute.height, @@ -781,85 +932,23 @@ function reCreateCustomNode(gantt: Gantt, taskBarGroup: Group, taskIndex: number } } -function resizeOrMoveTaskBar( - taskIndex: number, - target: GanttTaskBarNode, - dx: number, - newWidth: number, - state: StateManager -) { +function resizeOrMoveTaskBar(target: GanttTaskBarNode, dx: number, dy: number, newWidth: number, state: StateManager) { // const taskIndex = getTaskIndexByY(state.moveTaskBar.startOffsetY, state._gantt); - const record = state._gantt.getRecordByIndex(taskIndex); + const taskIndex = target.task_index; + const sub_task_index = target.sub_task_index; if (dx) { target.setAttribute('x', target.attribute.x + dx); } + if (state._gantt.parsedOptions.tasksShowMode !== TasksShowMode.Tasks_Separate) { + if (dy) { + target.setAttribute('y', target.attribute.y + dy); + } + } else { + dy = 0; + } if (newWidth) { target.setAttribute('width', newWidth); } - const vtable_gantt_linkedTo = record.vtable_gantt_linkedTo; - const vtable_gantt_linkedFrom = record.vtable_gantt_linkedFrom; - for (let i = 0; i < vtable_gantt_linkedTo?.length; i++) { - const link = vtable_gantt_linkedTo[i]; - const linkLineNode = link.vtable_gantt_linkLineNode; - const lineArrowNode = link.vtable_gantt_linkArrowNode; - const { linkedToTaskKey, linkedFromTaskKey, type } = link; - const { taskKeyField, minDate } = state._gantt.parsedOptions; - const linkedFromTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedFromTaskKey); - - const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskIndex); - const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedFromTaskRecord.index); - const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); - const { linePoints, arrowPoints } = updateLinkLinePoints( - type, - linkedFromTaskStartDate, - linkedFromTaskEndDate, - taskShowIndex, - linkedToTaskStartDate, - linkedToTaskEndDate, - taskIndex, - minDate, - state._gantt.parsedOptions.rowHeight, - state._gantt.parsedOptions.colWidthPerDay, - null, - target - ); - linkLineNode.setAttribute('points', linePoints); - lineArrowNode.setAttribute('points', arrowPoints); - } - - for (let i = 0; i < vtable_gantt_linkedFrom?.length; i++) { - const link = vtable_gantt_linkedFrom[i]; - const linkLineNode = link.vtable_gantt_linkLineNode; - const lineArrowNode = link.vtable_gantt_linkArrowNode; - - const { linkedToTaskKey, linkedFromTaskKey, type } = link; - const { taskKeyField, minDate } = state._gantt.parsedOptions; - const linkedToTaskRecord = findRecordByTaskKey(state._gantt.records, taskKeyField, linkedToTaskKey); - - const { startDate: linkedFromTaskStartDate, endDate: linkedFromTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskIndex); - const taskShowIndex = state._gantt.getTaskShowIndexByRecordIndex(linkedToTaskRecord.index); - const { startDate: linkedToTaskStartDate, endDate: linkedToTaskEndDate } = - state._gantt.getTaskInfoByTaskListIndex(taskShowIndex); - const { linePoints, arrowPoints } = updateLinkLinePoints( - type, - linkedFromTaskStartDate, - linkedFromTaskEndDate, - taskIndex, - linkedToTaskStartDate, - linkedToTaskEndDate, - taskShowIndex, - minDate, - state._gantt.parsedOptions.rowHeight, - state._gantt.parsedOptions.colWidthPerDay, - target, - null - ); - - linkLineNode.setAttribute('points', linePoints); - lineArrowNode.setAttribute('points', arrowPoints); - } + state._gantt.scenegraph.refreshRecordLinkNodes(taskIndex, sub_task_index, target, dy); } diff --git a/packages/vtable-gantt/src/sub-tasks-inline-helper.ts b/packages/vtable-gantt/src/sub-tasks-inline-helper.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/vtable-gantt/src/tools/util.ts b/packages/vtable-gantt/src/tools/util.ts index 8b758890a..93dbd4c28 100644 --- a/packages/vtable-gantt/src/tools/util.ts +++ b/packages/vtable-gantt/src/tools/util.ts @@ -158,8 +158,8 @@ export function parseDateFormat(dateString: string) { 'yyyy/mm/dd', 'dd/mm/yyyy', 'yyyy.mm.dd', - 'dd.mm.yyyy', - 'mm.dd.yyyy' + 'mm.dd.yyyy', + 'dd.mm.yyyy' ]; dateString = dateString.replace(/\s+/g, ''); // 移除空格 for (let i = 0; i < formats.length; i++) { diff --git a/packages/vtable-gantt/src/ts-types/events.ts b/packages/vtable-gantt/src/ts-types/events.ts index 90327ff6d..e48f58232 100644 --- a/packages/vtable-gantt/src/ts-types/events.ts +++ b/packages/vtable-gantt/src/ts-types/events.ts @@ -17,6 +17,7 @@ export interface TableEventHandlersEventArgumentMap { mouseenter_task_bar: { /** 第几条数据 */ index: number; + sub_task_index?: number; record: any; event: Event; federatedEvent: FederatedPointerEvent; @@ -24,6 +25,7 @@ export interface TableEventHandlersEventArgumentMap { mouseleave_task_bar: { /** 第几条数据 */ index: number; + sub_task_index?: number; record: any; event: Event; federatedEvent: FederatedPointerEvent; @@ -31,6 +33,7 @@ export interface TableEventHandlersEventArgumentMap { click_task_bar: { /** 第几条数据 */ index: number; + sub_task_index?: number; record: any; event: Event; federatedEvent: FederatedPointerEvent; @@ -38,6 +41,7 @@ export interface TableEventHandlersEventArgumentMap { change_date_range: { /** 第几条数据 */ index: number; + sub_task_index?: number; /** 改变后的起始日期 */ startDate: Date; /** 改变后的结束日期 */ @@ -54,6 +58,7 @@ export interface TableEventHandlersEventArgumentMap { event: Event; /** 第几条数据 */ index: number; + sub_task_index?: number; /** 改变后的起始日期 */ startDate: Date; /** 改变后的结束日期 */ diff --git a/packages/vtable-gantt/src/ts-types/gantt-engine.ts b/packages/vtable-gantt/src/ts-types/gantt-engine.ts index 6f92082e6..58713cad7 100644 --- a/packages/vtable-gantt/src/ts-types/gantt-engine.ts +++ b/packages/vtable-gantt/src/ts-types/gantt-engine.ts @@ -88,14 +88,14 @@ export interface GanttConstructorOptions { resizable?: boolean; /** 任务条是否可移动 */ moveable?: boolean; + /** 任务条是否可以被拖拽来改变顺序 */ + dragOrder?: boolean; /** 任务条hover时的样式 */ hoverBarStyle?: ITaskBarHoverStyle; /** 任务条选择时的样式 TODO */ selectedBarStyle?: ITaskBarSelectedStyle; /** 任务条是否可选择,默认为true */ selectable?: boolean; - /** 任务条是否可以被拖拽来改变顺序 */ - dragOrder?: boolean; /** 任务条右键菜单 */ menu?: { /** 右键菜单。代替原来的option.contextmenu */ @@ -189,6 +189,9 @@ export interface GanttConstructorOptions { /** 表格绘制范围外的canvas上填充的颜色 */ underlayBackgroundColor?: string; + groupBy?: true | string | string[]; + /** 展示嵌套结构数据时的模式,默认为full。*/ + tasksShowMode?: TasksShowMode; } /** * IBarLabelText @@ -222,10 +225,10 @@ export interface ITaskBarStyle { width?: number; /** 任务条的圆角 */ cornerRadius?: number; - // /** 任务条的边框 */ - // borderWidth?: number; - // /** 边框颜色 */ - // borderColor?: string; + /** 任务条的边框 */ + borderWidth?: number; + /** 边框颜色 */ + borderColor?: string; } export type ILineStyle = { lineColor?: string; @@ -331,6 +334,18 @@ export enum DependencyType { FinishToFinish = 'finish_to_finish', StartToFinish = 'start_to_finish' } +export enum TasksShowMode { + /** 每一个任务节点用单独一行来展示,父任务占用一行,子任务分别占用一行。这是默认的显示效果 */ + Tasks_Separate = 'tasks_separate', + /** 省去父任务节点不展示,并把所有子任务的节点都放到同一行来展示。 */ + Sub_Tasks_Inline = 'sub_tasks_inline', + /** 省去父任务节点不展示,且所有子任务的节点分别用一行展示。*/ + Sub_Tasks_Separate = 'sub_tasks_separate', + /** 省去父任务节点不展示,且所有子任务会维持records中的数据顺序布局,并保证节点不重叠展示 */ + Sub_Tasks_Arrange = 'sub_tasks_arrange', + /** 省去父任务节点不展示,且所有子任务会按照日期早晚的属性来布局,并保证节点不重叠的紧凑型展示 */ + Sub_Tasks_Compact = 'sub_tasks_compact' +} export type ITaskBarSelectedStyle = { shadowBlur?: number; //阴影宽度 shadowOffsetX?: number; //x方向偏移 diff --git a/packages/vtable-plugins/demo/header-highlight/header-highlight.ts b/packages/vtable-plugins/demo/header-highlight/header-highlight.ts index 88d852bb3..da1c564b2 100644 --- a/packages/vtable-plugins/demo/header-highlight/header-highlight.ts +++ b/packages/vtable-plugins/demo/header-highlight/header-highlight.ts @@ -1,5 +1,7 @@ import * as VTable from '@visactor/vtable'; import { bindDebugTool } from '@visactor/vtable/es/scenegraph/debug-tool'; +import * as VTable_editors from '@visactor/vtable-editors'; + import { HeaderHighlightPlugin } from '../../src'; const CONTAINER_ID = 'vTable'; const generatePersons = count => { @@ -19,6 +21,9 @@ const generatePersons = count => { }; export function createTable() { + const input_editor = new VTable_editors.InputEditor(); + VTable.register.editor('input-editor', input_editor); + const records = generatePersons(20); const columns: VTable.ColumnsDefine = [ { @@ -26,7 +31,9 @@ export function createTable() { title: 'ID', width: 'auto', minWidth: 50, - sort: true + sort: true, + headerEditor: 'input-editor', + editor: 'input-editor' }, { field: 'email1', @@ -39,21 +46,6 @@ export function createTable() { underlineOffset: 3 } }, - { - title: 'full name', - columns: [ - { - field: 'name', - title: 'First Name', - width: 200 - }, - { - field: 'name', - title: 'Last Name', - width: 200 - } - ] - }, { field: 'date1', title: 'birthday', @@ -69,7 +61,16 @@ export function createTable() { container: document.getElementById(CONTAINER_ID), records, columns, - rowSeriesNumber: {} + rowSeriesNumber: {}, + select: { + outsideClickDeselect: true, + headerSelectMode: 'body' + }, + autoWrapText: true, + editor: 'input-editor', + menu: { + contextMenuItems: ['copy', 'paste', 'delete', '...'] + } }; const tableInstance = new VTable.ListTable(option); window.tableInstance = tableInstance; @@ -101,5 +102,19 @@ export function createTable() { const highlightPlugin = new HeaderHighlightPlugin(tableInstance); window.highlightPlugin = highlightPlugin; + const onShowMenu = () => { + console.log('show_menu'); + // 菜单dom位置 及 层级处理 + const menuElement = document.getElementsByClassName('vtable__menu-element')[0]; + if (menuElement) { + console.log('find_menu'); + // menuElement.setAttribute('style', `top: -${menuElement.clientHeight}px !important`) + menuElement.setAttribute('style', `top: -${0}px !important`); + menuElement.setAttribute('style', `z-index: 9999`); + } + }; + + tableInstance.on('show_menu', onShowMenu); + // tableInstance.scenegraph.temporarilyUpdateSelectRectStyle({stroke: false}) } diff --git a/packages/vtable-plugins/demo/index.html b/packages/vtable-plugins/demo/index.html index 922a54969..8af448ba9 100644 --- a/packages/vtable-plugins/demo/index.html +++ b/packages/vtable-plugins/demo/index.html @@ -15,13 +15,6 @@
-
- - 0/0 - - - -
diff --git a/packages/vtable-plugins/demo/main.ts b/packages/vtable-plugins/demo/main.ts index 334ab9ec9..dbf5efc7e 100644 --- a/packages/vtable-plugins/demo/main.ts +++ b/packages/vtable-plugins/demo/main.ts @@ -130,35 +130,4 @@ const run = () => { ); }; -function bindSearch() { - const searchInput = document.getElementById('search-component-input') as HTMLInputElement; - const searchBtn = document.getElementById('search-component-search') as HTMLButtonElement; - const searchResult = document.getElementById('search-component-result') as HTMLSpanElement; - const searchPrevBtn = document.getElementById('search-component-prev') as HTMLButtonElement; - const searchNextBtn = document.getElementById('search-component-next') as HTMLButtonElement; - - searchBtn.addEventListener('click', () => { - if (window.search) { - const result = window.search.search(searchInput.value); - searchResult.innerText = `${result.index + 1}/${result.results.length}`; - } - }); - - searchPrevBtn.addEventListener('click', () => { - if (window.search) { - const result = window.search.prev(); - searchResult.innerText = `${result.index + 1}/${result.results.length}`; - } - }); - - searchNextBtn.addEventListener('click', () => { - if (window.search) { - const result = window.search.next(); - searchResult.innerText = `${result.index + 1}/${result.results.length}`; - } - }); -} - run(); - -bindSearch(); diff --git a/packages/vtable-plugins/package.json b/packages/vtable-plugins/package.json index 925df00e7..134f7250e 100644 --- a/packages/vtable-plugins/package.json +++ b/packages/vtable-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-plugins", - "version": "1.11.5", + "version": "1.12.0", "description": "The search util of VTable", "author": { "name": "VisActor", @@ -41,6 +41,7 @@ }, "devDependencies": { "@visactor/vtable": "workspace:*", + "@visactor/vtable-editors": "workspace:*", "@visactor/vchart": "1.12.12", "@internal/bundler": "workspace:*", "@internal/eslint-config": "workspace:*", diff --git a/packages/vtable-plugins/src/header-highlight.ts b/packages/vtable-plugins/src/header-highlight.ts index ba2c3eb20..03e541ea1 100644 --- a/packages/vtable-plugins/src/header-highlight.ts +++ b/packages/vtable-plugins/src/header-highlight.ts @@ -36,7 +36,7 @@ export class HeaderHighlightPlugin { } bindEvent() { - this.table.on('selected_cell', e => { + this.table.on('selected_cell', () => { this.updateHighlight(); }); diff --git a/packages/vtable-search/package.json b/packages/vtable-search/package.json index c4a7e0df9..d65b10ee5 100644 --- a/packages/vtable-search/package.json +++ b/packages/vtable-search/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable-search", - "version": "1.11.5", + "version": "1.12.0", "description": "The search util of VTable", "author": { "name": "VisActor", diff --git a/packages/vtable/CHANGELOG.json b/packages/vtable/CHANGELOG.json index 5146c79e0..ae1f44eff 100644 --- a/packages/vtable/CHANGELOG.json +++ b/packages/vtable/CHANGELOG.json @@ -1,6 +1,53 @@ { "name": "@visactor/vtable", "entries": [ + { + "version": "1.12.0", + "tag": "@visactor/vtable_v1.12.0", + "date": "Tue, 03 Dec 2024 11:06:35 GMT", + "comments": { + "none": [ + { + "comment": "feat: add option customComputeRowHeight and defaultRowHeight can set \"auto\"\n\n" + }, + { + "comment": "docs: add getTaskBarRelativeRect api #2920\n\n" + }, + { + "comment": "fix: when edit record task date update taskbar occor error #2938\n\n" + }, + { + "comment": "fix: fix last col&row editor size #2926" + }, + { + "comment": "fix: fix cell update event problem in CustomCellStylePlugin #2927" + }, + { + "comment": "fix: fix react-component in tree mode update" + }, + { + "comment": "fix: fix default row height in computeRowHeight() #2903" + }, + { + "comment": "fix: fix legend visible problem when reize table" + }, + { + "comment": "fix: fix cache problem in Icon.loadGif() #2905" + }, + { + "comment": "fix: fix merge radio cell check update #2881" + }, + { + "comment": "fix: fix strokeArrayWidth update in updateCell() #2811" + } + ], + "minor": [ + { + "comment": "feat: add taskShowMode for gantt chart #2849\n\n" + } + ] + } + }, { "version": "1.11.5", "tag": "@visactor/vtable_v1.11.5", diff --git a/packages/vtable/CHANGELOG.md b/packages/vtable/CHANGELOG.md index 39b3be65b..7c0aa571f 100644 --- a/packages/vtable/CHANGELOG.md +++ b/packages/vtable/CHANGELOG.md @@ -1,6 +1,35 @@ # Change Log - @visactor/vtable -This log was last generated on Fri, 29 Nov 2024 07:59:16 GMT and should not be manually modified. +This log was last generated on Tue, 03 Dec 2024 11:06:35 GMT and should not be manually modified. + +## 1.12.0 +Tue, 03 Dec 2024 11:06:35 GMT + +### Minor changes + +- feat: add taskShowMode for gantt chart #2849 + + + +### Updates + +- feat: add option customComputeRowHeight and defaultRowHeight can set "auto" + + +- docs: add getTaskBarRelativeRect api #2920 + + +- fix: when edit record task date update taskbar occor error #2938 + + +- fix: fix last col&row editor size #2926 +- fix: fix cell update event problem in CustomCellStylePlugin #2927 +- fix: fix react-component in tree mode update +- fix: fix default row height in computeRowHeight() #2903 +- fix: fix legend visible problem when reize table +- fix: fix cache problem in Icon.loadGif() #2905 +- fix: fix merge radio cell check update #2881 +- fix: fix strokeArrayWidth update in updateCell() #2811 ## 1.11.5 Fri, 29 Nov 2024 07:59:16 GMT diff --git a/packages/vtable/package.json b/packages/vtable/package.json index dda2ceb19..de46d6276 100644 --- a/packages/vtable/package.json +++ b/packages/vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "canvas table width high performance", "keywords": [ "grid", diff --git a/packages/vtable/src/ListTable.ts b/packages/vtable/src/ListTable.ts index 88a8d2425..e1fc278ee 100644 --- a/packages/vtable/src/ListTable.ts +++ b/packages/vtable/src/ListTable.ts @@ -924,12 +924,15 @@ export class ListTable extends BaseTable implements ListTableAPI { // reset proxy row config this.scenegraph.proxy.refreshRowCount(); } + this.reactCustomLayout?.clearCache(); this.scenegraph.updateRow( diffPositions.removeCellPositions, diffPositions.addCellPositions, updateCells, recalculateColWidths ); + this.reactCustomLayout?.updateAllCustomCell(); + if (checkHasChart) { // 检查更新节点状态后总宽高未撑满autoFill是否在起作用 if (this.autoFillWidth && !notFillWidth) { @@ -1096,9 +1099,9 @@ export class ListTable extends BaseTable implements ListTableAPI { /** * 设置表格数据 及排序状态 * @param records - * @param sort + * @param option 附近参数,其中的sortState为排序状态,如果设置null 将清除目前的排序状态 */ - setRecords(records: Array, option?: { sortState?: SortState | SortState[] }): void { + setRecords(records: Array, option?: { sortState?: SortState | SortState[] | null }): void { // 释放事件 及 对象 this.internalProps.dataSource?.release(); // 过滤掉dataSource的引用 diff --git a/packages/vtable/src/PivotTable.ts b/packages/vtable/src/PivotTable.ts index 6c6d0c9bc..448704a38 100644 --- a/packages/vtable/src/PivotTable.ts +++ b/packages/vtable/src/PivotTable.ts @@ -1416,12 +1416,15 @@ export class PivotTable extends BaseTable implements PivotTableAPI { // this.invalidate(); this.clearCellStyleCache(); this.scenegraph.updateHierarchyIcon(col, row); + this.reactCustomLayout?.clearCache(); this.scenegraph.updateRow( result.removeCellPositions, result.addCellPositions, result.updateCellPositions, recalculateColWidths ); + this.reactCustomLayout?.updateAllCustomCell(); + if (checkHasChart) { // 检查更新节点状态后总宽高未撑满autoFill是否在起作用 if (this.autoFillWidth && !notFillWidth) { diff --git a/packages/vtable/src/components/legend/continue-legend/continue-legend.ts b/packages/vtable/src/components/legend/continue-legend/continue-legend.ts index 9c0b09eb4..a8d790ea9 100644 --- a/packages/vtable/src/components/legend/continue-legend/continue-legend.ts +++ b/packages/vtable/src/components/legend/continue-legend/continue-legend.ts @@ -52,13 +52,20 @@ export class ContinueTableLegend { } legend.name = 'legend'; this.legendComponent = legend; + if (this.visible === false) { + legend.setAttributes({ + visible: false, + visibleAll: false + }); + legend.hideAll(); + } this.table.scenegraph.stage.defaultLayer.appendChild(legend); this.adjustTableSize(attrs); } resize() { - if (!this.legendComponent) { + if (!this.legendComponent || this.visible === false) { return; } @@ -71,6 +78,9 @@ export class ContinueTableLegend { } adjustTableSize(attrs: any) { + if (!this.legendComponent || this.visible === false) { + return; + } // 调整位置 let width = isFinite(this.legendComponent.AABBBounds.width()) ? this.legendComponent.AABBBounds.width() : 0; let height = isFinite(this.legendComponent.AABBBounds.height()) ? this.legendComponent.AABBBounds.height() : 0; diff --git a/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts b/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts index 9ab2bf14f..533145cc6 100644 --- a/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts +++ b/packages/vtable/src/components/legend/discrete-legend/discrete-legend.ts @@ -43,12 +43,8 @@ export class DiscreteTableLegend { this.legendComponent = legend; if (this.visible === false) { legend.setAttributes({ - maxWidth: 0, - width: 0, - maxHeight: 0, - height: 0, - clip: true - // visible: false + visible: false, + visibleAll: false }); legend.hideAll(); } @@ -58,7 +54,7 @@ export class DiscreteTableLegend { } resize() { - if (!this.legendComponent) { + if (!this.legendComponent || this.visible === false) { return; } @@ -71,6 +67,9 @@ export class DiscreteTableLegend { } adjustTableSize(attrs: any) { + if (!this.legendComponent || this.visible === false) { + return; + } // 调整位置 let width = isFinite(this.legendComponent.AABBBounds.width()) ? this.legendComponent.AABBBounds.width() : 0; let height = isFinite(this.legendComponent.AABBBounds.height()) ? this.legendComponent.AABBBounds.height() : 0; diff --git a/packages/vtable/src/core/BaseTable.ts b/packages/vtable/src/core/BaseTable.ts index 3bf836184..145cd3333 100644 --- a/packages/vtable/src/core/BaseTable.ts +++ b/packages/vtable/src/core/BaseTable.ts @@ -670,12 +670,15 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { * */ get defaultRowHeight(): number { - return this.internalProps.defaultRowHeight; + if (isNumber(this.internalProps.defaultRowHeight)) { + return this.internalProps.defaultRowHeight as number; + } + return 40; } /** * Set the default row height. */ - set defaultRowHeight(defaultRowHeight: number) { + set defaultRowHeight(defaultRowHeight: number | 'auto') { this.internalProps.defaultRowHeight = defaultRowHeight; this.options.defaultRowHeight = defaultRowHeight; } @@ -1244,6 +1247,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { endRow = Math.min(endRow, (this.rowCount ?? Infinity) - 1); let h = 0; + const isDefaultRowHeightIsAuto = this.options.defaultRowHeight === 'auto'; // autoRowHeight || all rows in header, use accumulation if ( this.heightMode === 'standard' && @@ -1252,6 +1256,7 @@ export abstract class BaseTable extends EventTarget implements BaseTableAPI { // endRow >= this.columnHeaderLevelCount && // !this.bottomFrozenRowCount && !this.hasAutoImageColumn() && + !isDefaultRowHeightIsAuto && this.internalProps._heightResizedRowMap.size === 0 ) { // part in header diff --git a/packages/vtable/src/core/utils/get-cell-position.ts b/packages/vtable/src/core/utils/get-cell-position.ts index bff4d80a4..9db48b813 100644 --- a/packages/vtable/src/core/utils/get-cell-position.ts +++ b/packages/vtable/src/core/utils/get-cell-position.ts @@ -298,7 +298,7 @@ export function getTargetRowAtConsiderBottomFrozen( * @returns */ export function computeTargetRowByY(absoluteY: number, _this: BaseTableAPI): number { - let defaultRowHeight = _this.internalProps.defaultRowHeight; + let defaultRowHeight = _this.defaultRowHeight; //使用二分法计算出row if (_this._rowRangeHeightsMap.get(`$0$${_this.rowCount - 1}`)) { diff --git a/packages/vtable/src/data/DataSource.ts b/packages/vtable/src/data/DataSource.ts index 59b86a1ec..7ca3f9d99 100644 --- a/packages/vtable/src/data/DataSource.ts +++ b/packages/vtable/src/data/DataSource.ts @@ -687,7 +687,8 @@ export class DataSource extends EventTarget implements DataSourceAPI { if (!this.beforeChangedRecordsMap[dataIndex]) { const originRecord = this.getOriginalRecord(dataIndex); - this.beforeChangedRecordsMap[dataIndex] = cloneDeep(originRecord) ?? {}; + this.beforeChangedRecordsMap[dataIndex] = + cloneDeep(originRecord, undefined, ['vtable_gantt_linkedFrom', 'vtable_gantt_linkedTo']) ?? {}; } if (typeof field === 'string' || typeof field === 'number') { const beforeChangedValue = this.beforeChangedRecordsMap[dataIndex][field as any]; // this.getOriginalField(index, field, col, row, table); diff --git a/packages/vtable/src/edit/edit-manager.ts b/packages/vtable/src/edit/edit-manager.ts index 7a73d4891..adebe9052 100644 --- a/packages/vtable/src/edit/edit-manager.ts +++ b/packages/vtable/src/edit/edit-manager.ts @@ -94,6 +94,14 @@ export class EditManeger { const rect = this.table.getCellRangeRelativeRect(this.table.getCellRange(col, row)); const referencePosition = { rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height } }; + // adjust last col&row, same as packages/vtable/src/scenegraph/graphic/contributions/group-contribution-render.ts getCellSizeForDraw + if (col === this.table.colCount - 1) { + referencePosition.rect.width = rect.width - 1; + } + if (row === this.table.rowCount - 1) { + referencePosition.rect.height = rect.height - 1; + } + editor.beginEditing && console.warn('VTable Warn: `beginEditing` is deprecated, please use `onStart` instead.'); editor.beginEditing?.(this.table.getElement(), referencePosition, dataValue); diff --git a/packages/vtable/src/index.ts b/packages/vtable/src/index.ts index 384c6ad15..66b018349 100644 --- a/packages/vtable/src/index.ts +++ b/packages/vtable/src/index.ts @@ -22,7 +22,6 @@ import type { ListTableConstructorOptions, PivotTableConstructorOptions, PivotChartConstructorOptions, - GanttConstructorOptions, IHeaderTreeDefine, IDimension, IIndicator, @@ -73,7 +72,6 @@ export { PivotTableConstructorOptions, PivotChartConstructorOptions, PivotChart, - GanttConstructorOptions, IHeaderTreeDefine, IDimension, IIndicator, diff --git a/packages/vtable/src/layout/row-height-map.ts b/packages/vtable/src/layout/row-height-map.ts index 571741af3..eee1199d2 100644 --- a/packages/vtable/src/layout/row-height-map.ts +++ b/packages/vtable/src/layout/row-height-map.ts @@ -300,7 +300,7 @@ export class NumberRangeMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, targetIndex); i < sourceIndex + sourceCount; i++) { + for (let i = indexFirst(keys, targetIndex); i < indexFirst(keys, sourceIndex) + sourceCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); @@ -319,7 +319,7 @@ export class NumberRangeMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, sourceIndex); i < targetIndex + targetCount; i++) { + for (let i = indexFirst(keys, sourceIndex); i < indexFirst(keys, targetIndex) + targetCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); diff --git a/packages/vtable/src/plugins/custom-cell-style.ts b/packages/vtable/src/plugins/custom-cell-style.ts index 7fa91555f..4799146de 100644 --- a/packages/vtable/src/plugins/custom-cell-style.ts +++ b/packages/vtable/src/plugins/custom-cell-style.ts @@ -1,6 +1,12 @@ import { merge } from '@visactor/vutils'; import type { BaseTableAPI } from '../ts-types/base-table'; -import type { CellRange, ColumnStyleOption, CustomCellStyle, CustomCellStyleArrangement } from '../ts-types'; +import { + cellStyleKeys, + type CellRange, + type ColumnStyleOption, + type CustomCellStyle, + type CustomCellStyleArrangement +} from '../ts-types'; import type { Style } from '../body-helper/style'; import { Factory } from '../core/factory'; @@ -163,6 +169,18 @@ export class CustomCellStylePlugin { this.customCellStyleArrangement.splice(index, 1); } + const style = this.getCustomCellStyleOption(customStyleId)?.style; + let forceFastUpdate; + if (style) { + forceFastUpdate = true; + for (const key in style) { + if (cellStyleKeys.indexOf(key) === -1) { + forceFastUpdate = false; + break; + } + } + } + // update cell group if (cellPos.range) { for ( @@ -178,14 +196,14 @@ export class CustomCellStylePlugin { const range = this.table.getCellRange(col, row); for (let c = range.start.col; c <= range.end.col; c++) { for (let r = range.start.row; r <= range.end.row; r++) { - this.table.scenegraph.updateCellContent(c, r); + this.table.scenegraph.updateCellContent(c, r, forceFastUpdate); } } // this.table.scenegraph.updateCellContent(col, row); } } } else { - this.table.scenegraph.updateCellContent(cellPos.col, cellPos.row); + this.table.scenegraph.updateCellContent(cellPos.col, cellPos.row, forceFastUpdate); } this.table.scenegraph.updateNextFrame(); diff --git a/packages/vtable/src/scenegraph/graphic/icon.ts b/packages/vtable/src/scenegraph/graphic/icon.ts index e77ae3a8e..731240e9c 100644 --- a/packages/vtable/src/scenegraph/graphic/icon.ts +++ b/packages/vtable/src/scenegraph/graphic/icon.ts @@ -57,7 +57,7 @@ export class Icon extends Image { loadGif() { this.playing = false; - ResourceLoader.GetFile((this.attribute as any).gif, 'arrayBuffer') + ResourceLoader.GetFile((this.attribute as any).gif + '?role=gif', 'arrayBuffer') // ?role=gif: hack for ResourceLoader cache .then((res: ArrayBuffer) => { const gif = parseGIF(res); const frames = decompressFrames(gif, true); diff --git a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts index 5bdf39a01..824eb379b 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-helper.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-helper.ts @@ -37,6 +37,7 @@ import { isArray, isValid } from '@visactor/vutils'; import { breakString } from '../utils/break-string'; import type { CreateRadioCellGroup } from './cell-type/radio-cell'; import { onBeforeAttributeUpdateForInvertHighlight } from '../../plugins/invert-highlight'; +import { getCellBorderStrokeWidth } from '../utils/cell-border-stroke-width'; export function createCell( type: ColumnTypeOption, @@ -384,7 +385,8 @@ export function createCell( textBaseline, table, cellTheme, - define as RadioColumnDefine + define as RadioColumnDefine, + range ); } @@ -392,7 +394,14 @@ export function createCell( return cellGroup; } -export function updateCell(col: number, row: number, table: BaseTableAPI, addNew?: boolean, isShadow?: boolean) { +export function updateCell( + col: number, + row: number, + table: BaseTableAPI, + addNew?: boolean, + isShadow?: boolean, + forceFastUpdate?: boolean +) { // const oldCellGroup = table.scenegraph.getCell(col, row, true); const oldCellGroup = table.scenegraph.highPerformanceGetCell(col, row, true); const cellLocation = table.getCellLocation(col, row); @@ -515,11 +524,13 @@ export function updateCell(col: number, row: number, table: BaseTableAPI, addNew !addNew && !isMerge && !(define?.customLayout || define?.customRender || define?.headerCustomLayout || define?.headerCustomRender) && - canUseFastUpdate(col, row, oldCellGroup, autoWrapText, mayHaveIcon, table) + (forceFastUpdate || canUseFastUpdate(col, row, oldCellGroup, autoWrapText, mayHaveIcon, table)) ) { // update group const cellWidth = table.getColWidth(col); const cellHeight = table.getRowHeight(row); + const strokeArrayWidth = getCellBorderStrokeWidth(col, row, cellTheme, table); + oldCellGroup.setAttributes({ width: cellWidth, height: cellHeight, @@ -527,7 +538,7 @@ export function updateCell(col: number, row: number, table: BaseTableAPI, addNew lineWidth: cellTheme?.group?.lineWidth ?? undefined, fill: cellTheme?.group?.fill ?? undefined, stroke: cellTheme?.group?.stroke ?? undefined, - strokeArrayWidth: (cellTheme?.group as any)?.strokeArrayWidth ?? undefined, + strokeArrayWidth: strokeArrayWidth ?? undefined, strokeArrayColor: (cellTheme?.group as any)?.strokeArrayColor ?? undefined, cursor: (cellTheme?.group as any)?.cursor ?? undefined, cornerRadius: cellTheme?.group?.cornerRadius ?? 0, diff --git a/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts b/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts index e62c97f9a..c881ce397 100644 --- a/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts +++ b/packages/vtable/src/scenegraph/group-creater/cell-type/radio-cell.ts @@ -1,6 +1,6 @@ import type { IThemeSpec } from '@src/vrender'; import { Group } from '../../graphic/group'; -import type { RadioColumnDefine, RadioStyleOption } from '../../../ts-types'; +import type { CellRange, RadioColumnDefine, RadioStyleOption } from '../../../ts-types'; import type { BaseTableAPI } from '../../../ts-types/base-table'; import { cos, isArray, isBoolean, isNumber, isObject, isValid, merge } from '@visactor/vutils'; import type { RadioAttributes } from '@visactor/vrender-components'; @@ -26,7 +26,8 @@ export function createRadioCellGroup( textBaseline: CanvasTextBaseline, table: BaseTableAPI, cellTheme: IThemeSpec, - define: RadioColumnDefine + define: RadioColumnDefine, + range: CellRange ) { // cell if (!cellGroup) { @@ -71,6 +72,7 @@ export function createRadioCellGroup( cellTheme, define, cellGroup, + range, table ); @@ -110,6 +112,7 @@ function createRadio( cellTheme: IThemeSpec, define: RadioColumnDefine, cellGroup: Group, + range: CellRange, table: BaseTableAPI ) { const style = table._getCellStyle(col, row) as RadioStyle; @@ -206,7 +209,7 @@ function createRadio( if (radioComponent) { cellGroup.appendChild(radioComponent); } - radioComponent.id = `radio-${col}-${row}-${index}`; + radioComponent.id = `radio-${range?.start.col ?? col}-${range?.start.row ?? row}-${index}`; radioComponent.render(); const bounds = radioComponent.AABBBounds; @@ -235,7 +238,7 @@ function createRadio( if (radioComponent) { cellGroup.appendChild(radioComponent); } - radioComponent.id = `radio-${col}-${row}`; + radioComponent.id = `radio-${range?.start.col ?? col}-${range?.start.row ?? row}`; radioComponent.render(); const bounds = radioComponent.AABBBounds; width = bounds.width(); diff --git a/packages/vtable/src/scenegraph/group-creater/column.ts b/packages/vtable/src/scenegraph/group-creater/column.ts index 5e82d9d53..41ab682f7 100644 --- a/packages/vtable/src/scenegraph/group-creater/column.ts +++ b/packages/vtable/src/scenegraph/group-creater/column.ts @@ -125,7 +125,8 @@ export function createColGroup( if (colStart > colEnd || rowStart > rowEnd) { return; } - const { layoutMap, defaultRowHeight, defaultHeaderRowHeight, defaultColWidth } = table.internalProps; + const { layoutMap, defaultHeaderRowHeight, defaultColWidth } = table.internalProps; + const defaultRowHeight = table.defaultRowHeight; let x = 0; let heightMax = 0; for (let i = colStart; i <= colEnd; i++) { @@ -150,7 +151,9 @@ export function createColGroup( rowStart, rowEnd, table.scenegraph.mergeMap, - cellLocation === 'columnHeader' && isNumber(defaultHeaderRowHeight) ? defaultHeaderRowHeight : defaultRowHeight, + cellLocation === 'columnHeader' && isNumber(defaultHeaderRowHeight) + ? (defaultHeaderRowHeight as number) + : defaultRowHeight, table, // cellLocation, rowLimit diff --git a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts index 1c98ed78f..948cfb07f 100644 --- a/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts +++ b/packages/vtable/src/scenegraph/group-creater/progress/proxy.ts @@ -313,7 +313,7 @@ export class SceneProxy { this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, - this.table.internalProps.defaultRowHeight, + this.table.defaultRowHeight, this.table // cellLocation ); @@ -335,7 +335,7 @@ export class SceneProxy { this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, - this.table.internalProps.defaultRowHeight, + this.table.defaultRowHeight, this.table // cellLocation ); @@ -359,7 +359,7 @@ export class SceneProxy { this.currentRow + 1, endRow, this.table.scenegraph.mergeMap, - this.table.internalProps.defaultRowHeight, + this.table.defaultRowHeight, this.table // cellLocation ); diff --git a/packages/vtable/src/scenegraph/layout/compute-row-height.ts b/packages/vtable/src/scenegraph/layout/compute-row-height.ts index 2f00cd649..ae5af2347 100644 --- a/packages/vtable/src/scenegraph/layout/compute-row-height.ts +++ b/packages/vtable/src/scenegraph/layout/compute-row-height.ts @@ -2,7 +2,13 @@ import type { Group as VGroup } from '@src/vrender'; import { RichText, Text } from '@src/vrender'; import type { PivotHeaderLayoutMap } from '../../layout/pivot-header-layout'; import { validToString } from '../../tools/util'; -import type { ColumnIconOption, ColumnTypeOption, IRowSeriesNumber } from '../../ts-types'; +import type { + ColumnIconOption, + ColumnTypeOption, + IRowSeriesNumber, + ListTableAPI, + ListTableConstructorOptions +} from '../../ts-types'; import { IconPosition } from '../../ts-types'; import type { BaseTableAPI, HeaderData } from '../../ts-types/base-table'; import type { ColumnData, ColumnDefine, TextColumnDefine } from '../../ts-types/list-table/layout-map/api'; @@ -60,8 +66,9 @@ export function computeRowsHeight( const isAllRowsAuto = table.heightMode === 'autoHeight' || (table.heightMode === 'adaptive' && table.options.autoHeightInAdaptiveMode !== false); + const isDefaultRowHeightIsAuto = table.options.defaultRowHeight === 'auto'; - if (isAllRowsAuto || isDefaultHeaderHasAuto) { + if (isAllRowsAuto || isDefaultHeaderHasAuto || isDefaultRowHeightIsAuto) { rowStart = rowStart ?? 0; rowEnd = rowEnd ?? table.rowCount - 1; @@ -111,7 +118,7 @@ export function computeRowsHeight( } } - if (rowEnd < table.columnHeaderLevelCount || !isAllRowsAuto) { + if (rowEnd < table.columnHeaderLevelCount || (!isAllRowsAuto && !isDefaultRowHeightIsAuto)) { // do nothing } else { // compute body row @@ -121,6 +128,7 @@ export function computeRowsHeight( table.internalProps.transpose || (table.isPivotTable() && !(table.internalProps.layoutMap as PivotHeaderLayoutMap).indicatorsAsCol) ) && + !(table.options as ListTableConstructorOptions).customComputeRowHeight && checkFixedStyleAndNoWrap(table) ) { // check fixed style and no wrap situation, fill all row width single compute @@ -341,7 +349,17 @@ export function computeRowsHeight( } export function computeRowHeight(row: number, startCol: number, endCol: number, table: BaseTableAPI): number { + const isAllRowsAuto = + table.heightMode === 'autoHeight' || + (table.heightMode === 'adaptive' && table.options.autoHeightInAdaptiveMode !== false); + if (!isAllRowsAuto && table.getDefaultRowHeight(row) !== 'auto') { + return table.getDefaultRowHeight(row) as number; + } + let maxHeight; + if ((table.options as ListTableConstructorOptions).customComputeRowHeight) { + return (table.options as ListTableConstructorOptions).customComputeRowHeight({ row, table: table as ListTableAPI }); + } // 如果是透视图 if ( table.isPivotChart() && diff --git a/packages/vtable/src/scenegraph/scenegraph.ts b/packages/vtable/src/scenegraph/scenegraph.ts index 4670fd710..264e1d50a 100644 --- a/packages/vtable/src/scenegraph/scenegraph.ts +++ b/packages/vtable/src/scenegraph/scenegraph.ts @@ -1920,11 +1920,11 @@ export class Scenegraph { this.component.drillIcon.update(visible, x, y, drillDown, drillUp, this); } - updateCellContent(col: number, row: number) { + updateCellContent(col: number, row: number, forceFastUpdate: boolean = false) { if (this.clear) { return undefined; } - return updateCell(col, row, this.table); + return updateCell(col, row, this.table, undefined, undefined, forceFastUpdate); } setPixelRatio(pixelRatio: number) { diff --git a/packages/vtable/src/tools/NumberMap.ts b/packages/vtable/src/tools/NumberMap.ts index 68ae1bb4f..025aa6efb 100644 --- a/packages/vtable/src/tools/NumberMap.ts +++ b/packages/vtable/src/tools/NumberMap.ts @@ -107,7 +107,7 @@ export class NumberMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, targetIndex); i < sourceIndex + sourceCount; i++) { + for (let i = indexFirst(keys, targetIndex); i < indexFirst(keys, sourceIndex) + sourceCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); @@ -126,7 +126,7 @@ export class NumberMap { //先将target部分的值存起来 const targetVals = []; const sourceVals = []; - for (let i = indexFirst(keys, sourceIndex); i < targetIndex + targetCount; i++) { + for (let i = indexFirst(keys, sourceIndex); i < indexFirst(keys, targetIndex) + targetCount; i++) { const key = keys[i]; if (key >= sourceIndex && key < sourceIndex + sourceCount) { sourceVals.push(this.get(key)); diff --git a/packages/vtable/src/ts-types/base-table.ts b/packages/vtable/src/ts-types/base-table.ts index d55483259..fa4c31925 100644 --- a/packages/vtable/src/ts-types/base-table.ts +++ b/packages/vtable/src/ts-types/base-table.ts @@ -119,7 +119,7 @@ export interface IBaseTableProtected { frozenRowCount: number; rightFrozenColCount: number; bottomFrozenRowCount: number; - defaultRowHeight: number; + defaultRowHeight: number | 'auto'; /**表头默认行高 可以按逐行设置 如果没有就取defaultRowHeight */ defaultHeaderRowHeight: (number | 'auto') | (number | 'auto')[]; defaultColWidth: number; @@ -305,7 +305,7 @@ export interface BaseTableConstructorOptions { /** * 默认行高. 默认 40 */ - defaultRowHeight?: number; + defaultRowHeight?: number | 'auto'; /** 列表头默认行高 可以按逐行设置 如果没有就取defaultRowHeight */ defaultHeaderRowHeight?: (number | 'auto') | (number | 'auto')[]; /** diff --git a/packages/vtable/src/ts-types/column/style.ts b/packages/vtable/src/ts-types/column/style.ts index ee9a224dc..3e827824a 100644 --- a/packages/vtable/src/ts-types/column/style.ts +++ b/packages/vtable/src/ts-types/column/style.ts @@ -99,6 +99,8 @@ export interface IStyleOption { marked?: MarkedPropertyDefine; } +export const cellStyleKeys = ['bgColor', 'color', 'strokeColor', 'borderColor', 'linkColor']; // keys of style not change cell layout + export interface ITextStyleOption extends IStyleOption { // lineHeight?: string | number;//移入IStyleOption中 单行文本类型也可以有 autoWrapText?: boolean; diff --git a/packages/vtable/src/ts-types/table-engine.ts b/packages/vtable/src/ts-types/table-engine.ts index ef694fa82..8ecdb794b 100644 --- a/packages/vtable/src/ts-types/table-engine.ts +++ b/packages/vtable/src/ts-types/table-engine.ts @@ -265,6 +265,8 @@ export interface ListTableConstructorOptions extends BaseTableConstructorOptions groupTitleCustomLayout?: ICustomLayout; enableTreeStickCell?: boolean; + + customComputeRowHeight?: (computeArgs: { row: number; table: ListTableAPI }) => number; } export type GroupByOption = string | string[] | GroupConfig | GroupConfig[]; @@ -595,78 +597,3 @@ export type CustomMerge = { export type ColumnInfo = { col: number; left: number; right: number; width: number }; export type RowInfo = { row: number; top: number; bottom: number; height: number }; - -//#region gantt -export interface GanttConstructorOptions { - container?: HTMLElement; - /** - * 数据集合 - */ - records?: any[]; - /** 时间刻度 */ - timelineScales: { - unit: 'day' | 'week' | 'month' | 'quarter' | 'year'; - step: number; - format: (date: Date) => string; - // 时间刻度对应的字段名 - headerStyle?: ITextStyleOption | ((styleArg: StylePropertyFunctionArg) => ITextStyleOption); - }[]; - /** 定义列 */ - infoTableColumns?: ColumnsDefine; // (string | IDimension)[]; - infoTableWidth?: 'auto' | number; - gridStyle?: { - vertical: { - lineColor?: string; - lineWidth?: number; - }; - horizontal: { - lineColor?: string; - lineWidth?: number; - }; - }; - timelineStyle?: {} | {}[]; - /** 时间刻度对应的字段名 */ - startField: string; - /** 时间刻度对应的字段名 */ - endField: string; - /** 指定整个甘特图的最小日期 */ - minDate?: string; - /** 指定整个甘特图的最大日期 不设置的话用默认规则*/ - maxDate?: string; - - /** 设置的表格主题 */ - theme?: TableTheme; - /** 设置任务条样式 可以设置多组 依次循环使用 */ - barStyle?: IBarStyleOption[]; - defaultHeaderRowHeight?: number; - defaultRowHeight?: number; - timelineColWidth?: number; - - rowSeriesNumber?: IRowSeriesNumber; - dragHeader?: boolean; - - /** - * 'auto':和浏览器滚动行为一致 表格滚动到顶部/底部时 触发浏览器默认行为; - * 设置为 'none' 时, 表格滚动到顶部/底部时, 不再触发父容器滚动 - * */ - overscrollBehavior?: 'auto' | 'none'; - - // infoTableTheme?: ITableThemeDefine; -} - -export type IBarStyleOption = { - /** 任务条的颜色 */ - barColor?: ColorPropertyDefine; - /** 已完成部分任务条的颜色 */ - barColor2?: ColorPropertyDefine; - /** 任务条的宽度 */ - width?: number; - /** 任务条的圆角 */ - cornerRadius?: number; - /** 任务条的边框 */ - borderWidth?: number; - /** 边框颜色 */ - borderColor?: ColorPropertyDefine; - font?: ITextStyleOption; -}; -//#endregion diff --git a/packages/vue-vtable/package.json b/packages/vue-vtable/package.json index a4689e510..589914468 100644 --- a/packages/vue-vtable/package.json +++ b/packages/vue-vtable/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vue-vtable", - "version": "1.11.5", + "version": "1.12.0", "description": "The vue version of VTable", "keywords": [ "vue",