echart的封装

2022-11-10  本文已影响0人  kingLeft7

第一种方式

// myCharts.js
/**
 * 各种画echarts图表的方法都封装在这里
 * 注意:这里echarts没有采用按需引入的方式,只是为了方便学习
 */
import * as echarts from 'echarts';
const install = function(Vue) {
  Object.defineProperties(Vue.prototype, {
    $chart: {
      get() {
        return {
          //线形图
          line: function(id, xdata, ydata, data3, sdata, that, isno) {
            this.chart = echarts.init(document.getElementById(id));
            this.chart.clear();
            const optionData = {
              tooltip: {
                trigger: 'axis'
              },
              legend: {
                orient: 'horizontal',
                itemGap: 40,
                textStyle: {
                  color: '#333'
                  // ...
                },
                top: '0px',
                right: "10%",
                itemHeight: 16,

                fontSize: 12,
                padding: [0, 0, -3, 0], // 修改文字和图标距离
                // ...
              },
              grid: {
                // show:false,
                top: '20%',
                right: '10%',
                bottom: '25%',
                left: '20%'
              },
              toolbox: {
                // feature: {
                //   saveAsImage: {}
                // }
              },
              xAxis: {
                data: xdata,
                splitLine: {
                  show: false
                },
                axisLine: {
                  show: false
                },
                axisTick: {
                  show: false
                },
              },
              yAxis: {
                // splitLine: {
                //   show: false
                // },
                axisLine: {
                  show: false
                },
                axisTick: {
                  show: false
                },
                // data: [0, 500, 1000, 1500,1800]
              },
              series: [{
                data: ydata,
                smooth: true,
                type: 'line',
                symbol: 'none',
                lineStyle: {
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                      offset: 0,
                      color: '#FBC95C'
                    },

                    {
                      offset: 1,
                      color: '#FF6550'
                    }
                  ])
                },
                areaStyle: {},
                itemStyle: {
                  normal: {
                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                        offset: 0,
                        color: '#FFDDCB'
                      },

                      {
                        offset: 1,
                        color: '#FAFAFC'
                      }
                    ])
                  },

                },
              }]

            }
            this.chart.setOption(optionData)
            let _this = this
            window.addEventListener('resize', () => {
              if (_this.chart) {
                _this.chart.resize()
              }
            })
            this.chart.on('click', function(params) {
              console.log(params.name)
              // that.$router.push({
              //    path: '/EventSearch',
              //    query: {
              //        language: params.seriesName,
              //        times: params.name
              //    }
              // });
            })

          },
          crile: function(id, xdata, that, isno) {
            this.chart = echarts.init(document.getElementById(id));
            this.chart.clear();
            const optionData = {
              tooltip: {
                trigger: 'item'
              },
              legend: {
                itemHeight: 10,
                itemWidth: 10,
                icon: "circle",
                orient: 'vertical',
                left: 'center',
                bottom: 30,
                orient: 'horizontal',
                textStyle: {
                  fontSize: 12,
                  color: '#333333'
                }
              },
              series: [{
                name: '',
                type: 'pie',
                radius: ['35%', '50%'],
                center: ["50%", "40%"],
                avoidLabelOverlap: false,
                label: {
                  show: false,
                  position: 'center'
                },
                emphasis: {
                  label: {
                    show: true,
                    fontSize: '20',
                    fontWeight: 'bold'
                  }
                },
                labelLine: {
                  show: false
                },
                data: xdata,
                itemStyle: {
                  normal: {
                    color: function(colors) {
                      var colorList = [
                        '#558DFF',
                        '#59D7FF',
                        '#9ADA70',
                        '#FFBF5C',
                        '#B8B8B8',
                        '#F8746C'
                      ];
                      return colorList[colors.dataIndex];
                    }
                  },
                },
              }]
            };
            this.chart.setOption(optionData)
            let _this = this
            window.addEventListener('resize', () => {
              if (_this.chart) {
                _this.chart.resize()
              }
            })
            this.chart.on('click', function(params) {
            })

          },
          hcolumnar: function(id, xdata, ydata, data3, sdata, that, isno) {
            this.chart = echarts.init(document.getElementById(id));
            this.chart.clear();
            console.log('sdfsfsdfsdf')
            const optionData = {
              tooltip: {
                trigger: 'axis',
                axisPointer: {
                  type: 'shadow'
                }
              },
              grid: {
                left: '3%',
                right: '4%',
                bottom: '3%',
                containLabel: true
              },
              xAxis: [{
                type: 'value',
                // splitLine: {
                //   show: false
                // },
                // axisTick: {
                //    show: false
                // },
                show: false,
              }],
              yAxis: [{
                type: 'category',
                splitLine: {
                  show: false
                },
                axisLine: {
                  show: false
                },
                axisTick: {
                  show: false
                },
                data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
              }],
              series: [{
                name: 'Direct',
                type: 'bar',
                barWidth: '8',
                backgroundStyle: {
                  color: 'rgba(111, 162, 135, 0.2)'
                },
                itemStyle: {
                  normal: {
                    //这里设置柱形图圆角 [左上角,右上角,右下角,左下角]
                    barBorderRadius: [10, 10, 10, 10],
                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                        offset: 0,
                        color: '#FEC949'
                      },
                      {
                        offset: 1,
                        color: '#FEC949'
                      }
                    ])
                  },
                },
                data: [10, 52, 200, 334, 390, 330, 220]
              }]
            };
            this.chart.setOption(optionData)
            let _this = this
            window.addEventListener('resize', () => {
              if (_this.chart) {
                _this.chart.resize()
              }

            })
            this.chart.on('click', function(params) {
              // 控制台打印数据的名称'chart1'
              console.log(params)
              console.log(params.name)
              // that.$router.push({
              //    path: '/EventSearch',
              //    query: {
              //        language: params.seriesName,
              //        times: params.name
              //    }
              // });
            })

          },
          hcake: function(id, xdata, ydata, data3, sdata, that, isno) {
            console.log(id, '大饼图')
            this.chart = echarts.init(document.getElementById(id));
            this.chart.clear();
            const optionData = {
              title: {
                // text: 'Referer of a Website',
                // subtext: 'Fake Data',
                left: 'center'
              },
              tooltip: {
                trigger: 'item'
              },
              legend: {
                itemHeight: 10,
                itemWidth: 10,
                icon: "circle",
                orient: 'vertical',
                left: 'center',
                bottom: 30,
                orient: 'horizontal',
                textStyle: {
                  fontSize: 12,
                  color: '#333333'
                }
              },
              series: [{
                name: 'Access From',
                type: 'pie',
                center: ["50%", "40%"],
                radius: '50%',
                data: [{
                    value: 1048,
                    name: '按时填写'
                  },
                  {
                    value: 735,
                    name: '未按时填写'
                  },
                  {
                    value: 580,
                    name: '未填写'
                  }
                ],
                itemStyle: {
                  normal: {
                    color: function(colors) {
                      var colorList = [
                        '#92CE6B',
                        '#FFBF5C',
                        '#C0C6CE'
                      ];
                      return colorList[colors.dataIndex];
                    }
                  },
                },
                emphasis: {
                  itemStyle: {
                    shadowBlur: 10,
                    shadowOffsetX: 0,
                    shadowColor: 'rgba(0, 0, 0, 0.5)'
                  }
                }
              }]
            };
            this.chart.setOption(optionData)
            let _this = this
            window.addEventListener('resize', () => {
              if (_this.chart) {
                _this.chart.resize()
              }

            })
            this.chart.on('click', function(params) {
              // 控制台打印数据的名称'chart1'
              console.log(params)
              console.log(params.name)
              // that.$router.push({
              //    path: '/EventSearch',
              //    query: {
              //        language: params.seriesName,
              //        times: params.name
              //    }
              // });
            })

          },
          supercolumn: function(id, xdata, ydata, that, isno) {
            console.log(id, '大饼图')
            this.chart = echarts.init(document.getElementById(id));
            this.chart.clear();
            const optionData = {
              grid: {
                left: "10%",
                right: "10%",
                top: "10%",
                bottom: "20%",
                containLable: true,
              },
              tooltip: {
                trigger: "axis",
                axisPointer: {
                  type: "line",
                  lineStyle: {
                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                        offset: 0,
                        color: 'rgba(255,255,255,0.33)'
                      },
                      {
                        offset: 1,
                        color: 'rgba(75,139,253,0.02)'
                      }
                    ]),
                    width: 40,
                    type: "solid",
                  },
                  z: 0, //注意要设置层级,不然会在覆盖在柱子前面,设置为0就在柱子后面显示了。
                },
              },
              xAxis: {
                type: "category",
                data: xdata,
                splitLine: {
                  show: false
                },
                axisLine: {
                  show: false
                },
                axisTick: {
                  show: false
                },
              },
              yAxis: {
                type: "value",
                show: false,

              },
              series: [{
                data: ydata,
                type: "bar",
                // name: "留存",
                // stack: "用户",
                showBackground: true,
                backgroundStyle: {
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                      offset: 0,
                      color: 'rgba(255,255,255,0.33)'
                    },
                    {
                      offset: 1,
                      color: 'rgba(75,139,253,0.02)'
                    }
                  ]),
                  width: 40,
                  type: "solid"
                },
                emphasis: {
                  focus: "series",
                },
                barWidth: 24,
                itemStyle: {
                  normal: {
                    //这里设置柱形图圆角 [左上角,右上角,右下角,左下角]
                    barBorderRadius: [10, 10, 10, 10],
                    color: '#E9EBF1',
                    // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                    //     offset: 0,
                    //     color: '#FEC949'
                    //   },
                    //   {
                    //     offset: 1,
                    //     color: '#FEC949'
                    //   }
                    // ])
                  },
                  emphasis: {
                              color: '#4B8BFD',
                          }
                },
              }, ],
            };
            this.chart.setOption(optionData)
            let _this = this
            window.addEventListener('resize', () => {
              if (_this.chart) {
                _this.chart.resize()
              }

            })
            this.chart.on('click', function(params) {
              // 控制台打印数据的名称'chart1'
              console.log(params)
              console.log(params.name)
              // that.$router.push({
              //    path: '/EventSearch',
              //    query: {
              //        language: params.seriesName,
              //        times: params.name
              //    }
              // });
            })

          },
        }
      }
    }
  })
}
export default {
  install
}

