Browse Source

Merge branch 'dev' of https://gitee.com/MTrun/go-view into master-fetch-dev

奔跑的面条 3 years ago
parent
commit
20be7f2e45
93 changed files with 2432 additions and 661 deletions
  1. 3 0
      package.json
  2. 27 3
      pnpm-lock.yaml
  3. 6 0
      src/api/mock/index.ts
  4. 17 0
      src/api/mock/test.mock.ts
  5. BIN
      src/assets/images/chart/decorates/threeEarth01.png
  6. 27 0
      src/components/Pages/ChartItemSetting/GlobalSetting.vue
  7. 1 1
      src/components/Pages/ChartItemSetting/GlobalSettingPosition.vue
  8. 4 4
      src/hooks/useChartDataFetch.hook.ts
  9. 10 9
      src/packages/components/Charts/Bars/BarCommon/config.ts
  10. 31 26
      src/packages/components/Charts/Bars/BarCommon/config.vue
  11. 14 10
      src/packages/components/Charts/Bars/BarCommon/index.vue
  12. 8 7
      src/packages/components/Charts/Bars/BarCrossrange/config.ts
  13. 33 0
      src/packages/components/Charts/Bars/BarCrossrange/config.vue
  14. 14 10
      src/packages/components/Charts/Bars/BarCrossrange/index.vue
  15. 13 9
      src/packages/components/Charts/Lines/LineCommon/config.ts
  16. 42 17
      src/packages/components/Charts/Lines/LineCommon/config.vue
  17. 14 10
      src/packages/components/Charts/Lines/LineCommon/index.vue
  18. 8 4
      src/packages/components/Charts/Lines/LineGradientSingle/config.ts
  19. 39 17
      src/packages/components/Charts/Lines/LineGradientSingle/config.vue
  20. 20 19
      src/packages/components/Charts/Lines/LineGradientSingle/index.vue
  21. 14 4
      src/packages/components/Charts/Lines/LineGradients/config.ts
  22. 44 1
      src/packages/components/Charts/Lines/LineGradients/config.vue
  23. 20 17
      src/packages/components/Charts/Lines/LineGradients/index.vue
  24. 2 4
      src/packages/components/Charts/Lines/LineLinearSingle/config.ts
  25. 11 0
      src/packages/components/Charts/Lines/LineLinearSingle/config.vue
  26. 13 9
      src/packages/components/Charts/Lines/LineLinearSingle/index.vue
  27. 23 15
      src/packages/components/Charts/Maps/MapBase/index.vue
  28. 1 1
      src/packages/components/Charts/Mores/Heatmap/config.ts
  29. 5 1
      src/packages/components/Charts/Mores/Heatmap/index.vue
  30. 5 1
      src/packages/components/Charts/Mores/Process/index.vue
  31. 5 1
      src/packages/components/Charts/Mores/Radar/index.vue
  32. 6 2
      src/packages/components/Charts/Mores/TreeMap/index.vue
  33. 21 17
      src/packages/components/Charts/Mores/WaterPolo/index.vue
  34. 7 1
      src/packages/components/Charts/Pies/PieCircle/index.vue
  35. 13 9
      src/packages/components/Charts/Pies/PieCommon/index.vue
  36. 1 1
      src/packages/components/Charts/Scatters/ScatterCommon/config.ts
  37. 15 11
      src/packages/components/Charts/Scatters/ScatterCommon/index.vue
  38. 1 1
      src/packages/components/Charts/Scatters/ScatterLogarithmicRegression/config.ts
  39. 8 4
      src/packages/components/Decorates/Mores/CountDown/index.vue
  40. 5 1
      src/packages/components/Decorates/Mores/FlipperNumber/index.vue
  41. 47 46
      src/packages/components/Decorates/Mores/TimeCommon/index.vue
  42. 236 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/Utils/arc.ts
  43. 137 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/Utils/common.ts
  44. 4 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/interfaces/IEvents.ts
  45. 6 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/interfaces/IWord.ts
  46. 23 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/shaders/earth/fragment.fs
  47. 12 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/shaders/earth/vertex.vs
  48. 34 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/world/Assets.ts
  49. 62 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/world/Basic.ts
  50. 496 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/world/Earth.ts
  51. 54 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/world/Resources.ts
  52. 112 0
      src/packages/components/Decorates/Three/ThreeEarth01/code/world/Word.ts
  53. 17 0
      src/packages/components/Decorates/Three/ThreeEarth01/config.ts
  54. 14 0
      src/packages/components/Decorates/Three/ThreeEarth01/config.vue
  55. 84 0
      src/packages/components/Decorates/Three/ThreeEarth01/data.json
  56. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/aircraft.png
  57. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/aperture.png
  58. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/earth.png
  59. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/glow.png
  60. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/gradient.png
  61. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/label-old.png
  62. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/label.png
  63. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/light_column.png
  64. BIN
      src/packages/components/Decorates/Three/ThreeEarth01/images/earth/redCircle.png
  65. 15 0
      src/packages/components/Decorates/Three/ThreeEarth01/index.ts
  66. 81 0
      src/packages/components/Decorates/Three/ThreeEarth01/index.vue
  67. 3 0
      src/packages/components/Decorates/Three/index.ts
  68. 2 0
      src/packages/components/Decorates/index.d.ts
  69. 2 1
      src/packages/components/Decorates/index.ts
  70. 6 3
      src/packages/components/Informations/Mores/WordCloud/index.vue
  71. 25 26
      src/packages/components/Tables/Tables/TableList/index.vue
  72. 61 31
      src/packages/components/Tables/Tables/TableScrollBoard/index.vue
  73. 0 1
      src/packages/index.d.ts
  74. 0 6
      src/packages/public/publicConfig.ts
  75. 11 2
      src/settings/chartThemes/global.theme.json
  76. 19 22
      src/store/modules/chartEditStore/chartEditStore.ts
  77. 0 0
      src/utils/components.ts
  78. 1 1
      src/utils/index.ts
  79. 43 18
      src/utils/utils.ts
  80. 3 2
      src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue
  81. 1 1
      src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMatchingAndShow/index.vue
  82. 17 10
      src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue
  83. 5 1
      src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataRequest/components/RequestTargetConfig/index.vue
  84. 130 125
      src/views/chart/ContentEdit/components/EditAlignLine/index.vue
  85. 9 11
      src/views/chart/ContentEdit/components/EditBottom/index.vue
  86. 14 6
      src/views/chart/ContentEdit/components/EditTools/hooks/useFile.hooks.ts
  87. 35 38
      src/views/chart/ContentEdit/hooks/useDrag.hook.ts
  88. 2 10
      src/views/chart/ContentLayers/components/LayersGroupListItem/index.vue
  89. 2 2
      src/views/chart/ContentLayers/components/LayersListItem/index.vue
  90. 1 6
      src/views/chart/ContentLayers/index.vue
  91. 20 11
      src/views/chart/hooks/useKeyboard.hook.ts
  92. 33 32
      src/views/chart/hooks/useSync.hook.ts
  93. 2 2
      types/global.d.ts

+ 3 - 0
package.json

@@ -22,6 +22,7 @@
     "echarts-liquidfill": "^3.1.0",
     "echarts-stat": "^1.2.0",
     "echarts-wordcloud": "^2.0.0",
+    "gsap": "^3.11.3",
     "highlight.js": "^11.5.0",
     "html2canvas": "^1.4.1",
     "keymaster": "^1.6.2",
@@ -29,6 +30,7 @@
     "naive-ui": "2.33.4",
     "pinia": "^2.0.13",
     "screenfull": "^6.0.1",
+    "three": "^0.145.0",
     "vue": "^3.2.31",
     "vue-demi": "^0.13.1",
     "vue-i18n": "9.1.10",
@@ -41,6 +43,7 @@
     "@commitlint/cli": "^17.0.2",
     "@commitlint/config-conventional": "^17.0.2",
     "@types/node": "^16.11.26",
+    "@types/three": "^0.144.0",
     "@typescript-eslint/eslint-plugin": "^5.18.0",
     "@typescript-eslint/parser": "^5.18.0",
     "@vicons/carbon": "^0.12.0",

+ 27 - 3
pnpm-lock.yaml

@@ -8,6 +8,7 @@ specifiers:
   '@types/keymaster': ^1.6.30
   '@types/lodash': ^4.14.184
   '@types/node': ^16.11.26
+  '@types/three': ^0.144.0
   '@typescript-eslint/eslint-plugin': ^5.18.0
   '@typescript-eslint/parser': ^5.18.0
   '@vicons/carbon': ^0.12.0
@@ -31,6 +32,7 @@ specifiers:
   eslint-plugin-import: ^2.26.0
   eslint-plugin-prettier: ^4.0.0
   eslint-plugin-vue: ^8.5.0
+  gsap: ^3.11.3
   highlight.js: ^11.5.0
   html2canvas: ^1.4.1
   husky: ^8.0.1
@@ -45,6 +47,7 @@ specifiers:
   sass: ^1.49.11
   sass-loader: ^12.6.0
   screenfull: ^6.0.1
+  three: ^0.145.0
   typescript: 4.6.3
   vite: 2.9.9
   vite-plugin-compression: ^0.5.1
@@ -73,6 +76,7 @@ dependencies:
   echarts-liquidfill: 3.1.0_echarts@5.3.3
   echarts-stat: 1.2.0
   echarts-wordcloud: 2.0.0_echarts@5.3.3
+  gsap: 3.11.3
   highlight.js: 11.5.1
   html2canvas: 1.4.1
   keymaster: 1.6.2
@@ -80,6 +84,7 @@ dependencies:
   naive-ui: 2.33.4_vue@3.2.37
   pinia: 2.0.14_ub5l46u3nefphax5x2tezui4oq
   screenfull: 6.0.1
+  three: 0.145.0
   vue: 3.2.37
   vue-demi: 0.13.1_vue@3.2.37
   vue-i18n: 9.1.10_vue@3.2.37
@@ -92,6 +97,7 @@ devDependencies:
   '@commitlint/cli': 17.0.2
   '@commitlint/config-conventional': 17.0.2
   '@types/node': 16.11.40
+  '@types/three': 0.144.0
   '@typescript-eslint/eslint-plugin': 5.28.0_evi7yu7wunhzwb24olrfvzynny
   '@typescript-eslint/parser': 5.28.0_sfmgizikprcxt7r54j7cnzjamu
   '@vicons/carbon': 0.12.0
@@ -905,7 +911,7 @@ packages:
     dev: true
 
   /@types/node/17.0.43:
