Ver código fonte

图表拖拽

liujiejie 1 ano atrás
pai
commit
ad0545b154
35 arquivos alterados com 1662 adições e 14 exclusões
  1. 57 10
      package-lock.json
  2. 5 1
      package.json
  3. 18 0
      src/assets/js/class/chart.js
  4. 7 0
      src/assets/js/constants/config.js
  5. 39 0
      src/assets/js/constants/echarts-config/bar.js
  6. 127 0
      src/assets/js/constants/echarts-config/barandline.js
  7. 83 0
      src/assets/js/constants/echarts-config/line.js
  8. 40 0
      src/assets/js/constants/echarts-config/pie.js
  9. 43 0
      src/assets/js/constants/echarts-config/radar.js
  10. 57 0
      src/assets/js/constants/echarts-config/scatter.js
  11. 27 0
      src/assets/js/constants/index.js
  12. 1 0
      src/icons/svg/bar.svg
  13. 0 0
      src/icons/svg/barandline.svg
  14. 1 0
      src/icons/svg/baseData.svg
  15. 0 0
      src/icons/svg/baseData1.svg
  16. 1 0
      src/icons/svg/fields.svg
  17. 0 0
      src/icons/svg/fields2.svg
  18. 0 0
      src/icons/svg/line.svg
  19. 1 0
      src/icons/svg/pie.svg
  20. 0 0
      src/icons/svg/radar.svg
  21. 0 0
      src/icons/svg/scatter.svg
  22. 0 0
      src/icons/svg/text.svg
  23. 6 0
      src/main.js
  24. 109 0
      src/store/dragChart.js
  25. 2 0
      src/store/index.js
  26. 87 0
      src/views/performance/components/custonAsCom/dragChart/components/chart/index.vue
  27. 37 0
      src/views/performance/components/custonAsCom/dragChart/components/chartConfig/config.js
  28. 4 0
      src/views/performance/components/custonAsCom/dragChart/components/chartConfig/form/index.js
  29. 136 0
      src/views/performance/components/custonAsCom/dragChart/components/chartConfig/form/legend.vue
  30. 126 0
      src/views/performance/components/custonAsCom/dragChart/components/chartConfig/form/title.vue
  31. 172 0
      src/views/performance/components/custonAsCom/dragChart/components/chartsAttributes.vue
  32. 266 0
      src/views/performance/components/custonAsCom/dragChart/components/chartsContent.vue
  33. 129 0
      src/views/performance/components/custonAsCom/dragChart/components/chartsData.vue
  34. 68 0
      src/views/performance/components/custonAsCom/dragChart/index.vue
  35. 13 3
      src/views/performance/customAnalysis.vue

+ 57 - 10
package-lock.json

@@ -14,12 +14,13 @@
         "axios": "^1.6.8",
         "core-js": "^3.8.3",
         "css-minimizer-webpack-plugin": "^7.0.0",
-        "echarts": "^5.5.0",
+        "echarts": "^5.5.1",
         "el-tree-select": "^3.1.14",
         "element-ui": "^2.15.14",
         "file-saver": "^2.0.5",
         "happypack": "^5.0.1",
         "jszip": "^3.10.1",
+        "lodash": "^4.17.21",
         "luckyexcel": "^1.0.1",
         "luckysheet": "^2.1.13",
         "ol": "^9.2.3",
@@ -32,8 +33,11 @@
         "svg-sprite-loader": "^6.0.11",
         "terser-webpack-plugin": "^5.3.10",
         "vue": "^2.6.14",
+        "vue-draggable-resizable": "^2.3.0",
+        "vue-draggable-resizable-gorkys": "^2.4.8",
         "vue-quill-editor": "^3.0.6",
         "vue-router": "^3.5.1",
+        "vue-ruler-tool": "^1.2.4",
         "vuex": "^3.6.2",
         "vuex-persistedstate": "^4.1.0",
         "workbox-webpack-plugin": "^7.1.0",
@@ -7877,12 +7881,13 @@
       }
     },
     "node_modules/echarts": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.5.0.tgz",
-      "integrity": "sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw==",
+      "version": "5.5.1",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/echarts/-/echarts-5.5.1.tgz",
+      "integrity": "sha512-Fce8upazaAXUVUVsjgV6mBnGuqgO+JNDlcgF79Dksy4+wgGpQB2lmYoO4TSweFg/mZITdpGHomw/cNBJZj1icA==",
+      "license": "Apache-2.0",
       "dependencies": {
         "tslib": "2.3.0",
-        "zrender": "5.5.0"
+        "zrender": "5.6.0"
       }
     },
     "node_modules/edges-to-adjacency-list": {
@@ -12337,8 +12342,9 @@
     },
     "node_modules/lodash": {
       "version": "4.17.21",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
-      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+      "resolved": "https://repo.huaweicloud.com/repository/npm/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "license": "MIT"
     },
     "node_modules/lodash-es": {
       "version": "4.17.21",
@@ -19870,6 +19876,37 @@
       "integrity": "sha512-LluhjWTZmpGl3tiXg51EciF+T70IN/9t6UvfmgluJBqxbrb6OV9i7L5lTd+OKtcTeghDkhcBmYhtTxxU4w/8sQ==",
       "dev": true
     },