// main.js
import myCharts from "./myCharts.js"
Vue.use(myCharts)
// index.vue
  <div id="line" />

  this.$chart.line('line', xdata, ydata, '', xAxisData, this, true)

#line {
  width: 100%;
  height: 3.5rem;
}

第二种 单个

image.png
//ChartsCircle.vue
<template>
  <div class="visualization-charts-common">
    <div ref="charts" class="visualization-charts"></div>
    <Select v-if="selectOptions && selectOptions.length&&showSelect" :options="selectOptions" @change="select">
    </Select>
    <!-- 返回上一级 -->
    <p class="back" v-show="showBack" @click="back">返回</p>
    <!-- 中心数据展示 -->
    <div class="center-data" v-if="total || total == 0">
      <p>总数</p>
      <p>{{total}}</p>
    </div>
  </div>
</template>

<script setup>
import { ref, toRefs, onMounted, onUnmounted, watch } from 'vue'
import Store from '@/store'
import Select from '../components/Select.vue'
import * as echarts from 'echarts/core';

let props = defineProps({
  data: Object,
  selectOptions: Array,
  total: [String, Number]
})
let emit = defineEmits(['select'])
let { data, selectOptions, total } = toRefs(props)

let charts = ref(null)
let chart = ref(null)
let showBack = ref(false)
let showSelect = ref(true)
let clickData = ref(null)
// let loading = ref(true)

onMounted(() => {
  chart.value = echarts.init(charts.value)
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  window.addEventListener('resize', resize)
  // chart.value.on('click', function (params) {
  //   if (params.data.list.length !== 0) {
  //     emit('clickChart', params)
  //     // chart.value.setOption(initOption(params.data.list))
  //   }
  // })
  chart.value.on('click', ({ data: newData }) => {

    if (newData.list) {
      let newList = []
      newData.list.forEach(item => {
        newList.push({
          count: item.amount,
          name: item.name,
          ratio: item.ratio,
        })
      })
      // newData.list = [{
      //   amount: "280000",
      //   count: 280000,
      //   list: null,
      //   name: "基于药物代谢动力学特性的复方依达拉奉注射液立题依据研究",
      //   ratio: "0.48%",
      //   type: "普通合同"
      // }]

      showSelect.value = false
      showBack.value = true
      clickData.value = {
        ...data.value,
        formatter: '{b}\n\n{d}%',

        tipFormat: ({ data: { name, count, ratio } }) => {
          console.log(data, "data")
          return `${name}:<br/>${count}  |  ${ratio}`
        },
        colors: ['#73DEB3', '#F2D459', '#4DC2FF', '#E6645D', '#A285D2'],
        source: newList
      }
      chart.value.setOption(initOption(clickData.value))
    }
  })
})

onUnmounted(() => {
  window.removeEventListener('resize', resize)
})

watch(() => data.value, () => {

  chart.value.setOption(initOption())
  // loading.value = false
  // chart.value.hideLoading()
})

// 适配
const resize = () => {

  if (chart.value) {
    if (clickData.value) {
      chart.value.setOption(initOption(clickData.value))
      chart.value.resize()
    } else {
      chart.value.setOption(initOption())
      chart.value.resize()
    }

  }
}

const loadingConfig = () => {
  const rate = Store.state.defaultData.width
  return {
    text: '数据加载中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,
    // 字体大小。从 `v4.8.0` 开始支持。
    fontSize: 12 * rate,
    // 是否显示旋转动画(spinner)。从 `v4.8.0` 开始支持。
    showSpinner: true,
    // 旋转动画(spinner)的半径。从 `v4.8.0` 开始支持。
    spinnerRadius: 10 * rate,
    // 旋转动画(spinner)的线宽。从 `v4.8.0` 开始支持。
    lineWidth: 2 * rate,
    // 字体粗细。从 `v5.0.1` 开始支持。
    fontWeight: 'normal',
    // 字体风格。从 `v5.0.1` 开始支持。
    fontStyle: 'normal',
  }
}

const initOption = (newData) => {

  const rate = Store.state.defaultData.width
  let tipFormat, formatter, source, colors
  console.log(newData, data.value)
  // const { dimensions, formatter, source, colors, tipFormat } = data.value
  if (newData) {
    tipFormat = newData.tipFormat
    formatter = newData.formatter
    source = newData.source
    colors = newData.colors
    // dimensions = newData.dimensions
  } else {
    tipFormat = data.value.tipFormat
    formatter = data.value.formatter
    source = data.value.source
    colors = data.value.colors
    // dimensions = data.value.dimensions
  }
  console.log(source, "source")
  // console.log(source, "source")
  return {

    // 数据集
    dataset: { source },
    tooltip: {
      show: true,
      formatter: tipFormat || formatter
    },
    legend: {
      type: 'scroll', // type 普通模式,滚动模式
      icon: 'circle',
      bottom: 12 * rate,
      left: 'center',
      padding: [0, 10 * rate], // 与 left top 类似
      itemGap: 36 * rate, // 图例间距
      itemWidth: 9 * rate, // 图标宽度
      itemHeight: 9 * rate, // 图标高度
      pageIconSize: 12 * rate, // 滚动icon大小
      pageIconInactiveColor: '#0933AA', // 默认颜色
      pageIconColor: '#4DC2FF', // 激活颜色
      pageTextStyle: {
        color: '#D0E0FF'
      },
      textStyle: {
        color: '#D0E0FF',
        fontSize: 12 * rate,
        lineHeight: 14 * rate
      }
    },
    series: [
      {
        type: 'pie', // 图表类型
        radius: [100 / 2 * rate, 140 / 2 * rate], // 圆环半径
        left: 'center',
        top: 9 * rate, // 中心偏移
        height: 210 * rate,
        color: colors || ['#73DEB3', '#F2D459', '#4DC2FF', '#E6645D', '#A285D2', '#b2fcb6', '#f09278', '#eb5ac8', '#8ec55e', '#5bb3f9', '#eda93b', '#c028ec', '#ec633f', '#7246be', '#5cbb7f', '#3672f6', '#e9752e', '#9c1ff5'],
        clockwise: false, // 顺时针 true 混乱 false
        minAngle: 5, // 最小占比
        itemStyle: {
          borderWidth: 2 * rate,
          borderColor: '#273989'
        },
        label: {
          show: true,
          fontSize: 12 * rate,
          color: '#D0E0FF',
          formatter,
          padding: [0, -60 * rate, 0, -60 * rate]
        },
        labelLine: {
          show: true,
          length: 16 * rate,
          length2: 96 * rate,
          lineStyle: {
            color: '#D0E0FF',
            width: 0.5
          }
        },
        emphasis: {
          scaleSize: 6 * rate
        },
      }
    ]
  }

}

const select = val => {
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  showBack.value = false

  emit('select', val)
}