-    resolution: {integrity: sha512-jnUpgw8fL9kP2iszfIDyBQtw5Mf4/XSqy0Loc1J9pI14ejL83XcCEvSf50Gs/4ET0I9VCCDoOfufQysj0S66xA==, registry: https://registry.npm.taobao.org/}
+    resolution: {integrity: sha512-jnUpgw8fL9kP2iszfIDyBQtw5Mf4/XSqy0Loc1J9pI14ejL83XcCEvSf50Gs/4ET0I9VCCDoOfufQysj0S66xA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/@types/node/-/node-17.0.43.tgz}
 
   /@types/normalize-package-data/2.4.1:
     resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
@@ -921,12 +927,22 @@ packages:
       '@types/node': 17.0.43
     dev: true
 
+  /@types/three/0.144.0:
+    resolution: {integrity: sha512-psvEs6q5rLN50jUYZ3D4pZMfxTbdt3A243blt0my7/NcL6chaCZpHe2csbCtx0SOD9fI/XnF3wnVUAYZGqCSYg==}
+    dependencies:
+      '@types/webxr': 0.5.0
+    dev: true
+
   /@types/through/0.0.30:
     resolution: {integrity: sha512-FvnCJljyxhPM3gkRgWmxmDZyAQSiBQQWLI0A0VFL0K7W1oRUrPJSqNO0NvTnLkBcotdlp3lKvaT0JrnyRDkzOg==}
     dependencies:
       '@types/node': 17.0.43
     dev: true
 
+  /@types/webxr/0.5.0:
+    resolution: {integrity: sha512-IUMDPSXnYIbEO2IereEFcgcqfDREOgmbGqtrMpVPpACTU6pltYLwHgVkrnYv0XhWEcjio9sYEfIEzgn3c7nDqA==}
+    dev: true
+
   /@typescript-eslint/eslint-plugin/5.28.0_evi7yu7wunhzwb24olrfvzynny:
     resolution: {integrity: sha512-DXVU6Cg29H2M6EybqSg2A+x8DgO9TCUBRp4QEXQHJceLS7ogVDP0g3Lkg/SZCqcvkAP/RruuQqK0gdlkgmhSUA==}
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -1884,7 +1900,7 @@ packages:
     dev: true
 
   /csstype/2.6.20:
-    resolution: {integrity: sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==, registry: https://registry.npm.taobao.org/}
+    resolution: {integrity: sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/csstype/-/csstype-2.6.20.tgz}
     dev: false
 
   /csstype/3.0.11:
@@ -2091,7 +2107,7 @@ packages:
     dev: false
 
   /echarts-wordcloud/2.0.0_echarts@5.3.3:
-    resolution: {integrity: sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==, registry: https://registry.npm.taobao.org/}
+    resolution: {integrity: sha512-K7l6pTklqdW7ZWzT/1CS0KhBSINr/cd7c5N1fVMzZMwLQHEwT7x+nivK7g5hkVh7WNcAv4Dn6/ZS5zMKRozC1g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npm.taobao.org/echarts-wordcloud/-/echarts-wordcloud-2.0.0.tgz}
     peerDependencies:
       echarts: ^5.0.1
     dependencies:
@@ -3032,6 +3048,10 @@ packages:
     resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
     dev: true
 
+  /gsap/3.11.3:
+    resolution: {integrity: sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA==}
+    dev: false
+
   /handlebars/4.7.7:
     resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==}
     engines: {node: '>=0.4.7'}
@@ -4825,6 +4845,10 @@ packages:
     resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
     dev: true
 
+  /three/0.145.0:
+    resolution: {integrity: sha512-EKoHQEtEJ4CB6b2BGMBgLZrfwLjXcSUfoI/MiIXUuRpeYsfK5aPWbYhdtIVWOH+x6X0TouldHKHBuc/LAiFzAw==}
+    dev: false
+
   /through/2.3.8:
     resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
     dev: true

+ 6 - 0
src/api/mock/index.ts

@@ -17,6 +17,7 @@ export const scatterBasicUrl = '/mock/scatterBasic'
 export const mapUrl = '/mock/map'
 export const wordCloudUrl = '/mock/wordCloud'
 export const treemapUrl = '/mock/treemap'
+export const threeEarth01Url = '/mock/threeEarth01Data'
 
 const mockObject: MockMethod[] = [
   {
@@ -91,6 +92,11 @@ const mockObject: MockMethod[] = [
     method: RequestHttpEnum.GET,
     response: () => test.fetchTreemap
   },
+  {
+    url: threeEarth01Url,
+    method: RequestHttpEnum.GET,
+    response: () => test.threeEarth01Data
+  },
 ]
 
 export default mockObject

+ 17 - 0
src/api/mock/test.mock.ts

@@ -254,4 +254,21 @@ export default {
     msg: '请求成功',
     data: tTreemapJson
   },
+  // 三维地球
+  threeEarth01Data: {
+    code: 0,
+    status: 200,
+    msg: '请求成功',
+    data: [
+      {
+        startArray: { name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
+        endArray: [
+          { name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
+          { name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
+          { name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' },
+          { name: '@name', N: '@integer(10, 100)', E: '@integer(10, 100)' }
+        ]
+      }
+    ]
+  }
 }

BIN
src/assets/images/chart/decorates/threeEarth01.png


+ 27 - 0
src/components/Pages/ChartItemSetting/GlobalSetting.vue

@@ -21,6 +21,23 @@
     </setting-item-box>
   </collapse-item>
 
+  <collapse-item v-if="grid" name="容器">
+    <setting-item-box name="距离">
+      <setting-item name="左侧距离">
+        <n-input v-model:value="grid.left" size="small"></n-input>
+      </setting-item>
+      <setting-item name="右侧距离">
+        <n-input v-model:value="grid.right" size="small"></n-input>
+      </setting-item>
+      <setting-item name="上侧距离">
+        <n-input v-model:value="grid.top" size="small"></n-input>
+      </setting-item>
+      <setting-item name="下侧距离">
+        <n-input v-model:value="grid.bottom" size="small"></n-input>
+      </setting-item>
+    </setting-item-box>
+  </collapse-item>
+
   <collapse-item v-if="xAxis" name="X轴">
     <template #header>
       <n-switch v-model:value="xAxis.show" size="small"></n-switch>
@@ -51,6 +68,9 @@
       <setting-item name="大小">
         <n-input-number v-model:value="xAxis.axisLabel.fontSize" :min="8" size="small"></n-input-number>
       </setting-item>
+      <setting-item name="偏移量">
+        <n-input-number v-model:value="xAxis.axisLabel.rotate" :min="-90" :max="90" size="small"></n-input-number>
+      </setting-item>
     </setting-item-box>
     <setting-item-box name="轴线">
       <setting-item name="展示">
@@ -140,6 +160,9 @@
       <setting-item name="大小">
         <n-input-number v-model:value="yAxis.axisLabel.fontSize" :min="8" size="small"></n-input-number>
       </setting-item>
+      <setting-item name="偏移量">
+        <n-input-number v-model:value="yAxis.axisLabel.rotate" :min="-90" :max="90" size="small"></n-input-number>
+      </setting-item>
     </setting-item-box>
     <setting-item-box name="轴线">
       <setting-item name="展示">
@@ -289,6 +312,10 @@ const legend = computed(() => {
   return props.optionData.legend
 })
 
+const grid = computed(() => {
+  return props.optionData.grid
+})
+
 const visualMap = computed(() => {
   return props.optionData.visualMap
 })

+ 1 - 1
src/components/Pages/ChartItemSetting/GlobalSettingPosition.vue

@@ -1,5 +1,5 @@
 <template>
-  <setting-item-box name="位置">
+  <setting-item-box v-if="targetData" name="位置">
     <setting-item :name="`偏移 X:${targetData.left || 0}px`">
       <n-input-number v-model:value="targetData.left" size="small" step="10"></n-input-number>
     </setting-item>

+ 4 - 4
src/hooks/useChartDataFetch.hook.ts

@@ -59,18 +59,18 @@ export const useChartDataFetch = (
 
         const fetchFn = async () => {
           const res = await customizeHttp(toRaw(targetComponent.request), toRaw(chartEditStore.requestGlobalConfig))
-          if (res && res.data) {
+          if (res) {
             try {
               const filter = targetComponent.filter
               // eCharts 组件配合 vChart 库更新方式
               if (chartFrame === ChartFrameEnum.ECHARTS) {
                 if (vChartRef.value) {
-                  vChartRef.value.setOption({ dataset: newFunctionHandle(res.data, filter) })
+                  vChartRef.value.setOption({ dataset: newFunctionHandle(res?.data, res, filter) })
                 }
               }
               // 更新回调函数
               if (updateCallback) {
-                updateCallback(newFunctionHandle(res.data, filter))
+                updateCallback(newFunctionHandle(res?.data, res, filter))
               }
             } catch (error) {
               console.error(error)
@@ -90,7 +90,7 @@ export const useChartDataFetch = (
       }
       // eslint-disable-next-line no-empty
     } catch (error) {
-      console.log(error);
+      console.log(error)
     }
   }
 

+ 10 - 9
src/packages/components/Charts/Bars/BarCommon/config.ts

@@ -4,17 +4,21 @@ import { CreateComponentType } from '@/packages/index.d'
 import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
-
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 export const seriesItem = {
   type: 'bar',
-  barWidth: null,
+  barWidth: 15,
+  label: {
+    show: true,
+    position: 'top',
+    color: '#fff',
+    fontSize: 12
+  },
   itemStyle: {
     color: null,
-    borderRadius: 0
+    borderRadius: 2
   }
 }
-
 export const option = {
   tooltip: {
     show: true,
@@ -24,10 +28,7 @@ export const option = {
       type: 'shadow'
     }
   },
-  legend: {
-    show: true
-  },
-  xAxis: {
+xAxis: {
     show: true,
     type: 'category'
   },

+ 31 - 26
src/packages/components/Charts/Bars/BarCommon/config.vue

@@ -1,24 +1,13 @@
 <template>
-   <!-- Echarts 全局设置 --> 
+  <!-- Echarts 全局设置 -->
   <global-setting :optionData="optionData"></global-setting>
-  <CollapseItem
-    v-for="(item, index) in seriesList"
-    :key="index"
-    :name="`柱状图-${index + 1}`"
-    :expanded="true"
-  >
+  <CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`柱状图-${index + 1}`" :expanded="true">
     <SettingItemBox name="图形">
       <SettingItem name="颜色">
-        <n-color-picker
-          size="small"
-          :modes="['hex']"
-          v-model:value="item.itemStyle.color"
-        ></n-color-picker>
+        <n-color-picker size="small" :modes="['hex']" v-model:value="item.itemStyle.color"></n-color-picker>
       </SettingItem>
       <SettingItem>
-        <n-button size="small" @click="item.itemStyle.color = null">
-          恢复默认
-        </n-button>
+        <n-button size="small" @click="item.itemStyle.color = null"> 恢复默认 </n-button>
       </SettingItem>
       <SettingItem name="宽度">
         <n-input-number
@@ -30,24 +19,40 @@
         ></n-input-number>
       </SettingItem>
       <SettingItem name="圆角">
-        <n-input-number
-          v-model:value="item.itemStyle.borderRadius"
-          :min="0"
-          size="small"
-        ></n-input-number>
+        <n-input-number v-model:value="item.itemStyle.borderRadius" :min="0" size="small"></n-input-number>
       </SettingItem>
     </SettingItemBox>
+    <setting-item-box name="标签">
+      <setting-item>
+        <n-space>
+          <n-switch v-model:value="item.label.show" size="small" />
+          <n-text>展示标签</n-text>
+        </n-space>
+      </setting-item>
+      <setting-item name="大小">
+        <n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
+      </setting-item>
+      <setting-item name="颜色">
+        <n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
+      </setting-item>
+      <setting-item name="位置">
+        <n-select
+          v-model:value="item.label.position"
+          :options="[
+            { label: 'top', value: 'top' },
+            { label: 'left', value: 'left' },
+            { label: 'right', value: 'right' },
+            { label: 'bottom', value: 'bottom' }
+          ]"
+        />
+      </setting-item>
+    </setting-item-box>
   </CollapseItem>
 </template>
 
 <script setup lang="ts">
 import { PropType, computed } from 'vue'
-import {
-  GlobalSetting,
-  CollapseItem,
-  SettingItemBox,
-  SettingItem
-} from '@/components/Pages/ChartItemSetting'
+import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
 import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
 
 const props = defineProps({

+ 14 - 10
src/packages/components/Charts/Bars/BarCommon/index.vue

@@ -53,17 +53,21 @@ const option = computed(() => {
 watch(
   () => props.chartConfig.option.dataset,
   (newData: { dimensions: any }, oldData) => {
-    if (!isObject(newData) || !('dimensions' in newData)) return
-    if (newData?.dimensions.length !== oldData?.dimensions.length) {
-      const seriesArr = []
-      for (let i = 0; i < newData.dimensions.length - 1; i++) {
-        seriesArr.push(seriesItem)
+    try {
+      if (!isObject(newData) || !('dimensions' in newData)) return
+      if (Array.isArray(newData?.dimensions)) {
+        const seriesArr = []
+        for (let i = 0; i < newData.dimensions.length - 1; i++) {
+          seriesArr.push(seriesItem)
+        }
+        replaceMergeArr.value = ['series']
+        props.chartConfig.option.series = seriesArr
+        nextTick(() => {
+          replaceMergeArr.value = []
+        })
       }
-      replaceMergeArr.value = ['series']
-      props.chartConfig.option.series = seriesArr
-      nextTick(() => {
-        replaceMergeArr.value = []
-      })
+    } catch (error) {
+      console.log(error)
     }
   },
   {

+ 8 - 7
src/packages/components/Charts/Bars/BarCrossrange/config.ts

@@ -4,17 +4,21 @@ import { CreateComponentType } from '@/packages/index.d'
 import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
-
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 export const seriesItem = {
   type: 'bar',
   barWidth: null,
+  label: {
+    show: true,
+    position: 'right',
+    color: '#fff',
+    fontSize: 12
+  },
   itemStyle: {
     color: null,
     borderRadius: 0
   }
 }
-
 export const option = {
   tooltip: {
     show: true,
@@ -24,12 +28,9 @@ export const option = {
       type: 'shadow'
     }
   },
-  legend: {
-    show: true
-  },
   xAxis: {
     show: true,
-    type: 'value',
+    type: 'value'
   },
   yAxis: {
     show: true,

+ 33 - 0
src/packages/components/Charts/Bars/BarCrossrange/config.vue

@@ -26,6 +26,39 @@
        ></n-input-number>
       </SettingItem>
     </SettingItemBox>
+        <setting-item-box name="标签">
+      <setting-item>
+        <n-space>
+          <n-switch v-model:value="item.label.show" size="small" />
+          <n-text>展示标签</n-text>
+        </n-space>
+      </setting-item>
+      <setting-item name="大小">
+        <n-input-number
+          v-model:value="item.label.fontSize"
+          size="small"
+          :min="1"
+        ></n-input-number>
+      </setting-item>
+      <setting-item name="颜色">
+        <n-color-picker
+          size="small"
+          :modes="['hex']"
+          v-model:value="item.label.color"
+        ></n-color-picker>
+      </setting-item>
+      <setting-item name="位置">
+        <n-select
+          v-model:value="item.label.position"
+          :options="[
+            { label: 'top', value: 'top' },
+            { label: 'left', value: 'left' },
+            { label: 'right', value: 'right' },
+            { label: 'bottom', value: 'bottom' },
+          ]"
+        />
+      </setting-item>
+    </setting-item-box>
   </CollapseItem>
 </template>
 

+ 14 - 10
src/packages/components/Charts/Bars/BarCrossrange/index.vue

@@ -52,17 +52,21 @@ const option = computed(() => {
 watch(
   () => props.chartConfig.option.dataset,
   (newData: { dimensions: any }, oldData) => {
-    if (!isObject(newData) || !('dimensions' in newData)) return
-    if (newData?.dimensions.length !== oldData?.dimensions.length) {
-      const seriesArr = []
-      for (let i = 0; i < newData.dimensions.length - 1; i++) {
-        seriesArr.push(seriesItem)
+    try {
+      if (!isObject(newData) || !('dimensions' in newData)) return
+      if (Array.isArray(newData?.dimensions)) {
+        const seriesArr = []
+        for (let i = 0; i < newData.dimensions.length - 1; i++) {
+          seriesArr.push(seriesItem)
+        }
+        replaceMergeArr.value = ['series']
+        props.chartConfig.option.series = seriesArr
+        nextTick(() => {
+          replaceMergeArr.value = []
+        })
       }
-      replaceMergeArr.value = ['series']
-      props.chartConfig.option.series = seriesArr
-      nextTick(() => {
-        replaceMergeArr.value = []
-      })
+    } catch (error) {
+      console.log(error)
     }
   },
   {

+ 13 - 9
src/packages/components/Charts/Lines/LineCommon/config.ts

@@ -4,17 +4,24 @@ import { CreateComponentType } from '@/packages/index.d'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
-
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 export const seriesItem = {
   type: 'line',
+  label: {
+    show: true,
+    position: 'top',
+    color: '#fff',
+    fontSize: 12
+  },
+  symbolSize: 5, //设定实心点的大小
+  itemStyle: {
+    color: null,
+    borderRadius: 0
+  },
   lineStyle: {
     type: 'solid',
     width: 3,
-    itemStyle: {
-      color: null,
-      borderRadius: 0
-    }
+    color: null
   }
 }
 
@@ -26,9 +33,6 @@ export const option = {
       type: 'line'
     }
   },
-  legend: {
-    show: true
-  },
   xAxis: {
     show: true,
     type: 'category'

+ 42 - 17
src/packages/components/Charts/Lines/LineCommon/config.vue

@@ -1,13 +1,11 @@
 <template>
   <!-- Echarts 全局设置 -->
   <global-setting :optionData="optionData"></global-setting>
-  <CollapseItem
-    v-for="(item, index) in seriesList"
-    :key="index"
-    :name="`折线图-${index + 1}`"
-    :expanded="true"
-  >
+  <CollapseItem v-for="(item, index) in seriesList" :key="index" :name="`折线图-${index + 1}`" :expanded="true">
     <SettingItemBox name="线条">
+      <setting-item name="颜色">
+        <n-color-picker size="small" :modes="['hex']" v-model:value="item.lineStyle.color"></n-color-picker>
+      </setting-item>
       <SettingItem name="宽度">
         <n-input-number
           v-model:value="item.lineStyle.width"
@@ -15,16 +13,48 @@
           :max="100"
           size="small"
           placeholder="自动计算"
-       ></n-input-number>
+        ></n-input-number>
       </SettingItem>
       <SettingItem name="类型">
-        <n-select
-          v-model:value="item.lineStyle.type"
+        <n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select>
+      </SettingItem>
+    </SettingItemBox>
+    <SettingItemBox name="实心点">
+      <SettingItem name="大小">
+        <n-input-number
+          v-model:value="item.symbolSize"
+          :min="1"
+          :max="100"
           size="small"
-          :options="lineConf.lineStyle.type"
-       ></n-select>
+          placeholder="自动计算"
+        ></n-input-number>
       </SettingItem>
     </SettingItemBox>
+    <setting-item-box name="标签">
+      <setting-item>
+        <n-space>
+          <n-switch v-model:value="item.label.show" size="small" />
+          <n-text>展示标签</n-text>
+        </n-space>
+      </setting-item>
+      <setting-item name="大小">
+        <n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
+      </setting-item>
+      <setting-item name="颜色">
+        <n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
+      </setting-item>
+      <setting-item name="位置">
+        <n-select
+          v-model:value="item.label.position"
+          :options="[
+            { label: 'top', value: 'top' },
+            { label: 'left', value: 'left' },
+            { label: 'right', value: 'right' },
+            { label: 'bottom', value: 'bottom' }
+          ]"
+        />
+      </setting-item>
+    </setting-item-box>
   </CollapseItem>
 </template>
 
@@ -32,12 +62,7 @@
 import { PropType, computed } from 'vue'
 import { lineConf } from '@/packages/chartConfiguration/echarts/index'
 import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
-import {
-  GlobalSetting,
-  CollapseItem,
-  SettingItemBox,
-  SettingItem
-} from '@/components/Pages/ChartItemSetting'
+import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
 
 const props = defineProps({
   optionData: {

+ 14 - 10
src/packages/components/Charts/Lines/LineCommon/index.vue

@@ -53,17 +53,21 @@ const option = computed(() => {
 watch(
   () => props.chartConfig.option.dataset,
   (newData: { dimensions: any }, oldData) => {
-    if (!isObject(newData) || !('dimensions' in newData)) return
-    if (newData?.dimensions.length !== oldData?.dimensions.length) {
-      const seriesArr = []
-      for (let i = 0; i < newData.dimensions.length - 1; i++) {
-        seriesArr.push(seriesItem)
+    try {
+      if (!isObject(newData) || !('dimensions' in newData)) return
+      if (Array.isArray(newData?.dimensions)) {
+        const seriesArr = []
+        for (let i = 0; i < newData.dimensions.length - 1; i++) {
+          seriesArr.push(seriesItem)
+        }
+        replaceMergeArr.value = ['series']
+        props.chartConfig.option.series = seriesArr
+        nextTick(() => {
+          replaceMergeArr.value = []
+        })
       }
-      replaceMergeArr.value = ['series']
-      props.chartConfig.option.series = seriesArr
-      nextTick(() => {
-        replaceMergeArr.value = []
-      })
+    } catch (error) {
+      console.log(error)
     }
   },
   {

+ 8 - 4
src/packages/components/Charts/Lines/LineGradientSingle/config.ts

@@ -5,7 +5,7 @@ import { graphic } from 'echarts/core'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 
 const options = {
   tooltip: {
@@ -15,9 +15,6 @@ const options = {
       type: 'line'
     }
   },
-  legend: {
-    show: true
-  },
   xAxis: {
     show: true,
     type: 'category'
@@ -31,6 +28,13 @@ const options = {
     {
       type: 'line',
       smooth: false,
+      symbolSize: 5, //设定实心点的大小
+      label: {
+        show: true,
+        position: 'top',
+        color: '#fff',
+        fontSize: 12
+      },
       lineStyle: {
         type: 'solid',
         width: 3

+ 39 - 17
src/packages/components/Charts/Lines/LineGradientSingle/config.vue

@@ -1,12 +1,7 @@
 <template>
   <!-- Echarts 全局设置 -->
   <global-setting :optionData="optionData"></global-setting>
-  <CollapseItem
-    v-for="(item, index) in seriesList"
-    :key="index"
-    name="单折线面积图"
-    :expanded="true"
-  >
+  <CollapseItem v-for="(item, index) in seriesList" :key="index" name="单折线面积图" :expanded="true">
     <SettingItemBox name="线条">
       <SettingItem name="宽度">
         <n-input-number
@@ -18,13 +13,45 @@
         ></n-input-number>
       </SettingItem>
       <SettingItem name="类型">
-        <n-select
-          v-model:value="item.lineStyle.type"
+        <n-select v-model:value="item.lineStyle.type" size="small" :options="lineConf.lineStyle.type"></n-select>
+      </SettingItem>
+    </SettingItemBox>
+    <SettingItemBox name="实心点">
+      <SettingItem name="大小">
+        <n-input-number
+          v-model:value="item.symbolSize"
+          :min="1"
+          :max="100"
           size="small"
-          :options="lineConf.lineStyle.type"
-        ></n-select>
+          placeholder="自动计算"
+        ></n-input-number>
       </SettingItem>
     </SettingItemBox>
+    <setting-item-box name="标签">
+      <setting-item>
+        <n-space>
+          <n-switch v-model:value="item.label.show" size="small" />
+          <n-text>展示标签</n-text>
+        </n-space>
+      </setting-item>
+      <setting-item name="大小">
+        <n-input-number v-model:value="item.label.fontSize" size="small" :min="1"></n-input-number>
+      </setting-item>
+      <setting-item name="颜色">
+        <n-color-picker size="small" :modes="['hex']" v-model:value="item.label.color"></n-color-picker>
+      </setting-item>
+      <setting-item name="位置">
+        <n-select
+          v-model:value="item.label.position"
+          :options="[
+            { label: 'top', value: 'top' },
+            { label: 'left', value: 'left' },
+            { label: 'right', value: 'right' },
+            { label: 'bottom', value: 'bottom' }
+          ]"
+        />
+      </setting-item>
+    </setting-item-box>
   </CollapseItem>
 </template>
 
@@ -32,18 +59,13 @@
 import { PropType, computed } from 'vue'
 import { lineConf } from '@/packages/chartConfiguration/echarts/index'
 import { GlobalThemeJsonType } from '@/settings/chartThemes/index'
-import {
-  GlobalSetting,
-  CollapseItem,
-  SettingItemBox,
-  SettingItem
-} from '@/components/Pages/ChartItemSetting'
+import { GlobalSetting, CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
 
 const props = defineProps({
   optionData: {
     type: Object as PropType<GlobalThemeJsonType>,
     required: true
-  },
+  }
 })
 
 const seriesList = computed(() => {

+ 20 - 19
src/packages/components/Charts/Lines/LineGradientSingle/index.vue

@@ -43,23 +43,27 @@ const option = reactive({
 watch(
   () => chartEditStore.getEditCanvasConfig.chartThemeColor,
   (newColor: keyof typeof chartColorsSearch) => {
-    if (!isPreview()) {
-      const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
-      props.chartConfig.option.series.forEach((value: any, index: number) => {
-        value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
-          {
-            offset: 0,
-            color: themeColor[3]
-          },
-          {
-            offset: 1,
-            color: 'rgba(0,0,0, 0)'
-          }
-        ])
-      })
+    try {
+      if (!isPreview()) {
+        const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
+        props.chartConfig.option.series.forEach((value: any, index: number) => {
+          value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
+            {
+              offset: 0,
+              color: themeColor[3]
+            },
+            {
+              offset: 1,
+              color: 'rgba(0,0,0, 0)'
+            }
+          ])
+        })
+      }
+      option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
+      props.chartConfig.option = option.value
+    } catch (error) {
+      console.log(error)
     }
-    option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
-    props.chartConfig.option = option.value
   },
   {
     immediate: true
@@ -70,9 +74,6 @@ watch(
   () => props.chartConfig.option.dataset,
   () => {
     option.value = props.chartConfig.option
-  },
-  {
-    deep: false
   }
 )
 

+ 14 - 4
src/packages/components/Charts/Lines/LineGradients/config.ts

@@ -5,12 +5,9 @@ import { graphic } from 'echarts/core'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 
 const option = {
-  legend: {
-    show: true
-  },
   tooltip: {
     show: true,
     trigger: 'axis',
@@ -31,6 +28,13 @@ const option = {
     {
       type: 'line',
       smooth: false,
+      symbolSize: 5, //设定实心点的大小
+      label: {
+        show: true,
+        position: 'top',
+        color: '#fff',
+        fontSize: 12
+      },
       lineStyle: {
         width: 3,
         type: 'solid'
@@ -52,6 +56,12 @@ const option = {
     {
       type: 'line',
       smooth: false,
+      label: {
+        show: true,
+        position: 'top',
+        color: '#fff',
+        fontSize: 12
+      },
       lineStyle: {
         width: 3,
         type: 'solid'

+ 44 - 1
src/packages/components/Charts/Lines/LineGradients/config.vue

@@ -12,7 +12,6 @@
         <n-input-number
           v-model:value="item.lineStyle.width"
           :min="1"
-          :max="100"
           size="small"
           placeholder="自动计算"
         ></n-input-number>
@@ -25,6 +24,50 @@
         ></n-select>
       </SettingItem>
     </SettingItemBox>
+    <SettingItemBox name="实心点">
+      <SettingItem name="大小">
+        <n-input-number
+          v-model:value="item.symbolSize"
+          :min="1"
+          :max="100"
+          size="small"
+          placeholder="自动计算"
+        ></n-input-number>
+      </SettingItem>
+    </SettingItemBox>
+    <setting-item-box name="标签">
+      <setting-item>
+        <n-space>
+          <n-switch v-model:value="item.label.show" size="small" />
+          <n-text>展示标签</n-text>
+        </n-space>
+      </setting-item>
+      <setting-item name="大小">
+        <n-input-number
+          v-model:value="item.label.fontSize"
+          size="small"
+          :min="1"
+        ></n-input-number>
+      </setting-item>
+      <setting-item name="颜色">
+        <n-color-picker
+          size="small"
+          :modes="['hex']"
+          v-model:value="item.label.color"
+        ></n-color-picker>
+      </setting-item>
+      <setting-item name="位置">
+        <n-select
+          v-model:value="item.label.position"
+          :options="[
+            { label: 'top', value: 'top' },
+            { label: 'left', value: 'left' },
+            { label: 'right', value: 'right' },
+            { label: 'bottom', value: 'bottom' },
+          ]"
+        />
+      </setting-item>
+    </setting-item-box>
   </CollapseItem>
 </template>
 

+ 20 - 17
src/packages/components/Charts/Lines/LineGradients/index.vue

@@ -42,23 +42,27 @@ const option = reactive({
 watch(
   () => chartEditStore.getEditCanvasConfig.chartThemeColor,
   (newColor: keyof typeof chartColorsSearch) => {
-    if (!isPreview()) {
-      const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
-      props.chartConfig.option.series.forEach((value: any, index: number) => {
-        value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
-          {
-            offset: 0,
-            color: themeColor[3 + index]
-          },
-          {
-            offset: 1,
-            color: 'rgba(0,0,0, 0)'
-          }
-        ])
-      })
+    try {
+      if (!isPreview()) {
+        const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
+        props.chartConfig.option.series.forEach((value: any, index: number) => {
+          value.areaStyle.color = new graphic.LinearGradient(0, 0, 0, 1, [
+            {
+              offset: 0,
+              color: themeColor[3 + index]
+            },
+            {
+              offset: 1,
+              color: 'rgba(0,0,0, 0)'
+            }
+          ])
+        })
+      }
+      option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
+      props.chartConfig.option = option.value
+    } catch (error) {
+      console.log(error)
     }
-    option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
-    props.chartConfig.option = option.value
   },
   {
     immediate: true
@@ -71,6 +75,5 @@ watch(
     option.value = props.chartConfig.option
   }
 )
-
 const { vChartRef } = useChartDataFetch(props.chartConfig, useChartEditStore)
 </script>

+ 2 - 4
src/packages/components/Charts/Lines/LineLinearSingle/config.ts

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
 import { defaultTheme, chartColorsSearch } from '@/settings/chartThemes/index'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 
 export const option = {
   tooltip: {
@@ -14,9 +14,6 @@ export const option = {
       type: 'line'
     }
   },
-  legend: {
-    show: true
-  },
   xAxis: {
     show: true,
     type: 'category'
@@ -29,6 +26,7 @@ export const option = {
   series: [
     {
       type: 'line',
+      symbolSize: 5, //设定实心点的大小
       lineStyle: {
         type: 'solid',
         width: 3,

+ 11 - 0
src/packages/components/Charts/Lines/LineLinearSingle/config.vue

@@ -39,6 +39,17 @@
        ></n-select>
       </SettingItem>
     </SettingItemBox>
+    <SettingItemBox name="实心点">
+      <SettingItem name="大小">
+        <n-input-number
+          v-model:value="item.symbolSize"
+          :min="1"
+          :max="100"
+          size="small"
+          placeholder="自动计算"
+        ></n-input-number>
+      </SettingItem>
+    </SettingItemBox>
     <SettingItemBox name="阴影" :alone="true">
       <SettingItem name="颜色">
         <n-color-picker

+ 13 - 9
src/packages/components/Charts/Lines/LineLinearSingle/index.vue

@@ -43,17 +43,21 @@ const option = reactive({
 watch(
   () => chartEditStore.getEditCanvasConfig.chartThemeColor,
   (newColor: keyof typeof chartColorsSearch) => {
-    if (!isPreview()) {
-      const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
-      props.chartConfig.option.series.forEach((value: any) => {
-        value.lineStyle.shadowColor = themeColor[2]
-        value.lineStyle.color.colorStops.forEach((v: { color: string }, i: number) => {
-          v.color = themeColor[i]
+    try {
+      if (!isPreview()) {
+        const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
+        props.chartConfig.option.series.forEach((value: any) => {
+          value.lineStyle.shadowColor = themeColor[2]
+          value.lineStyle.color.colorStops.forEach((v: { color: string }, i: number) => {
+            v.color = themeColor[i]
+          })
         })
-      })
+      }
+      option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
+      props.chartConfig.option = option.value
+    } catch (error) {
+      console.log(error)
     }
-    option.value = mergeTheme(props.chartConfig.option, props.themeSetting, includes)
-    props.chartConfig.option = option.value
   },
   {
     immediate: true

+ 23 - 15
src/packages/components/Charts/Maps/MapBase/index.vue

@@ -64,9 +64,9 @@ registerMap(props.chartConfig.option.mapRegion.adcode, { geoJSON: {} as any, spe
 // 进行更换初始化地图 如果为china 单独处理
 const registerMapInitAsync = async () => {
   await nextTick()
-  if (props.chartConfig.option.mapRegion.adcode!="china") {
+  if (props.chartConfig.option.mapRegion.adcode != 'china') {
     await getGeojson(props.chartConfig.option.mapRegion.adcode)
-  }else{
+  } else {
     await hainanLandsHandle(props.chartConfig.option.mapRegion.showHainanIsLands)
   }
   vEchartsSetOption()
@@ -90,12 +90,12 @@ const dataSetHandle = async (dataset: any) => {
   isPreview() && vEchartsSetOption()
 }
 // 处理海南群岛
-const hainanLandsHandle=async(newData:boolean)=>{
+const hainanLandsHandle = async (newData: boolean) => {
   if (newData) {
-      await getGeojson('china')
-    } else {
-      registerMap('china', { geoJSON: mapJsonWithoutHainanIsLands as any, specialAreas: {} })
-    }
+    await getGeojson('china')
+  } else {
+    registerMap('china', { geoJSON: mapJsonWithoutHainanIsLands as any, specialAreas: {} })
+  }
 }
 //监听 dataset 数据发生变化
 watch(
@@ -113,8 +113,12 @@ watch(
 watch(
   () => props.chartConfig.option.mapRegion.showHainanIsLands,
   async newData => {
-    await hainanLandsHandle(newData)
-    vEchartsSetOption()
+    try {
+      await hainanLandsHandle(newData)
+      vEchartsSetOption()
+    } catch (error) {
+      console.log(error)
+    }
   },
   {
     deep: false
@@ -125,12 +129,16 @@ watch(
 watch(
   () => props.chartConfig.option.mapRegion.adcode,
   async newData => {
-    await getGeojson(newData)
-    props.chartConfig.option.geo.map = newData
-    props.chartConfig.option.series.forEach((item: any) => {
-      if (item.type === 'map') item.map = newData
-    })
-    vEchartsSetOption()
+    try {
+      await getGeojson(newData)
+      props.chartConfig.option.geo.map = newData
+      props.chartConfig.option.series.forEach((item: any) => {
+        if (item.type === 'map') item.map = newData
+      })
+      vEchartsSetOption()
+    } catch (error) {
+      console.log(error)
+    }
   },
   {
     deep: false

+ 1 - 1
src/packages/components/Charts/Mores/Heatmap/config.ts

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
 import { cloneDeep } from 'lodash'
 import dataJson from './data.json'
 
-export const includes = ['xAxis', 'yAxis']
+export const includes = ['xAxis', 'yAxis', 'grid']
 
 export const option = {
   dataset: { ...dataJson },

+ 5 - 1
src/packages/components/Charts/Mores/Heatmap/index.vue

@@ -75,7 +75,11 @@ const dataSetHandle = (dataset: typeof dataJson) => {
 watch(
   () => props.chartConfig.option.dataset,
   newData => {
-    dataSetHandle(newData)
+    try {
+      dataSetHandle(newData)
+    } catch (error) {
+      console.log(error)
+    }
   },
   {
     deep: false

+ 5 - 1
src/packages/components/Charts/Mores/Process/index.vue

@@ -57,7 +57,11 @@ const option = shallowReactive({
 watch(
   () => props.chartConfig.option.dataset,
   (newData: any) => {
-    option.dataset = toNumber(newData, 2)
+    try {
+      option.dataset = toNumber(newData, 2)
+    } catch (error) {
+      console.log(error)
+    }
   },
   {
     deep: false

+ 5 - 1
src/packages/components/Charts/Mores/Radar/index.vue

@@ -57,7 +57,11 @@ const dataSetHandle = (dataset: typeof dataJson) => {
 watch(
   () => props.chartConfig.option.dataset,
   newData => {
-    dataSetHandle(newData)
+    try {
+      dataSetHandle(newData)
+    } catch (error) {
+      console.log(error)
+    }
   },
   {
     deep: false

+ 6 - 2
src/packages/components/Charts/Mores/TreeMap/index.vue

@@ -49,8 +49,12 @@ const dataSetHandle = (dataset: typeof dataJson) => {
 watch(
   () => props.chartConfig.option.dataset,
   newData => {
-    if(!isArray(newData)) return
-    dataSetHandle(newData)
+    try {
+      if (!isArray(newData)) return
+      dataSetHandle(newData)
+    } catch (error) {
+      console.log(error)
+    }
   },
   {
     deep: false

+ 21 - 17
src/packages/components/Charts/Mores/WaterPolo/index.vue

@@ -42,23 +42,27 @@ const option = reactive({
 watch(
   () => chartEditStore.getEditCanvasConfig.chartThemeColor,
   (newColor: keyof typeof chartColorsSearch) => {
-    if (!isPreview()) {
-      const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
-      // 背景颜色
-      props.chartConfig.option.series[0].backgroundStyle.color = themeColor[2]
-      // 水球颜色
-      props.chartConfig.option.series[0].color[0].colorStops = [
-        {
-          offset: 0,
-          color: themeColor[0]
-        },
-        {
-          offset: 1,
-          color: themeColor[1]
-        }
-      ]
+    try {
+      if (!isPreview()) {
+        const themeColor = chartColorsSearch[newColor] || chartColorsSearch[defaultTheme]
+        // 背景颜色
+        props.chartConfig.option.series[0].backgroundStyle.color = themeColor[2]
+        // 水球颜色
+        props.chartConfig.option.series[0].color[0].colorStops = [
+          {
+            offset: 0,
+            color: themeColor[0]
+          },
+          {
+            offset: 1,
+            color: themeColor[1]
+          }
+        ]
+      }
+      option.value = props.chartConfig.option
+    } catch (error) {
+      console.log(error)
     }
-    option.value = props.chartConfig.option
   },
   {
     immediate: true
@@ -75,7 +79,7 @@ const dataHandle = (newData: number | string) => {
 watch(
   () => props.chartConfig.option.dataset,
   newData => {
-    if(!isString(newData) && !isNumber(newData)) return
+    if (!isString(newData) && !isNumber(newData)) return
     props.chartConfig.option.series[0].data = [dataHandle(newData)]
     option.value = props.chartConfig.option
   },

+ 7 - 1
src/packages/components/Charts/Pies/PieCircle/index.vue

@@ -48,7 +48,13 @@ const dataHandle = (newData: any) => {
 // 配置时
 watch(
   () => props.chartConfig.option.dataset,
-  newData => dataHandle(newData),
+  newData => {
+    try {
+      dataHandle(newData)
+    } catch (error) {
+      console.log(error)
+    }
+  },
   {
     immediate: true,
     deep: false

+ 13 - 9
src/packages/components/Charts/Pies/PieCommon/index.vue

@@ -39,15 +39,19 @@ const option = computed(() => {
 watch(
   () => props.chartConfig.option.type,
   newData => {
-    if (newData === 'nomal') {
-      props.chartConfig.option.series[0].radius = '70%'
-      props.chartConfig.option.series[0].roseType = false
-    } else if (newData === 'ring') {
-      props.chartConfig.option.series[0].radius = ['40%', '65%']
-      props.chartConfig.option.series[0].roseType = false
-    } else {
-      props.chartConfig.option.series[0].radius = '70%'
-      props.chartConfig.option.series[0].roseType = true
+    try {
+      if (newData === 'nomal') {
+        props.chartConfig.option.series[0].radius = '70%'
+        props.chartConfig.option.series[0].roseType = false
+      } else if (newData === 'ring') {
+        props.chartConfig.option.series[0].radius = ['40%', '65%']
+        props.chartConfig.option.series[0].roseType = false
+      } else {
+        props.chartConfig.option.series[0].radius = '70%'
+        props.chartConfig.option.series[0].roseType = true
+      }
+    } catch (error) {
+      console.log(error)
     }
   },
   { deep: false, immediate: true }

+ 1 - 1
src/packages/components/Charts/Scatters/ScatterCommon/config.ts

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
 import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 
 export const seriesItem = {
   type: 'scatter',

+ 15 - 11
src/packages/components/Charts/Scatters/ScatterCommon/index.vue

@@ -69,17 +69,21 @@ const option = computed(() => {
 watch(
   () => props.chartConfig.option.dataset,
   (newData, oldData) => {
-    if (!isArray(newData)) return
-    if (newData?.length !== oldData?.length) {
-      replaceMergeArr.value = ['series']
-      // eslint-disable-next-line vue/no-mutating-props
-      props.chartConfig.option.series = newData.map((item: { dimensions: any[] }, index: number) => ({
-        ...seriesItem,
-        datasetIndex: index
-      }))
-      nextTick(() => {
-        replaceMergeArr.value = []
-      })
+    try {
+      if (!isArray(newData)) return
+      if (Array.isArray(newData)) {
+        replaceMergeArr.value = ['series']
+        // eslint-disable-next-line vue/no-mutating-props
+        props.chartConfig.option.series = newData.map((item: { dimensions: any[] }, index: number) => ({
+          ...seriesItem,
+          datasetIndex: index
+        }))
+        nextTick(() => {
+          replaceMergeArr.value = []
+        })
+      }
+    } catch (error) {
+      console.log(error)
     }
   },
   {

+ 1 - 1
src/packages/components/Charts/Scatters/ScatterLogarithmicRegression/config.ts

@@ -4,7 +4,7 @@ import { CreateComponentType } from '@/packages/index.d'
 import cloneDeep from 'lodash/cloneDeep'
 import dataJson from './data.json'
 
-export const includes = ['legend', 'xAxis', 'yAxis']
+export const includes = ['legend', 'xAxis', 'yAxis', 'grid']
 
 export const option = {
   dataset: dataJson,

+ 8 - 4
src/packages/components/Decorates/Mores/CountDown/index.vue

@@ -133,10 +133,14 @@ const renderCountdown: CountdownProps['render'] = ({ hours, minutes, seconds })
 }
 
 const updateTotalDuration = () => {
-  countdownActive.value = false
-  totalDuration.value = useEndDate.value ? endDate.value - new Date().getTime() : dataset.value * 1000
-  countdownRef.value?.reset && countdownRef.value?.reset()
-  countdownActive.value = true
+  try {
+    countdownActive.value = false
+    totalDuration.value = useEndDate.value ? endDate.value - new Date().getTime() : dataset.value * 1000
+    countdownRef.value?.reset && countdownRef.value?.reset()
+    countdownActive.value = true
+  } catch (error) {
+    console.log(error)
+  }
 }
 
 watch(

+ 5 - 1
src/packages/components/Decorates/Mores/FlipperNumber/index.vue

@@ -60,7 +60,11 @@ const updateDatasetHandler = (newVal: string | number) => {
 watch(
   () => props.chartConfig.option,
   newVal => {
-    updateDatasetHandler((newVal as OptionType).dataset)
+    try {
+      updateDatasetHandler((newVal as OptionType).dataset)
+    } catch (error) {
+      console.log(error)
+    }
   },
   {
     immediate: true,

+ 47 - 46
src/packages/components/Decorates/Mores/TimeCommon/index.vue

@@ -11,23 +11,23 @@
 </template>
 
 <script setup lang="ts">
-import { PropType, toRefs, ref, reactive, watch, onMounted, onUnmounted } from "vue";
-import { CreateComponentType } from "@/packages/index.d";
-import { useChartEditStore } from "@/store/modules/chartEditStore/chartEditStore";
-import { useChartDataFetch } from "@/hooks";
+import { PropType, toRefs, ref, reactive, watch, onMounted, onUnmounted } from 'vue'
+import { CreateComponentType } from '@/packages/index.d'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { useChartDataFetch } from '@/hooks'
 
 const props = defineProps({
   chartConfig: {
     type: Object as PropType<CreateComponentType>,
-    required: true,
-  },
-});
-let yearMonthDay = ref("2021-2-3");
-let nowData = ref("08:00:00");
-let newData = ref("2021-2-3 08:00:00");
-let boxShadow = ref("none");
+    required: true
+  }
+})
+let yearMonthDay = ref('2021-2-3')
+let nowData = ref('08:00:00')
+let newData = ref('2021-2-3 08:00:00')
+let boxShadow = ref('none')
 
-const { w, h } = toRefs(props.chartConfig.attr);
+const { w, h } = toRefs(props.chartConfig.attr)
 
 let {
   timeColor,
@@ -39,54 +39,55 @@ let {
   hShadow,
   vShadow,
   blurShadow,
-  colorShadow,
-} = toRefs(props.chartConfig.option);
+  colorShadow
+} = toRefs(props.chartConfig.option)
 
 watch(
   props.chartConfig.option,
   () => {
-    if (props.chartConfig.option.showShadow) {
-      boxShadow.value = `${props.chartConfig.option.hShadow}px ${props.chartConfig.option.vShadow}px ${props.chartConfig.option.blurShadow}px ${props.chartConfig.option.colorShadow}`;
-    } else {
-      boxShadow.value = "none";
+    try {
+      if (props.chartConfig.option.showShadow) {
+        boxShadow.value = `${props.chartConfig.option.hShadow}px ${props.chartConfig.option.vShadow}px ${props.chartConfig.option.blurShadow}px ${props.chartConfig.option.colorShadow}`
+      } else {
+        boxShadow.value = 'none'
+      }
+    } catch (error) {
+      console.log(error)
     }
   },
   {
-    immediate: true,
+    immediate: true
   }
-);
+)
 onMounted(() => {
   const timer = setInterval(() => {
-    var datetime = new Date();
-    var year = datetime.getFullYear();
-    var month =
-      datetime.getMonth() + 1 < 10
-        ? "0" + (datetime.getMonth() + 1)
-        : datetime.getMonth() + 1;
-    var date = datetime.getDate() < 10 ? "0" + datetime.getDate() : datetime.getDate();
-    var hh = datetime.getHours(); // 时
-    var mm = datetime.getMinutes(); // 分
-    var ss = datetime.getSeconds(); // 分
-    let time = "";
-    if (hh < 10) time += "0";
-    time += hh + ":";
-    if (mm < 10) time += "0";
-    time += mm + ":";
-    if (ss < 10) time += "0";
-    time += ss;
-    yearMonthDay.value = `${year}-${month}-${date}`;
-    nowData.value = time;
-    newData.value = yearMonthDay.value + " " + nowData.value;
-  }, 500);
-});
+    var datetime = new Date()
+    var year = datetime.getFullYear()
+    var month = datetime.getMonth() + 1 < 10 ? '0' + (datetime.getMonth() + 1) : datetime.getMonth() + 1
+    var date = datetime.getDate() < 10 ? '0' + datetime.getDate() : datetime.getDate()
+    var hh = datetime.getHours() // 时
+    var mm = datetime.getMinutes() // 分
+    var ss = datetime.getSeconds() // 分
+    let time = ''
+    if (hh < 10) time += '0'
+    time += hh + ':'
+    if (mm < 10) time += '0'
+    time += mm + ':'
+    if (ss < 10) time += '0'
+    time += ss
+    yearMonthDay.value = `${year}-${month}-${date}`
+    nowData.value = time
+    newData.value = yearMonthDay.value + ' ' + nowData.value
+  }, 500)
+})
 onUnmounted(() => {
-  clearInterval();
-});
-useChartDataFetch(props.chartConfig, useChartEditStore);
+  clearInterval()
+})
+useChartDataFetch(props.chartConfig, useChartEditStore)
 </script>
 
 <style lang="scss" scoped>
-@include go("decorates-number") {
+@include go('decorates-number') {
   text-align: center;
 }
 </style>

+ 236 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/Utils/arc.ts

@@ -0,0 +1,236 @@
+// eslint-disable-next-line @typescript-eslint/ban-ts-comment
+// @ts-nocheck
+import {
+  ArcCurve,
+  BufferAttribute,
+  BufferGeometry,
+  Color,
+  Line,
+  LineBasicMaterial,
+  Points,
+  PointsMaterial,
+  Quaternion,
+  Vector3
+} from 'three'
+import { lon2xyz } from './common'
+
+/*
+ * 绘制一条圆弧飞线
+ * 5个参数含义:( 飞线圆弧轨迹半径, 开始角度, 结束角度)
+ */
+function createFlyLine(radius, startAngle, endAngle, color) {
+  const geometry = new BufferGeometry() //声明一个几何体对象BufferGeometry
+  //  ArcCurve创建圆弧曲线
+  const arc = new ArcCurve(0, 0, radius, startAngle, endAngle, false)
+  //getSpacedPoints是基类Curve的方法,返回一个vector2对象作为元素组成的数组
+  const pointsArr = arc.getSpacedPoints(100) //分段数80,返回81个顶点
+  geometry.setFromPoints(pointsArr) // setFromPoints方法从pointsArr中提取数据改变几何体的顶点属性vertices
+  // 每个顶点对应一个百分比数据attributes.percent 用于控制点的渲染大小
+  const percentArr = [] //attributes.percent的数据
+  for (let i = 0; i < pointsArr.length; i++) {
+    percentArr.push(i / pointsArr.length)
+  }
+  const percentAttribue = new BufferAttribute(new Float32Array(percentArr), 1)
+  // 通过顶点数据percent点模型从大到小变化,产生小蝌蚪形状飞线
+  geometry.attributes.percent = percentAttribue
+  // 批量计算所有顶点颜色数据
+  const colorArr = []
+  for (let i = 0; i < pointsArr.length; i++) {
+    const color1 = new Color(0xec8f43) //轨迹线颜色 青色
+    const color2 = new Color(0xf3ae76) //黄色
+    const color = color1.lerp(color2, i / pointsArr.length)
+    colorArr.push(color.r, color.g, color.b)
+  }
+  // 设置几何体顶点颜色数据
+  geometry.attributes.color = new BufferAttribute(new Float32Array(colorArr), 3)
+  const size = 1.3
+  // 点模型渲染几何体每个顶点
+  const material = new PointsMaterial({
+    size, //点大小
+    // vertexColors: VertexColors, //使用顶点颜色渲染
+    transparent: true,
+    depthWrite: false
+  })
+  // 修改点材质的着色器源码(注意:不同版本细节可能会稍微会有区别,不过整体思路是一样的)
+  material.onBeforeCompile = function (shader) {
+    // 顶点着色器中声明一个attribute变量:百分比
+    shader.vertexShader = shader.vertexShader.replace(
+      'void main() {',
+      [
+        'attribute float percent;', //顶点大小百分比变量,控制点渲染大小
+        'void main() {'
+      ].join('\n') // .join()把数组元素合成字符串
+    )
+    // 调整点渲染大小计算方式
+    shader.vertexShader = shader.vertexShader.replace(
+      'gl_PointSize = size;',
+      ['gl_PointSize = percent * size;'].join('\n') // .join()把数组元素合成字符串
+    )
+  }
+  const FlyLine = new Points(geometry, material)
+  material.color = new Color(color)
+  FlyLine.name = '飞行线'
+
+  return FlyLine
+}
+
+/**输入地球上任意两点的经纬度坐标,通过函数flyArc可以绘制一个飞线圆弧轨迹
+ * lon1,lat1:轨迹线起点经纬度坐标
+ * lon2,lat2:轨迹线结束点经纬度坐标
+ */
+function flyArc(radius, lon1, lat1, lon2, lat2, options) {
+  const sphereCoord1 = lon2xyz(radius, lon1, lat1) //经纬度坐标转球面坐标
+  // startSphereCoord:轨迹线起点球面坐标
+  const startSphereCoord = new Vector3(sphereCoord1.x, sphereCoord1.y, sphereCoord1.z)
+  const sphereCoord2 = lon2xyz(radius, lon2, lat2)
+  // startSphereCoord:轨迹线结束点球面坐标
+  const endSphereCoord = new Vector3(sphereCoord2.x, sphereCoord2.y, sphereCoord2.z)
+
+  //计算绘制圆弧需要的关于y轴对称的起点、结束点和旋转四元数
+  const startEndQua = _3Dto2D(startSphereCoord, endSphereCoord)
+  // 调用arcXOY函数绘制一条圆弧飞线轨迹
+  const arcline = arcXOY(radius, startEndQua.startPoint, startEndQua.endPoint, options)
+  arcline.quaternion.multiply(startEndQua.quaternion)
+  return arcline
+}
+/*
+ * 把3D球面上任意的两个飞线起点和结束点绕球心旋转到到XOY平面上,
+ * 同时保持关于y轴对称,借助旋转得到的新起点和新结束点绘制
+ * 一个圆弧,最后把绘制的圆弧反向旋转到原来的起点和结束点即可
+ */
+function _3Dto2D(startSphere, endSphere) {
+  /*计算第一次旋转的四元数:表示从一个平面如何旋转到另一个平面*/
+  const origin = new Vector3(0, 0, 0) //球心坐标
+  const startDir = startSphere.clone().sub(origin) //飞线起点与球心构成方向向量
+  const endDir = endSphere.clone().sub(origin) //飞线结束点与球心构成方向向量
+  // dir1和dir2构成一个三角形,.cross()叉乘计算该三角形法线normal
+  const normal = startDir.clone().cross(endDir).normalize()
+  const xoyNormal = new Vector3(0, 0, 1) //XOY平面的法线
+  //.setFromUnitVectors()计算从normal向量旋转达到xoyNormal向量所需要的四元数
+  // quaternion表示把球面飞线旋转到XOY平面上需要的四元数
+  const quaternion3D_XOY = new Quaternion().setFromUnitVectors(normal, xoyNormal)
+  /*第一次旋转:飞线起点、结束点从3D空间第一次旋转到XOY平面*/
+  const startSphereXOY = startSphere.clone().applyQuaternion(quaternion3D_XOY)
+  const endSphereXOY = endSphere.clone().applyQuaternion(quaternion3D_XOY)
+
+  /*计算第二次旋转的四元数*/
+  // middleV3:startSphereXOY和endSphereXOY的中点
+  const middleV3 = startSphereXOY.clone().add(endSphereXOY).multiplyScalar(0.5)
+  const midDir = middleV3.clone().sub(origin).normalize() // 旋转前向量midDir,中点middleV3和球心构成的方向向量
+  const yDir = new Vector3(0, 1, 0) // 旋转后向量yDir,即y轴
+  // .setFromUnitVectors()计算从midDir向量旋转达到yDir向量所需要的四元数
+  // quaternion2表示让第一次旋转到XOY平面的起点和结束点关于y轴对称需要的四元数
+  const quaternionXOY_Y = new Quaternion().setFromUnitVectors(midDir, yDir)
+
+  /*第二次旋转:使旋转到XOY平面的点再次旋转,实现关于Y轴对称*/
+  const startSpherXOY_Y = startSphereXOY.clone().applyQuaternion(quaternionXOY_Y)
+  const endSphereXOY_Y = endSphereXOY.clone().applyQuaternion(quaternionXOY_Y)
+
+  /**一个四元数表示一个旋转过程
+   *.invert()方法表示四元数的逆,简单说就是把旋转过程倒过来
+   * 两次旋转的四元数执行.invert()求逆,然后执行.multiply()相乘
+   *新版本.invert()对应旧版本.invert()
+   */
+  const quaternionInverse = quaternion3D_XOY.clone().invert().multiply(quaternionXOY_Y.clone().invert())
+  return {
+    // 返回两次旋转四元数的逆四元数
+    quaternion: quaternionInverse,
+    // 范围两次旋转后在XOY平面上关于y轴对称的圆弧起点和结束点坐标
+    startPoint: startSpherXOY_Y,
+    endPoint: endSphereXOY_Y
+  }
+}
+/**通过函数arcXOY()可以在XOY平面上绘制一个关于y轴对称的圆弧曲线
+ * startPoint, endPoint:表示圆弧曲线的起点和结束点坐标值,起点和结束点关于y轴对称
+ * 同时在圆弧轨迹的基础上绘制一段飞线*/
+function arcXOY(radius, startPoint, endPoint, options) {
+  // 计算两点的中点
+  const middleV3 = new Vector3().addVectors(startPoint, endPoint).multiplyScalar(0.5)
+  // 弦垂线的方向dir(弦的中点和圆心构成的向量)
+  const dir = middleV3.clone().normalize()
+  // 计算球面飞线的起点、结束点和球心构成夹角的弧度值
+  const earthRadianAngle = radianAOB(startPoint, endPoint, new Vector3(0, 0, 0))
+  /*设置飞线轨迹圆弧的中间点坐标
+  弧度值 * radius * 0.2:表示飞线轨迹圆弧顶部距离地球球面的距离
+  起点、结束点相聚越远,构成的弧线顶部距离球面越高*/
+  const arcTopCoord = dir.multiplyScalar(radius + earthRadianAngle * radius * 0.2) // 黄色飞行线的高度
+  //求三个点的外接圆圆心(飞线圆弧轨迹的圆心坐标)
+  const flyArcCenter = threePointCenter(startPoint, endPoint, arcTopCoord)
+  // 飞线圆弧轨迹半径flyArcR
+  const flyArcR = Math.abs(flyArcCenter.y - arcTopCoord.y)
+  /*坐标原点和飞线起点构成直线和y轴负半轴夹角弧度值
+  参数分别是:飞线圆弧起点、y轴负半轴上一点、飞线圆弧圆心*/
+  const flyRadianAngle = radianAOB(startPoint, new Vector3(0, -1, 0), flyArcCenter)
+  const startAngle = -Math.PI / 2 + flyRadianAngle //飞线圆弧开始角度
+  const endAngle = Math.PI - startAngle //飞线圆弧结束角度
+  // 调用圆弧线模型的绘制函数
+  const arcline = circleLine(flyArcCenter.x, flyArcCenter.y, flyArcR, startAngle, endAngle, options.color)
+  // const arcline = new  Group();// 不绘制轨迹线,使用 Group替换circleLine()即可
+  arcline.center = flyArcCenter //飞线圆弧自定一个属性表示飞线圆弧的圆心
+  arcline.topCoord = arcTopCoord //飞线圆弧自定一个属性表示飞线圆弧中间也就是顶部坐标
+
+  // const flyAngle = Math.PI/ 10; //飞线圆弧固定弧度
+  const flyAngle = (endAngle - startAngle) / 7 //飞线圆弧的弧度和轨迹线弧度相关
+  // 绘制一段飞线,圆心做坐标原点
+  const flyLine = createFlyLine(flyArcR, startAngle, startAngle + flyAngle, options.flyLineColor)
+  flyLine.position.y = flyArcCenter.y //平移飞线圆弧和飞线轨迹圆弧重合
+  //飞线段flyLine作为飞线轨迹arcLine子对象,继承飞线轨迹平移旋转等变换
+  arcline.add(flyLine)
+  //飞线段运动范围startAngle~flyEndAngle
+  flyLine.flyEndAngle = endAngle - startAngle - flyAngle
+  flyLine.startAngle = startAngle
+  // arcline.flyEndAngle:飞线段当前角度位置,这里设置了一个随机值用于演示
+  flyLine.AngleZ = arcline.flyEndAngle * Math.random()
+  // flyLine.rotation.z = arcline.AngleZ;
+  // arcline.flyLine指向飞线段,便于设置动画是访问飞线段
+  arcline.userData['flyLine'] = flyLine
+
+  return arcline
+}
+/*计算球面上两点和球心构成夹角的弧度值
+参数point1, point2:表示地球球面上两点坐标Vector3
+计算A、B两点和顶点O构成的AOB夹角弧度值*/
+function radianAOB(A, B, O) {
+  // dir1、dir2:球面上两个点和球心构成的方向向量
+  const dir1 = A.clone().sub(O).normalize()
+  const dir2 = B.clone().sub(O).normalize()
+  //点乘.dot()计算夹角余弦值
+  const cosAngle = dir1.clone().dot(dir2)
+  const radianAngle = Math.acos(cosAngle) //余弦值转夹角弧度值,通过余弦值可以计算夹角范围是0~180度
+  return radianAngle
+}
+/*绘制一条圆弧曲线模型Line
+5个参数含义:(圆心横坐标, 圆心纵坐标, 飞线圆弧轨迹半径, 开始角度, 结束角度)*/
+function circleLine(x, y, r, startAngle, endAngle, color) {
+  const geometry = new BufferGeometry() //声明一个几何体对象Geometry
+  //  ArcCurve创建圆弧曲线
+  const arc = new ArcCurve(x, y, r, startAngle, endAngle, false)
+  //getSpacedPoints是基类Curve的方法,返回一个vector2对象作为元素组成的数组
+  const points = arc.getSpacedPoints(80) //分段数50,返回51个顶点
+  geometry.setFromPoints(points) // setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
+  const material = new LineBasicMaterial({
+    color: color || 0xd18547
+  }) //线条材质
+  const line = new Line(geometry, material) //线条模型对象
+  return line
+}
+//求三个点的外接圆圆心,p1, p2, p3表示三个点的坐标Vector3。
+function threePointCenter(p1, p2, p3) {
+  const L1 = p1.lengthSq() //p1到坐标原点距离的平方
+  const L2 = p2.lengthSq()
+  const L3 = p3.lengthSq()
+  const x1 = p1.x,
+    y1 = p1.y,
+    x2 = p2.x,
+    y2 = p2.y,
+    x3 = p3.x,
+    y3 = p3.y
+  const S = x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2
+  const x = (L2 * y3 + L1 * y2 + L3 * y1 - L2 * y1 - L3 * y2 - L1 * y3) / S / 2
+  const y = (L3 * x2 + L2 * x1 + L1 * x3 - L1 * x2 - L2 * x3 - L3 * x1) / S / 2
+  // 三点外接圆圆心坐标
+  const center = new Vector3(x, y, 0)
+  return center
+}
+
+export { arcXOY, flyArc }

+ 137 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/Utils/common.ts

@@ -0,0 +1,137 @@
+import {
+  CatmullRomCurve3,
+  DoubleSide,
+  Group,
+  Mesh,
+  MeshBasicMaterial,
+  PlaneGeometry,
+  Texture,
+  TubeGeometry,
+  Vector3
+} from 'three'
+import { punctuation } from '../world/Earth'
+
+/**
+ * 经纬度坐标转球面坐标
+ * @param {地球半径} R
+ * @param {经度(角度值)} longitude
+ * @param {维度(角度值)} latitude
+ */
+export const lon2xyz = (R: number, longitude: number, latitude: number): Vector3 => {
+  let lon = (longitude * Math.PI) / 180 // 转弧度值
+  const lat = (latitude * Math.PI) / 180 // 转弧度值
+  lon = -lon // js坐标系z坐标轴对应经度-90度,而不是90度
+
+  // 经纬度坐标转球面坐标计算公式
+  const x = R * Math.cos(lat) * Math.cos(lon)
+  const y = R * Math.sin(lat)
+  const z = R * Math.cos(lat) * Math.sin(lon)
+  // 返回球面坐标
+  return new Vector3(x, y, z)
+}
+
+// 创建波动光圈
+export const createWaveMesh = (options: { radius: number; lon: number; lat: number; textures: any }) => {
+  const geometry = new PlaneGeometry(1, 1) //默认在XOY平面上
+  const texture = options.textures.aperture
+
+  const material = new MeshBasicMaterial({
+    color: 0xe99f68,
+    map: texture,
+    transparent: true, //使用背景透明的png贴图,注意开启透明计算
+    opacity: 1.0,
+    depthWrite: false //禁止写入深度缓冲区数据
+  })
+  const mesh = new Mesh(geometry, material)
+  // 经纬度转球面坐标
+  const coord = lon2xyz(options.radius * 1.001, options.lon, options.lat)
+  const size = options.radius * 0.12 //矩形平面Mesh的尺寸
+  mesh.scale.set(size, size, size) //设置mesh大小
+  mesh.userData['size'] = size //自顶一个属性,表示mesh静态大小
+  mesh.userData['scale'] = Math.random() * 1.0 //自定义属性._s表示mesh在原始大小基础上放大倍数  光圈在原来mesh.size基础上1~2倍之间变化
+  mesh.position.set(coord.x, coord.y, coord.z)
+  const coordVec3 = new Vector3(coord.x, coord.y, coord.z).normalize()
+  const meshNormal = new Vector3(0, 0, 1)
+  mesh.quaternion.setFromUnitVectors(meshNormal, coordVec3)
+  return mesh
+}
+
+// 创建柱状
+export const createLightPillar = (options: {
+  radius: number
+  lon: number
+  lat: number
+  index: number
+  textures: Record<string, Texture>
+  punctuation: punctuation
+}) => {
+  const height = options.radius * 0.3
+  const geometry = new PlaneGeometry(options.radius * 0.05, height)
+  geometry.rotateX(Math.PI / 2)
+  geometry.translate(0, 0, height / 2)
+  const material = new MeshBasicMaterial({
+    map: options.textures.light_column,
+    color: options.index == 0 ? options.punctuation.lightColumn.startColor : options.punctuation.lightColumn.endColor,
+    transparent: true,
+    side: DoubleSide,
+    depthWrite: false //是否对深度缓冲区有任何的影响
+  })
+  const mesh = new Mesh(geometry, material)
+  const group = new Group()
+  // 两个光柱交叉叠加
+  group.add(mesh, mesh.clone().rotateZ(Math.PI / 2)) //几何体绕x轴旋转了,所以mesh旋转轴变为z
+  // 经纬度转球面坐标
+  const SphereCoord = lon2xyz(options.radius, options.lon, options.lat) //SphereCoord球面坐标
+  group.position.set(SphereCoord.x, SphereCoord.y, SphereCoord.z) //设置mesh位置
+  const coordVec3 = new Vector3(SphereCoord.x, SphereCoord.y, SphereCoord.z).normalize()
+  const meshNormal = new Vector3(0, 0, 1)
+  group.quaternion.setFromUnitVectors(meshNormal, coordVec3)
+  return group
+}
+
+// 光柱底座矩形平面
+export const createPointMesh = (options: { radius: number; lon: number; lat: number; material: MeshBasicMaterial }) => {
+  const geometry = new PlaneGeometry(1, 1) //默认在XOY平面上
+  const mesh = new Mesh(geometry, options.material)
+  // 经纬度转球面坐标
+  const coord = lon2xyz(options.radius * 1.001, options.lon, options.lat)
+  const size = options.radius * 0.05 // 矩形平面Mesh的尺寸
+  mesh.scale.set(size, size, size) // 设置mesh大小
+
+  // 设置mesh位置
+  mesh.position.set(coord.x, coord.y, coord.z)
+  const coordVec3 = new Vector3(coord.x, coord.y, coord.z).normalize()
+  const meshNormal = new Vector3(0, 0, 1)
+  mesh.quaternion.setFromUnitVectors(meshNormal, coordVec3)
+  return mesh
+}
+
+// 获取点
+export const getCirclePoints = (option: any) => {
+  const list = []
+  for (let j = 0; j < 2 * Math.PI - 0.1; j += (2 * Math.PI) / (option.number || 100)) {
+    list.push([
+      parseFloat((Math.cos(j) * (option.radius || 10)).toFixed(2)),
+      0,
+      parseFloat((Math.sin(j) * (option.radius || 10)).toFixed(2))
+    ])
+  }
+  if (option.closed) list.push(list[0])
+  return list
+}
+
+// 创建线
+
+/**
+ * 创建动态的线
+ */
+export const createAnimateLine = (option: any) => {
+  // 由多个点数组构成的曲线 通常用于道路
+  const l: Array<any> = []
+  option.pointList.forEach((e: Array<any>) => l.push(new Vector3(e[0], e[1], e[2])))
+  const curve = new CatmullRomCurve3(l) // 曲线路径
+
+  // 管道体
+  const tubeGeometry = new TubeGeometry(curve, option.number || 50, option.radius || 1, option.radialSegments)
+  return new Mesh(tubeGeometry, option.material)
+}

+ 4 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/interfaces/IEvents.ts

@@ -0,0 +1,4 @@
+
+export interface IEvents {
+  resize: () => void
+}

+ 6 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/interfaces/IWord.ts

@@ -0,0 +1,6 @@
+export interface IWord {
+  dom: HTMLElement
+  data: any
+  width: number
+  height: number
+}

+ 23 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/shaders/earth/fragment.fs

@@ -0,0 +1,23 @@
+uniform vec3 glowColor;
+uniform float bias;
+uniform float power;
+uniform float time;
+varying vec3 vp;
+varying vec3 vNormal;
+varying vec3 vPositionNormal;
+uniform float scale;
+// 获取纹理
+uniform sampler2D map;
+// 纹理坐标
+varying vec2 vUv;
+
+void main(void){
+  float a = pow( bias + scale * abs(dot(vNormal, vPositionNormal)), power );
+  if(vp.y > time && vp.y < time + 20.0) {
+    float t =  smoothstep(0.0, 0.8,  (1.0 - abs(0.5 - (vp.y - time) / 20.0)) / 3.0  );
+    gl_FragColor = mix(gl_FragColor, vec4(glowColor, 1.0), t * t );
+  }
+  gl_FragColor = mix(gl_FragColor, vec4( glowColor, 1.0 ), a);
+  float b = 0.8;
+  gl_FragColor = gl_FragColor + texture2D( map, vUv );
+}

+ 12 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/shaders/earth/vertex.vs

@@ -0,0 +1,12 @@
+
+varying vec2 vUv;
+varying vec3 vNormal;
+varying vec3 vp;
+varying vec3 vPositionNormal;
+void main(void){
+  vUv = uv;
+  vNormal = normalize( normalMatrix * normal ); // 转换到视图空间
+  vp = position;
+  vPositionNormal = normalize(( modelViewMatrix * vec4(position, 1.0) ).xyz);
+  gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
+}

+ 34 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/world/Assets.ts

@@ -0,0 +1,34 @@
+/**
+ * 资源文件
+ * 把模型和图片分开进行加载
+ */
+
+interface ITextures {
+  name: string
+  url: string
+}
+
+export interface IResources {
+  textures?: ITextures[]
+}
+
+const fileSuffix = ['earth', 'gradient', 'redCircle', 'label', 'aperture', 'glow', 'light_column', 'aircraft']
+const textures: ITextures[] = []
+
+const modules = import.meta.globEager("../../images/earth/*");
+
+for(let item in modules) {
+  const n = item.split('/').pop()
+  if(n) {
+    textures.push({
+      name: n.split('.')[0],
+      url: modules[item].default
+    })
+  }
+}
+
+const resources: IResources = {
+  textures
+}
+
+export { resources }

+ 62 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/world/Basic.ts

@@ -0,0 +1,62 @@
+/**
+ * 创建 threejs 四大天王
+ * 场景、相机、渲染器、控制器
+ */
+
+import * as THREE from 'three'
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
+
+export class Basic {
+  public scene!: THREE.Scene
+  public camera!: THREE.PerspectiveCamera
+  public renderer!: THREE.WebGLRenderer
+  public controls!: OrbitControls
+  public dom: HTMLElement
+
+  constructor(dom: HTMLElement) {
+    this.dom = dom
+    this.initScenes()
+    this.setControls()
+  }
+
+  /**
+   * 初始化场景
+   */
+  initScenes() {
+    this.scene = new THREE.Scene()
+
+    this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 100000)
+    this.camera.position.set(0, 30, -250)
+
+    this.renderer = new THREE.WebGLRenderer({
+      // canvas: this.dom,
+      alpha: true, // 透明
+      antialias: true // 抗锯齿
+    })
+    this.renderer.setPixelRatio(window.devicePixelRatio) // 设置屏幕像素比
+    this.renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器宽高
+    this.dom.appendChild(this.renderer.domElement) // 添加到dom中
+  }
+
+  /**
+   * 设置控制器
+   */
+  setControls() {
+    // 鼠标控制      相机,渲染dom
+    this.controls = new OrbitControls(this.camera, this.renderer.domElement)
+
+    this.controls.autoRotateSpeed = 3
+    // 使动画循环使用时阻尼或自转 意思是否有惯性
+    this.controls.enableDamping = true
+    // 动态阻尼系数 就是鼠标拖拽旋转灵敏度
+    this.controls.dampingFactor = 0.05
+    // 是否可以缩放
+    this.controls.enableZoom = true
+    // 设置相机距离原点的最远距离
+    this.controls.minDistance = 100
+    // 设置相机距离原点的最远距离
+    this.controls.maxDistance = 300
+    // 是否开启右键拖拽
+    this.controls.enablePan = false
+  }
+}

+ 496 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/world/Earth.ts

@@ -0,0 +1,496 @@
+import {
+  BufferAttribute,
+  BufferGeometry,
+  Color,
+  DoubleSide,
+  Group,
+  Material,
+  Mesh,
+  MeshBasicMaterial,
+  NormalBlending,
+  Object3D,
+  Points,
+  PointsMaterial,
+  ShaderMaterial,
+  SphereGeometry,
+  Sprite,
+  SpriteMaterial,
+  Texture,
+  TextureLoader,
+  Vector3
+} from 'three'
+
+import {
+  createAnimateLine,
+  createLightPillar,
+  createPointMesh,
+  createWaveMesh,
+  getCirclePoints,
+  lon2xyz
+} from '../Utils/common'
+import gsap from 'gsap'
+import { flyArc } from '../Utils/arc'
+import earthVertex from '../shaders/earth/vertex.vs?raw'
+import earthFragment from '../shaders/earth/fragment.fs?raw'
+
+export type punctuation = {
+  circleColor: number
+  lightColumn: {
+    startColor: number // 起点颜色
+    endColor: number // 终点颜色
+  }
+}
+
+type options = {
+  data: {
+    startArray: {
+      name: string
+      E: number // 经度
+      N: number // 维度
+    }
+    endArray: {
+      name: string
+      E: number // 经度
+      N: number // 维度
+    }[]
+  }[]
+  dom: HTMLElement
+  textures: Record<string, Texture> // 贴图
+  earth: {
+    radius: number // 地球半径
+    rotateSpeed: number // 地球旋转速度
+    isRotation: boolean // 地球组是否自转
+  }
+  satellite: {
+    show: boolean // 是否显示卫星
+    rotateSpeed: number // 旋转速度
+    size: number // 卫星大小
+    number: number // 一个圆环几个球
+  }
+  punctuation: punctuation
+  flyLine: {
+    color: number // 飞线的颜色
+    speed: number // 飞机拖尾线速度
+    flyLineColor: number // 飞行线的颜色
+  }
+}
+type uniforms = {
+  glowColor: { value: Color }
+  scale: { type: string; value: number }
+  bias: { type: string; value: number }
+  power: { type: string; value: number }
+  time: { type: string; value: any }
+  isHover: { value: boolean }
+  map: { value?: Texture }
+}
+
+export default class earth {
+  public group: Group
+  public earthGroup: Group
+
+  public around!: BufferGeometry
+  public aroundPoints!: Points<BufferGeometry, PointsMaterial>
+
+  public options: options
+  public uniforms: uniforms
+  public timeValue: number
+
+  public earth!: Mesh<SphereGeometry, ShaderMaterial>
+  public punctuationMaterial!: MeshBasicMaterial
+  public markupPoint: Group
+  public waveMeshArr: Object3D[]
+
+  public circleLineList: any[]
+  public circleList: any[]
+  public x: number
+  public n: number
+  public isRotation: boolean
+  public flyLineArcGroup!: Group
+
+  constructor(options: options) {
+    this.options = options
+
+    this.group = new Group()
+    this.group.name = 'group'
+    this.group.scale.set(0, 0, 0)
+    this.earthGroup = new Group()
+    this.group.add(this.earthGroup)
+    this.earthGroup.name = 'EarthGroup'
+
+    // 标注点效果
+    this.markupPoint = new Group()
+    this.markupPoint.name = 'markupPoint'
+    this.waveMeshArr = []
+
+    // 卫星和标签
+    this.circleLineList = []
+    this.circleList = []
+    this.x = 0
+    this.n = 0
+
+    // 地球自转
+    this.isRotation = this.options.earth.isRotation
+
+    // 扫光动画 shader
+    this.timeValue = 200
+
+    this.uniforms = {
+      glowColor: {
+        value: new Color(0x0cd1eb)
+      },
+      scale: {
+        type: 'f',
+        value: -1.0
+      },
+      bias: {
+        type: 'f',
+        value: 1.0
+      },
+      power: {
+        type: 'f',
+        value: 3.3
+      },
+      time: {
+        type: 'f',
+        value: this.timeValue
+      },
+      isHover: {
+        value: false
+      },
+      map: {
+        value: undefined
+      }
+    }
+  }
+
+  async init(): Promise<void> {
+    return new Promise(resolve => {
+      const init = async () => {
+        this.createEarth() // 创建地球
+        this.createEarthGlow() // 创建地球辉光
+        this.createEarthAperture() // 创建地球的大气层
+        await this.createMarkupPoint() // 创建柱状点位
+        this.createAnimateCircle() // 创建环绕卫星
+        this.createFlyLine() // 创建飞线
+        this.show()
+        resolve()
+      }
+      init()
+    })
+  }
+
+  createEarth() {
+    const earth_geometry = new SphereGeometry(this.options.earth.radius, 50, 50)
+    const earth_border = new SphereGeometry(this.options.earth.radius + 10, 60, 60)
+
+    const pointMaterial = new PointsMaterial({
+      color: 0x81ffff, //设置颜色,默认 0xFFFFFF
+      transparent: true,
+      sizeAttenuation: true,
+      opacity: 0.1,
+      vertexColors: false, //定义材料是否使用顶点颜色,默认false ---如果该选项设置为true,则color属性失效
+      size: 0.2 //定义粒子的大小。默认为1.0
+    })
+    const points = new Points(earth_border, pointMaterial) //将模型添加到场景
+
+    this.earthGroup.add(points)
+
+    this.uniforms.map.value = this.options.textures.earth
+
+    const earth_material = new ShaderMaterial({
+      // wireframe:true, // 显示模型线条
+      uniforms: this.uniforms as any,
+      vertexShader: earthVertex,
+      fragmentShader: earthFragment
+    })
+
+    earth_material.needsUpdate = true
+    this.earth = new Mesh(earth_geometry, earth_material)
+    this.earth.name = 'earth'
+    this.earthGroup.add(this.earth)
+  }
+
+  createEarthGlow() {
+    const R = this.options.earth.radius //地球半径
+
+    // TextureLoader创建一个纹理加载器对象,可以加载图片作为纹理贴图
+    const texture = this.options.textures.glow // 加载纹理贴图
+
+    // 创建精灵材质对象SpriteMaterial
+    const spriteMaterial = new SpriteMaterial({
+      map: texture, // 设置精灵纹理贴图
+      color: 0x4390d1,
+      transparent: true, //开启透明
+      opacity: 0.7, // 可以通过透明度整体调节光圈
+      depthWrite: false //禁止写入深度缓冲区数据
+    })
+
+    // 创建表示地球光圈的精灵模型
+    const sprite = new Sprite(spriteMaterial)
+    sprite.scale.set(R * 3.0, R * 3.0, 1) //适当缩放精灵
+    this.earthGroup.add(sprite)
+  }
+
+  createEarthAperture() {
+    const vertexShader = [
+      'varying vec3	vVertexWorldPosition;',
+      'varying vec3	vVertexNormal;',
+      'varying vec4	vFragColor;',
+      'void main(){',
+      '	vVertexNormal	= normalize(normalMatrix * normal);', //将法线转换到视图坐标系中
+      '	vVertexWorldPosition	= (modelMatrix * vec4(position, 1.0)).xyz;', //将顶点转换到世界坐标系中
+      '	// set gl_Position',
+      '	gl_Position	= projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
+      '}'
+    ].join('\n')
+
+    //大气层效果
+    const AeroSphere = {
+      uniforms: {
+        coeficient: {
+          type: 'f',
+          value: 1.0
+        },
+        power: {
+          type: 'f',
+          value: 3
+        },
+        glowColor: {
+          type: 'c',
+          value: new Color(0x4390d1)
+        }
+      },
+      vertexShader: vertexShader,
+      fragmentShader: [
+        'uniform vec3	glowColor;',
+        'uniform float	coeficient;',
+        'uniform float	power;',
+
+        'varying vec3	vVertexNormal;',
+        'varying vec3	vVertexWorldPosition;',
+
+        'varying vec4	vFragColor;',
+
+        'void main(){',
+        '	vec3 worldCameraToVertex = vVertexWorldPosition - cameraPosition;', //世界坐标系中从相机位置到顶点位置的距离
+        '	vec3 viewCameraToVertex	= (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;', //视图坐标系中从相机位置到顶点位置的距离
+        '	viewCameraToVertex= normalize(viewCameraToVertex);', //规一化
+        '	float intensity	= pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);',
+        '	gl_FragColor = vec4(glowColor, intensity);',
+        '}'
+      ].join('\n')
+    }
+    //球体 辉光 大气层
+    const material1 = new ShaderMaterial({
+      uniforms: AeroSphere.uniforms,
+      vertexShader: AeroSphere.vertexShader,
+      fragmentShader: AeroSphere.fragmentShader,
+      blending: NormalBlending,
+      transparent: true,
+      depthWrite: false
+    })
+    const sphere = new SphereGeometry(this.options.earth.radius + 0, 50, 50)
+    const mesh = new Mesh(sphere, material1)
+    this.earthGroup.add(mesh)
+  }
+
+  async createMarkupPoint() {
+    await Promise.all(
+      this.options.data.map(async item => {
+        const radius = this.options.earth.radius
+        const lon = item.startArray.E //经度
+        const lat = item.startArray.N //纬度
+
+        this.punctuationMaterial = new MeshBasicMaterial({
+          color: this.options.punctuation.circleColor,
+          map: this.options.textures.label,
+          transparent: true, //使用背景透明的png贴图,注意开启透明计算
+          depthWrite: false //禁止写入深度缓冲区数据
+        })
+
+        const mesh = createPointMesh({ radius, lon, lat, material: this.punctuationMaterial }) //光柱底座矩形平面
+        this.markupPoint.add(mesh)
+        const LightPillar = createLightPillar({
+          radius: this.options.earth.radius,
+          lon,
+          lat,
+          index: 0,
+          textures: this.options.textures,
+          punctuation: this.options.punctuation
+        }) //光柱
+        this.markupPoint.add(LightPillar)
+        const WaveMesh = createWaveMesh({ radius, lon, lat, textures: this.options.textures }) //波动光圈
+        this.markupPoint.add(WaveMesh)
+        this.waveMeshArr.push(WaveMesh)
+
+        await Promise.all(
+          item.endArray.map(obj => {
+            const lon = obj.E //经度
+            const lat = obj.N //纬度
+            const mesh = createPointMesh({ radius, lon, lat, material: this.punctuationMaterial }) //光柱底座矩形平面
+            this.markupPoint.add(mesh)
+            const LightPillar = createLightPillar({
+              radius: this.options.earth.radius,
+              lon,
+              lat,
+              index: 1,
+              textures: this.options.textures,
+              punctuation: this.options.punctuation
+            }) //光柱
+            this.markupPoint.add(LightPillar)
+            const WaveMesh = createWaveMesh({ radius, lon, lat, textures: this.options.textures }) //波动光圈
+            this.markupPoint.add(WaveMesh)
+            this.waveMeshArr.push(WaveMesh)
+          })
+        )
+        this.earthGroup.add(this.markupPoint)
+      })
+    )
+  }
+
+  createAnimateCircle() {
+    // 创建 圆环 点
+    const list = getCirclePoints({
+      radius: this.options.earth.radius + 15,
+      number: 150, //切割数
+      closed: true // 闭合
+    })
+    const mat = new MeshBasicMaterial({
+      color: '#0c3172',
+      transparent: true,
+      opacity: 0.4,
+      side: DoubleSide
+    })
+    const line = createAnimateLine({
+      pointList: list,
+      material: mat,
+      number: 100,
+      radius: 0.1
+    })
+    this.earthGroup.add(line)
+
+    // 在clone两条线出来
+    const l2 = line.clone()
+    l2.scale.set(1.2, 1.2, 1.2)
+    l2.rotateZ(Math.PI / 6)
+    this.earthGroup.add(l2)
+
+    const l3 = line.clone()
+    l3.scale.set(0.8, 0.8, 0.8)
+    l3.rotateZ(-Math.PI / 6)
+    this.earthGroup.add(l3)
+
+    /**
+     * 旋转的球
+     */
+    const ball = new Mesh(
+      new SphereGeometry(this.options.satellite.size, 32, 32),
+      new MeshBasicMaterial({
+        color: '#e0b187' // 745F4D
+      })
+    )
+
+    const ball2 = new Mesh(
+      new SphereGeometry(this.options.satellite.size, 32, 32),
+      new MeshBasicMaterial({
+        color: '#628fbb' // 324A62
+      })
+    )
+
+    const ball3 = new Mesh(
+      new SphereGeometry(this.options.satellite.size, 32, 32),
+      new MeshBasicMaterial({
+        color: '#806bdf' //6D5AC4
+      })
+    )
+
+    this.circleLineList.push(line, l2, l3)
+    ball.name = ball2.name = ball3.name = '卫星'
+
+    for (let i = 0; i < this.options.satellite.number; i++) {
+      const ball01 = ball.clone()
+      // 一根线上总共有几个球,根据数量平均分布一下
+      const num = Math.floor(list.length / this.options.satellite.number)
+      ball01.position.set(list[num * (i + 1)][0] * 1, list[num * (i + 1)][1] * 1, list[num * (i + 1)][2] * 1)
+      line.add(ball01)
+
+      const ball02 = ball2.clone()
+      const num02 = Math.floor(list.length / this.options.satellite.number)
+      ball02.position.set(list[num02 * (i + 1)][0] * 1, list[num02 * (i + 1)][1] * 1, list[num02 * (i + 1)][2] * 1)
+      l2.add(ball02)
+
+      const ball03 = ball2.clone()
+      const num03 = Math.floor(list.length / this.options.satellite.number)
+      ball03.position.set(list[num03 * (i + 1)][0] * 1, list[num03 * (i + 1)][1] * 1, list[num03 * (i + 1)][2] * 1)
+      l3.add(ball03)
+    }
+  }
+
+  createFlyLine() {
+    this.flyLineArcGroup = new Group()
+    this.flyLineArcGroup.userData['flyLineArray'] = []
+    this.earthGroup.add(this.flyLineArcGroup)
+    this.options.data.forEach(cities => {
+      cities.endArray.forEach(item => {
+        // 调用函数flyArc绘制球面上任意两点之间飞线圆弧轨迹
+        const arcline = flyArc(
+          this.options.earth.radius,
+          cities.startArray.E,
+          cities.startArray.N,
+          item.E,
+          item.N,
+          this.options.flyLine
+        )
+
+        this.flyLineArcGroup.add(arcline) // 飞线插入flyArcGroup中
+        this.flyLineArcGroup.userData['flyLineArray'].push(arcline.userData['flyLine'])
+      })
+    })
+  }
+
+  show() {
+    gsap.to(this.group.scale, {
+      x: 1,
+      y: 1,
+      z: 1,
+      duration: 2,
+      ease: 'Quadratic'
+    })
+  }
+
+  render() {
+    this.flyLineArcGroup?.userData['flyLineArray']?.forEach((fly: any) => {
+      fly.rotation.z += this.options.flyLine.speed // 调节飞线速度
+      if (fly.rotation.z >= fly.flyEndAngle) fly.rotation.z = 0
+    })
+
+    if (this.isRotation) {
+      this.earthGroup.rotation.y += this.options.earth.rotateSpeed
+    }
+
+    this.circleLineList.forEach(e => {
+      e.rotateY(this.options.satellite.rotateSpeed)
+    })
+
+    this.uniforms.time.value =
+      this.uniforms.time.value < -this.timeValue ? this.timeValue : this.uniforms.time.value - 1
+
+    if (this.waveMeshArr.length) {
+      this.waveMeshArr.forEach((mesh: any) => {
+        mesh.userData['scale'] += 0.007
+        mesh.scale.set(
+          mesh.userData['size'] * mesh.userData['scale'],
+          mesh.userData['size'] * mesh.userData['scale'],
+          mesh.userData['size'] * mesh.userData['scale']
+        )
+        if (mesh.userData['scale'] <= 1.5) {
+          (mesh.material as Material).opacity = (mesh.userData['scale'] - 1) * 2 //2等于1/(1.5-1.0),保证透明度在0~1之间变化
+        } else if (mesh.userData['scale'] > 1.5 && mesh.userData['scale'] <= 2) {
+          (mesh.material as Material).opacity = 1 - (mesh.userData['scale'] - 1.5) * 2 //2等于1/(2.0-1.5) mesh缩放2倍对应0 缩放1.5被对应1
+        } else {
+          mesh.userData['scale'] = 1
+        }
+      })
+    }
+  }
+}

+ 54 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/world/Resources.ts

@@ -0,0 +1,54 @@
+/**
+ * 资源管理和加载
+ */
+import { LoadingManager, Texture, TextureLoader } from 'three'
+import { loadingStart, loadingFinish, loadingError } from '@/utils'
+import { resources } from './Assets'
+export class Resources {
+  private manager!: LoadingManager
+  private callback: () => void
+  private textureLoader!: InstanceType<typeof TextureLoader>
+  public textures: Record<string, Texture>
+  constructor(callback: () => void) {
+    this.callback = callback // 资源加载完成的回调
+    this.textures = {} // 贴图对象
+    this.setLoadingManager()
+    this.loadResources()
+  }
+
+  /**
+   * 管理加载状态
+   */
+  private setLoadingManager() {
+    this.manager = new LoadingManager()
+    // 开始加载
+    this.manager.onStart = () => {
+      loadingStart()
+    }
+    // 加载完成
+    this.manager.onLoad = () => {
+      this.callback()
+    }
+    // 正在进行中
+    this.manager.onProgress = url => {
+      loadingFinish()
+    }
+
+    this.manager.onError = url => {
+      loadingError()
+      window['$message'].error('数据加载失败,请刷新重试!')
+    }
+  }
+
+  /**
+   * 加载资源
+   */
+  private loadResources(): void {
+    this.textureLoader = new TextureLoader(this.manager)
+    resources.textures?.forEach(item => {
+      this.textureLoader.load(item.url, t => {
+        this.textures[item.name] = t
+      })
+    })
+  }
+}

+ 112 - 0
src/packages/components/Decorates/Three/ThreeEarth01/code/world/Word.ts

@@ -0,0 +1,112 @@
+import { MeshBasicMaterial, PerspectiveCamera, Scene, ShaderMaterial, WebGLRenderer } from 'three'
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
+// interfaces
+import { IWord } from '../interfaces/IWord'
+import { Basic } from './Basic'
+import { Resources } from './Resources'
+// earth
+import Earth from './Earth'
+
+export default class World {
+  public basic: Basic
+  public scene: Scene
+  public camera: PerspectiveCamera
+  public renderer: WebGLRenderer
+  public controls: OrbitControls
+  public material!: ShaderMaterial | MeshBasicMaterial
+  public resources: Resources
+  public option: IWord
+  public earth!: Earth
+
+  constructor(option: IWord) {
+    /**
+     * 加载资源
+     */
+    this.option = option
+    this.basic = new Basic(option.dom)
+    this.scene = this.basic.scene
+    this.renderer = this.basic.renderer
+    this.controls = this.basic.controls
+    this.camera = this.basic.camera
+    this.updateSize()
+    this.resources = new Resources(async () => {
+      await this.createEarth()
+      // 开始渲染
+      this.render()
+    })
+  }
+
+  async createEarth(data?: any) {
+    // 资源加载完成,开始制作地球,注释在new Earth()类型里面
+    this.earth = new Earth({
+      data: data || this.option.data,
+      dom: this.option.dom,
+      textures: this.resources.textures,
+      earth: {
+        radius: 50,
+        rotateSpeed: 0.002,
+        isRotation: true
+      },
+      satellite: {
+        show: true,
+        rotateSpeed: -0.01,
+        size: 1,
+        number: 2
+      },
+      punctuation: {
+        circleColor: 0x3892ff,
+        lightColumn: {
+          startColor: 0xe4007f, // 起点颜色
+          endColor: 0xffffff // 终点颜色
+        }
+      },
+      flyLine: {
+        color: 0xf3ae76, // 飞线的颜色
+        flyLineColor: 0xff7714, // 飞行线的颜色
+        speed: 0.004 // 拖尾飞线的速度
+      }
+    })
+
+    this.scene.add(this.earth.group)
+    await this.earth.init()
+  }
+
+  /**
+   * 渲染函数
+   */
+  public render() {
+    requestAnimationFrame(this.render.bind(this))
+    this.renderer.render(this.scene, this.camera)
+    this.controls && this.controls.update()
+    this.earth && this.earth.render()
+  }
+
+  // 更新
+  public updateSize(width?: number, height?: number) {
+    let w = width || this.option.width
+    let h = height || this.option.height
+    // 取小值
+    if (w < h) h = w
+    else w = h
+
+    this.renderer.setSize(w, h)
+    this.camera.aspect = w / h
+    this.camera.updateProjectionMatrix()
+  }
+
+  // 数据更新重新渲染
+  public updateData(data?: any) {
+    if (!this.earth.group) return
+    // 先删除旧的
+    this.scene.remove(this.earth.group)
+    // 递归遍历组对象group释放所有后代网格模型绑定几何体占用内存
+    this.earth.group.traverse((obj: any) => {
+      if (obj.type === 'Mesh') {
+        obj.geometry.dispose()
+        obj.material.dispose()
+      }
+    })
+    // 重新创建
+    this.createEarth(data)
+  }
+}

+ 17 - 0
src/packages/components/Decorates/Three/ThreeEarth01/config.ts

@@ -0,0 +1,17 @@
+import { PublicConfigClass } from '@/packages/public'
+import { CreateComponentType } from '@/packages/index.d'
+import { chartInitConfig } from '@/settings/designSetting'
+import { ThreeEarth01Config } from './index'
+import dataJson from './data.json'
+import cloneDeep from 'lodash/cloneDeep'
+
+export const option = {
+  dataset: dataJson
+}
+
+export default class Config extends PublicConfigClass implements CreateComponentType {
+  public key = ThreeEarth01Config.key
+  public attr = { ...chartInitConfig, w: 800, h: 800, zIndex: -1 }
+  public chartConfig = cloneDeep(ThreeEarth01Config)
+  public option = cloneDeep(option)
+}

+ 14 - 0
src/packages/components/Decorates/Three/ThreeEarth01/config.vue

@@ -0,0 +1,14 @@
+<template></template>
+
+<script setup lang="ts">
+import { PropType } from 'vue'
+import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
+import { option } from './config'
+
+const props = defineProps({
+  optionData: {
+    type: Object as PropType<typeof option>,
+    required: true
+  }
+})
+</script>

+ 84 - 0
src/packages/components/Decorates/Three/ThreeEarth01/data.json

@@ -0,0 +1,84 @@
+[
+  {
+    "startArray": {
+      "name": "杭州",
+      "N": 30.246026,
+      "E": 120.210792
+    },
+    "endArray": [
+      {
+        "name": "曼谷",
+        "N": 22,
+        "E": 100.49074172973633
+      },
+      {
+        "name": "澳大利亚",
+        "N": -23.68477416688374,
+        "E": 133.857421875
+      },
+
+      {
+        "name": "新疆维吾尔自治区",
+        "N": 41.748,
+        "E": 84.9023
+      },
+
+      {
+        "name": "德黑兰",
+        "N": 35,
+        "E": 51
+      },
+      {
+        "name": "德黑兰",
+        "N": 35,
+        "E": 51
+      },
+      {
+        "name": "美国",
+        "N": 34.125447565116126,
+        "E": 241.7431640625
+      },
+      {
+        "name": "英国",
+        "N": 51.508742458803326,
+        "E": 359.82421875
+      },
+      {
+        "name": "巴西",
+        "N": -9.96885060854611,
+        "E": 668.1445312499999
+      }
+    ]
+  },
+  {
+    "startArray": {
+      "name": "北京",
+      "N": 39.89491,
+      "E": 116.322056
+    },
+    "endArray": [
+      {
+        "name": "西藏",
+        "N": 29.660361,
+        "E": 91.132212
+      },
+      {
+        "name": "广西",
+        "N": 22.830824,
+        "E": 108.30616
+      },
+
+      {
+        "name": "江西",
+        "N": 28.676493,
+        "E": 115.892151
+      },
+
+      {
+        "name": "贵阳",
+        "N": 26.647661,
+        "E": 106.630153
+      }
+    ]
+  }
+]

BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/aircraft.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/aperture.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/earth.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/glow.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/gradient.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/label-old.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/label.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/light_column.png


BIN
src/packages/components/Decorates/Three/ThreeEarth01/images/earth/redCircle.png


+ 15 - 0
src/packages/components/Decorates/Three/ThreeEarth01/index.ts

@@ -0,0 +1,15 @@
+import image from '@/assets/images/chart/decorates/threeEarth01.png'
+import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
+import { ChatCategoryEnum, ChatCategoryEnumName } from '../../index.d'
+
+export const ThreeEarth01Config: ConfigType = {
+  key: 'ThreeEarth01',
+  chartKey: 'VThreeEarth01',
+  conKey: 'VCThreeEarth01',
+  title: '三维地球',
+  category: ChatCategoryEnum.THREE,
+  categoryName: ChatCategoryEnumName.THREE,
+  package: PackagesCategoryEnum.DECORATES,
+  chartFrame: ChartFrameEnum.STATIC,
+  image
+}

+ 81 - 0
src/packages/components/Decorates/Three/ThreeEarth01/index.vue

@@ -0,0 +1,81 @@
+<template>
+  <div ref="chartRef"></div>
+</template>
+
+<script setup lang="ts">
+import { onMounted, PropType, ref, toRefs, watch } from 'vue'
+import { CreateComponentType } from '@/packages/index.d'
+import { useChartDataFetch } from '@/hooks'
+import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
+import { option } from './config'
+import World from './code/world/Word'
+import throttle from 'lodash/throttle'
+
+const props = defineProps({
+  chartConfig: {
+    type: Object as PropType<CreateComponentType & typeof option>,
+    required: true
+  }
+})
+
+const chartEditStore = useChartEditStore()
+
+const chartRef = ref<HTMLElement>()
+const { w, h } = toRefs(props.chartConfig.attr)
+let threeClassInstance: World
+
+// 初始化
+const init = () => {
+  const dom: HTMLElement | undefined = chartRef.value
+  if (dom) {
+    threeClassInstance = new World({
+      dom: dom,
+      data: props.chartConfig.option.dataset,
+      width: w.value,
+      height: h.value
+    })
+  }
+}
+
+const updateData = (data: any) => {
+  try {
+    threeClassInstance.updateData(data)
+  } catch (error) {
+    console.log(error)
+  }
+}
+
+// 改变大小
+watch(
+  () => [w.value, h.value],
+  throttle(([newWidth], [newHeight]) => {
+    threeClassInstance.updateSize(newWidth, newHeight)
+  }, 100)
+)
+
+watch(
+  () => props.chartConfig.option.dataset,
+  (newData: any) => {
+    updateData(newData)
+  },
+  {
+    deep: false
+  }
+)
+
+// DOM 渲染之后进行初始化
+onMounted(() => {
+  try {
+    if (navigator.userAgent.indexOf('Chrome') < -1 || navigator.userAgent.indexOf('Edg') > -1) {
+      window['$message'].error('此组件仅在【谷歌】浏览器上能正常展示!')
+      chartEditStore.removeComponentList(undefined, false)
+      return
+    }
+    init()
+  } catch (error) {
+    console.log(error)
+  }
+})
+
+useChartDataFetch(props.chartConfig, useChartEditStore, updateData)
+</script>

+ 3 - 0
src/packages/components/Decorates/Three/index.ts

@@ -0,0 +1,3 @@
+import { ThreeEarth01Config } from './ThreeEarth01/index'
+
+export default [ThreeEarth01Config]

+ 2 - 0
src/packages/components/Decorates/index.d.ts

@@ -1,11 +1,13 @@
 export enum ChatCategoryEnum {
   BORDER = 'Borders',
   DECORATE = 'Decorates',
+  THREE = 'Three',
   MORE = 'Mores'
 }
 
 export enum ChatCategoryEnumName {
   BORDER = '边框',
   DECORATE = '装饰',
+  THREE = '三维',
   MORE = '更多'
 }

+ 2 - 1
src/packages/components/Decorates/index.ts

@@ -1,5 +1,6 @@
 import Borders from './Borders'
 import Decorates from './Decorates'
+import Three from './Three'
 import Mores from './Mores'
 
-export const DecorateList = [...Borders, ...Decorates, ...Mores]
+export const DecorateList = [...Borders, ...Decorates, ...Three, ...Mores]

+ 6 - 3
src/packages/components/Informations/Mores/WordCloud/index.vue

@@ -47,9 +47,12 @@ const option = computed(() => {
 })
 
 const dataSetHandle = (dataset: typeof dataJson) => {
-  dataset && (props.chartConfig.option.series[0].data = dataset)
-
-  vChartRef.value && isPreview() && vChartRef.value.setOption(props.chartConfig.option)
+  try {
+    dataset && (props.chartConfig.option.series[0].data = dataset)
+    vChartRef.value && isPreview() && vChartRef.value.setOption(props.chartConfig.option)
+  } catch (error) {
+    console.log(error)
+  }
 }
 
 // dataset 无法变更条数的补丁

+ 25 - 26
src/packages/components/Tables/Tables/TableList/index.vue

@@ -10,19 +10,12 @@
         <div class="rank" :style="`color: ${color};font-size: ${indexFontSize}px`">No.{{ item.ranking }}</div>
         <div class="info-name" :style="`font-size: ${leftFontSize}px`" v-html="item.name" />
         <div class="ranking-value" :style="`color: ${textColor};font-size: ${rightFontSize}px`">
-          {{
-            status.mergedConfig.valueFormatter
-              ? status.mergedConfig.valueFormatter(item)
-              : item.value
-          }}
+          {{ status.mergedConfig.valueFormatter ? status.mergedConfig.valueFormatter(item) : item.value }}
           {{ unit }}
         </div>
       </div>
       <div class="ranking-column" :style="`border-color: ${borderColor}`">
-        <div
-          class="inside-column"
-          :style="`width: ${item.percent}%;background-color: ${color}`"
-        >
+        <div class="inside-column" :style="`width: ${item.percent}%;background-color: ${color}`">
           <div class="shine" />
         </div>
       </div>
@@ -39,8 +32,8 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
 const props = defineProps({
   chartConfig: {
     type: Object as PropType<CreateComponentType>,
-    required: true,
-  },
+    required: true
+  }
 })
 const { w, h } = toRefs(props.chartConfig.attr)
 const { rowNum, unit, color, textColor, borderColor, indexFontSize, leftFontSize, rightFontSize } = toRefs(
@@ -50,13 +43,15 @@ const { rowNum, unit, color, textColor, borderColor, indexFontSize, leftFontSize
 const status = reactive({
   mergedConfig: props.chartConfig.option,
   rowsData: [],
-  rows: [{
-    scroll: 0,
-    ranking: 1,
-    name: '',
-    value: '',
-    percent: 0
-  }],
+  rows: [
+    {
+      scroll: 0,
+      ranking: 1,
+      name: '',
+      value: '',
+      percent: 0
+    }
+  ],
   heights: [0],
   animationIndex: 0,
   animationHandler: 0,
@@ -81,16 +76,16 @@ const calcRowsData = () => {
   // abs of max
   const maxAbs = Math.abs(max)
   const total = max + minAbs
-  dataset = dataset.map((row: any, i:number) => ({
+  dataset = dataset.map((row: any, i: number) => ({
     ...row,
     ranking: i + 1,
-    percent: ((row.value + minAbs) / total) * 100,
+    percent: ((row.value + minAbs) / total) * 100
   }))
   const rowLength = dataset.length
   if (rowLength > rowNum && rowLength < 2 * rowNum) {
     dataset = [...dataset, ...dataset]
   }
-  dataset = dataset.map((d:any, i:number) => ({ ...d, scroll: i }))
+  dataset = dataset.map((d: any, i: number) => ({ ...d, scroll: i }))
   status.rowsData = dataset
   status.rows = dataset
 }
@@ -134,11 +129,15 @@ const stopAnimation = () => {
 }
 
 const onRestart = async () => {
-  if (!status.mergedConfig) return
-  stopAnimation()
-  calcRowsData()
-  calcHeights(true)
-  animation(true)
+  try {
+    if (!status.mergedConfig) return
+    stopAnimation()
+    calcRowsData()
+    calcHeights(true)
+    animation(true)
+  } catch (error) {
+    console.log(error)
+  }
 }
 
 onRestart()

+ 61 - 31
src/packages/components/Tables/Tables/TableScrollBoard/index.vue

@@ -1,24 +1,47 @@
 <template>
   <div class="dv-scroll-board">
-    <div class="header" v-if="status.header.length && status.mergedConfig"
-      :style="`background-color: ${status.mergedConfig.headerBGC};`">
-      <div class="header-item" v-for="(headerItem, i) in status.header" :key="`${headerItem}${i}`" :style="`
+    <div
+      class="header"
+      v-if="status.header.length && status.mergedConfig"
+      :style="`background-color: ${status.mergedConfig.headerBGC};`"
+    >
+      <div
+        class="header-item"
+        v-for="(headerItem, i) in status.header"
+        :key="`${headerItem}${i}`"
+        :style="`
         height: ${status.mergedConfig.headerHeight}px;
         line-height: ${status.mergedConfig.headerHeight}px;
         width: ${status.widths[i]}px;
-      `" :align="status.aligns[i]" v-html="headerItem" />
+      `"
+        :align="status.aligns[i]"
+        v-html="headerItem"
+      />
     </div>
 
-    <div v-if="status.mergedConfig" class="rows"
-      :style="`height: ${h - (status.header.length ? status.mergedConfig.headerHeight : 0)}px;`">
-      <div class="row-item" v-for="(row, ri) in status.rows" :key="`${row.toString()}${row.scroll}`" :style="`
+    <div
+      v-if="status.mergedConfig"
+      class="rows"
+      :style="`height: ${h - (status.header.length ? status.mergedConfig.headerHeight : 0)}px;`"
+    >
+      <div
+        class="row-item"
+        v-for="(row, ri) in status.rows"
+        :key="`${row.toString()}${row.scroll}`"
+        :style="`
         height: ${status.heights[ri]}px;
         line-height: ${status.heights[ri]}px;
         background-color: ${status.mergedConfig[row.rowIndex % 2 === 0 ? 'evenRowBGC' : 'oddRowBGC']};
-      `">
-        <div class="ceil" v-for="(ceil, ci) in row.ceils" :key="`${ceil}${ri}${ci}`"
-          :style="`width: ${status.widths[ci]}px;`" :align="status.aligns[ci]" v-html="ceil" />
-
+      `"
+      >
+        <div
+          class="ceil"
+          v-for="(ceil, ci) in row.ceils"
+          :key="`${ceil}${ri}${ci}`"
+          :style="`width: ${status.widths[ci]}px;`"
+          :align="status.aligns[ci]"
+          v-html="ceil"
+        />
       </div>
     </div>
   </div>
@@ -35,8 +58,8 @@ import cloneDeep from 'lodash/cloneDeep'
 const props = defineProps({
   chartConfig: {
     type: Object as PropType<CreateComponentType>,
-    required: true,
-  },
+    required: true
+  }
 })
 
 // 这里能拿到图表宽高等
@@ -138,11 +161,13 @@ const status = reactive({
   mergedConfig: props.chartConfig.option,
   header: [],
   rowsData: [],
-  rows: [{
-    ceils: [],
-    rowIndex: 0,
-    scroll: 0
-  }],
+  rows: [
+    {
+      ceils: [],
+      rowIndex: 0,
+      scroll: 0
+    }
+  ],
   widths: [],
   heights: [0],
   avgHeight: 0,
@@ -163,7 +188,7 @@ const calcData = () => {
   animation(true)
 }
 
-onMounted(()=> {
+onMounted(() => {
   calcData()
 })
 
@@ -185,19 +210,21 @@ const calcHeaderData = () => {
 const calcRowsData = () => {
   let { dataset, index, headerBGC, rowNum } = status.mergedConfig
   if (index) {
-    dataset = dataset.map((row:any, i:number) => {
+    dataset = dataset.map((row: any, i: number) => {
       row = [...row]
-      const indexTag = `<span class="index" style="background-color: ${headerBGC};border-radius: 3px;padding: 0px 3px;">${i + 1}</span>`
+      const indexTag = `<span class="index" style="background-color: ${headerBGC};border-radius: 3px;padding: 0px 3px;">${
+        i + 1
+      }</span>`
       row.unshift(indexTag)
       return row
     })
   }
-  dataset = dataset.map((ceils:any, i:number) => ({ ceils, rowIndex: i }))
+  dataset = dataset.map((ceils: any, i: number) => ({ ceils, rowIndex: i }))
   const rowLength = dataset.length
   if (rowLength > rowNum && rowLength < 2 * rowNum) {
     dataset = [...dataset, ...dataset]
   }
-  dataset = dataset.map((d:any, i:number) => ({ ...d, scroll: i }))
+  dataset = dataset.map((d: any, i: number) => ({ ...d, scroll: i }))
 
   status.rowsData = dataset
   status.rows = dataset
@@ -206,7 +233,7 @@ const calcRowsData = () => {
 const calcWidths = () => {
   const { mergedConfig, rowsData } = status
   const { columnWidth, header } = mergedConfig
-  const usedWidth = columnWidth.reduce((all:any, ws:number) => all + ws, 0)
+  const usedWidth = columnWidth.reduce((all: any, ws: number) => all + ws, 0)
   let columnNum = 0
   if (rowsData[0]) {
     columnNum = (rowsData[0] as any).ceils.length
@@ -254,7 +281,7 @@ const animation = async (start = false) => {
   const rowLength = rowsData.length
   if (rowNum >= rowLength) return
   if (start) {
-    await new Promise(resolve => setTimeout(resolve, waitTime*1000))
+    await new Promise(resolve => setTimeout(resolve, waitTime * 1000))
     if (updater !== status.updater) return
   }
   const animationNum = carousel === 'single' ? 1 : rowNum
@@ -269,7 +296,7 @@ const animation = async (start = false) => {
   const back = animationIndex - rowLength
   if (back >= 0) animationIndex = back
   status.animationIndex = animationIndex
-  status.animationHandler = setTimeout(animation, waitTime*1000 - 300) as any
+  status.animationHandler = setTimeout(animation, waitTime * 1000 - 300) as any
 }
 
 const stopAnimation = () => {
@@ -279,9 +306,13 @@ const stopAnimation = () => {
 }
 
 const onRestart = async () => {
-  if (!status.mergedConfig) return
-  stopAnimation()
-  calcData()
+  try {
+    if (!status.mergedConfig) return
+    stopAnimation()
+    calcData()
+  } catch (error) {
+    console.log(error)
+  }
 }
 
 watch(
@@ -304,7 +335,7 @@ watch(
   () => {
     onRestart()
   },
-  {deep:true}
+  { deep: true }
 )
 
 // 数据更新 (默认更新 dataset,若更新之后有其它操作,可添加回调函数)
@@ -316,7 +347,6 @@ useChartDataFetch(props.chartConfig, useChartEditStore, (resData: any[]) => {
 onUnmounted(() => {
   stopAnimation()
 })
-
 </script>
 
 <style lang="scss" scoped>

+ 0 - 1
src/packages/index.d.ts

@@ -115,7 +115,6 @@ export interface PublicConfigType {
   }
   filter?: string
   status: StatusType
-  setPosition: Function
 }
 
 export interface CreateComponentType extends PublicConfigType, requestConfig {

+ 0 - 6
src/packages/public/publicConfig.ts

@@ -81,12 +81,6 @@ export class PublicConfigClass implements PublicConfigType {
   public request = cloneDeep(requestConfig)
   // 数据过滤
   public filter = undefined
-
-  // 设置坐标
-  public setPosition(x: number, y: number): void {
-    this.attr.x = x
-    this.attr.y = y
-  }
 }
 
 // 多选成组类

+ 11 - 2
src/settings/chartThemes/global.theme.json

@@ -22,7 +22,8 @@
     "axisLabel": {
       "show": true,
       "fontSize": 12,
-      "color": "#B9B8CE"
+      "color": "#B9B8CE",
+      "rotate": 0
     },
     "position": "bottom",
     "axisLine": {
@@ -58,7 +59,8 @@
     "axisLabel": {
       "show": true,
       "fontSize": 12,
-      "color": "#B9B8CE"
+      "color": "#B9B8CE",
+      "rotate": 0
     },
     "position": "left",
     "axisLine": {
@@ -88,5 +90,12 @@
     "textStyle": {
       "color": "#B9B8CE"
     }
+  },
+  "grid": {
+    "show": false,
+    "left": "10%",
+    "top": "60",
+    "right": "10%",
+    "bottom": "60"
   }
 }

+ 19 - 22
src/store/modules/chartEditStore/chartEditStore.ts

@@ -518,34 +518,31 @@ export const useChartEditStore = defineStore({
         }
         const parseHandle = (e: CreateComponentType | CreateComponentGroupType) => {
           e = cloneDeep(e)
-          // 生成新 id
-          e.id = getUUID()
           e.attr.x = this.getMousePosition.x + 30
           e.attr.y = this.getMousePosition.y + 30
+          // 外层生成新 id
+          e.id = getUUID()
+          // 分组列表生成新 id
+          if (e.isGroup) {
+            (e as CreateComponentGroupType).groupList.forEach((item: CreateComponentType) => {
+              item.id = getUUID()
+            })
+          }
+        
           return e
         }
         const isCut = recordCharts.type === HistoryActionTypeEnum.CUT
+        const targetList = Array.isArray(recordCharts.charts) ? recordCharts.charts : [ recordCharts.charts ]
         // 多项
-        if (Array.isArray(recordCharts.charts)) {
-          recordCharts.charts.forEach((e: CreateComponentType) => {
-            this.addComponentList(parseHandle(e), undefined, true)
-            // 剪切需删除原数据
-            if (isCut) {
-              this.setTargetSelectChart(e.id)
-              this.removeComponentList(undefined, true)
-            }
-          })
-          if (isCut) this.setRecordChart(undefined)
-          loadingFinish()
-          return
-        }
-        // 单项
-        this.addComponentList(parseHandle(recordCharts.charts), undefined, true)
-        if (isCut) {
-          this.setTargetSelectChart(recordCharts.charts.id)
-          this.removeComponentList()
-          this.setRecordChart(undefined)
-        }
+        targetList.forEach((e: CreateComponentType | CreateComponentGroupType) => {
+          this.addComponentList(parseHandle(e), undefined, true)
+          // 剪切需删除原数据
+          if (isCut) {
+            this.setTargetSelectChart(e.id)
+            this.removeComponentList(undefined, true)
+          }
+        })
+        if (isCut) this.setRecordChart(undefined)
         loadingFinish()
       } catch (value) {
         loadingError()

+ 0 - 0
src/utils/componets.ts → src/utils/components.ts


+ 1 - 1
src/utils/index.ts

@@ -4,7 +4,7 @@ export * from '@/utils/router'
 export * from '@/utils/storage'
 export * from '@/utils/style'
 export * from '@/utils/plugin'
-export * from '@/utils/componets'
+export * from '@/utils/components'
 export * from '@/utils/type'
 export * from '@/utils/file'
 export * from '@/utils/http'

+ 43 - 18
src/utils/utils.ts

@@ -7,7 +7,9 @@ import html2canvas from 'html2canvas'
 import { downloadByA } from './file'
 import { toString } from './type'
 import cloneDeep from 'lodash/cloneDeep'
+import { WinKeyboard } from '@/enums/editPageEnum'
 import { RequestHttpIntervalEnum, RequestParamsObjType } from '@/enums/httpEnum'
+import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
 
 /**
  * * 判断是否是开发环境
@@ -22,7 +24,7 @@ export const isDev = () => {
  * @param { Number } randomLength
  */
 export const getUUID = (randomLength = 10) => {
-  return Number(Math.random().toString().substr(2, randomLength) + Date.now()).toString(36)
+  return Number(Math.random().toString().substring(2, randomLength) + Date.now()).toString(36)
 }
 
 /**
@@ -43,21 +45,8 @@ export const renderLang = (lang: string, set = {}, tag = 'span') => {
   return () => h(tag, set, { default: () => window['$t'](lang) })
 }
 
-/**
- * ! 编译会报错,暂不使用
- * * 处理 vite 中无法使用 require 的问题,utils 文件为根路径
- * @param path
- * @param name
- * @returns url
- */
-// export const requireUrl = (path: string, name: string) => {
-//   return new URL(`${path}/${name}`, import.meta.url).href
-// }
-
 /**
  * * 获取错误处理图片,默认 404 图
- * @param path
- * @param name
  * @returns url
  */
 export const requireErrorImg = () => {
@@ -85,6 +74,21 @@ export const screenfullFn = (isFullscreen?: boolean, isEnabled?: boolean) => {
   window['$message'].warning('您的浏览器不支持全屏功能!')
 }
 
+/**
+ * 修改元素位置
+ * @param target 对象
+ * @param x X轴
+ * @param y Y轴
+ */
+export const setComponentPosition = (
+  target: CreateComponentType | CreateComponentGroupType,
+  x?: number,
+  y?: number
+) => {
+  x && (target.attr.x = x)
+  y && (target.attr.y = y)
+}
+
 /**
  * * 设置元素属性
  * @param HTMLElement 元素
@@ -170,14 +174,16 @@ export const canvasCut = (html: HTMLElement | null, callback?: Function) => {
 /**
  * * 函数过滤器
  * @param data 数据值
+ * @param res 返回顶级对象
  * @param funcStr 函数字符串
- * @param toString 转为字符串
+ * @param isToString 是否转为字符串
  * @param errorCallBack 错误回调函数
  * @param successCallBack 成功回调函数
  * @returns
  */
 export const newFunctionHandle = (
   data: any,
+  res: any,
   funcStr?: string,
   isToString?: boolean,
   errorCallBack?: Function,
@@ -185,8 +191,8 @@ export const newFunctionHandle = (
 ) => {
   try {
     if (!funcStr) return data
-    const fn = new Function('data', funcStr)
-    const fnRes = fn(cloneDeep(data))
+    const fn = new Function('data', 'res', funcStr)
+    const fnRes = fn(cloneDeep(data), cloneDeep(res))
     const resHandle = isToString ? toString(fnRes) : fnRes
     // 成功回调
     successCallBack && successCallBack(resHandle)
@@ -235,5 +241,24 @@ export const objToCookie = (obj: RequestParamsObjType) => {
   for (const key in obj) {
     str += key + '=' + obj[key] + ';'
   }
-  return str.substr(0, str.length - 1)
+  return str.substring(0, str.length - 1)
+}
+
+/**
+ * * 设置按下键盘按键的底部展示
+ * @param keyCode
+ * @returns
+ */
+export const setKeyboardDressShow = (keyCode?: number) => {
+  const code = new Map([[17, WinKeyboard.CTRL]])
+
+  const dom = document.getElementById('keyboard-dress-show')
+  if (!dom) return
+  if (!keyCode) {
+    dom.innerText = ''
+    return
+  }
+  if (keyCode && code.has(keyCode)) {
+    dom.innerText = `按下了「${code.get(keyCode)}」键`
+  }
 }

+ 3 - 2
src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataAjax/index.vue

@@ -127,8 +127,9 @@ const sendHandle = async () => {
   try {
     const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.requestGlobalConfig))
     loading.value = false
-    if (res && res.data) {
-      targetData.value.option.dataset = newFunctionHandle(res.data, targetData.value.filter)
+    if (res) {
+      if(!res?.data && !targetData.value.filter) window['$message'].warning('您的数据不符合默认格式,请配置过滤器!')
+      targetData.value.option.dataset = newFunctionHandle(res?.data, res, targetData.value.filter)
       showMatching.value = true
       return
     }

+ 1 - 1
src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMatchingAndShow/index.vue

@@ -27,7 +27,7 @@
     </n-timeline-item>
     <n-timeline-item v-show="filterShow" color="#97846c" :title="TimelineTitleEnum.FILTER">
       <n-space :size="18" vertical>
-        <n-text depth="3">过滤器处理接口返回值的「data」字段</n-text>
+        <n-text depth="3">过滤器默认处理接口返回值的「data」字段</n-text>
         <chart-data-monaco-editor></chart-data-monaco-editor>
       </n-space>
     </n-timeline-item>

+ 17 - 10
src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataMonacoEditor/index.vue

@@ -1,7 +1,7 @@
 <template>
   <template v-if="targetData.filter">
     <n-card>
-      <p><span class="func-keyword">function</span>&nbsp;&nbsp;filter(data)&nbsp;&nbsp;{</p>
+      <p><span class="func-keyword">function</span>&nbsp;&nbsp;filter(data, res)&nbsp;&nbsp;{</p>
       <!-- 函数体 -->
       <div class="go-ml-4">
         <n-code :code="targetData.filter" language="typescript"></n-code>
@@ -47,7 +47,7 @@
           <div>
             <n-space vertical>
               <n-tag type="info">
-                <span class="func-keyword">function</span>&nbsp;&nbsp;filter(data)&nbsp;&nbsp;{
+                <span class="func-keyword">function</span>&nbsp;&nbsp;filter(data, res)&nbsp;&nbsp;{
               </n-tag>
               <monaco-editor v-model:modelValue="filter" width="460px" height="380px" language="javascript" />
               <n-tag type="info">}</n-tag>
@@ -58,14 +58,20 @@
             <n-space :size="15" vertical>
               <div class="editor-data-show">
                 <n-space>
-                  <n-text depth="3">目标数据:</n-text>
-                  <n-code :code="toString(sourceData)" language="json" :word-wrap="true"></n-code>
+                  <n-text depth="3">默认过滤数据(data):</n-text>
+                  <n-code :code="toString(sourceData?.data) || '暂无'" language="json" :word-wrap="true"></n-code>
+                </n-space>
+              </div>
+              <div class="editor-data-show">
+                <n-space>
+                  <n-text depth="3">接口返回数据(res):</n-text>
+                  <n-code :code="toString(sourceData) || '暂无'" language="json" :word-wrap="true"></n-code>
                 </n-space>
               </div>
               <div class="editor-data-show">
                 <n-space>
                   <n-text depth="3">过滤器结果:</n-text>
-                  <n-code :code="filterRes" language="json" :word-wrap="true"></n-code>
+                  <n-code :code="filterRes || '暂无'" language="json" :word-wrap="true"></n-code>
                 </n-space>
               </div>
             </n-space>
@@ -81,7 +87,7 @@
               </template>
               规则
             </n-tag>
-            <n-text class="go-ml-2" depth="2">过滤器处理接口返回值的「data」字段</n-text>
+            <n-text class="go-ml-2" depth="2">过滤器默认处理接口返回值的「data」字段</n-text>
           </div>
 
           <n-space>
@@ -123,8 +129,8 @@ const sourceData = ref<any>('')
 const fetchTargetData = async () => {
   try {
     const res = await customizeHttp(toRaw(targetData.value.request), toRaw(chartEditStore.requestGlobalConfig))
-    if (res && res.data) {
-      sourceData.value = res.data
+    if (res) {
+      sourceData.value = res
       return
     }
     window['$message'].warning('数据异常,请检查参数!')
@@ -136,8 +142,9 @@ const fetchTargetData = async () => {
 // 过滤结果
 const filterRes = computed(() => {
   try {
-    const fn = new Function('data', filter.value)
-    const res = fn(cloneDeep(sourceData.value))
+    const fn = new Function('data', 'res', filter.value)
+    const response = cloneDeep(sourceData.value)
+    const res = fn(response?.data, response)
     // eslint-disable-next-line vue/no-side-effects-in-computed-properties
     errorFlag.value = false
     return toString(res)

+ 5 - 1
src/views/chart/ContentConfigurations/components/ChartData/components/ChartDataRequest/components/RequestTargetConfig/index.vue

@@ -76,7 +76,8 @@ import {
   scatterBasicUrl,
   mapUrl,
   wordCloudUrl,
-  treemapUrl
+  treemapUrl,
+  threeEarth01Url
 } from '@/api/mock'
 
 const { HelpOutlineIcon } = icon.ionicons5
@@ -127,6 +128,9 @@ const apiList = [
   {
     value: `【树图】${treemapUrl}`
   },
+  {
+    value: `【三维地球】${threeEarth01Url}`
+  },
 ]
 </script>
 

+ 130 - 125
src/views/chart/ContentEdit/components/EditAlignLine/index.vue

@@ -17,6 +17,7 @@ import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore
 import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
 import { useSettingStore } from '@/store/modules/settingStore/settingStore'
 import { CreateComponentType, CreateComponentGroupType } from '@/packages/index.d'
+import { setComponentPosition } from '@/utils'
 import throttle from 'lodash/throttle'
 import cloneDeep from 'lodash/cloneDeep'
 // 全局颜色
@@ -96,146 +97,150 @@ const canvasPositionList = computed(() => {
 watch(
   () => chartEditStore.getMousePosition,
   throttle(() => {
-    if (!isComputedLine.value || selectId.value.length !== 1) return
-    // 获取目标组件数据
+    try {
+      if (!isComputedLine.value || selectId.value.length !== 1) return
+      // 获取目标组件数据
 
-    const selectW = selectAttr.value.w
-    const selectH = selectAttr.value.h
+      const selectW = selectAttr.value.w
+      const selectH = selectAttr.value.h
 
-    // 距离左侧
-    const selectLeftX = selectAttr.value.x
-    const selectHalfX = selectLeftX + selectW / 2
-    const selectRightX = selectLeftX + selectW
-    const seletX = [selectLeftX, selectHalfX, selectRightX]
+      // 距离左侧
+      const selectLeftX = selectAttr.value.x
+      const selectHalfX = selectLeftX + selectW / 2
+      const selectRightX = selectLeftX + selectW
+      const seletX = [selectLeftX, selectHalfX, selectRightX]
 
-    // 距离顶部
-    const selectTopY = selectAttr.value.y
-    const selectHalfY = selectTopY + selectH / 2
-    const selectBottomY = selectTopY + selectH
-    const selectY = [selectTopY, selectHalfY, selectBottomY]
+      // 距离顶部
+      const selectTopY = selectAttr.value.y
+      const selectHalfY = selectTopY + selectH / 2
+      const selectBottomY = selectTopY + selectH
+      const selectY = [selectTopY, selectHalfY, selectBottomY]
 
-    line.select.clear()
-    line.sorptioned.y = false
-    // 循环查询所有组件数据
-    const componentList = chartEditStore.getComponentList.map((e: CreateComponentType | CreateComponentGroupType) => {
-      return {
-        id: e.id,
-        attr: e.attr
-      }
-    })
-    componentList.push(canvasPositionList.value)
-    // 传入画布数据
-    line.lineArr.forEach(lineItem => {
-      componentList.forEach((component: typeof canvasPositionList.value) => {
-        // 排除自身
-        if (selectId.value[0] === component.id) return
-        const componentW = component.attr.w
-        const componentH = component.attr.h
+      line.select.clear()
+      line.sorptioned.y = false
+      // 循环查询所有组件数据
+      const componentList = chartEditStore.getComponentList.map((e: CreateComponentType | CreateComponentGroupType) => {
+        return {
+          id: e.id,
+          attr: e.attr
+        }
+      })
+      componentList.push(canvasPositionList.value)
+      // 传入画布数据
+      line.lineArr.forEach(lineItem => {
+        componentList.forEach((component: typeof canvasPositionList.value) => {
+          // 排除自身
+          if (selectId.value[0] === component.id) return
+          const componentW = component.attr.w
+          const componentH = component.attr.h
 
-        // 距离左侧
-        const componentLeftX = component.attr.x
-        const componentHalfX = componentLeftX + componentW / 2
-        const componentRightX = componentLeftX + componentW
-        const componentX = [componentLeftX, componentHalfX, componentRightX]
+          // 距离左侧
+          const componentLeftX = component.attr.x
+          const componentHalfX = componentLeftX + componentW / 2
+          const componentRightX = componentLeftX + componentW
+          const componentX = [componentLeftX, componentHalfX, componentRightX]
 
-        // 距离顶部
-        const componentTopY = component.attr.y
-        const componentHalfY = componentTopY + componentH / 2
-        const componentBottomY = componentTopY + componentH
-        const componentY = [componentTopY, componentHalfY, componentBottomY]
+          // 距离顶部
+          const componentTopY = component.attr.y
+          const componentHalfY = componentTopY + componentH / 2
+          const componentBottomY = componentTopY + componentH
+          const componentY = [componentTopY, componentHalfY, componentBottomY]
 
-        // 横线对比的是 Y
-        if (lineItem.includes('rowt')) {
-          // 顶部
-          if (isSorption(selectTopY, componentTopY)) {
-            line.select.set(lineItem, { y: componentTopY })
-            selectTarget.value.setPosition(selectLeftX, componentTopY)
-          }
-          if (isSorption(selectTopY, componentHalfY)) {
-            line.select.set(lineItem, { y: componentHalfY })
-            selectTarget.value.setPosition(selectLeftX, componentHalfY)
-          }
-          if (isSorption(selectTopY, componentBottomY)) {
-            line.select.set(lineItem, { y: componentBottomY })
-            selectTarget.value.setPosition(selectLeftX, componentBottomY)
-          }
-        }
-        if (lineItem.includes('rowc')) {
-          // 顶部
-          if (isSorption(selectHalfY, componentTopY)) {
-            line.select.set(lineItem, { y: componentTopY })
-            selectTarget.value.setPosition(selectLeftX, componentTopY - selectH / 2)
-          }
-          if (isSorption(selectHalfY, componentHalfY)) {
-            line.select.set(lineItem, { y: componentHalfY })
-            selectTarget.value.setPosition(selectLeftX, componentHalfY - selectH / 2)
-          }
-          if (isSorption(selectHalfY, componentBottomY)) {
-            line.select.set(lineItem, { y: componentBottomY })
-            selectTarget.value.setPosition(selectLeftX, componentBottomY - selectH / 2)
-          }
-        }
-        if (lineItem.includes('rowb')) {
-          // 顶部
-          if (isSorption(selectBottomY, componentTopY)) {
-            line.select.set(lineItem, { y: componentTopY })
-            selectTarget.value.setPosition(selectLeftX, componentTopY - selectH)
+          // 横线对比的是 Y
+          if (lineItem.includes('rowt')) {
+            // 顶部
+            if (isSorption(selectTopY, componentTopY)) {
+              line.select.set(lineItem, { y: componentTopY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentTopY)
+            }
+            if (isSorption(selectTopY, componentHalfY)) {
+              line.select.set(lineItem, { y: componentHalfY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentHalfY)
+            }
+            if (isSorption(selectTopY, componentBottomY)) {
+              line.select.set(lineItem, { y: componentBottomY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentBottomY)
+            }
           }
-          if (isSorption(selectBottomY, componentHalfY)) {
-            line.select.set(lineItem, { y: componentHalfY })
-            selectTarget.value.setPosition(selectLeftX, componentHalfY - selectH)
+          if (lineItem.includes('rowc')) {
+            // 顶部
+            if (isSorption(selectHalfY, componentTopY)) {
+              line.select.set(lineItem, { y: componentTopY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentTopY - selectH / 2)
+            }
+            if (isSorption(selectHalfY, componentHalfY)) {
+              line.select.set(lineItem, { y: componentHalfY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentHalfY - selectH / 2)
+            }
+            if (isSorption(selectHalfY, componentBottomY)) {
+              line.select.set(lineItem, { y: componentBottomY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentBottomY - selectH / 2)
+            }
           }
-          if (isSorption(selectBottomY, componentBottomY)) {
-            line.select.set(lineItem, { y: componentBottomY })
-            selectTarget.value.setPosition(selectLeftX, componentBottomY - selectH)
+          if (lineItem.includes('rowb')) {
+            // 顶部
+            if (isSorption(selectBottomY, componentTopY)) {
+              line.select.set(lineItem, { y: componentTopY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentTopY - selectH)
+            }
+            if (isSorption(selectBottomY, componentHalfY)) {
+              line.select.set(lineItem, { y: componentHalfY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentHalfY - selectH)
+            }
+            if (isSorption(selectBottomY, componentBottomY)) {
+              line.select.set(lineItem, { y: componentBottomY })
+              setComponentPosition(selectTarget.value, selectLeftX, componentBottomY - selectH)
+            }
           }
-        }
 
-        // 纵线对比的是 X
-        if (lineItem.includes('coll')) {
-          if (isSorption(selectLeftX, componentLeftX)) {
-            line.select.set(lineItem, { x: componentLeftX })
-            selectTarget.value.setPosition(componentLeftX, selectTopY)
-          }
-          if (isSorption(selectLeftX, componentHalfX)) {
-            line.select.set(lineItem, { x: componentHalfX })
-            selectTarget.value.setPosition(componentHalfX, selectTopY)
-          }
-          if (isSorption(selectLeftX, componentRightX)) {
-            line.select.set(lineItem, { x: componentRightX })
-            selectTarget.value.setPosition(componentRightX, selectTopY)
+          // 纵线对比的是 X
+          if (lineItem.includes('coll')) {
+            if (isSorption(selectLeftX, componentLeftX)) {
+              line.select.set(lineItem, { x: componentLeftX })
+              setComponentPosition(selectTarget.value, componentLeftX, selectTopY)
+            }
+            if (isSorption(selectLeftX, componentHalfX)) {
+              line.select.set(lineItem, { x: componentHalfX })
+              setComponentPosition(selectTarget.value, componentHalfX, selectTopY)
+            }
+            if (isSorption(selectLeftX, componentRightX)) {
+              line.select.set(lineItem, { x: componentRightX })
+              setComponentPosition(selectTarget.value, componentRightX, selectTopY)
+            }
           }
-        }
-        if (lineItem.includes('colc')) {
-          if (isSorption(selectHalfX, componentLeftX)) {
-            line.select.set(lineItem, { x: componentLeftX })
-            selectTarget.value.setPosition(componentLeftX - selectW / 2, selectTopY)
-          }
-          if (isSorption(selectHalfX, componentHalfX)) {
-            line.select.set(lineItem, { x: componentHalfX })
-            selectTarget.value.setPosition(componentHalfX - selectW / 2, selectTopY)
+          if (lineItem.includes('colc')) {
+            if (isSorption(selectHalfX, componentLeftX)) {
+              line.select.set(lineItem, { x: componentLeftX })
+              setComponentPosition(selectTarget.value, componentLeftX - selectW / 2, selectTopY)
+            }
+            if (isSorption(selectHalfX, componentHalfX)) {
+              line.select.set(lineItem, { x: componentHalfX })
+              setComponentPosition(selectTarget.value, componentHalfX - selectW / 2, selectTopY)
+            }
+            if (isSorption(selectHalfX, componentRightX)) {
+              line.select.set(lineItem, { x: componentRightX })
+              setComponentPosition(selectTarget.value, componentRightX - selectW / 2, selectTopY)
+            }
           }
-          if (isSorption(selectHalfX, componentRightX)) {
-            line.select.set(lineItem, { x: componentRightX })
-            selectTarget.value.setPosition(componentRightX - selectW / 2, selectTopY)
+          if (lineItem.includes('colr')) {
+            if (isSorption(selectRightX, componentLeftX)) {
+              line.select.set(lineItem, { x: componentLeftX })
+              setComponentPosition(selectTarget.value, componentLeftX - selectW, selectTopY)
+            }
+            if (isSorption(selectRightX, componentHalfX)) {
+              line.select.set(lineItem, { x: componentHalfX })
+              setComponentPosition(selectTarget.value, componentHalfX - selectW, selectTopY)
+            }
+            if (isSorption(selectRightX, componentRightX)) {
+              line.select.set(lineItem, { x: componentRightX })
+              setComponentPosition(selectTarget.value, componentRightX - selectW, selectTopY)
+            }
           }
-        }
-        if (lineItem.includes('colr')) {
-          if (isSorption(selectRightX, componentLeftX)) {
-            line.select.set(lineItem, { x: componentLeftX })
-            selectTarget.value.setPosition(componentLeftX - selectW, selectTopY)
-          }
-          if (isSorption(selectRightX, componentHalfX)) {
-            line.select.set(lineItem, { x: componentHalfX })
-            selectTarget.value.setPosition(componentHalfX - selectW, selectTopY)
-          }
-          if (isSorption(selectRightX, componentRightX)) {
-            line.select.set(lineItem, { x: componentRightX })
-            selectTarget.value.setPosition(componentRightX - selectW, selectTopY)
-          }
-        }
+        })
       })
-    })
+    } catch (err) {
+      console.log(err)
+    }
   }, 200),
   {
     deep: true

+ 9 - 11
src/views/chart/ContentEdit/components/EditBottom/index.vue

@@ -1,15 +1,18 @@
 <template>
   <div class="go-edit-bottom">
-    <div class="go-flex-items-center">
+    <n-space>
+      <!-- 历史记录 -->
       <edit-history></edit-history>
+      <!-- CTRL按键触发展示 -->
+      <n-text id="keyboard-dress-show" depth="3"></n-text>
       <n-divider vertical />
       <edit-data-sync></edit-data-sync>
-    </div>
+    </n-space>
 
     <n-space class="bottom-ri">
       <!-- 快捷键提示 -->
       <edit-shortcut-key />
-      
+
       <!-- 缩放比例 -->
       <n-select
         :disabled="lockScale"
@@ -18,18 +21,13 @@
         size="mini"
         :options="filterOptions"
         @update:value="selectHandle"
-     ></n-select>
+      ></n-select>
 
       <!-- 锁定缩放 -->
       <n-tooltip trigger="hover">
         <template #trigger>
           <n-button @click="lockHandle" text>
-            <n-icon
-              class="lock-icon"
-              :class="{ color: lockScale }"
-              size="18"
-              :depth="2"
-            >
+            <n-icon class="lock-icon" :class="{ color: lockScale }" size="18" :depth="2">
               <lock-closed-outline-icon v-if="lockScale"></lock-closed-outline-icon>
               <lock-open-outline-icon v-else></lock-open-outline-icon>
             </n-icon>
@@ -50,7 +48,7 @@
         :disabled="lockScale"
         :marks="sliderMaks"
         @update:value="sliderHandle"
-     ></n-slider>
+      ></n-slider>
     </n-space>
   </div>
 </template>

+ 14 - 6
src/views/chart/ContentEdit/components/EditTools/hooks/useFile.hooks.ts

@@ -32,15 +32,23 @@ export const useFile = () => {
             negativeButtonProps: { type: 'info', ghost: false },
             // 新增
             onPositiveCallback: async () => {
-              fileData = JSON.parse(fileData)
-              await updateComponent(fileData, false, true)
-              window['$message'].success('导入成功!')
+              try {
+                fileData = JSON.parse(fileData)
+                await updateComponent(fileData, false, true)
+                window['$message'].success('导入成功!')
+              } catch (error) {
+                window['$message'].error('组件导入失败,请检查文件完整性!')
+              }
             },
             // 覆盖
             onNegativeCallback: async () => {
-              fileData = JSON.parse(fileData)
-              await updateComponent(fileData, true, true)
-              window['$message'].success('导入成功!')
+              try {
+                fileData = JSON.parse(fileData)
+                await updateComponent(fileData, true, true)
+                window['$message'].success('导入成功!')
+              } catch (error) {
+                window['$message'].error('组件导入失败,请检查文件完整性!')
+              }
             }
           })
         })

+ 35 - 38
src/views/chart/ContentEdit/hooks/useDrag.hook.ts

@@ -1,12 +1,12 @@
 import { toRaw } from 'vue'
-import { DragKeyEnum, MouseEventButton, WinKeyboard, MacKeyboard } from '@/enums/editPageEnum'
+import { DragKeyEnum, MouseEventButton } from '@/enums/editPageEnum'
 import { createComponent } from '@/packages'
 import { ConfigType } from '@/packages/index.d'
 import { CreateComponentType, CreateComponentGroupType, PickCreateComponentType } from '@/packages/index.d'
 import { useContextMenu } from '@/views/chart/hooks/useContextMenu.hook'
 import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
 import { EditCanvasTypeEnum } from '@/store/modules/chartEditStore/chartEditStore.d'
-import { loadingStart, loadingFinish, loadingError } from '@/utils'
+import { loadingStart, loadingFinish, loadingError, setComponentPosition } from '@/utils'
 import { throttle, cloneDeep } from 'lodash'
 
 const chartEditStore = useChartEditStore()
@@ -33,7 +33,7 @@ export const dragHandle = async (e: DragEvent) => {
     // 创建新图表组件
     let newComponent: CreateComponentType = await createComponent(dropData)
 
-    newComponent.setPosition(e.offsetX - newComponent.attr.w / 2, e.offsetY - newComponent.attr.h / 2)
+    setComponentPosition(newComponent, e.offsetX - newComponent.attr.w / 2, e.offsetY - newComponent.attr.h / 2)
     chartEditStore.addComponentList(newComponent, false, true)
     chartEditStore.setTargetSelectChart(newComponent.id)
     loadingFinish()
@@ -172,10 +172,7 @@ export const useMouseHandle = () => {
     e.stopPropagation()
     if (item.status.lock) return
     // 若此时按下了 CTRL, 表示多选
-    if (
-      window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
-      window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY)
-    ) {
+    if (window.$KeyboardActive?.ctrl) {
       // 若已选中,则去除
       if (chartEditStore.targetChart.selectId.includes(item.id)) {
         const exList = chartEditStore.targetChart.selectId.filter(e => e !== item.id)
@@ -193,12 +190,7 @@ export const useMouseHandle = () => {
     if (item.status.lock) return
     onClickOutSide()
     // 按下左键 + CTRL
-    if (
-      e.buttons === MouseEventButton.LEFT &&
-      (window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
-        window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY))
-    )
-      return
+    if (e.buttons === MouseEventButton.LEFT && window.$KeyboardActive?.ctrl) return
 
     // 按下右键 + 选中多个 + 目标元素是多选子元素
     const selectId = chartEditStore.getTargetChart.selectId
@@ -271,38 +263,43 @@ export const useMouseHandle = () => {
         // 基于右下角位置检测
         currX = currX > canvasWidth - distance ? canvasWidth - distance : currX
         currY = currY > canvasHeight - distance ? canvasHeight - distance : currY
-
-        componentInstance.attr = Object.assign(componentInstance.attr, {
-          x: currX,
-          y: currY
-        })
+        if (componentInstance) {
+          componentInstance.attr = Object.assign(componentInstance.attr, {
+            x: currX,
+            y: currY
+          })
+        }
       })
       return
     }, 20)
 
     const mouseup = () => {
-      chartEditStore.setMousePosition(0, 0, 0, 0)
-      chartEditStore.setEditCanvas(EditCanvasTypeEnum.IS_DRAG, false)
-      // 加入历史栈
-      if (prevComponentInstance.length) {
-        chartEditStore.getTargetChart.selectId.forEach(id => {
-          if (!targetMap.has(id)) return
-          const index = chartEditStore.fetchTargetIndex(id)
-          const curComponentInstance = chartEditStore.getComponentList[index]
-          // 找到记录的所选组件
-          prevComponentInstance.forEach(preItem => {
-            if (preItem.id === id) {
-              preItem.attr = Object.assign(preItem.attr, {
-                offsetX: curComponentInstance.attr.x - preItem.attr.x,
-                offsetY: curComponentInstance.attr.y - preItem.attr.y
-              })
-            }
+      try {
+        chartEditStore.setMousePosition(0, 0, 0, 0)
+        chartEditStore.setEditCanvas(EditCanvasTypeEnum.IS_DRAG, false)
+        // 加入历史栈
+        if (prevComponentInstance.length) {
+          chartEditStore.getTargetChart.selectId.forEach(id => {
+            if (!targetMap.has(id)) return
+            const index = chartEditStore.fetchTargetIndex(id)
+            const curComponentInstance = chartEditStore.getComponentList[index]
+            // 找到记录的所选组件
+            prevComponentInstance.forEach(preItem => {
+              if (preItem.id === id) {
+                preItem.attr = Object.assign(preItem.attr, {
+                  offsetX: curComponentInstance.attr.x - preItem.attr.x,
+                  offsetY: curComponentInstance.attr.y - preItem.attr.y
+                })
+              }
+            })
           })
-        })
-        chartEditStore.moveComponentList(prevComponentInstance)
+          chartEditStore.moveComponentList(prevComponentInstance)
+        }
+        document.removeEventListener('mousemove', mousemove)
+        document.removeEventListener('mouseup', mouseup)
+      } catch (err) {
+        console.log(err)
       }
-      document.removeEventListener('mousemove', mousemove)
-      document.removeEventListener('mouseup', mouseup)
     }
 
     document.addEventListener('mousemove', mousemove)

+ 2 - 10
src/views/chart/ContentLayers/components/LayersGroupListItem/index.vue

@@ -142,11 +142,7 @@ const optionsHandle = (
 // 点击
 const clickHandle = (e: MouseEvent) => {
   // 按下左键 + CTRL
-  if (
-    window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
-    window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY)
-  )
-    return
+  if (window.$KeyboardActive?.ctrl) return
   // 判断左右键
   expend.value = !expend.value
   mousedownHandle(e, props.componentGroupData)
@@ -157,11 +153,7 @@ const groupMousedownHandle = (e: MouseEvent) => {
   onClickOutSide()
   // 若此时按下了 CTRL, 表示多选
   const id = props.componentGroupData.id
-  if (
-    e.buttons === MouseEventButton.LEFT &&
-    (window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
-      window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY))
-  ) {
+  if (e.buttons === MouseEventButton.LEFT && window.$KeyboardActive?.ctrl) {
     // 若已选中,则去除
     if (chartEditStore.targetChart.selectId.includes(id)) {
       const exList = chartEditStore.targetChart.selectId.filter(e => e !== id)

+ 2 - 2
src/views/chart/ContentLayers/components/LayersListItem/index.vue

@@ -9,9 +9,9 @@
         :fallback-src="requireErrorImg()"
       ></n-image>
       <n-ellipsis style="margin-right: auto">
-        <n-text class="list-text" :depth="2">
+        <span class="list-text">
           {{ props.componentData.chartConfig.title }}
-        </n-text>
+        </span>
       </n-ellipsis>
       <layers-status :isGroup="isGroup" :hover="hover" :status="status"></layers-status>
     </div>

+ 1 - 6
src/views/chart/ContentLayers/index.vue

@@ -166,11 +166,7 @@ const mousedownHandle = (e: MouseEvent, item: CreateComponentType) => {
   onClickOutSide()
   // 若此时按下了 CTRL, 表示多选
   const id = item.id
-  if (
-    e.buttons === MouseEventButton.LEFT &&
-    (window.$KeyboardActive?.has(WinKeyboard.CTRL_SOURCE_KEY) ||
-      window.$KeyboardActive?.has(MacKeyboard.CTRL_SOURCE_KEY))
-  ) {
+  if (e.buttons === MouseEventButton.LEFT && window.$KeyboardActive?.ctrl) {
     // 若已选中,则去除
     if (chartEditStore.targetChart.selectId.includes(id)) {
       const exList = chartEditStore.targetChart.selectId.filter(e => e !== id)
@@ -198,7 +194,6 @@ const changeLayerType = (value: LayerModeEnum) => {
   layerMode.value = value
   chartLayoutStore.setItem(ChartLayoutStoreEnum.LAYER_TYPE, value)
 }
-
 </script>
 
 <style lang="scss" scoped>

+ 20 - 11
src/views/chart/hooks/useKeyboard.hook.ts

@@ -3,8 +3,9 @@ import { useSync } from './useSync.hook'
 import { WinKeyboard, MacKeyboard, MenuEnum } from '@/enums/editPageEnum'
 import throttle from 'lodash/throttle'
 import debounce from 'lodash/debounce'
-
 import keymaster from 'keymaster'
+import { setKeyboardDressShow } from '@/utils'
+
 // Keymaster可以支持识别以下组合键: ⇧,shift,option,⌥,alt,ctrl,control,command,和⌘
 const chartEditStore = useChartEditStore()
 const useSyncIns = useSync()
@@ -29,7 +30,7 @@ export const winKeyboardValue = {
   [MenuEnum.LOCK]: winCtrlMerge('l'),
   [MenuEnum.UNLOCK]: winCtrlMerge(winShiftMerge('l')),
   [MenuEnum.HIDE]: winCtrlMerge('h'),
-  [MenuEnum.SHOW]: winCtrlMerge(winShiftMerge('h')),
+  [MenuEnum.SHOW]: winCtrlMerge(winShiftMerge('h'))
 }
 
 // 这个 Ctrl 后面还是换成了 ⌘
@@ -55,7 +56,7 @@ export const macKeyboardValue = {
   [MenuEnum.LOCK]: macCtrlMerge('l'),
   [MenuEnum.UNLOCK]: macCtrlMerge(macShiftMerge('l')),
   [MenuEnum.HIDE]: macCtrlMerge('h'),
-  [MenuEnum.SHOW]: macCtrlMerge(macShiftMerge('h')),
+  [MenuEnum.SHOW]: macCtrlMerge(macShiftMerge('h'))
 }
 
 // Win 快捷键列表
@@ -81,7 +82,7 @@ const winKeyList: Array<string> = [
   winKeyboardValue.unLock,
 
   winKeyboardValue.hide,
-  winKeyboardValue.show,
+  winKeyboardValue.show
 ]
 
 // Mac 快捷键列表
@@ -107,21 +108,29 @@ const macKeyList: Array<string> = [
   macKeyboardValue.unLock,
 
   macKeyboardValue.hide,
-  macKeyboardValue.show,
+  macKeyboardValue.show
 ]
 
 // 处理键盘记录
 const keyRecordHandle = () => {
-  // 初始化清空
-  if(window.$KeyboardActive) window.$KeyboardActive = new Set([])
-
+  // 默认赋值
+  window.$KeyboardActive = {
+    ctrl: false
+  }
+  
   document.onkeydown = (e: KeyboardEvent) => {
-    if(window.$KeyboardActive) window.$KeyboardActive.add(e.key.toLocaleLowerCase())
-    else window.$KeyboardActive = new Set([e.key.toLocaleLowerCase()])
+    if(e.keyCode === 17 && window.$KeyboardActive) {
+      setKeyboardDressShow(e.keyCode)
+      window.$KeyboardActive.ctrl = true
+    }
   }
 
   document.onkeyup = (e: KeyboardEvent) => {
-    if(window.$KeyboardActive) window.$KeyboardActive.delete(e.key.toLocaleLowerCase())
+    if(e.keyCode === 17 && window.$KeyboardActive) 
+    {
+      window.$KeyboardActive.ctrl = false
+      setKeyboardDressShow()
+    }
   }
 }
 

+ 33 - 32
src/views/chart/hooks/useSync.hook.ts

@@ -29,7 +29,7 @@ const componentMerge = (object: any, sources: any, notComponent = false) => {
   if (notComponent) return merge(object, sources)
   // 组件排除 options
   const option = sources.option
-  if(!option) return merge(object, sources)
+  if (!option) return merge(object, sources)
 
   // 为 undefined 的 sources 来源对象属性将被跳过详见 https://www.lodashjs.com/docs/lodash.merge
   sources.option = undefined
@@ -78,37 +78,38 @@ export const useSync = () => {
         intComponent(e as CreateComponentType)
       }
     })
+
+    // 创建函数-重新创建是为了处理类种方法消失的问题
+    const create = async (
+      _componentInstance: CreateComponentType,
+      callBack?: (componentInstance: CreateComponentType) => void
+    ) => {
+      // 补充 class 上的方法
+      let newComponent: CreateComponentType = await createComponent(_componentInstance.chartConfig)
+      if (callBack) {
+        if (changeId) {
+          callBack(componentMerge(newComponent, { ..._componentInstance, id: getUUID() }))
+        } else {
+          callBack(componentMerge(newComponent, _componentInstance))
+        }
+      } else {
+        if (changeId) {
+          chartEditStore.addComponentList(
+            componentMerge(newComponent, { ..._componentInstance, id: getUUID() }),
+            false,
+            true
+          )
+        } else {
+          chartEditStore.addComponentList(componentMerge(newComponent, _componentInstance), false, true)
+        }
+      }
+    }
+
     // 数据赋值
     for (const key in projectData) {
       // 组件
       if (key === ChartEditStoreEnum.COMPONENT_LIST) {
         for (const comItem of projectData[key]) {
-          // 重新创建是为了处理类种方法消失的问题
-          const create = async (
-            _componentInstance: CreateComponentType,
-            callBack?: (componentInstance: CreateComponentType) => void
-          ) => {
-            // 补充 class 上的方法
-            let newComponent: CreateComponentType = await createComponent(_componentInstance.chartConfig)
-            if (callBack) {
-              if (changeId) {
-                callBack(componentMerge(newComponent, { ..._componentInstance, id: getUUID() }))
-              } else {
-                callBack(componentMerge(newComponent, _componentInstance))
-              }
-            } else {
-              if (changeId) {
-                chartEditStore.addComponentList(
-                  componentMerge(newComponent, { ..._componentInstance, id: getUUID() }),
-                  false,
-                  true
-                )
-              } else {
-                chartEditStore.addComponentList(componentMerge(newComponent, _componentInstance), false, true)
-              }
-            }
-          }
-
           if (comItem.isGroup) {
             // 创建分组
             let groupClass = new PublicGroupConfigClass()
@@ -118,19 +119,19 @@ export const useSync = () => {
               groupClass = componentMerge(groupClass, comItem)
             }
 
-            // 注册子应用
+            // 异步注册子应用
             const targetList: CreateComponentType[] = []
-            ;(comItem as CreateComponentGroupType).groupList.forEach(groupItem => {
-              create(groupItem, e => {
+            for (const groupItem of (comItem as CreateComponentGroupType).groupList) {
+              await create(groupItem, e => {
                 targetList.push(e)
               })
-            })
+            }
             groupClass.groupList = targetList
 
             // 分组插入到列表
             chartEditStore.addComponentList(groupClass, false, true)
           } else {
-            create(comItem as CreateComponentType)
+            await  create(comItem as CreateComponentType)
           }
         }
       } else {

+ 2 - 2
types/global.d.ts

@@ -6,7 +6,7 @@ interface Window {
   $t: any
   $vue: any
   // 键盘按键记录
-  $KeyboardActive?: Set<string>
+  $KeyboardActive?: { [T: string]: boolean }
 }
 
 
@@ -16,4 +16,4 @@ declare interface MyResponseType {
   data: any;
 }
 
-declare type Recordable<T = any> = Record<string, T>
+declare type Recordable<T = any> = Record<string, T>