+    "node_modules/vue-draggable-resizable": {
+      "version": "2.3.0",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-draggable-resizable/-/vue-draggable-resizable-2.3.0.tgz",
+      "integrity": "sha512-77CLRj1TPwB30pwsjOf3pkd1UzYanCdKXbqhILJ0Oo5QQl50lvBfyQCXxMFzwWwTc3sbBbQH3FfWSV+BkoSElA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/vue-draggable-resizable-gorkys": {
+      "version": "2.4.8",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-draggable-resizable-gorkys/-/vue-draggable-resizable-gorkys-2.4.8.tgz",
+      "integrity": "sha512-DjhelFtRN1cy/AVpYbxlhmTTC5KwLvU490nUZzKp05kMddhzu5TBrBEeYo/9rhmdsrSkxjQguyRvF6IYumw9yw==",
+      "license": "MIT",
+      "dependencies": {
+        "core-js": "^2.6.11"
+      },
+      "engines": {
+        "node": ">= 4.0.0",
+        "npm": ">= 3.0.0"
+      }
+    },
+    "node_modules/vue-draggable-resizable-gorkys/node_modules/core-js": {
+      "version": "2.6.12",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/core-js/-/core-js-2.6.12.tgz",
+      "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+      "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+      "hasInstallScript": true,
+      "license": "MIT"
+    },
     "node_modules/vue-hot-reload-api": {
       "version": "2.3.4",
       "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
@@ -19957,6 +19994,15 @@
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz",
       "integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ=="
     },
+    "node_modules/vue-ruler-tool": {
+      "version": "1.2.4",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-ruler-tool/-/vue-ruler-tool-1.2.4.tgz",
+      "integrity": "sha512-0PvzJ4YRaYsb3os7EVdpdjvBimRtIgVumve4MGUIqCY7HMctNSiIt/gRblL0JuGEMQyurAeLjhdN/GYvmvuHRQ==",
+      "license": "MIT",
+      "dependencies": {
+        "vue": "^2.5.11"
+      }
+    },
     "node_modules/vue-style-loader": {
       "version": "4.1.3",
       "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@@ -20906,9 +20952,10 @@
       }
     },
     "node_modules/zrender": {
-      "version": "5.5.0",
-      "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.5.0.tgz",
-      "integrity": "sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w==",
+      "version": "5.6.0",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/zrender/-/zrender-5.6.0.tgz",
+      "integrity": "sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==",
+      "license": "BSD-3-Clause",
       "dependencies": {
         "tslib": "2.3.0"
       }

+ 5 - 1
package.json

@@ -14,12 +14,13 @@
     "axios": "^1.6.8",
     "core-js": "^3.8.3",
     "css-minimizer-webpack-plugin": "^7.0.0",
-    "echarts": "^5.5.0",
+    "echarts": "^5.5.1",
     "el-tree-select": "^3.1.14",
     "element-ui": "^2.15.14",
     "file-saver": "^2.0.5",
     "happypack": "^5.0.1",
     "jszip": "^3.10.1",
+    "lodash": "^4.17.21",
     "luckyexcel": "^1.0.1",
     "luckysheet": "^2.1.13",
     "ol": "^9.2.3",
@@ -32,8 +33,11 @@
     "svg-sprite-loader": "^6.0.11",
     "terser-webpack-plugin": "^5.3.10",
     "vue": "^2.6.14",
+    "vue-draggable-resizable": "^2.3.0",
+    "vue-draggable-resizable-gorkys": "^2.4.8",
     "vue-quill-editor": "^3.0.6",
     "vue-router": "^3.5.1",
+    "vue-ruler-tool": "^1.2.4",
     "vuex": "^3.6.2",
     "vuex-persistedstate": "^4.1.0",
     "workbox-webpack-plugin": "^7.1.0",

+ 18 - 0
src/assets/js/class/chart.js

@@ -0,0 +1,18 @@
+/**
+ * @description 图表类
+ * @params {Object} 图表 option
+ * @params {Number} 横坐标百分比 x
+ * @params {Number} 纵坐标百分比 y
+ * @params {Number} 宽度百分比 width
+ * @params {Number} 高度百分比 height
+ */
+export default class chartClass {
+  constructor({ option, x = 0, y = 0, width = 300, height = 300 }) {
+    this.id = Math.random()
+    this.option = option
+    this.x = x
+    this.y = y
+    this.width = width
+    this.height = height
+  }
+}

+ 7 - 0
src/assets/js/constants/config.js

@@ -0,0 +1,7 @@
+module.exports = {
+  screenSize: {
+    large: {},
+  },
+  SCALE: 1, // 画布缩放尺寸
+  DEFAULT_THEME: "",
+};

+ 39 - 0
src/assets/js/constants/echarts-config/bar.js

@@ -0,0 +1,39 @@
+export const option = {
+  title: {
+    text: "Dynamic Data",
+  },
+  tooltip: {
+    trigger: "axis",
+    axisPointer: {
+      type: "shadow",
+    },
+  },
+  grid: {
+    left: "3%",
+    right: "4%",
+    bottom: "3%",
+    containLabel: true,
+  },
+  xAxis: {
+    name: "周",
+    type: "category",
+    data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+    axisTick: {
+      alignWithLabel: true,
+    },
+  },
+
+  yAxis: {
+    name: "个",
+    type: "value",
+  },
+
+  series: [
+    {
+      name: "Direct",
+      type: "bar",
+      barWidth: "60%",
+      data: [10, 52, 200, 334, 390, 330, 220],
+    },
+  ],
+};

+ 127 - 0
src/assets/js/constants/echarts-config/barandline.js

@@ -0,0 +1,127 @@
+const colors = ["#5470C6", "#91CC75", "#EE6666"];
+export const option = {
+  color: colors,
+  tooltip: {
+    trigger: "axis",
+    axisPointer: {
+      type: "cross",
+    },
+  },
+  title: {
+    text: "Dynamic Data",
+  },
+  //   xAxis: {
+  //     type: "value",
+  //     name: "xlable",
+  //   },
+  //   yAxis: {
+  //     type: "value",
+  //     name: "温度",
+  //   },
+  grid: {
+    right: "20%",
+  },
+  toolbox: {
+    feature: {
+      dataView: { show: true, readOnly: false },
+      restore: { show: true },
+      saveAsImage: { show: true },
+    },
+  },
+  legend: {
+    data: ["Evaporation", "Precipitation", "Temperature"],
+  },
+  xAxis: [
+    {
+      type: "category",
+      axisTick: {
+        alignWithLabel: true,
+      },
+      data: [
+        "Jan",
+        "Feb",
+        "Mar",
+        "Apr",
+        "May",
+        "Jun",
+        "Jul",
+        "Aug",
+        "Sep",
+        "Oct",
+        "Nov",
+        "Dec",
+      ],
+    },
+  ],
+  yAxis: [
+    {
+      type: "value",
+      name: "Evaporation",
+      position: "right",
+      alignTicks: true,
+      axisLine: {
+        show: false, // 隐藏蒸发的 Y 轴轴线
+      },
+      axisLabel: {
+        show: false, // 隐藏蒸发的 Y 轴标签
+      },
+      splitLine: {
+        show: false, // 隐藏蒸发的 Y 轴网格线
+      },
+    },
+    {
+      type: "value",
+      name: "Precipitation",
+      position: "right",
+      alignTicks: true,
+      offset: 80,
+      axisLine: {
+        show: false, // 隐藏降水的 Y 轴轴线
+      },
+      axisLabel: {
+        show: false, // 隐藏降水的 Y 轴标签
+      },
+      splitLine: {
+        show: false, // 隐藏降水的 Y 轴网格线
+      },
+    },
+    {
+      type: "value",
+      name: "温度",
+      position: "left",
+      alignTicks: true,
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: colors[2],
+        },
+      },
+      axisLabel: {
+        formatter: "{value} °C",
+      },
+    },
+  ],
+  series: [
+    {
+      name: "Evaporation",
+      type: "bar",
+      data: [
+        2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3,
+      ],
+    },
+    {
+      name: "Precipitation",
+      type: "bar",
+      yAxisIndex: 1,
+      data: [
+        2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3,
+      ],
+    },
+    {
+      name: "Temperature",
+      type: "line",
+      yAxisIndex: 2,
+      data: [2.0, 2.2, 3.3, 4.5, 6.3, 10.2, 20.3, 23.4, 23.0, 16.5, 12.0, 6.2],
+    },
+  ],
+};

+ 83 - 0
src/assets/js/constants/echarts-config/line.js

@@ -0,0 +1,83 @@
+export const option = {
+  title: {
+    text: '堆叠区域图'
+  },
+  tooltip: {
+    trigger: 'axis',
+    axisPointer: {
+      type: 'cross',
+      label: {
+        backgroundColor: '#6a7985'
+      }
+    }
+  },
+  legend: {
+    data: ['邮件营销', '联盟广告', '视频广告', '直接访问', '搜索引擎']
+  },
+  toolbox: {
+    feature: {
+      saveAsImage: {}
+    }
+  },
+  grid: {
+    left: '3%',
+    right: '4%',
+    bottom: '3%',
+    containLabel: true
+  },
+  xAxis: [
+    {
+      type: 'category',
+      boundaryGap: false,
+      data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
+    }
+  ],
+  yAxis: [
+    {
+      type: 'value'
+    }
+  ],
+  series: [
+    {
+      name: '邮件营销',
+      type: 'line',
+      stack: '总量',
+      areaStyle: {},
+      data: [120, 132, 101, 134, 90, 230, 210]
+    },
+    {
+      name: '联盟广告',
+      type: 'line',
+      stack: '总量',
+      areaStyle: {},
+      data: [220, 182, 191, 234, 290, 330, 310]
+    },
+    {
+      name: '视频广告',
+      type: 'line',
+      stack: '总量',
+      areaStyle: {},
+      data: [150, 232, 201, 154, 190, 330, 410]
+    },
+    {
+      name: '直接访问',
+      type: 'line',
+      stack: '总量',
+      areaStyle: {},
+      data: [320, 332, 301, 334, 390, 330, 320]
+    },
+    {
+      name: '搜索引擎',
+      type: 'line',
+      stack: '总量',
+      label: {
+        normal: {
+          show: true,
+          position: 'top'
+        }
+      },
+      areaStyle: {},
+      data: [820, 932, 901, 934, 1290, 1330, 1320]
+    }
+  ]
+}