const back = () => {
  showBack.value = false
  showSelect.value = true
  chart.value.setOption(initOption())
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.visualization-charts-common {
  width: 100%;
  height: 100%;
  position: relative;
  background: url('~img/visualization/img-chart-bg.png') no-repeat center 20px/185px 185px;

  .visualization-charts {
    width: 100%;
    height: 100%;
    position: relative;
  }

  .center-data {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    margin-top: -10px;

    p {
      font-size: 14px;
      color: #d0e0ff;
      text-align: center;
    }
  }
}
.back {
  position: absolute;
  right: 10px;
  top: 10px;
  width: 80px;
  height: 26px;
  border-radius: 4px;
  background: #33539a;
  font-size: 14px;
  font-weight: 400;
  color: #91b3f3;
  line-height: 26px;
  text-align: center;

  cursor: pointer;
}
</style>

//ChartsColumn
<template>
  <div class="visualization-charts-common">
    <div ref="charts" class="visualization-charts"></div>
    <Select
      v-if="selectOptions && selectOptions.length"
      :options="selectOptions"
      @change="select">
    </Select>
    <Radio
      v-if="radioOptions && radioOptions.length"
      :options="radioOptions"
      :value="radioValue"
      @change="change">
    </Radio>
  </div>
</template>

<script setup>
import { ref, toRefs, onMounted, onUnmounted, watch } from 'vue'
import Select from '../components/Select.vue'
import Radio from '../components/Radio.vue'
import Store from '@/store'
import * as echarts from 'echarts/core';

let props = defineProps({
  data: Object,
  selectOptions: Array,
  radioOptions: Array,
  radioValue: [String, Number]
})
let emit = defineEmits(['select', 'change'])
let { data, selectOptions, radioOptions } = toRefs(props)

let charts = ref(null)
let chart = ref(null)
let loading = ref(true)

onMounted(() => {
  chart.value = echarts.init(charts.value)
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  window.addEventListener('resize', resize)
})

onUnmounted(() => {
  window.removeEventListener('resize', resize)
})

watch(() => data.value, () => {
  chart.value.setOption(initOption())
  // loading.value = false
  // chart.value.hideLoading()
})

const resize = () => {
  if (chart.value) {
    chart.value.setOption(initOption())
    chart.value.resize()
  }
}

const loadingConfig = () => {
  const rate = Store.state.defaultData.width
  return {
    text: '数据加载中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字体大小。从 `v4.8.0` 开始支持。
    fontSize: 12 * rate,
    // 是否显示旋转动画(spinner)。从 `v4.8.0` 开始支持。
    showSpinner: true,
    // 旋转动画(spinner)的半径。从 `v4.8.0` 开始支持。
    spinnerRadius: 10 * rate,
    // 旋转动画(spinner)的线宽。从 `v4.8.0` 开始支持。
    lineWidth: 2 * rate,
    // 字体粗细。从 `v5.0.1` 开始支持。
    fontWeight: 'normal',
    // 字体风格。从 `v5.0.1` 开始支持。
    fontStyle: 'normal',
  }
}

const initOption = () => {
  const rate = Store.state.defaultData.width
  const { line, inverse, legend, rotate, dimensions, source, colors, formatter } = data.value
  // 数据
  const series = []
  colors.forEach((color) => {
    const firstColor = color[0], secondColor = color[1]
    series.push({
      type: 'bar',
      barWidth: 8 * rate,
      itemStyle: {
        barBorderRadius: [10 * rate, 10 * rate, 0, 0],
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: firstColor },
          { offset: 1, color: secondColor }
        ])
      },
      // hover 效果
      // emphasis: {
      //   focus: 'series',
      //   scaleSize: 6 * rate
      // },
    })
  })
  // 如果有线图
  if (line) {
    // 线
    series.push(
      {
        type: 'line',
        yAxisIndex: 1,
        // 标记
        showSymbol: false, // 显示标记
        symbol: 'circle',
        symbolSize: 6 * rate,
        itemStyle: {
          width: 0 * rate,
          color: line.color
        },
        // 线
        lineStyle: {
          color: line.color,
          width: 2 * rate
        },
        smooth: true, // 平滑曲线
        axisLine: {
          show: true,
        },
      }
    )
  }
  //配置项
  const rorateNum = rotate || 0 // 控制 X轴文字过多的旋转
  let height = 86, top = 10
  if (legend) height -= 10
  const hasTop =
    (selectOptions.value && selectOptions.value.length) ||
    (radioOptions.value && radioOptions.value.length)
  if (hasTop) {
    height -= 10
    top = 20
  }
  return {
    color: data.value.colors,
    // 数据集
    dataset: { dimensions, source },
    // 图例配置
    legend: {
      show: legend,
      type: 'scroll', // type 普通模式,滚动模式
      icon: 'rect',
      bottom: 12 * rate,
      left: 'center',
      padding: [0, 10 * rate], // 与 left top 类似
      itemGap: 36 * rate, // 图例间距
      itemWidth: 12 * rate, // 图标宽度
      itemHeight: 2 * rate, // 图标高度
      pageIconSize: 12 * rate, // 滚动icon大小
      pageIconInactiveColor: '#0933AA', // 默认颜色
      pageIconColor: '#4DC2FF', // 激活颜色
      pageTextStyle: {
        color: '#D0E0FF'
      },
      textStyle: {
        color: '#D0E0FF',
        fontSize: 12 * rate
      },
    },
    tooltip: {
      trigger: 'axis',
      formatter,
      axisPointer: {
        type: 'line',
        label: {
          backgroundColor: '#6a7985'
        },
        lineStyle: {
          type: 'solid',
          width: 1,
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            {
              offset: 0,
              color: 'rgba(77,194,255,1)'
            },
            {
              offset: 1,
              color: 'rgba(25,104,255,0)'
            }
          ])
        }
      }
    },
    xAxis: [
      {
        inverse,
        axisLabel: {
          fontSize: 12 * rate, // 更改坐标轴文字大小
          color: '#D0E0FF', // '#5A6382',
          interval: 0,
          rotate: rorateNum,
          formatter: params => getChar(params, 8)
        },
        type: 'category',
        // inverse: false, // 反转坐标轴
        boundaryGap: true, // 是否留白
        // min: 0, // 最小刻度个数
        // max: 10, // 最大刻度个数
        scale: false,
        // 刻度线设置
        axisLine: {
          lineStyle: {
            color: 'rgba(90,99,130,0.6)',
            width: 1
          }
        },
        // 刻度
        axisTick: {
          show: false,
          // alignWithLabel: false, // 刻度对齐 line 不生效
          inside: true, // 刻度是否向上
          length: 5 * rate, // 刻度长度
          lineStyle: {
            color: 'rgba(90,99,130,0.6)',
            width: 1
          }
        },
        // 背景网格线
        splitLine: {
          show: false
        }
      }
    ],
    yAxis: [
      {
        minInterval: 1,
        axisLabel: {
          fontSize: 12 * rate, // 更改坐标轴文字大小
          color: '#D0E0FF' // '#5A6382'
        },
        type: 'value',
        splitLine: {
          show: false
        }
      },
      {
        show: true,
        type: 'value',
        axisLabel: { show: false, formatter: '{value}' },
        splitLine: { show: false },
        axisTick: { show: false }
      }
    ],
    grid: {
      left: '3%',
      top: `${top}%`,
      width: '90%',
      height: `${height}%`,
      containLabel: true
    },
    series
  }
}

const select = val => {
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  emit('select', val)
}

const change = val => {
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  emit('change', val)
}

