isv-robot vor 2 Jahren
Ursprung
Commit
d8628b6d12

+ 1 - 0
package.json

@@ -18,6 +18,7 @@
     "axios": "0.18.1",
     "core-js": "3.6.5",
     "echarts": "^5.4.1",
+    "echarts-gl": "^2.0.9",
     "element-ui": "2.13.2",
     "fuse.js": "^6.4.6",
     "js-base64": "^3.7.2",

BIN
public/static/images/chart_opacity_bg.png


+ 3 - 1
src/main.js

@@ -1,8 +1,10 @@
 import Vue from 'vue'
 import 'normalize.css/normalize.css' // a modern alternative to CSS resets
-import 'echarts'
+import * as echarts from 'echarts'
+import 'echarts-gl' // 3d图表库
 import ECharts from 'vue-echarts'
 
+Vue.prototype.$echarts = echarts
 import {
   Card,
   MenuItem,

+ 274 - 0
src/utils/chart.js

@@ -0,0 +1,274 @@
+/**
+ * 绘制3d图
+ * @param pieData 总数据
+ * @param internalDiameterRatio:透明的空心占比
+ * @param distance 视角到主体的距离
+ * @param alpha 旋转角度
+ * @param pieHeight 立体的高度
+ * @param opacity 饼或者环的透明度
+ */
+const getPie3D = (pieData, internalDiameterRatio, distance, alpha, pieHeight, opacity = 1) => {
+  const series = []
+  let sumValue = 0
+  let startValue = 0
+  let endValue = 0
+  let legendData = []
+  let legendBfb = []
+  const k = 1 - internalDiameterRatio
+  pieData.sort((a, b) => {
+    return b.value - a.value
+  })
+  // 为每一个饼图数据,生成一个 series-surface 配置
+  for (let i = 0; i < pieData.length; i++) {
+    sumValue += pieData[i].value
+    const seriesItem = {
+      name:
+        typeof pieData[i].name === 'undefined'
+          ? `series${i}`
+          : pieData[i].name,
+      type: 'surface',
+      parametric: true,
+      wireframe: {
+        show: false
+      },
+      pieData: pieData[i],
+      pieStatus: {
+        selected: false,
+        hovered: false,
+        k: k
+      },
+      center: ['10%', '50%']
+    }
+    if (typeof pieData[i].itemStyle !== 'undefined') {
+      const itemStyle = {}
+      itemStyle.color =
+        typeof pieData[i].itemStyle.color !== 'undefined'
+          ? pieData[i].itemStyle.color
+          : opacity
+      itemStyle.opacity =
+        typeof pieData[i].itemStyle.opacity !== 'undefined'
+          ? pieData[i].itemStyle.opacity
+          : opacity
+      seriesItem.itemStyle = itemStyle
+    }
+    series.push(seriesItem)
+  }
+
+  // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
+  // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
+  legendData = []
+  legendBfb = []
+  for (let i = 0; i < series.length; i++) {
+    endValue = startValue + series[i].pieData.value
+    series[i].pieData.startRatio = startValue / sumValue
+    series[i].pieData.endRatio = endValue / sumValue
+    series[i].parametricEquation = getParametricEquation(
+      series[i].pieData.startRatio,
+      series[i].pieData.endRatio,
+      false,
+      false,
+      k,
+      series[i].pieData.value
+    )
+    startValue = endValue
+    const bfb = fomatFloat(series[i].pieData.value / sumValue, 4)
+    legendData.push({
+      name: series[i].name,
+      value: bfb
+    })
+    legendBfb.push({
+      name: series[i].name,
+      value: bfb
+    })
+  }
+  const boxHeight = getHeight3D(series, pieHeight) // 通过pieHeight设定3d饼/环的高度,单位是px
+  // 准备待返回的配置项,把准备好的 legendData、series 传入。
+  const option = {
+    legend: {
+      show: false,
+      data: legendData,
+      orient: 'vertical',
+      left: 10,
+      top: 10,
+      itemGap: 10,
+      textStyle: {
+        color: '#A1E2FF'
+      },
+      icon: 'circle',
+      formatter: function (param) {
+        const item = legendBfb.filter(item => item.name === param)[0]
+        const bfs = fomatFloat(item.value * 100, 2) + '%'
+        return `${item.name}  ${bfs}`
+      }
+    },
+    labelLine: {
+      show: true,
+      lineStyle: {
+        color: '#fff'
+      }
+    },
+    label: {
+      show: true,
+      position: 'outside',
+      formatter: '{b} \n{c} {d}%'
+    },
+    tooltip: {
+      backgroundColor: '#033b77',
+      borderColor: '#21f2c4',
+      textStyle: {
+        color: '#fff',
+        fontSize: 13
+      },
+      formatter: params => {
+        if (
+          params.seriesName !== 'mouseoutSeries' &&
+          params.seriesName !== 'pie2d'
+        ) {
+          const bfb = (
+            (option.series[params.seriesIndex].pieData.endRatio -
+              option.series[params.seriesIndex].pieData.startRatio) *
+            100
+          ).toFixed(2)
+          return (
+            `${params.seriesName}<br/>` +
+            `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>` +
+            `${bfb}%`
+          )
+        }
+      }
+    },
+    xAxis3D: {
+      min: -1,
+      max: 1
+    },
+    yAxis3D: {
+      min: -1,
+      max: 1
+    },
+    zAxis3D: {
+      min: -1,
+      max: 1
+    },
+    grid3D: {
+      show: false,
+      boxHeight: boxHeight, // 圆环的高度
+      viewControl: {
+        // 3d效果可以放大、旋转等,请自己去查看官方配置
+        alpha, // 角度
+        distance, // 调整视角到主体的距离,类似调整zoom
+        rotateSensitivity: 0, // 设置为0无法旋转
+        zoomSensitivity: 0, // 设置为0无法缩放
+        panSensitivity: 0, // 设置为0无法平移
+        autoRotate: false // 自动旋转
+      }
+    },
+    series: series
+  }
+  return option
+}
+
+/**
+ * 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
+ */
+const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h) => {
+  // 计算
+  const midRatio = (startRatio + endRatio) / 2
+  const startRadian = startRatio * Math.PI * 2
+  const endRadian = endRatio * Math.PI * 2
+  const midRadian = midRatio * Math.PI * 2
+  // 如果只有一个扇形,则不实现选中效果。
+  if (startRatio === 0 && endRatio === 1) {
+    isSelected = false
+  }
+  // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
+  k = typeof k !== 'undefined' ? k : 1 / 3
+  // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
+  const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
+  const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0
+  // 计算高亮效果的放大比例(未高亮,则比例为 1)
+  const hoverRate = isHovered ? 1.05 : 1
+  // 返回曲面参数方程
+  return {
+    u: {
+      min: -Math.PI,
+      max: Math.PI * 3,
+      step: Math.PI / 32
+    },
+    v: {
+      min: 0,
+      max: Math.PI * 2,
+      step: Math.PI / 20
+    },
+    x: function (u, v) {
+      if (u < startRadian) {
+        return (
+          offsetX +
+          Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
+        )
+      }
+      if (u > endRadian) {
+        return (
+          offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
+        )
+      }
+      return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
+    },
+    y: function (u, v) {
+      if (u < startRadian) {
+        return (
+          offsetY +
+          Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
+        )
+      }
+      if (u > endRadian) {
+        return (
+          offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
+        )
+      }
+      return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
+    },
+    z: function (u, v) {
+      if (u < -Math.PI * 0.5) {
+        return Math.sin(u)
+      }
+      if (u > Math.PI * 2.5) {
+        return Math.sin(u) * h * 0.1
+      }
+      return Math.sin(v) > 0 ? 1 * h * 0.1 : -1
+    }
+  }
+}
+
+/**
+ * 获取3d丙图的最高扇区的高度
+ */
+const getHeight3D = (series, height) => {
+  series.sort((a, b) => {
+    return b.pieData.value - a.pieData.value
+  })
+  return (height * 25) / series[0].pieData.value
+}
+
+/**
+ * 格式化浮点数
+ */
+const fomatFloat = (num, n) => {
+  let f = parseFloat(num)
+  if (isNaN(f)) {
+    return false
+  }
+  f = Math.round(num * Math.pow(10, n)) / Math.pow(10, n) // n 幂
+  let s = f.toString()
+  let rs = s.indexOf('.')
+  // 判定如果是整数,增加小数点再补0
+  if (rs < 0) {
+    rs = s.length
+    s += '.'
+  }
+  while (s.length <= rs + n) {
+    s += '0'
+  }
+  return s
+}
+
+export { getPie3D, getParametricEquation }