+ 40 - 0
src/assets/js/constants/echarts-config/pie.js

@@ -0,0 +1,40 @@
+export const option = {
+  tooltip: {
+    trigger: 'item',
+    formatter: '{a} <br/>{b}: {c} ({d}%)'
+  },
+  legend: {
+    orient: 'vertical',
+    left: 10,
+    data: ['直接访问', '邮件营销', '联盟广告', '视频广告', '搜索引擎']
+  },
+  series: [
+    {
+      name: '访问来源',
+      type: 'pie',
+      radius: ['50%', '70%'],
+      avoidLabelOverlap: false,
+      label: {
+        show: false,
+        position: 'center'
+      },
+      emphasis: {
+        label: {
+          show: true,
+          fontSize: '30',
+          fontWeight: 'bold'
+        }
+      },
+      labelLine: {
+        show: false
+      },
+      data: [
+        { value: 335, name: '直接访问' },
+        { value: 310, name: '邮件营销' },
+        { value: 234, name: '联盟广告' },
+        { value: 135, name: '视频广告' },
+        { value: 1548, name: '搜索引擎' }
+      ]
+    }
+  ]
+}

+ 43 - 0
src/assets/js/constants/echarts-config/radar.js

@@ -0,0 +1,43 @@
+/*
+ * @Author: your name
+ * @Date: 2024-11-04 11:33:31
+ * @LastEditTime: 2024-11-04 11:34:44
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/assets/js/constants/echarts-config/radar.js
+ */
+export const option = {
+  title: {
+    text: "Basic Radar Chart",
+  },
+  legend: {
+    data: ["Allocated Budget", "Actual Spending"],
+  },
+  radar: {
+    // shape: 'circle',
+    indicator: [
+      { name: "Sales", max: 6500 },
+      { name: "Administration", max: 16000 },
+      { name: "Information Technology", max: 30000 },
+      { name: "Customer Support", max: 38000 },
+      { name: "Development", max: 52000 },
+      { name: "Marketing", max: 25000 },
+    ],
+  },
+  series: [
+    {
+      name: "Budget vs spending",
+      type: "radar",
+      data: [
+        {
+          value: [4200, 3000, 20000, 35000, 50000, 18000],
+          name: "Allocated Budget",
+        },
+        {
+          value: [5000, 14000, 28000, 26000, 42000, 21000],
+          name: "Actual Spending",
+        },
+      ],
+    },
+  ],
+};

+ 57 - 0
src/assets/js/constants/echarts-config/scatter.js

@@ -0,0 +1,57 @@
+/*
+ * @Author: your name
+ * @Date: 2024-11-04 11:33:04
+ * @LastEditTime: 2024-11-04 14:32:08
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/assets/js/constants/echarts-config/scatter.js
+ */
+export const option = {
+  title: {
+    text: "Dynamic Data",
+  },
+  xAxis: {
+    type: "value",
+    name: "xlable",
+  },
+  yAxis: {
+    type: "value",
+    name: "温度",
+  },
+  grid: {
+    top: "20%",
+    right: "20%",
+    bottom: "10%",
+    containLabel: true, // 包含标签在内
+  },
+  series: [
+    {
+      symbolSize: 20,
+      data: [
+        [10.0, 8.04],
+        [8.07, 6.95],
+        [13.0, 7.58],
+        [9.05, 8.81],
+        [11.0, 8.33],
+        [14.0, 7.66],
+        [13.4, 6.81],
+        [10.0, 6.33],
+        [14.0, 8.96],
+        [12.5, 6.82],
+        [9.15, 7.2],
+        [11.5, 7.2],
+        [3.03, 4.23],
+        [12.2, 7.83],
+        [2.02, 4.47],
+        [1.05, 3.33],
+        [4.05, 4.96],
+        [6.03, 7.24],
+        [12.0, 6.26],
+        [12.0, 8.84],
+        [7.08, 5.82],
+        [5.02, 5.68],
+      ],
+      type: "scatter",
+    },
+  ],
+};

+ 27 - 0
src/assets/js/constants/index.js

@@ -0,0 +1,27 @@
+export const charts = [
+  {
+    name: "折线图",
+    type: "line",
+  },
+  {
+    name: "柱状图",
+    type: "bar",
+  },
+  {
+    name: "饼图",
+    type: "pie",
+  },
+
+  {
+    type: "scatter",
+    name: "散点图",
+  },
+  {
+    type: "barandline",
+    name: "柱状折线图",
+  },
+  {
+    type: "radar",
+    name: "雷达图",
+  },
+];

+ 1 - 0
src/icons/svg/bar.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1730425074944" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="25213" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M180.906667 798.72V126.293333H105.813333v747.52l808.96 3.413334v-78.506667z" fill="#5DA1FD" p-id="25214"></path><path d="M529.066667 180.906667c0-17.066667-17.066667-30.72-30.72-30.72H477.866667c-17.066667 0-27.306667 13.653333-27.306667 27.306666V750.933333h78.506667V180.906667z" fill="#5DA1FD" p-id="25215"></path><path d="M348.16 351.573333c0-17.066667-10.24-30.72-23.893333-30.72H307.2c-17.066667 0-34.133333 17.066667-34.133333 30.72V750.933333h75.093333V351.573333zM706.56 508.586667c0-17.066667-10.24-30.72-23.893333-30.72h-17.066667c-17.066667 0-30.72 17.066667-30.72 30.72V750.933333h75.093333v-242.346666z" fill="#F87D06" p-id="25216"></path><path d="M863.573333 641.706667h-17.066666c-17.066667 0-30.72 17.066667-30.72 30.72V750.933333H887.466667v-78.506666c0-13.653333-6.826667-30.72-23.893334-30.72z" fill="#5DA1FD" p-id="25217"></path></svg>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/icons/svg/barandline.svg


+ 1 - 0
src/icons/svg/baseData.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1730704866688" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="76349" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M892.544 146.432v91.008C892.544 317.696 692.608 384 446.336 384S0 318.336 0 237.44v-91.008C0 66.048 199.936 0 446.336 0s446.208 66.048 446.208 146.432z m0 202.24v205.952c0 80.256-199.936 146.304-446.208 146.304S0 635.52 0 554.624V348.672c95.744 66.048 271.36 97.024 446.336 97.024s350.464-30.336 446.208-97.024z m0 320.768v205.824c0 80.384-199.936 146.432-446.208 146.432S0 956.288 0 875.264V669.44c95.744 66.048 271.36 97.024 446.336 97.024s350.464-30.464 446.208-97.024z m0 0" fill="#52855e" p-id="76350" data-spm-anchor-id="a313x.search_index.0.i14.6c813a81BL04gj" class="selected"></path><path d="M316.288 588.288h695.68v420.864H316.288z" fill="#FFFFFF" p-id="76351"></path><path d="M309.376 517.888v503.808H1024V517.888z m232.192 486.4H327.296v-103.68h214.272z m0-121.6H327.296v-104.064h214.272z m0-121.472H327.296v-103.68h214.272z m232.32 243.2H559.488v-103.68h214.4z m0-121.6H559.488v-104.192h214.4z m0-121.472H559.488v-103.68h214.4z m232.192 243.2H791.808v-103.68H1006.08z m0-121.6H791.808v-104.32H1006.08z m0-121.472H791.808v-103.68H1006.08z" fill="#333333" p-id="76352"></path></svg>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/icons/svg/baseData1.svg