// 工具函数
const getChar = (str, limit) => {
  let text = ''
  let bytesCount = 0;
  for (let i = 0; i < str.length; i++) {
    let char = str.charAt(i);
    let c = char
    text += char
    //匹配双字节
    if (/^[u0000-u00ff]$/.test(c)) {
      bytesCount += 1;
    } else {
      bytesCount += 2;
    }
    if (bytesCount >= limit) return `${text}...`
  }
  return str
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.visualization-charts-common {
  width: 100%;
  height: 100%;
  position: relative;

  .visualization-charts {
    width: 100%;
    height: 100%;
  }

  .empty-tip {
    position: absolute;
    left: 50%;
    top: 50%;
    font-size: 14px;
    color: white;
  }
}
</style>

//ChartsKeyword
<template>
  <div class="visualization-charts-common">
    <div ref="charts" class="visualization-charts"></div>
  </div>
</template>

<script setup>
import { ref, toRefs, onMounted, onUnmounted, watch } from 'vue'
import Store from '@/store'
import * as echarts from 'echarts/core';

// type 0 科研项目关键字 1 研究方向 2 因子影响
let props = defineProps({ data: Object, })
let { data } = toRefs(props)

let charts = ref(null)
let chart = ref(null)

onMounted(() => {
  chart.value = echarts.init(charts.value)
  // chart.value.showLoading(loadingConfig())
  window.addEventListener('resize', resize)
})

onUnmounted(() => {
  window.removeEventListener('resize', resize)
})

watch(() => data.value, () => {
  chart.value.setOption(initOption())
  // chart.value.hideLoading()
})

const resize = () => {
  if (chart.value) {
    chart.value.setOption(initOption())
    chart.value.resize()
  }
}


const loadingConfig = () => {
  const rate = Store.state.defaultData.width
  return {
    text: '数据加载中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字体大小。从 `v4.8.0` 开始支持。
    fontSize: 12 * rate,
    // 是否显示旋转动画(spinner)。从 `v4.8.0` 开始支持。
    showSpinner: true,
    // 旋转动画(spinner)的半径。从 `v4.8.0` 开始支持。
    spinnerRadius: 10 * rate,
    // 旋转动画(spinner)的线宽。从 `v4.8.0` 开始支持。
    lineWidth: 2 * rate,
    // 字体粗细。从 `v5.0.1` 开始支持。
    fontWeight: 'normal',
    // 字体风格。从 `v5.0.1` 开始支持。
    fontStyle: 'normal',
  }
}

const color = ['#FF7F82', '#FFD867', '#38D8E4', '#D982E0', '#F74B4B', '#4DC2FF']
const initOption = () => {
  const rate = Store.state.defaultData.width

  return {
    tooltip: {},
    series: [{
      type: 'wordCloud',
      shape: 'pentagon', // 形状
      // 位置
      left: 'center',
      top: 'center',
      // 宽高
      width: '90%',
      height: '85%',
      sizeRange: [14 * rate, 32 * rate], // 文本大小范围
      rotationRange: [-0, 0], // 旋转范围
      rotationStep: 2, // 旋转单位
      gridSize: 10 * rate, // 网格(单词间距)
      drawOutOfBound: false, // 是否可以在画布外绘制
      textStyle: {
        color: () => {
          const index = Math.round(Math.random() * 5)
          return color[index]
        }
      },
      data: data.value.source
    }]
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.visualization-charts-common {
  width: 100%;
  height: 100%;
  position: relative;

  .visualization-charts {
    width: 100%;
    height: 100%;
  }
}
</style>

//ChartsLevel
<template>
  <div class="visualization-charts-common">
    <div ref="charts" class="visualization-charts"></div>
    <Select
      v-if="selectOptions && selectOptions.length"
      :options="selectOptions"
      @change="select">
    </Select>
    <Radio
      v-if="radioOptions && radioOptions.length"
      :options="radioOptions"
      @change="change">
    </Radio>
    <div class="button-more" v-if="showMore" @click="clickMore">
      <p class="button-text">更多</p>
      <i class="el-icon-arrow-right"></i>
    </div>
  </div>
</template>

<script setup>
import { ref, toRefs, onMounted, onUnmounted, watch } from 'vue'
import Select from '../components/Select.vue'
import Radio from '../components/Radio.vue'
import Store from '@/store'
import * as echarts from 'echarts/core';

let props = defineProps({
  data: Object,
  selectOptions: Array,
  radioOptions: Array,
  showMore: Boolean,
})
let emit = defineEmits(['select', 'change', 'more'])
let { data, selectOptions, radioOptions, showMore } = toRefs(props)

let charts = ref(null)
let chart = ref(null)
let loading = ref(true)

onMounted(() => {
  chart.value = echarts.init(charts.value)
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  window.addEventListener('resize', resize)
})

onUnmounted(() => {
  window.removeEventListener('resize', resize)
})

watch(() => data.value, () => {
  chart.value.setOption(initOption())
  // loading.value = false
  // chart.value.hideLoading()
})

const resize = () => {
  if (chart.value) {
    chart.value.setOption(initOption())
    chart.value.resize()
  }
}

const loadingConfig = () => {
  const rate = Store.state.defaultData.width
  return {
    text: '数据加载中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字体大小。从 `v4.8.0` 开始支持。
    fontSize: 12 * rate,
    // 是否显示旋转动画(spinner)。从 `v4.8.0` 开始支持。
    showSpinner: true,
    // 旋转动画(spinner)的半径。从 `v4.8.0` 开始支持。
    spinnerRadius: 10 * rate,
    // 旋转动画(spinner)的线宽。从 `v4.8.0` 开始支持。
    lineWidth: 2 * rate,
    // 字体粗细。从 `v5.0.1` 开始支持。
    fontWeight: 'normal',
    // 字体风格。从 `v5.0.1` 开始支持。
    fontStyle: 'normal',
  }
}

const initOption = () => {
  const rate = Store.state.defaultData.width
  const { legend, rotate, dimensions, source, colors, formatter, dot, xFormatter, yInverse } = data.value
  // 数据
  const series = []
  colors.forEach((color) => {
    const firstColor = color[0], secondColor = color[1]
    series.push({
      type: 'bar',
      barWidth: 4 * rate,
      itemStyle: {
        barBorderRadius: [0, 4 * rate, 4 * rate, 0],
        color: new echarts.graphic.LinearGradient(1, 0, 0, 0, [
          { offset: 0, color: firstColor },
          { offset: 1, color: secondColor }
        ])
      },
      // hover 效果
      // emphasis: {
      //   focus: 'series',
      //   scaleSize: 6 * rate
      // },
    })
  })
  // 顶部光点
  series.push({
    type: 'pictorialBar',
    symbol: () => (circleList[dot]),
    symbolPosition: 'end',
    symbolSize: [14 * rate, 14 * rate],
    symbolOffset: [7 * rate, 0],
    z: 14,
  })
  //配置项
  const rorateNum = rotate || 0 // 控制 X轴文字过多的旋转
  let height = 86, top = 10
  if (legend) height -= 10
  const hasTop =
    (selectOptions.value && selectOptions.value.length) ||
    (radioOptions.value && radioOptions.value.length) ||
    showMore.value
  if (hasTop) {
    height -= 10
    top = 20
  }
  return {
    color: data.value.colors,
    // 数据集
    dataset: { dimensions, source },
    // 图例配置
    legend: {
      show: legend,
      type: 'scroll', // type 普通模式,滚动模式
      icon: 'rect',
      bottom: 12 * rate,
      left: 'center',
      padding: [0, 10 * rate], // 与 left top 类似
      itemGap: 36 * rate, // 图例间距
      itemWidth: 12 * rate, // 图标宽度
      itemHeight: 2 * rate, // 图标高度
      pageIconSize: 12 * rate, // 滚动icon大小
      pageIconInactiveColor: '#0933AA', // 默认颜色
      pageIconColor: '#4DC2FF', // 激活颜色
      pageTextStyle: {
        color: '#D0E0FF'
      },
      textStyle: {
        color: '#D0E0FF',
        fontSize: 12 * rate
      },
    },
    tooltip: {
      trigger: 'axis',
      formatter,
      axisPointer: {
        type: 'cross',
        label: {
          backgroundColor: '#6a7985'
        },
        lineStyle: {
          type: 'solid',
          width: 1,
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            {
              offset: 0,
              color: 'rgba(77,194,255,1)'
            },
            {
              offset: 1,
              color: 'rgba(25,104,255,0)'
            }
          ])
        },
        crossStyle: {
          type: 'solid',
          width: 1,
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            {
              offset: 0,
              color: 'rgba(77,194,255,1)'
            },
            {
              offset: 1,
              color: 'rgba(25,104,255,0)'
            }
          ])
        }
      }
    },
    yAxis: [
      {
        inverse: yInverse,
        axisLabel: {
          fontSize: 12 * rate, // 更改坐标轴文字大小
          color: '#D0E0FF', // '#5A6382',
          interval: 0,
          rotate: rorateNum,
          formatter: params => getChar(params, 8)
        },
        type: 'category',
        // inverse: false, // 反转坐标轴
        boundaryGap: true, // 是否留白
        // min: 0, // 最小刻度个数
        // max: 10, // 最大刻度个数
        scale: false,
        // 刻度线设置
        axisLine: {
          lineStyle: {
            color: 'rgba(90,99,130,0.6)',
            width: 1
          }
        },
        // 刻度
        axisTick: {
          show: false,
          // alignWithLabel: false, // 刻度对齐 line 不生效
          inside: true, // 刻度是否向上
          length: 5 * rate, // 刻度长度
          lineStyle: {
            color: 'rgba(90,99,130,0.6)',
            width: 1
          }
        },
        // 背景网格线
        splitLine: {
          show: false
        }
      }
    ],
    xAxis: [
      {
        axisLabel: {
          fontSize: 12 * rate, // 更改坐标轴文字大小
          color: '#D0E0FF', // '#5A6382'
          formatter: xFormatter
        },
        type: 'value',
        splitLine: {
          show: false
        }
      }
    ],
    grid: {
      left: '3%',
      top: `${top}%`,
      width: '90%',
      height: `${height}%`,
      containLabel: true
    },
    series
  }
}

const select = val => {
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  emit('select', val)
}

const change = val => {
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  emit('change', val)
}

const clickMore = () => {
  emit('more')
}

// 工具函数
const getChar = (str, limit) => {
  let text = ''
  let bytesCount = 0;
  for (let i = 0; i < str.length; i++) {
    let char = str.charAt(i);
    let c = char
    text += char
    //匹配双字节
    if (/^[u0000-u00ff]$/.test(c)) {
      bytesCount += 1;
    } else {
      bytesCount += 2;
    }
    if (bytesCount >= limit) return `${text}...`
  }
  return str
}

let circleList = [
  'image://data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAACXBIWXMAAAsTAAALEwEAmpwYAAACFElEQVRIia3Wv24TQRDH8Y9PSZBwCmMrIY2hwDWIPwUNbfgnUfIC8BwgeAtEXgMRAh10iCCo08RpgiWCIiUUxLIpdg8cs2snZ77SNXM389ubnZ2d2sN3352AFpbRRB3z0X6EQ/zAN0wNNjfhXQ1tdKJIijPxaeJSFN/CDoanETyL62hMW/EYdVzBRXzCz/EPioRTC7cqiI3SiDFa0wSXcBMLM4iVLMRYSznBRdxILGIWihhzsTSUe1jDVZOLqI1VYW/PR9suNrEhFEqKuRj7A4alQFt+z+bwCPfiwsYX0cYDvMEafiViNOJ33TJ9nQliT3E/ITZKDXfwRD5LHUKOW/Ln7LFQ5iflspCNFHW0CqGDpLggrPq03I2+KZYLoUukWFWtYovom6JZyKfzWgWxab71wt9GPM7KDII53/n/echH6edeFMIVk2J3BsFexn5UCFdKis0ZBL9k7IeFcHmmeItBBbGB0OpS7BXCTZ1iG+sVBNejb4peIYwFubSuyacnxdfok+JQ/EPCWJCij2d4JTMyRIbCnz2Xr9AtI7fFjjAWNDKiL/Aat4VDvSJUd0/IwIZ8GmE/avzp7EN8FsaCXLfv4uWEoDn6QsUPOd4rD4TBp0pl5hjEmAelYbzT9PDRhE5xCvox1rEmkGptPbwX8l6V/Rjjn46T26+D6DBtEB6n8iAsOnSjc1MYnM6ZPOrv5YRKfgMBHnHO24sLIQAAAABJRU5ErkJggg==',
  'image://data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAACXBIWXMAAAsTAAALEwEAmpwYAAACFklEQVRIia3Wv24TQRDH8Y9PSZBwCmMrIY2hwDWIPwUNbfgn8RLwBvQgeAtE3gIhQiihQwRBHSHhNMESQZEcCmLFFLsHjtm1kzNf6Zq5m/ntzc7OTu3hlxeOQQvLaKKO+Wg/wD5+4Bu+Tws0N+FdDW10okiKU/Fp4kIU38I2hicRPI2raExb8Rh1XMJ5fMDP8Q+KhFMLNyqIjdKIMVrTBJdwHQsziJUsxFhLOcFFXEssYhaKGHOxNJR7WMNlk4uojVVhb89G2w42sSEUSoq5GPsdhqVAW37P5nAfd+LCxhfRxj28xhp+JWI04nfdMn2dCWKPcTchNkoNt/BIPksdQo5b8ufsgVDmx+WikI0UdbQKoYOkOCes+qTcjr4plguhS6RYVa1ii+ibolnIp/NKBbFpvvXC30Y8zsoMgjnf+f95yEcZ5F4UwhWTYmcGwV7GflAIV0qKzRkEP2Xs+4VweaZ4g8MKYodCq0uxWwg3dYqvWK8guB59U/QKYSzIpXVNPj0pPkefFPviHxLGghQDPMFLmZEhMhT+7Kl8hW4ZuS22hbGgkRF9hle4KRzqFaG6e0IGNuTTCHtR409nH+KjMBbkun0XzycEzTEQKn7I0V7ZFwafKpWZ4zDG7JeG8U7Tw3sTOsUJGMRYR5pAqrX18FbIe1X2Yox/Ok5uv/rRYdogPE7lQVh06EbnpjA4nTF51N/NCZX8Bj83cdPYyNDpAAAAAElFTkSuQmCC'
]
</script>

is component only -->
<style lang="scss" scoped>
.visualization-charts-common {
  width: 100%;
  height: 100%;
  position: relative;

  .visualization-charts {
    width: 100%;
    height: 100%;
  }

  .button-more {
    @include flexbox;
    justify-content: center;
    position: absolute;
    right: 10px;
    top: 10px;
    height: 26px;
    border-radius: 4px;
    background: #33539a;
    font-size: 14px;
    font-weight: 400;
    color: #91b3f3;
    line-height: 26px;
    padding-left: 10px;
    padding-right: 10px;
    cursor: pointer;

    i {
      color: #C0C4CC;
      font-size: 14px;
    }
  }
}
</style>

//ChartsLine
<template>
  <div class="visualization-charts-common">
    <div ref="charts" class="visualization-charts"></div>
    <Select
      v-if="selectOptions && selectOptions.length"
      :options="selectOptions"
      @change="select">
    </Select>
    <Radio
      v-if="radioOptions && radioOptions.length"
      :options="radioOptions"
      @change="change">
    </Radio>
  </div>
</template>

<script setup>

import { ref, toRefs, onMounted, onUnmounted, watch } from 'vue'
import Select from '../components/Select.vue'
import Radio from '../components/Radio.vue'
import Store from '@/store'
import * as echarts from 'echarts/core'

let props = defineProps({
  data: Object,
  selectOptions: Array,
  radioOptions: Array
})
let emit = defineEmits(['select', 'change'])
let { data, selectOptions, radioOptions } = toRefs(props)

let charts = ref(null)
let chart = ref(null)
let loading = ref(true)

onMounted(() => {
  chart.value = echarts.init(charts.value)
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  window.addEventListener('resize', resize)
})

onUnmounted(() => {
  window.removeEventListener('resize', resize)
})

watch(() => data.value, () => {
  chart.value.setOption(initOption())
  // loading.value = false
  // chart.value.hideLoading()
})

// 适配
const resize = () => {
  if (chart.value) {
    chart.value.setOption(initOption())
    chart.value.resize()
  }
}

const loadingConfig = () => {
  const rate = Store.state.defaultData.width
  return {
    text: '数据加载中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字体大小。从 `v4.8.0` 开始支持。
    fontSize: 12 * rate,
    // 是否显示旋转动画(spinner)。从 `v4.8.0` 开始支持。
    showSpinner: true,
    // 旋转动画(spinner)的半径。从 `v4.8.0` 开始支持。
    spinnerRadius: 10 * rate,
    // 旋转动画(spinner)的线宽。从 `v4.8.0` 开始支持。
    lineWidth: 2 * rate,
    // 字体粗细。从 `v5.0.1` 开始支持。
    fontWeight: 'normal',
    // 字体风格。从 `v5.0.1` 开始支持。
    fontStyle: 'normal',
  }
}

// 图表逻辑
const initOption = () => {
  const rate = Store.state.defaultData.width
  const { legend, rotate, dimensions, source, colors, inverse } = data.value
  // 数据
  const series = []
  colors.forEach((color) => {
    const firstColor = color[0], secondColor = color[1]
    series.push({
      type: 'line',
      // seriesLayoutBy: 'row',
      showSymbol: false, // 显示标记
      symbol: 'circle',
      symbolSize: 6 * rate,
      itemStyle: {
        color: firstColor
      },
      // stack: 'Total', // 数据堆叠值
      smooth: true, // 平滑曲线
      lineStyle: {
        color: firstColor,
        width: 2 * rate
      },
      areaStyle: {
        opacity: 0.4,
        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
          { offset: 0, color: firstColor },
          { offset: 1, color: secondColor }
        ])
      },
      // hover 效果
      emphasis: {
        focus: 'series',
        scaleSize: 6 * rate
      },
    })
  })
  //配置项
  const rorateNum = rotate || 0 // 控制 X轴文字过多的旋转
  let height = 86, top = 10
  if (legend) height -= 10
  const hasTop =
    (selectOptions.value && selectOptions.value.length) ||
    (radioOptions.value && radioOptions.value.length)
  if (hasTop) {
    height -= 10
    top = 20
  }
  return {
    color: data.value.colors,
    // 数据集
    dataset: { dimensions, source },
    // 图例配置
    legend: {
      show: legend,
      type: 'scroll', // type 普通模式,滚动模式
      icon: 'rect',
      bottom: 12 * rate,
      left: 'center',
      padding: [0, 10 * rate], // 与 left top 类似
      itemGap: 36 * rate, // 图例间距
      itemWidth: 12 * rate, // 图标宽度
      itemHeight: 2 * rate, // 图标高度
      pageIconSize: 12 * rate, // 滚动icon大小
      pageIconInactiveColor: '#0933AA', // 默认颜色
      pageIconColor: '#4DC2FF', // 激活颜色
      pageTextStyle: {
        color: '#D0E0FF'
      },
      textStyle: {
        color: '#C6C5DE',
        fontSize: 12 * rate
      },
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'line',
        label: {
          backgroundColor: '#6a7985'
        },
        lineStyle: {
          type: 'solid',
          width: 1,
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            {
              offset: 0,
              color: 'rgba(77,194,255,1)'
            },
            {
              offset: 1,
              color: 'rgba(25,104,255,0)'
            }
          ])
        }
      }
    },
    xAxis: [
      {
        inverse,
        axisLabel: {
          fontSize: 12 * rate, // 更改坐标轴文字大小
          color: '#D0E0FF', // '#5A6382',
          interval: 0,
          rotate: rorateNum,
          formatter: params => getChar(params, 8)
        },
        type: 'category',
        // inverse: false, // 反转坐标轴
        boundaryGap: true, // 是否留白
        // min: 0, // 最小刻度个数
        // max: 10, // 最大刻度个数
        scale: false,
        // 刻度线设置
        axisLine: {
          lineStyle: {
            color: 'rgba(90,99,130,0.6)',
            width: 1
          }
        },
        // 刻度
        axisTick: {
          alignWithLabel: true, // 搭配 boundaryGap,对齐刻度线
          inside: true, // 刻度是否向上
          length: 5 * rate, // 刻度长度
          lineStyle: {
            color: 'rgba(90,99,130,0.6)',
            width: 1
          }
        },
        // 背景网格线
        splitLine: {
          show: false
        }
      }
    ],
    yAxis: [
      {
        axisLabel: {
          fontSize: 12 * rate, // 更改坐标轴文字大小
          color: '#D0E0FF' // '#5A6382'
        },
        type: 'value',
        splitLine: {
          show: false
        }
      }
    ],
    grid: {
      left: '3%',
      top: `${top}%`,
      width: '90%',
      height: `${height}%`,
      containLabel: true
    },
    series
  }
}

const select = val => {
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  emit('select', val)
}

// const change = val = {
//   // emit('change', val)
// }

// 工具函数
const getChar = (str, limit) => {
  let text = ''
  let bytesCount = 0;
  for (let i = 0; i < str.length; i++) {
    let char = str.charAt(i);
    let c = char
    text += char
    //匹配双字节
    if (/^[u0000-u00ff]$/.test(c)) {
      bytesCount += 1;
    } else {
      bytesCount += 2;
    }
    if (bytesCount >= limit) return `${text}...`
  }
  return str
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.visualization-charts-common {
  width: 100%;
  height: 100%;
  position: relative;

  .visualization-charts {
    width: 100%;
    height: 100%;
  }
}
</style>

//ChartsPie
<template>
  <div class="visualization-charts-common">
    <div ref="charts" class="visualization-charts"></div>
    <Select v-if="selectOptions && selectOptions.length" :options="selectOptions" @change="select">
    </Select>
    <!-- 返回上一级 -->
    <p class="back" v-show="showBack" @click="back">返回</p>
  </div>
</template>

<script setup>
import { ref, toRefs, onMounted, onUnmounted, watch } from 'vue'
import Store from '@/store'
import Select from '../components/Select.vue'
import * as echarts from 'echarts/core';

let props = defineProps({
  data: Object,
  selectOptions: Array,
})
let emit = defineEmits(['select'])
let { data, selectOptions } = toRefs(props)

let charts = ref(null)
let chart = ref(null)
let loading = ref(true)
let showBack = ref(false)

onMounted(() => {
  chart.value = echarts.init(charts.value)
  chart.value.on('click', ({ data }) => {

    if (data.cone) {
      showBack.value = true
      chart.value.setOption(initOption({
        ...data.value,
        tipFormat: ({ data: { name, count, ratio } }) => {
          return `${name}:<br/>${count}  |  ${ratio}`
        },
        source: data.cone
      }))
    }
  })
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  window.addEventListener('resize', resize)
})

onUnmounted(() => {
  window.removeEventListener('resize', resize)
})

watch(() => data.value, () => {
  chart.value.setOption(initOption())
  // loading.value = false
  // chart.value.hideLoading()
})

const resize = () => {
  if (chart.value) {
    chart.value.setOption(initOption())
    chart.value.resize()
  }
}

const loadingConfig = () => {
  const rate = Store.state.defaultData.width
  return {
    text: '数据加载中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字体大小。从 `v4.8.0` 开始支持。
    fontSize: 12 * rate,
    // 是否显示旋转动画(spinner)。从 `v4.8.0` 开始支持。
    showSpinner: true,
    // 旋转动画(spinner)的半径。从 `v4.8.0` 开始支持。
    spinnerRadius: 10 * rate,
    // 旋转动画(spinner)的线宽。从 `v4.8.0` 开始支持。
    lineWidth: 2 * rate,
    // 字体粗细。从 `v5.0.1` 开始支持。
    fontWeight: 'normal',
    // 字体风格。从 `v5.0.1` 开始支持。
    fontStyle: 'normal',
  }
}

const initOption = (newData) => {
  const rate = Store.state.defaultData.width
  let tipFormat, formatter, source, colors
  if (newData) {
    tipFormat = newData.tipFormat
    formatter = newData.formatter
    source = newData.source
    colors = newData.colors || ['#73DEB3', '#F2D459', '#4DC2FF', '#E6645D', '#A285D2', '#b2fcb6', '#f09278', '#eb5ac8', '#8ec55e', '#5bb3f9', '#eda93b', '#c028ec', '#ec633f', '#7246be', '#5cbb7f', '#3672f6', '#e9752e', '#9c1ff5']
  } else {
    tipFormat = data.value.tipFormat
    formatter = data.value.formatter
    source = data.value.source
    colors = data.value.colors || ['#73DEB3', '#F2D459', '#4DC2FF', '#E6645D', '#A285D2', '#b2fcb6', '#f09278', '#eb5ac8', '#8ec55e', '#5bb3f9', '#eda93b', '#c028ec', '#ec633f', '#7246be', '#5cbb7f', '#3672f6', '#e9752e', '#9c1ff5']
  }
  return {
    // 数据集
    dataset: { source },
    tooltip: {
      show: true,
      formatter: tipFormat || formatter
    },
    legend: {
      type: 'scroll', // type 普通模式,滚动模式
      icon: 'circle',
      bottom: 12 * rate,
      left: 'center',
      padding: [0, 10 * rate], // 与 left top 类似
      itemGap: 36 * rate, // 图例间距
      itemWidth: 9 * rate, // 图标宽度
      itemHeight: 9 * rate, // 图标高度
      pageIconSize: 12 * rate, // 滚动icon大小
      pageIconInactiveColor: '#0933AA', // 默认颜色
      pageIconColor: '#4DC2FF', // 激活颜色
      pageTextStyle: {
        color: '#D0E0FF'
      },
      textStyle: {
        color: '#D0E0FF',
        fontSize: 12 * rate,
        lineHeight: 14 * rate
      }
    },
    series: [
      {
        type: 'pie', // 图表类型
        radius: [0, 140 / 2 * rate], // 圆环半径
        left: 'center',
        top: 9 * rate, // 中心偏移
        color: colors,
        clockwise: false, // 顺时针 true 混乱 false
        minAngle: 5, // 最小占比
        height: 210 * rate,
        label: {
          show: true,
          fontSize: 12 * rate,
          color: '#D0E0FF',
          formatter,
          padding: [0, -60 * rate, 0, -60 * rate],
          // textStyle: {
          //   color: '#D0E0FF',
          //   fontSize: 12 * rate,
          //   lineHeight: 14 * rate
          // }
        },
        labelLine: {
          show: true,
          length: 16 * rate,
          length2: 96 * rate,
          lineStyle: {
            color: '#D0E0FF',
            width: 0.5
          }
        },
        emphasis: {
          scaleSize: 6 * rate
        },
      }
    ]
  }
}

const select = val => {
  // chart.value.showLoading(loadingConfig())
  // loading.value = true
  showBack.value = false
  emit('select', val)
}

const back = () => {
  showBack.value = false
  chart.value.setOption(initOption())
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.visualization-charts-common {
  width: 100%;
  height: 100%;
  position: relative;
  background: url('~img/visualization/img-chart-bg.png') no-repeat center 20px/185px 185px;

  .visualization-charts {
    width: 100%;
    height: 100%;
  }

  .back {
    position: absolute;
    left: 10px;
    top: 10px;
    width: 70px;
    height: 26px;
    border-radius: 4px;
    background: #33539a;
    font-size: 14px;
    font-weight: 400;
    color: #91b3f3;
    line-height: 26px;
    text-align: center;
    padding-left: 10px;
    padding-right: 26px;
    cursor: pointer;
  }
}
</style>

// 使用
<template>
  <div class="visualization-page-container">
    <div class="visualization-content">
      <!-- 上 -->
      <div class="section col-3">
        <!-- 左 -->
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 曲线图 -->
            <Title title="科研成果近五年趋势"></Title>
            <Wrapper
              :loading="trend5YearLoading"
              :data="trend5YearData"
              showEmpty>
              <div class="charts-line-wrapper">
                <ChartsLine :data="trend5YearData"></ChartsLine>
              </div>
            </Wrapper>
          </div>
        </div>
        <!-- 中 -->
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 环形图 -->
            <Title title="科研成果总体概况"></Title>
            <Wrapper
              :loading="trendOverviewLoading"
              :data="trendOverviewData"
              showEmpty>
              <div class="charts-line-wrapper">
                <ChartsCircle
                  :data="trendOverviewData"
                  :selectOptions="trendOverviewSelectOptions"
                  @select="trendOverviewSelect">
                </ChartsCircle>
              </div>
            </Wrapper>
          </div>
        </div>
        <!-- 右 -->
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 词云图 -->
            <Title title="研究方向"></Title>
            <Wrapper
              :loading="keywordLoading"
              :data="keywordData"
              showEmpty>
              <div class="charts-line-wrapper">
                <ChartsKeyword :data="keywordData"></ChartsKeyword>
              </div>
            </Wrapper>
          </div>
        </div>
      </div>
      <!-- 中 -->
      <div class="section col-3">
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 曲线图 -->
            <Title title="各院系科研成果情况"></Title>
            <Wrapper
              :loading="departmentAchievLoading"
              :data="departmentAchievData"
              showEmpty>
              <div class="charts-line-wrapper">
                <ChartsLine
                  :data="departmentAchievData"
                  :selectOptions="departmentAchievSelectOptions"
                  @select="departmentAchievSelect">
                </ChartsLine>
              </div>
            </Wrapper>
          </div>
        </div>
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 饼状图 -->
            <Title title="科研论文语种情况"></Title>
            <Wrapper
              :loading="langLoading"
              :data="langData"
              showEmpty>
              <div class="charts-line-wrapper">
                <ChartsPie
                  :data="langData"
                  :selectOptions="langSelectOptions"
                  @select="langSelect">
                </ChartsPie>
              </div>
            </Wrapper>
          </div>
        </div>
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 柱状图 -->
            <Title title="研究方向负责人情况"></Title>
            <Wrapper
              :loading="directionLoading"
              :data="directionData"
              showEmpty>
              <div class="charts-line-wrapper">
                <!-- :radioOptions="directionRadioOptions" -->
                <ChartsColumn
                  :data="directionData"
                  :selectOptions="directionSelectOptions"
                  @select="directionSelect"
                  @change="directionChange">
                </ChartsColumn>
              </div>
            </Wrapper>
          </div>
        </div>
      </div>
      <!-- 下 -->
      <div class="section col-2">
        <!-- 左 -->
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 柱状图 -->
            <Title title="科研专利类型情况TOP"></Title>
            <Wrapper
              :loading="categoryLoading"
              :data="categoryData"
              showEmpty>
              <div class="charts-line-wrapper">
                <ChartsLevel
                  :data="categoryData"
                  :selectOptions="categorySelectOptions"
                  :radioOptions="categoryRadioOptions"
                  @select="categorySelect"
                  @change="categoryChange">
                </ChartsLevel>
              </div>
            </Wrapper>
          </div>
        </div>
        <!-- 右 -->
        <div class="section-wrapper">
          <div class="section-block">
            <!-- 柱状图 -->
            <Title title="著作类别情况TOP"></Title>
            <Wrapper
              :loading="bookCategoryLoading"
              :data="bookCategoryData"
              showEmpty>
              <div class="charts-line-wrapper">
                <ChartsLevel
                  :data="bookCategoryData"
                  :selectOptions="bookCategorySelectOptions"
                  :radioOptions="bookCategoryRadioOptions"
                  @select="bookCategorySelect"
                  @change="bookCategoryChange">
                </ChartsLevel>
              </div>
            </Wrapper>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onBeforeMount } from 'vue'
import Router from '@/router'
import { countDate, dict } from 'utils'
import {
  trend5Year, // 5年趋势
  departmentAchiev, // 各科成果
  trendOverview, // 科研成果总体
  articleLang, // 论文语种
  directionKeyword, // 研究方向词云
  directionPerson, // 研究方向负责人
  patentCategory, // 专利类型
  bookCategory // 著作类别
} from '../service'
import Title from '../components/Title.vue'
import Wrapper from '../components/Wrapper.vue';
import ChartsLine from '../charts/ChartsLine.vue';
import ChartsCircle from '../charts/ChartsCircle';
import ChartsKeyword from '../charts/ChartsKeyword.vue';
import ChartsPie from '../charts/ChartsPie.vue';
import ChartsColumn from '../charts/ChartsColumn.vue';
import ChartsLevel from '../charts/ChartsLevel.vue'

let id = ref('')
// 5年趋势
let trend5YearData = ref({})
let trend5YearLoading = ref(true)
// 各科成果
let departmentAchievData = ref({})
let departmentAchievSelectOptions = ref([])
let departmentAchievSelectAct = ref('')
let departmentAchievLoading = ref(true)
// 科研总体
let trendOverviewData = ref({})
let trendOverviewSelectOptions = ref([])
let trendOverviewSelectAct = ref('')
let trendOverviewLoading = ref(true)
// 论文语种
let langData = ref({})
let langSelectOptions = ref([])
let langSelectAct = ref('')
let langLoading = ref(true)
// 研究方向词云
let keywordData = ref({})
let keywordLoading = ref(true)
// 研究方向
let directionData = ref({})
let directionSelectOptions = ref([])
let directionSelectAct = ref('')
let directionRadioOptions = ref([]) // 暂时弃用
let directionRadioAct = ref('') // 暂时弃用
let directionLoading = ref(true)
// 专利类型
let categoryData = ref({})
let categorySelectOptions = ref([])
let categorySelectAct = ref('')
let categoryRadioOptions = ref([])
let categoryRadioAct = ref('')
let categoryLoading = ref(true)
// 著作类别
let bookCategoryData = ref()
let bookCategorySelectOptions = ref([])
let bookCategorySelectAct = ref('')
let bookCategoryRadioOptions = ref([])
let bookCategoryRadioAct = ref('')
let bookCategoryLoading = ref(true)

onBeforeMount(() => {
  if (Router.mode === 'history') {
    id.value = Router.history.current.params.id
  } else {
    id.value = Router.history.current.query.id
  }

  // 获取数据
  getTrend5Year() // 5年趋势
  getDepartmentAchiev() // 各科成果
  getTrendOverview() // 科研成果总体概况
  getLang() // 论文语种
  getKeyword() // 研究方向词云
  getDirection() // 研究方向负责人
  getCategory() // 专利类型
  getBookCategory() // 著作类别
})

// 5年趋势
const getTrend5Year = () => {
  trend5YearLoading.value = true
  trend5Year({ collegeCode: id.value }).then(({ data }) => {
    if (data) {
      const source = data.map(item => ({
        year: item.year,
        '论文数量': item.lw,
        '专利数量': item.zl,
        '著作数量': item.zz
      }))
      trend5YearData.value = {
        legend: true, // 是否显示图例
        dimensions: ['year', '论文数量', '专利数量', '著作数量'],
        source,
        colors: [
          ['rgba(77,194,255,1)', 'rgba(25,104,255,0)'],
          ['rgba(252,193,72,1)', 'rgba(252,193,72,0)'],
          ['rgba(115,222,179,1)', 'rgba(115,222,179,0)']
        ]
      }
    }
    trend5YearLoading.value = false
    // console.log(trend5YearData.value)
  })
}
// 各科成果
const getDepartmentAchiev = () => {
  departmentAchievLoading.value = true
  // 筛选项
  departmentAchievSelectOptions.value = countDate()
  const select =
    departmentAchievSelectAct.value ||
    departmentAchievSelectOptions.value[0].value
  // 数据
  departmentAchiev({ collegeCode: id.value, time: select }).then(({ data }) => {
    if (data) {
      const source = data.map(item => ({
        name: item.name,
        '获奖成果': item.hjCount,
        '论文数量': item.lwCount,
        '专利数量': item.zlCount,
        '著作数量': item.zzCount
      }))
      departmentAchievData.value = {
        // 配置项
        legend: true, // 是否显示图例
        rotate: 45,
        // 数据
        dimensions: ['name', '获奖成果', '论文数量', '专利数量', '著作数量'],
        source,
        colors: [
          ['rgba(232,104,74,1)', 'rgba(232,104,74,0)'],
          ['rgba(77,194,255,1)', 'rgba(25,104,255,0)'],
          ['rgba(252,193,72,1)', 'rgba(252,193,72,0)'],
          ['rgba(115,222,179,1)', 'rgba(115,222,179,0)']
        ]
      }
    }
    departmentAchievLoading.value = false
    // console.log(trend5YearData.value)
  })
}
const departmentAchievSelect = val => {
  departmentAchievSelectAct.value = val
  getDepartmentAchiev()
}
// 成果总体概况
const getTrendOverview = () => {
  trendOverviewLoading.value = true
  // 筛选项
  trendOverviewSelectOptions.value = countDate()
  const select =
    trendOverviewSelectAct.value ||
    trendOverviewSelectOptions.value[0].value
  // 数据
  trendOverview({ collegeCode: id.value, time: select }).then(({ data }) => {
    if (data) {
      trendOverviewData.value = {
        tipFormat: ({ data: { name, count, ratio } }) => {
          return `${name}:<br/>${count}  |  ${ratio}`
        },
        formatter: '{b}\n\n{d}%',
        source: data,
        colors: ['#73DEB3', '#F2D459', '#4DC2FF', '#E6645D', '#A285D2']
      }
    }
    trendOverviewLoading.value = false
    // console.log('trendoverview', trendOverviewData.value)
  })
}
const trendOverviewSelect = val => {
  trendOverviewSelectAct.value = val
  getTrendOverview()
}
// 论文语种
const getLang = () => {
  langLoading.value = true
  // 筛选项
  langSelectOptions.value = countDate()
  const select =
    langSelectAct.value ||
    langSelectOptions.value[0].value
  // 数据
  articleLang({ collegeCode: id.value, time: select }).then(({ data }) => {
    if (data) {
      langData.value = {
        tipFormat: ({ data: { name, count, ratio } }) => {
          return `${name}:<br/>${count}  |  ${ratio}`
        },
        formatter: ({ data }) => {
          return `${data.name}\n\n${data.ratio}`
        },
        source: data
      }
      // console.log('langData', langData.value)
    }
    langLoading.value = false
  })
}
const langSelect = val => {
  langSelectAct.value = val
  getLang()
}
// 研究词云
const getKeyword = () => {
  keywordLoading.value = true
  directionKeyword({ collegeCode: id.value, }).then(({ data }) => {
    if (data) {
      keywordData.value = { source: data }
    }
    keywordLoading.value = false
    // console.log('keyword', keywordData.value)
  })
}
// 研究负责人
const getDirection = () => {
  directionLoading.value = true
  // 筛选项
  directionSelectOptions.value = countDate()
  const select =
    directionSelectAct.value ||
    directionSelectOptions.value[0].value
  directionRadioOptions.value = dict.top
  let radio =
    directionRadioAct.value ||
    directionRadioOptions.value[0].value

  // 数据
  directionPerson({ collegeCode: id.value, time: select, sort: radio }).then(({ data }) => {
    if (data) {
      directionData.value = {
        legend: false,
        rotate: 45,
        formatter: params => {
          let temp = params && params[0]
          if (temp) {
            let { data: { name: label, value } } = temp
            return `${label} ${value}`
          }
          return ''
        },
        source: data,
        colors: [
          ['rgba(77,194,255,1)', 'rgba(25,104,255,0.3)'],
        ]
      }
      // console.log('directionData', directionData.value)
    } else {
      directionData.value = {
        legend: false,
        rotate: 45,
        formatter: params => {
          let temp = params && params[0]
          if (temp) {
            let { data: { name: label, value } } = temp
            return `${label} ${value}`
          }
          return ''
        },
        source: [],
        colors: [
          ['rgba(77,194,255,1)', 'rgba(25,104,255,0.3)'],
        ]
      }
    }
    directionLoading.value = false
  })
}
const directionSelect = val => {
  directionSelectAct.value = val
  getDirection()
}
const directionChange = val => {
  directionRadioAct.value = val
  getDirection()
}
// 专利情况
const getCategory = () => {
  categoryLoading.value = true
  // 筛选项
  categorySelectOptions.value = countDate()
  const select =
    categorySelectAct.value ||
    categorySelectOptions.value[0].value
  categoryRadioOptions.value = dict.radio
  const radio =
    categoryRadioAct.value ||
    categoryRadioOptions.value[0].value
  // 数据
  patentCategory({ collegeCode: id.value, time: select, sort: radio }).then(({ data }) => {
    if (data) {
      categoryData.value = {
        legend: false,
        dot: 0,
        formatter: params => {
          let temp = params && params[0]
          if (temp) {
            let { data: { name: label, count: value } } = temp
            return `${label} ${value}`
          }
          return ''
        },
        source: data,
        colors: [
          ['rgba(86,188,237,1)', 'rgba(74,87,186,1)'],
        ]
      }
      // console.log('categoryData', categoryData.value)
    }
    categoryLoading.value = false
  })
}
const categorySelect = val => {
  categorySelectAct.value = val
  getCategory()
}
const categoryChange = val => {
  categoryRadioAct.value = val
  getCategory()
}
// 著作类别
const getBookCategory = () => {
  bookCategoryLoading.value = true
  // 筛选项
  bookCategorySelectOptions.value = countDate()
  const select =
    bookCategorySelectAct.value ||
    bookCategorySelectOptions.value[0].value
  bookCategoryRadioOptions.value = dict.radio
  let radio =
    bookCategoryRadioAct.value ||
    bookCategoryRadioOptions.value[0].value
  // 数据
  bookCategory({ collegeCode: id.value, time: select, sort: radio }).then(({ data }) => {
    if (data) {
      bookCategoryData.value = {
        legend: false,
        dot: 1,
        formatter: params => {
          let temp = params && params[0]
          if (temp) {
            let { data: { name: label, count: value } } = temp
            return `${label} ${value}`
          }
          return ''
        },
        source: data,
        colors: [
          ['rgba(115,222,179,1)', 'rgba(77,194,255,1)'],
        ]
      }
      // console.log('bookCategoryData', bookCategoryData.value)
    } else {
      bookCategoryData.value = {
        legend: false,
        dot: 1,
        formatter: params => {
          let temp = params && params[0]
          if (temp) {
            let { data: { name: label, count: value } } = temp
            return `${label} ${value}`
          }
          return ''
        },
        source: [],
        colors: [
          ['rgba(115,222,179,1)', 'rgba(77,194,255,1)'],
        ]
      }
    }
    bookCategoryLoading.value = false
  })
}
const bookCategorySelect = val => {
  bookCategorySelectAct.value = val
  getBookCategory()
}
const bookCategoryChange = val => {
  bookCategoryRadioAct.value = val
  getBookCategory()
}
</script>

<style lang="scss" scoped>
.visualization-page-container {

  .visualization-content {
    padding-left: 120px;
    padding-right: 0; // 24px;

    .section {
      @include flexbox;
      flex-wrap: wrap;

      .section-block {
        height: 294px;
        margin-top: 24px;
      }

      &.col-2 {
        .section-wrapper {
          width: 50%;
        }

        .section-block {
          width: 874px;
        }
      }

      &.col-3 {
        .section-wrapper {
          width: 33.33%;
        }

        .section-block {
          width: 576px;
        }
      }
    }

    .charts-line-wrapper {
      width: 100%;
      height: 246px;
    }
  }
}
</style>

上一篇下一篇

猜你喜欢

热点阅读