+ 329 - 3
src/views/dashboard-screen/jxdxqk/index.vue

@@ -1,11 +1,337 @@
-<template/>
+<template>
+  <div class="chart-container">
+    <div class="chart">
+      <v-chart autoresize :option="option"/>
+
+    </div>
+    <!-- 底座背景 -->
+    <div class="bg"/>
+  </div>
+</template>
 
 <script>
+// import { getPie3D, getParametricEquation } from 'chart.js' // 工具类js,页面路径自己修改
+import * as echarts from 'echarts'
+
 export default {
-  name: 'Jxdxqk'
+  name: 'Chart',
+  data() {
+    return {
+      option: {}
+    }
+  },
+  created() {
+    window['is'] = this
+    this.getOption()
+  },
+
+  methods: {
+    // 初始化label样式
+    getOption() {
+      /** ***********************
+       pie3D 尝试
+
+       更新时间: 2020.8.6 17:12 v1.1
+       使用组件: grid3D、xAxis3D、yAxis3D、zAxis3D、surface
+       EC 版本: 4.8.0
+       GL 版本:0.4.3
+       公众号 : ZXand618的ECharts之旅
+       知乎号 : ZhXand618
+       如果转载: 请注明出处
+
+       *************************
+       【 getParametricEquation 函数说明 】 :
+       *************************
+       根据传入的
+       startRatio(浮点数): 当前扇形起始比例,取值区间 [0, endRatio)
+       endRatio(浮点数): 当前扇形结束比例,取值区间 (startRatio, 1]
+       isSelected(布尔值):是否选中,效果参照二维饼图选中效果(单选)
+       isHovered(布尔值): 是否放大,效果接近二维饼图高亮(放大)效果(未能实现阴影)
+       k(0~1之间的浮点数):用于参数方程的一个参数,取值 0~1 之间,通过「内径/外径」的值换算而来。
+
+       生成 3D 扇形环曲面
+
+       *************************
+       【 getPie3D 函数说明 】 :
+       *************************
+       根据传入的
+       pieData(object):饼图数据
+       internalDiameterRatio(0~1之间的浮点数):内径/外径的值(默认值 1/2),当该值等于 0 时,为普通饼图
+
+       生成模拟 3D 饼图的配置项 option
+
+       备注:饼图数据格式示意如下
+       [{
+        name: '数据1',
+        value: 10
+    }, {
+        // 数据项名称
+        name: '数据2',
+        value : 56,
+        itemStyle:{
+            // 透明度
+            opacity: 0.5,
+            // 扇形颜色
+            color: 'green'
+        }
+    }]
+
+       *************************
+       【 鼠标事件监听说明 】 :
+       *************************
+       click: 实现饼图的选中效果(单选)
+       大致思路是,通过监听点击事件,获取到被点击数据的系列序号 params.seriesIndex,
+       然后将对应扇形向外/向内移动 10% 的距离。
+
+       mouseover: 近似实现饼图的高亮(放大)效果
+       大致思路是,在饼图外部套一层透明的圆环,然后监听 mouseover 事件,获取
+       到对应数据的系列序号 params.seriesIndex 或系列名称 params.seriesName,
+       如果鼠标移到了扇形上,则先取消高亮之前的扇形(如果有),再高亮当前扇形;
+       如果鼠标移到了透明圆环上,则只取消高亮之前的扇形(如果有),不做任何高亮。
+
+       globalout: 当鼠标移动过快,直接划出图表区域时,有可能监听不到透明圆环的 mouseover,
+       导致此前高亮没能取消,所以补充了对 globalout 的监听。
+
+       *************************/
+
+      // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
+      function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, height) {
+        // 计算
+        const midRatio = (startRatio + endRatio) / 2
+
+        const startRadian = startRatio * Math.PI * 2
+        const endRadian = endRatio * Math.PI * 2
+        const midRadian = midRatio * Math.PI * 2
+
+        // 如果只有一个扇形,则不实现选中效果。
+        if (startRatio === 0 && endRatio === 1) {
+          isSelected = false
+        }
+
+        // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
+        k = typeof k !== 'undefined' ? k : 1 / 3
+
+        // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
+        const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
+        const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0
+
+        // 计算高亮效果的放大比例(未高亮,则比例为 1)
+        const hoverRate = isHovered ? 1.05 : 1
+
+        // 返回曲面参数方程
+        return {
+
+          u: {
+            min: -Math.PI,
+            max: Math.PI * 3,
+            step: Math.PI / 32
+          },
+
+          v: {
+            min: 0,
+            max: Math.PI * 2,
+            step: Math.PI / 20
+          },
+
+          x: (u, v) => {
+            if (u < startRadian) {
+              return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
+            }
+            if (u > endRadian) {
+              return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
+            }
+            return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
+          },
+
+          y: function(u, v) {
+            if (u < startRadian) {
+              return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
+            }
+            if (u > endRadian) {
+              return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
+            }
+            return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
+          },
+
+          z: function(u, v) {
+            if (u < -Math.PI * 0.5) {
+              return Math.sin(u)
+            }
+            if (u > Math.PI * 2.5) {
+              return Math.sin(u)
+            }
+            return Math.sin(v) > 0 ? 1 * height : -1
+          }
+        }
+      }
+
+      // 生成模拟 3D 饼图的配置项
+      const getPie3D = (pieData, internalDiameterRatio) => {
+        const series = []
+        let sumValue = 0
+        let startValue = 0
+        let endValue = 0
+        const legendData = []
+        const k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3
+
+        // 为每一个饼图数据,生成一个 series-surface 配置
+        for (let i = 0; i < pieData.length; i++) {
+          sumValue += pieData[i].value
+
+          const seriesItem = {
+            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
+            type: 'surface',
+            parametric: true,
+            wireframe: {
+              show: false
+            },
+            pieData: pieData[i],
+            pieStatus: {
+              selected: false,
+              hovered: false,
+              k: k
+            }
+          }
+
+          if (typeof pieData[i].itemStyle !== 'undefined') {
+            const itemStyle = {}
+
+            typeof pieData[i].itemStyle.color !== 'undefined' ? itemStyle.color = pieData[i].itemStyle.color : null
+            typeof pieData[i].itemStyle.opacity !== 'undefined' ? itemStyle.opacity = pieData[i].itemStyle.opacity : null
+
+            seriesItem.itemStyle = itemStyle
+          }
+          series.push(seriesItem)
+        }
+
+        // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
+        // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
+        for (let i = 0; i < series.length; i++) {
+          endValue = startValue + series[i].pieData.value
+          console.log(series[i])
+          series[i].pieData.startRatio = startValue / sumValue
+          series[i].pieData.endRatio = endValue / sumValue
+          series[i].parametricEquation = getParametricEquation(series[i].pieData.startRatio, series[i].pieData.endRatio, false, false, k, series[i].pieData.value)
+
+          startValue = endValue
+
+          legendData.push(series[i].name)
+        }
+
+        // 准备待返回的配置项,把准备好的 legendData、series 传入。
+        const option = {
+          tooltip: {
+            formatter: params => {
+              if (params.seriesName !== 'mouseoutSeries') {
+                return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${option.series[params.seriesIndex].pieData.value}`
+              }
+            }
+          },
+          legend: {
+            data: legendData,
+            position: 'right',
+            right: '5%',
+            top: '10%',
+            // height: '',
+            orient: 'vertical',
+            textStyle: {
+              color: '#fff',
+              fontSize: this.EchartfontSize(14)
+            }
+          },
+          xAxis3D: {
+            min: -1,
+            max: 1
+          },
+          yAxis3D: {
+            min: -1,
+            max: 1
+          },
+          zAxis3D: {
+            min: -1,
+            max: 1
+          },
+          labelLine: {
+            show: true,
+            lineStyle: {
+              color: '#ffffff'
+            }
+          },
+          label: {
+            show: true,
+            position: 'outside',
+          },
+          grid3D: {
+            show: false,
+            boxHeight: 10,
+            environment: undefined,
+            viewControl: {
+              distance: 200,
+              alpha: 25,
+              beta: 130
+            }
+
+          },
+          series: series
+        }
+        return option
+      }
+
+      // 传入数据生成 option
+      this.option = getPie3D([{
+        name: '已消除',
+        value: 3,
+        itemStyle: {
+          opacity: 0.5,
+          color: 'rgba(0,127,244,.8)'
+        }
+      },
+
+      {
+        name: '未消除',
+        value: 1,
+        itemStyle: {
+          opacity: 0.5,
+          color: 'rgba(209,126,23,.8)'
+        }
+      }
+
+      ], 2)
+    }
+
+  }
 }
 </script>
 
-<style scoped>
+<style lang='less' scoped>
+.chart-container {
+  position: relative;
+  width: 100%;
+  height: 100%;
 
+  .chart {
+    width: 100%;
+    height: 100%;
+    position: absolute;
+    z-index: 2;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+  }
+
+  .bg {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: -1vw;
+    z-index: 1;
+    width: 100%;
+    background-repeat: no-repeat;
+    background-image: url('/static/images/chart_opacity_bg.png');
+    background-size: 60% 80%;
+    background-position: center 70%;
+  }
+}
 </style>

+ 1 - 1
src/views/dashboard-screen/qb-dlsbxjdx/index.vue

@@ -106,7 +106,7 @@ export default {
             }
           },
           axisLabel: {
-            fontSize: this.EchartfontSize(10)
+            fontSize: `this.EchartfontSize(10)`
           }
 
         },