+ 1 - 0
src/icons/svg/fields.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1730704792760" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="71679" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M272 716.512h-15.648V314.368h511.264v108.416H275.296v275.008h492.32v18.752H272.032zM512 1024c282.784 0 512-229.216 512-512S794.784 0 512 0 0 229.216 0 512s229.216 512 512 512z m-186.4-552.064h277.152v32.416h-277.152v-32.416z m0 67.808h277.152v32.416h-277.152v-32.416z m0 63.744h196.192v32.416h-196.192v-32.416z m332.576-14.144h109.408v108.416h-109.408v-108.416z m81.44 43.648c1.472-2.528 0.256-4.576-2.656-4.576h-42.144c-2.912 0-4.128 2.048-2.656 4.576l21.088 36.192c1.472 2.528 3.872 2.528 5.312 0l21.088-36.192z m-81.44-181.504h109.408v108.416h-109.408v-108.416z m75.776 75.52h-42.144c-2.912 0-4.128-2.048-2.656-4.576l21.088-36.192c1.472-2.528 3.872-2.528 5.312 0l21.088 36.192c1.472 2.528 0.256 4.576-2.656 4.576z" fill="#52855e" p-id="71680" data-spm-anchor-id="a313x.search_index.0.i11.6c813a81BL04gj" class="selected"></path></svg>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/icons/svg/fields2.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/icons/svg/line.svg


+ 1 - 0
src/icons/svg/pie.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1730425168814" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="31707" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M509.827 514.31m-449.06 0a449.06 449.06 0 1 0 898.12 0 449.06 449.06 0 1 0-898.12 0Z" fill="#E0E0D1" p-id="31708"></path><path d="M832.589 500.276c0-155.006-125.657-280.662-280.662-280.662v280.662h280.662z" fill="#231F20" p-id="31709"></path><path d="M832.589 472.21c0-155.006-125.657-280.662-280.662-280.662V472.21h280.662z" fill="#4F5D73" p-id="31710"></path><path d="M495.794 247.68c-170.506 0-308.729 138.222-308.729 308.729s138.223 308.729 308.729 308.729c79.882 0 152.677-30.341 207.493-80.125L495.794 556.409V247.68z" fill="#231F20" p-id="31711"></path><path d="M495.794 219.614c-170.506 0-308.729 138.222-308.729 308.729s138.223 308.729 308.729 308.729c79.882 0 152.677-30.341 207.493-80.125L495.794 528.343V219.614z" fill="#76C2AF" p-id="31712"></path><path d="M804.523 528.343H495.794v28.066l207.493 228.604c62.187-56.478 101.236-166.046 101.236-256.67z" fill="#231F20" p-id="31713"></path><path d="M804.523 528.343H495.794l207.493 228.604c62.187-56.478 101.236-137.98 101.236-228.604z" fill="#C75C5C" p-id="31714"></path></svg>

Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/icons/svg/radar.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/icons/svg/scatter.svg


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
src/icons/svg/text.svg


+ 6 - 0
src/main.js

@@ -4,6 +4,7 @@ import router from "./router";
 import { store } from "./store/index.js";
 // import "./assets/style/element-variables.module.scss";
 import "./icons/index"; // icon
+import VueRulerTool from "vue-ruler-tool";
 // 引入element ui
 import ElementUI from "element-ui";
 import "element-ui/lib/theme-chalk/index.css";
@@ -12,7 +13,12 @@ import "@wangeditor/editor/dist/css/style.css";
 import "./styles/index.scss";
 // 注册svg组件
 import SvgIcons from "@/components/SvgIcons/index.vue";
+import VueDraggableResizable from "vue-draggable-resizable-gorkys";
+import "vue-draggable-resizable-gorkys/dist/VueDraggableResizable.css";
+
+Vue.component("VueDraggableResizable", VueDraggableResizable);
 Vue.component("SvgIcons", SvgIcons);
+Vue.component("vue-ruler-tool", VueRulerTool);
 // 序列化post方法
 import qs from "qs";
 // 将自动注册所有组件为全局组件

+ 109 - 0
src/store/dragChart.js

@@ -0,0 +1,109 @@
+/*
+ * @Author: your name
+ * @Date: 2024-11-04 10:06:23
+ * @LastEditTime: 2024-11-05 14:51:36
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/store/dragChart.js
+ */
+import { SCALE } from "@/assets/js/constants/config.js";
+import { Message } from "element-ui";
+const _ = require("lodash");
+
+export default {
+  namespaced: true,
+  state: {
+    curEdit: {}, // 当前编辑的图表
+    currentChartList: [], // 当前文件的当前数据
+    originChartList: [], // 当前文件的原始数据
+    fileList: [], // 文件列表
+  },
+  mutations: {
+    // 设置当前编辑的图表
+    setCurEdit(state, data) {
+      state.curEdit = data;
+    },
+
+    // 当前画布添加图表
+    addChart(state, data) {
+      state.currentChartList.push(data);
+    },
+
+    // 更新图表
+    updateChart(state, data) {
+      if (state.currentChartList[data.index]) {
+        state.currentChartList[data.index] = Object.assign(
+          state.currentChartList[data.index],
+          data
+        );
+      }
+    },
+
+    // 删除图表
+    deleteChart(state, index) {
+      state.currentChartList.splice(index, 1);
+    },
+
+    // 设置当前图表列表
+    setCurrentChartList(state, list = []) {
+      state.currentChartList = list;
+    },
+
+    // 文件删除
+    fileListDelete(state, item) {
+      const index = state.fileList.indexOf(item);
+      state.fileList.splice(index, 1);
+    },
+
+    // 文件添加
+    fileListAdd(state, item) {
+      state.fileList.push(item);
+    },
+
+    fileListUpdate(state, item) {
+      state.fileList = _.cloneDeep(item);
+    },
+
+    // 记录当前文件的原始数据
+    recordOriginChartList(state, id) {
+      const fileList = state.fileList;
+      const item = fileList.find((i) => +i.id === +id);
+      if (item) {
+        state.originChartList = _.cloneDeep(item);
+      } else {
+        state.originChartList = {};
+      }
+    },
+
+    // 还原当前文件的原始数据
+    restoreOriginChartList(state, id) {
+      const fileList = state.fileList;
+      const index = fileList.findIndex((i) => +i.id === +id);
+      if (index > -1) {
+        fileList.splice(index, 1, state.originChartList);
+      } else {
+        Message.warning("未找到文件!");
+      }
+    },
+
+    // 缩放屏幕
+    scaleScreen(state, isGrow) {
+      if (isGrow) {
+        state.currentChartList.forEach((item) => {
+          item.x = parseInt(item.x * SCALE);
+          item.y = parseInt(item.y * SCALE);
+          item.height = parseInt(item.height * SCALE);
+          item.width = parseInt(item.width * SCALE);
+        });
+      } else {
+        state.currentChartList.forEach((item) => {
+          item.x = parseInt(item.x / SCALE);
+          item.y = parseInt(item.y / SCALE);
+          item.height = parseInt(item.height / SCALE);
+          item.width = parseInt(item.width / SCALE);
+        });
+      }
+    },
+  },
+  actions: {},
+};

+ 2 - 0
src/store/index.js

@@ -6,6 +6,7 @@ import createPersistedState from "vuex-persistedstate";
 import settings from "./settings.js";
 import themes from "./themes.js";
 import menuTag from "./menuTag.js";
+import dragChart from "./dragChart.js";
 Vue.use(Vuex);
 export const store = new Vuex.Store({
   state: {
@@ -36,6 +37,7 @@ export const store = new Vuex.Store({
     settings,
     themes,
     menuTag,
+    dragChart,
   },
   plugins: [
     createPersistedState({

+ 87 - 0
src/views/performance/components/custonAsCom/dragChart/components/chart/index.vue

@@ -0,0 +1,87 @@
+<template>
+  <div ref="chart" style="height: 100%; width: 100%"></div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+
+export default {
+  name: "Chart",
+  props: {
+    theme: {
+      type: String,
+      default: "",
+    },
+    height: {
+      type: Number,
+      default: 0,
+    },
+    width: {
+      type: Number,
+      default: 0,
+    },
+    option: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  watch: {
+    option: {
+      handler(newVal) {
+        if (this.myChart) {
+          this.myChart.setOption(newVal, true);
+        }
+      },
+      deep: true,
+    },
+    theme(newVal, oldVal) {
+      if (newVal !== oldVal) {
+        this.reinitializeChart();
+      }
+    },
+    height(newVal, oldVal) {
+      if (newVal !== oldVal) {
+        this.onResize();
+      }
+    },
+    width(newVal, oldVal) {
+      if (newVal !== oldVal) {
+        this.onResize();
+      }
+    },
+  },
+  mounted() {
+    this.$nextTick(() => {
+      this.drawChart();
+    });
+    window.addEventListener("resize", this.onResize);
+  },
+  beforeDestroy() {
+    window.removeEventListener("resize", this.onResize);
+    if (this.myChart) {
+      this.myChart.dispose();
+    }
+  },
+  methods: {
+    drawChart() {
+      if (!this.myChart) {
+        this.myChart = echarts.init(this.$refs.chart, this.theme);
+      }
+      this.myChart.setOption(this.option);
+    },
+    reinitializeChart() {
+      if (this.myChart) {
+        this.myChart.dispose();
+      }
+      this.drawChart();
+    },
+    onResize() {
+      if (this.myChart) {
+        this.myChart.resize();
+      }
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss"></style>

+ 37 - 0
src/views/performance/components/custonAsCom/dragChart/components/chartConfig/config.js

@@ -0,0 +1,37 @@
+export const configApi = [
+  {
+    label: "图表文本配置", //提示文本
+    value: "title", //组件名
+    icon: "text", //图标
+    name: "configText", //tab name
+    desc: "标题组件,包含主标题和副标题。",
+  },
+  // {
+  //   label: "雷达图坐标系组件",
+  //   value: "radar",
+  //   desc: "雷达图坐标系组件,只适用于雷达图。",
+  //   icon: "text",
+  //   name: "configText",
+  // },
+  // {
+  //   label: "工具栏",
+  //   value: "toolbox",
+  //   desc: "工具栏。内置有导出图片,数据视图,动态类型切换,数据区域缩放,重置五个工具。",
+  //   icon: "text",
+  //   name: "configText",
+  // },
+  // {
+  //   label: "调色盘颜色列表",
+  //   value: "color",
+  //   desc: "调色盘颜色列表。如果系列没有设置颜色,则会依次循环从该列表中取颜色作为系列颜色。",
+  //   icon: "text",
+  //   name: "configText",
+  // },
+  // {
+  //   label: "背景色",
+  //   value: "backgroundColor",
+  //   desc: "背景色,默认无背景。",
+  //   icon: "text",
+  //   name: "configText",
+  // },
+];

+ 4 - 0
src/views/performance/components/custonAsCom/dragChart/components/chartConfig/form/index.js

@@ -0,0 +1,4 @@
+export default {
+  title: () => import(/* webpackChunkName:"" */ './title.vue'),
+  legend: () => import(/* webpackChunkName:"" */ './legend.vue')
+}

+ 136 - 0
src/views/performance/components/custonAsCom/dragChart/components/chartConfig/form/legend.vue

@@ -0,0 +1,136 @@
+<template>
+  <div>
+    <el-form
+      ref="form"
+      :model="form"
+      label-width="150px"
+      label-position="left"
+      label-suffix=":"
+    >
+      <el-form-item label="是否显示图例组件" prop="show">
+        <el-switch size="mini" v-model="form.show"></el-switch>
+      </el-form-item>
+      <el-form-item label="显示数据" prop="data">
+        <el-input
+          size="mini"
+          v-model="form.data"
+          placeholder="请输入要显示的图例,用空格分隔"
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="图例的类型" prop="type">
+        <el-select size="mini" v-model="form.type">
+          <el-option value="plain">普通图例</el-option>
+          <el-option value="scroll">可滚动翻页的图例</el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="离容器左侧的距离" prop="left">
+        <el-input size="mini" v-model="form.left"></el-input>
+      </el-form-item>
+      <el-form-item label="离容器右侧的距离" prop="right">
+        <el-input size="mini" v-model="form.right"></el-input>
+      </el-form-item>
+      <el-form-item label="离容器上侧的距离" prop="top">
+        <el-input size="mini" v-model="form.top"></el-input>
+      </el-form-item>
+      <el-form-item label="离容器下侧的距离" prop="bottom">
+        <el-input size="mini" v-model="form.bottom"></el-input>
+      </el-form-item>
+      <el-form-item label="高度" prop="height">
+        <el-input size="mini" v-model="form.height"></el-input>
+      </el-form-item>
+      <el-form-item label="宽度" prop="width">
+        <el-input size="mini" v-model="form.width"></el-input>
+      </el-form-item>
+      <el-form-item label="布局朝向" prop="orient">
+        <el-select size="mini" v-model="form.orient">
+          <el-option value="horizontal">水平</el-option>
+          <el-option value="vertical">垂直</el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="图例标记和文本的对齐" prop="align">
+        <el-select size="mini" v-model="form.align">
+          <el-option value="auto">自动</el-option>
+          <el-option value="left">左对齐</el-option>
+          <el-option value="right">右对齐</el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="图例内边距" prop>
+        <el-input size="mini" v-model="form.padding"></el-input>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { mapState, mapMutations } from "vuex";
+export default {
+  name: "legend",
+  props: {
+    curEdit: {
+      type: Object,
+      default: () => {},
+    },
+  },
+  data() {
+    return {
+      form: {
+        data: "",
+        type: "",
+        show: true,
+        left: "",
+        right: "",
+        top: "",
+        bottom: "",
+        height: "",
+        width: "",
+        orient: "",
+        align: "",
+        padding: 5,
+      },
+    };
+  },
+  computed: {
+    ...mapState({
+      currentChartList: "currentChartList",
+    }),
+  },
+  watch: {
+    curEdit(newVal) {
+      this.changeData();
+    },
+    form: {
+      handler(newVal) {
+        this.changeChart(newVal);
+      },
+      deep: true,
+    },
+  },
+  mounted() {
+    this.changeData();
+  },
+  methods: {
+    ...mapMutations(["updateChart"]),
+    changeData() {
+      this.$refs.form.resetFields();
+      this.curEdit.option.legend
+        ? (this.form.show = true)
+        : (this.form.show = false);
+      const data = this.curEdit.option.legend.data
+        ? this.curEdit.option.legend.data.join(",")
+        : "";
+      Object.assign(this.form, this.curEdit.option.legend, { data: data });
+    },
+    changeChart(newVal) {
+      const item = JSON.parse(JSON.stringify(this.curEdit));
+      Object.assign(item.option.legend, this.form);
+      this.updateChart(item);
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.el-form-item {
+  margin-bottom: 5px;
+}
+</style>

+ 126 - 0
src/views/performance/components/custonAsCom/dragChart/components/chartConfig/form/title.vue

@@ -0,0 +1,126 @@
+<template>
+  <div>
+    <el-form
+      label-position="top"
+      label-width="80px"
+      :model="formLabelAlign"
+      size="mini"
+      ref="form"
+    >
+      <el-form-item label="Y轴数据">
+        <el-select
+          v-model="formLabelAlign.Ydata"
+          placeholder="请先进行数据配置"
+          :disabled="true"
+        >
+          <el-option label="区域一" value="shanghai"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="X轴数据">
+        <el-select
+          v-model="formLabelAlign.Xdata"
+          placeholder="请先进行数据配置"
+          :disabled="true"
+        >
+          <el-option label="区域一" value="shanghai"></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="标题名称">
+        <el-input v-model="formLabelAlign.text"></el-input>
+      </el-form-item>
+      <el-form-item label="X轴名称">
+        <el-input v-model="formLabelAlign.Xlable"></el-input>
+      </el-form-item>
+      <el-form-item label="Y轴名称">
+        <el-input v-model="formLabelAlign.Ylable"></el-input>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import { mapState, mapMutations } from "vuex";
+export default {
+  name: "title",
+  props: {
+    curEdit: {
+      type: Object,
+      default: () => ({
+        option: {
+          title: { text: "" },
+          xAxis: { name: "" },
+          yAxis: { name: "" },
+        },
+      }),
+    },
+  },
+  data() {
+    return {
+      formLabelAlign: {
+        Xdata: "",
+        Ydata: "",
+        text: "",
+        Xlable: "",
+        Ylable: "",
+      },
+    };
+  },
+  computed: {
+    ...mapState("dragChart", {
+      currentChartList: "currentChartList",
+    }),
+  },
+  watch: {
+    curEdit(newVal) {
+      this.changeData();
+    },
+    formLabelAlign: {
+      handler() {
+        this.changeChart();
+      },
+      deep: true,
+    },
+  },
+  mounted() {
+    this.changeData();
+  },
+  methods: {
+    ...mapMutations("dragChart", ["updateChart"]),
+    changeData() {
+      this.$refs.form.resetFields();
+
+      if (this.curEdit && this.curEdit.option) {
+        console.log("curEdit.option 存在", this.curEdit.option);
+        this.formLabelAlign = {
+          ...this.formLabelAlign,
+          text: this.curEdit.option.title?.text || "",
+          Xlable: this.curEdit.option.xAxis?.name || "",
+          Ylable: this.curEdit.option.yAxis?.name || "",
+        };
+      } else {
+        console.log("curEdit.option 不存在", this.curEdit);
+      }
+    },
+    changeChart() {
+      if (!this.curEdit || !this.curEdit.option) return; // 确保 curEdit 和 option 存在
+      const item = JSON.parse(JSON.stringify(this.curEdit));
+
+      item.option.title = item.option.title || {};
+      item.option.xAxis = item.option.xAxis || {};
+      item.option.yAxis = item.option.yAxis || {};
+
+      Object.assign(item.option.title, { text: this.formLabelAlign.text });
+      Object.assign(item.option.xAxis, { name: this.formLabelAlign.Xlable });
+      Object.assign(item.option.yAxis, { name: this.formLabelAlign.Ylable });
+
+      this.updateChart(item); // 更新图表
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.el-form-item {
+  margin-bottom: 5px;
+}
+</style>

+ 172 - 0
src/views/performance/components/custonAsCom/dragChart/components/chartsAttributes.vue

@@ -0,0 +1,172 @@
+<!--
+ * @Author: your name
+ * @Date: 2024-11-01 10:14:11
+ * @LastEditTime: 2024-11-05 14:27:18
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/custonAsCom/dragChart/components/chartsContent.vue
+-->
+<template>
+  <div class="chartContiner">
+    <el-card class="card" shadow="always">
+      <h6>图表配置</h6>
+      <el-row>
+        <el-col :span="8" v-for="chartItem in svgIcon" :key="chartItem.type">
+          <el-tooltip
+            class="item"
+            effect="dark"
+            :content="chartItem.name"
+            placement="top-start"
+          >
+            <div @click="selectClick(chartItem)">
+              <svg-icon
+                :icon-class="chartItem.type"
+                style="width: 50px; height: 40px; margin: 5px 10px 5px 0"
+              />
+            </div>
+          </el-tooltip>
+        </el-col>
+      </el-row>
+      <el-divider></el-divider>
+      <el-tabs v-model="activeName" @tab-click="handleChangeApi">
+        <template v-for="item in configApi">
+          <el-tab-pane :name="item.name">
+            <template #label>
+              <el-tooltip
+                class="item"
+                effect="dark"
+                :content="item.label"
+                placement="top-start"
+              >
+                <div>
+                  <svg-icon
+                    :icon-class="item.icon"
+                    style="width: 30px; height: 30px"
+                  />
+                </div>
+              </el-tooltip>
+            </template>
+            <div class="attributes">
+              <component
+                v-bind:is="currentView"
+                style="margin-top: 20px"
+                :curEdit="curEdit"
+              ></component>
+            </div>
+          </el-tab-pane>
+        </template>
+      </el-tabs>
+    </el-card>
+  </div>
+</template>
+<script>
+import { configApi } from "./chartConfig/config";
+import { mapState } from "vuex";
+import Vue from "vue";
+export default {
+  props: {
+    addChartType: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      configApi,
+      activeName: "configText",
+      form: {
+        configApi: "",
+      },
+      description: "",
+      currentView: "",
+      svgIcon: [
+        {
+          type: "bar",
+          name: "柱状图",
+        },
+        {
+          type: "scatter",
+          name: "散点图",
+        },
+        {
+          type: "line",
+          name: "折线图",
+        },
+        {
+          type: "barandline",
+          name: "柱状折线图",
+        },
+        {
+          type: "radar",
+          name: "雷达图",
+        },
+        {
+          type: "pie",
+          name: "饼图",
+        },
+      ],
+    };
+  },
+  created() {
+    this.handleChangeApi("configText");
+  },
+  computed: {
+    ...mapState("dragChart", {
+      curEdit: "curEdit",
+    }),
+  },
+  methods: {
+    /**
+     * @description 选择图表
+     * @params {String} 图表类型
+     */
+    selectClick(item) {
+      item.name && this.$message.success(`「${item.name}」添加成功`);
+      this.$emit("addChart", item.type);
+    },
+    handleChangeApi(value) {
+      const item = this.configApi.find((item) => item.name === value);
+      this.description = item && item.desc;
+      try {
+        this.registerComponent(item.value).then((component) => {
+          this.currentView = component;
+        });
+      } catch (error) {
+        console.log(error);
+      }
+    },
+    /**
+     *@desc 统一加载注册组件资源
+     */
+    registerComponent(name) {
+      const files = require.context("./chartConfig/form", false, /\.vue$/);
+      if (files.keys().includes(`./${name}.vue`)) {
+        return import("./chartConfig/form/" + name).then((component) => {
+          //eslint-disable-line
+          return Vue.extend(component.default);
+        });
+      } else {
+        console.log("未找到icon组件");
+      }
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.chartContiner {
+  background-color: pink;
+  width: 200px;
+  height: 100%;
+
+  ::v-deep .card {
+    height: 100%;
+    border-radius: 0 !important;
+  }
+}
+::v-deep .el-divider--horizontal {
+  margin: 5px 0;
+}
+::v-deep .el-form--label-top .el-form-item__label {
+  padding: 0;
+}
+</style>

+ 266 - 0
src/views/performance/components/custonAsCom/dragChart/components/chartsContent.vue

@@ -0,0 +1,266 @@
+<!--
+ * @Author: your name
+ * @Date: 2024-11-01 10:14:11
+ * @LastEditTime: 2024-11-04 16:57:16
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/custonAsCom/dragChart/components/chartsContent.vue
+-->
+<template>
+  <div class="chartContiner">
+    <vue-ruler-tool
+      :height="500"
+      :zoom="1"
+      :unit="10"
+      :scale="1"
+      :precision="0"
+      :rulerColor="'#00aaff'"
+      :bgColor="'#ffffff'"
+      :cornerColor="'#333'"
+      :guidelineColor="'#f00'"
+      :cornerSize="10"
+      :scrollLeft="0"
+      :scrollTop="0"
+      @onRulerScroll="handleScroll"
+    >
+      <div class="canvas-wrapper" ref="canvas">
+        <template v-if="IsCanvasPrepared">
+          <vue-draggable-resizable
+            v-for="(item, index) in currentChartList"
+            :key="item.id"
+            :w="item.width"
+            :h="item.height"
+            :x="item.x"
+            :y="item.y"
+            :snap="true"
+            :is-conflict-check="false"
+            :parent="true"
+            @dragging="onDrag"
+            @resizing="onResize"
+            @activated="onClickChart(item, index)"
+            @deactivated="onMoveout(index)"
+          >
+            <chart
+              :height="item.height"
+              :width="item.width"
+              :option="item.option"
+            />
+            <div class="info">
+              <div>x轴:{{ item.x }} 高度:{{ item.height }}</div>
+              <div>y轴:{{ item.y }} 宽度:{{ item.width }}</div>
+              <el-button
+                class="icon-wrapper"
+                size="mini"
+                type="danger"
+                plain
+                @click="deleteTheChart(index)"
+                icon="el-icon-delete"
+              ></el-button>
+            </div>
+          </vue-draggable-resizable>
+        </template>
+      </div>
+    </vue-ruler-tool>
+  </div>
+</template>
+<script>
+import { mapState, mapMutations } from "vuex";
+import Chart from "./chart/index";
+import ChartClass from "@/assets/js/class/chart.js";
+import { SCALE } from "@/assets/js/constants/config.js";
+const OTHER_CONFIG = ["background", "theme"];
+const _ = require("lodash");
+export default {
+  components: {
+    Chart,
+  },
+  props: {
+    addChartType: {
+      type: String,
+      default: "",
+    },
+    config: {
+      type: String,
+      default: "",
+    },
+  },
+  data() {
+    return {
+      fileId: +this.$route.params.id,
+      IsCanvasPrepared: false,
+      chartIndex: 0,
+      screenHeight: 0,
+      screenWidth: 0,
+    };
+  },
+  created() {},
+  watch: {
+    addChartType(type) {
+      if (type !== "") {
+        !OTHER_CONFIG.includes(type) && this.appendChart(type);
+      }
+    },
+    config(val) {
+      this.handleImportConfig(val);
+    },
+    // background(val) {
+    //   this.changeBg(val);
+    // },
+    // isFullSize(val) {
+    //   this.handleFullScreen();
+    // },
+  },
+  computed: {
+    ...mapState("dragChart", {
+      currentChartList: "currentChartList",
+      curChart: "curEdit",
+    }),
+  },
+  mounted() {
+    this.getScrollSize();
+  },
+  methods: {
+    ...mapMutations("dragChart", [
+      "scaleScreen",
+      "setCurrentChartList",
+      "addChart",
+      "updateChart",
+      "deleteChart",
+      "setCurEdit",
+      "recordOriginChartList",
+      "restoreOriginChartList",
+    ]),
+    // ruler-tool 组件标尺滚动事件
+    handleScroll({ scrollLeft, scrollTop }) {
+      console.log("标尺滚动:", scrollLeft, scrollTop);
+    },
+    /**
+     * @description 响应图表缩放
+     * @params {Array} 左上角(x,y)坐标,选中图形宽高
+     */
+    onResize(x, y, width, height) {
+      this.updateChart({
+        index: this.chartIndex,
+        x,
+        y,
+        height,
+        width,
+      });
+    },
+    /**
+     * @description 响应图表拖拽
+     * @params {Array} 图形左上角(x,y)坐标
+     */
+    onDrag(x, y) {
+      this.updateChart({
+        index: this.chartIndex,
+        x,
+        y,
+      });
+    },
+
+    onClickChart(item, index) {
+      this.setCurEdit(item);
+      this.chartIndex = index;
+      const nodes = document.getElementsByClassName("info");
+      nodes[index].style.opacity = 1;
+    },
+
+    onMoveout(index) {
+      const nodes = document.getElementsByClassName("info");
+      nodes[index].style.opacity = 0;
+    },
+    appendChart(type) {
+      const config = require(`@/assets/js/constants/echarts-config/${type}.js`);
+      this.addChart(
+        new ChartClass({
+          option: config.option,
+        })
+      );
+    },
+
+    /**
+     * @description 响应图表删除
+     * @params {Number} 序号
+     */
+    deleteTheChart(index) {
+      this.$confirm("是否删除改图表?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.deleteChart(index);
+          this.$message({
+            type: "success",
+            message: "删除成功!",
+          });
+        })
+        .catch(() => {});
+    },
+
+    handleImportConfig(option) {
+      this.addChart(
+        new ChartClass({
+          option: new Function("return " + option)() /* eslint-disable-line */,
+        })
+      );
+    },
+    /**
+     * @description 修改画布元素宽高为屏幕大小
+     */
+    getScrollSize() {
+      this.screenHeight = window.screen.height;
+      this.screenWidth = document.body.clientWidth;
+      this.$refs.canvas.style.setProperty(
+        "--canvasHeight",
+        `${this.screenHeight / SCALE}px`
+      );
+      this.$refs.canvas.style.setProperty(
+        "--canvasWidth",
+        `${this.screenWidth / SCALE}px`
+      );
+      this.IsCanvasPrepared = true;
+    },
+
+    /**
+     * @description 百分比转为像素值
+     */
+    calPercent(value, type) {
+      return type === "x"
+        ? value / this.screenWidth
+        : value / this.screenHeight;
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.chartContiner {
+  //   background-color: #fff;
+  flex: 1;
+  height: 100%;
+  width: 1800px;
+  overflow: scroll;
+  ::v-deep .vue-ruler-wrapper {
+    // width: 100% !important;
+  }
+  .canvas-wrapper {
+    position: relative;
+    width: var(--canvasWidth);
+    height: var(--canvasHeight);
+    background: #fff;
+    border-radius: 10px;
+    box-shadow: 0 0 0 10px #d6dce0;
+    outline: 10px solid #d6dce0;
+    outline-offset: 0px;
+    .info {
+      opacity: 0;
+      .icon-wrapper {
+        position: absolute;
+        bottom: -35px;
+        right: 5px;
+      }
+    }
+  }
+}
+</style>

+ 129 - 0
src/views/performance/components/custonAsCom/dragChart/components/chartsData.vue

@@ -0,0 +1,129 @@
+<!--
+ * @Author: your name
+ * @Date: 2024-11-01 10:17:39
+ * @LastEditTime: 2024-11-04 15:24:11
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/custonAsCom/dragChart/components/chartsData.vue
+-->
+<template>
+  <div class="chartContiner">
+    <el-card shadow="never" class="card">
+      <h6>数据配置</h6>
+      <el-input placeholder="输入关键字进行过滤" v-model="filterText">
+      </el-input>
+      <el-tree
+        class="filter-tree"
+        :data="data"
+        :props="defaultProps"
+        default-expand-all
+        :filter-node-method="filterNode"
+        ref="tree"
+        :render-content="renderContent"
+      >
+      </el-tree>
+    </el-card>
+  </div>
+</template>
+<script>
+import {
+  getDataFromIndexedDB,
+  checkObjectStoreExists,
+} from "@/utils/indexedDb";
+export default {
+  data() {
+    return {
+      defaultProps: {
+        children: "children",
+        label: "label",
+      },
+      filterText: "", // 初始化 filterText
+      data: [],
+    };
+  },
+  watch: {
+    filterText(val) {
+      this.$refs.tree.filter(val);
+    },
+  },
+  async created() {
+    //判断indexedDb中是否存在这个数据表
+    checkObjectStoreExists("FileDataDB", "files")
+      .then((exists) => {
+        if (exists) {
+          console.log("对象存储 'files' 存在!");
+          this.getIndexDbData();
+        } else {
+          this.loading = false;
+          console.log("对象存储 'files' 不存在!");
+        }
+      })
+      .catch((error) => {
+        console.error("检查对象存储时出错:", error);
+      });
+  },
+  methods: {
+    async getIndexDbData() {
+      const jsonData = await getDataFromIndexedDB();
+      this.data = jsonData.map((item) => {
+        return {
+          label: item.filename,
+          id: item.fileId,
+          children: [...Object.keys(item.fileData[0])].map((val) => ({
+            label: val,
+            id: item.fileId + val,
+          })),
+        };
+      });
+    },
+    filterNode(value, data) {
+      if (!value) return true;
+      return data.label.indexOf(value) !== -1;
+    },
+    renderContent(h, { node, data }) {
+      return h("span", [
+        // 检查是否为子节点,如果是,则显示 checkbox
+        node.level === 1
+          ? h("el-checkbox", {
+              props: {
+                value: node.checked,
+                // disabled: node.disabled,
+              },
+              on: {
+                change: (val) => {
+                  this.$refs.tree.setChecked(node, val);
+                },
+              },
+            })
+          : null, // 父节点不显示 checkbox
+
+        node.level > 1
+          ? h("svg-icon", {
+              props: {
+                "icon-class": "fields2", // 设置自定义组件的属性
+              },
+              style: { margin: "0 10px" }, // 设置边距
+            })
+          : h("svg-icon", {
+              props: {
+                "icon-class": "baseData", // 设置自定义组件的属性
+              },
+              style: { margin: "0 10px" }, // 设置边距
+            }),
+        h("span", data.label),
+      ]);
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.chartContiner {
+  background-color: skyblue;
+  width: 300px;
+  height: 100%;
+  ::v-deep .card {
+    height: 100%;
+    border-radius: 0 !important;
+  }
+}
+</style>

+ 68 - 0
src/views/performance/components/custonAsCom/dragChart/index.vue

@@ -0,0 +1,68 @@
+<!--
+ * @Author: your name
+ * @Date: 2024-10-31 16:01:27
+ * @LastEditTime: 2024-11-04 15:09:27
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/views/performance/components/custonAsCom/dragChart/index.vue
+-->
+<template>
+  <div class="chartsBox">
+    <ChartsContent
+      :addChartType="addChartType"
+      :config="config"
+    ></ChartsContent>
+    <ChartsAttributes
+      @addChart="addChart"
+      :addChartType="addChartType"
+    ></ChartsAttributes>
+    <ChartsData :addChartType="addChartType"></ChartsData>
+  </div>
+</template>
+<script>
+import ChartsData from "./components/chartsData.vue";
+import ChartsAttributes from "./components/chartsAttributes.vue";
+import ChartsContent from "./components/chartsContent.vue";
+export default {
+  components: {
+    ChartsContent,
+    ChartsData,
+    ChartsAttributes,
+  },
+  data() {
+    return {
+      addChartType: "",
+      options: "",
+      config: "",
+      configType: "",
+    };
+  },
+  mounted() {},
+  methods: {
+    /**
+     * @description 响应添加图表
+     * @params {String} 图表类型
+     */
+    addChart(type) {
+      console.log("添加一个图表", type);
+      // 如果连续两次添加相同的类型,强制触发更新
+      if (this.addChartType === type) {
+        this.addChartType = ""; // 先清空
+        this.$nextTick(() => {
+          this.addChartType = type; // 再赋值
+        });
+      } else {
+        this.addChartType = type;
+      }
+    },
+  },
+};
+</script>
+<style scoped lang="scss">
+.chartsBox {
+  width: 100%;
+  display: flex;
+  height: 100%;
+  //   justify-content: space-between;
+}
+</style>

+ 13 - 3
src/views/performance/customAnalysis.vue

@@ -50,11 +50,11 @@
         </template>
       </el-tab-pane>
 
-      <el-tab-pane name="chart">
+      <el-tab-pane name="chart" class="chartsTab">
         <span slot="label" class="iconFont">
           <svg-icon icon-class="table(1)" style="width: 30px; height: 30px" />
         </span>
-        图表拖拽图表拖拽图表拖拽图表拖拽图表拖拽图表拖拽图表拖拽图表拖拽
+        <DragChart></DragChart>
       </el-tab-pane>
       <el-tab-pane name="table">
         <span slot="label" class="iconFont">
@@ -119,6 +119,7 @@
 <script>
 import DataTable from "./components/custonAsCom/dataTable.vue";
 import DatabaseTable from "./components/custonAsCom/DatabaseTable.vue";
+import DragChart from "./components/custonAsCom/dragChart/index.vue";
 import Papa from "papaparse";
 import * as XLSX from "xlsx";
 import { storeSetData } from "@/utils/indexedDb";
@@ -126,11 +127,12 @@ export default {
   components: {
     DataTable,
     DatabaseTable,
+    DragChart,
   },
   data() {
     return {
       db: null,
-      activeName: "table",
+      activeName: "chart",
       showDatabaseTable: false,
       showPopover: false, // 控制 popover 的显示状态
       dialogVisible: false, //计算函数对话框状态
@@ -305,6 +307,14 @@ export default {
   .iconFont {
     padding: 0 20px;
   }
+
+  ::v-deep .el-tabs__content {
+    height: 100% !important;
+  }
+  .chartsTab {
+    width: 100%;
+    height: 100%;
+  }
 }
 .uploadBoxContent {
   margin: 5px;

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff