echart的封裝

第一種方式

// 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: {
                    //這里設(shè)置柱形圖圓角 [左上角狐肢,右上角爹脾,右下角吃引,左下角]
                    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) {
              // 控制臺打印數(shù)據(jù)的名稱'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) {
              // 控制臺打印數(shù)據(jù)的名稱'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, //注意要設(shè)置層級,不然會在覆蓋在柱子前面,設(shè)置為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: {
                    //這里設(shè)置柱形圖圓角 [左上角蒋川,右上角,右下角撩笆,左下角]
                    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) {
              // 控制臺打印數(shù)據(jù)的名稱'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>
    <!-- 中心數(shù)據(jù)展示 -->
    <div class="center-data" v-if="total || total == 0">
      <p>總數(shù)</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: "基于藥物代謝動力學特性的復方依達拉奉注射液立題依據(jù)研究",
      //   ratio: "0.48%",
      //   type: "普通合同"
      // }]

      showSelect.value = false
      showBack.value = true
      clickData.value = {
        ...data.value,
        formatter: '捺球\n\nhmbbag7%',

        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: '數(shù)據(jù)加載中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,
    // 字體大小。從 `v4.8.0` 開始支持夕冲。
    fontSize: 12 * rate,
    // 是否顯示旋轉(zhuǎn)動畫(spinner)氮兵。從 `v4.8.0` 開始支持。
    showSpinner: true,
    // 旋轉(zhuǎn)動畫(spinner)的半徑歹鱼。從 `v4.8.0` 開始支持泣栈。
    spinnerRadius: 10 * rate,
    // 旋轉(zhuǎn)動畫(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 {

    // 數(shù)據(jù)集
    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], // 圓環(huán)半徑
        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: '數(shù)據(jù)加載中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字體大小螺捐。從 `v4.8.0` 開始支持颠悬。
    fontSize: 12 * rate,
    // 是否顯示旋轉(zhuǎn)動畫(spinner)。從 `v4.8.0` 開始支持定血。
    showSpinner: true,
    // 旋轉(zhuǎn)動畫(spinner)的半徑赔癌。從 `v4.8.0` 開始支持。
    spinnerRadius: 10 * rate,
    // 旋轉(zhuǎn)動畫(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
  // 數(shù)據(jù)
  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軸文字過多的旋轉(zhuǎn)
  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,
    // 數(shù)據(jù)集
    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, // 反轉(zhuǎn)坐標軸
        boundaryGap: true, // 是否留白
        // min: 0, // 最小刻度個數(shù)
        // max: 10, // 最大刻度個數(shù)
        scale: false,
        // 刻度線設(shè)置
        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
          }
        },
        // 背景網(wǎng)格線
        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)
}

// 工具函數(shù)
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
    //匹配雙字節(jié)
    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 科研項目關(guān)鍵字 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: '數(shù)據(jù)加載中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字體大小正什。從 `v4.8.0` 開始支持。
    fontSize: 12 * rate,
    // 是否顯示旋轉(zhuǎn)動畫(spinner)号杏。從 `v4.8.0` 開始支持婴氮。
    showSpinner: true,
    // 旋轉(zhuǎn)動畫(spinner)的半徑。從 `v4.8.0` 開始支持盾致。
    spinnerRadius: 10 * rate,
    // 旋轉(zhuǎn)動畫(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], // 旋轉(zhuǎn)范圍
      rotationStep: 2, // 旋轉(zhuǎn)單位
      gridSize: 10 * rate, // 網(wǎng)格(單詞間距)
      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: '數(shù)據(jù)加載中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字體大小鉴腻。從 `v4.8.0` 開始支持。
    fontSize: 12 * rate,
    // 是否顯示旋轉(zhuǎn)動畫(spinner)百揭。從 `v4.8.0` 開始支持爽哎。
    showSpinner: true,
    // 旋轉(zhuǎn)動畫(spinner)的半徑。從 `v4.8.0` 開始支持器一。
    spinnerRadius: 10 * rate,
    // 旋轉(zhuǎn)動畫(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
  // 數(shù)據(jù)
  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軸文字過多的旋轉(zhuǎn)
  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,
    // 數(shù)據(jù)集
    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, // 反轉(zhuǎn)坐標軸
        boundaryGap: true, // 是否留白
        // min: 0, // 最小刻度個數(shù)
        // max: 10, // 最大刻度個數(shù)
        scale: false,
        // 刻度線設(shè)置
        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
          }
        },
        // 背景網(wǎng)格線
        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')
}

// 工具函數(shù)
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
    //匹配雙字節(jié)
    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: '數(shù)據(jù)加載中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字體大小瞭亮。從 `v4.8.0` 開始支持。
    fontSize: 12 * rate,
    // 是否顯示旋轉(zhuǎn)動畫(spinner)固棚。從 `v4.8.0` 開始支持统翩。
    showSpinner: true,
    // 旋轉(zhuǎn)動畫(spinner)的半徑。從 `v4.8.0` 開始支持此洲。
    spinnerRadius: 10 * rate,
    // 旋轉(zhuǎn)動畫(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
  // 數(shù)據(jù)
  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', // 數(shù)據(jù)堆疊值
      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軸文字過多的旋轉(zhuǎn)
  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,
    // 數(shù)據(jù)集
    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, // 反轉(zhuǎn)坐標軸
        boundaryGap: true, // 是否留白
        // min: 0, // 最小刻度個數(shù)
        // max: 10, // 最大刻度個數(shù)
        scale: false,
        // 刻度線設(shè)置
        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
          }
        },
        // 背景網(wǎng)格線
        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)
// }

// 工具函數(shù)
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
    //匹配雙字節(jié)
    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: '數(shù)據(jù)加載中...',
    color: '#fff',
    textColor: '#FFF',
    maskColor: 'rgba(255, 255, 255, 0.1)',
    zlevel: 0,

    // 字體大小碰酝。從 `v4.8.0` 開始支持霎匈。
    fontSize: 12 * rate,
    // 是否顯示旋轉(zhuǎn)動畫(spinner)。從 `v4.8.0` 開始支持送爸。
    showSpinner: true,
    // 旋轉(zhuǎn)動畫(spinner)的半徑铛嘱。從 `v4.8.0` 開始支持。
    spinnerRadius: 10 * rate,
    // 旋轉(zhuǎn)動畫(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 {
    // 數(shù)據(jù)集
    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], // 圓環(huán)半徑
        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">
            <!-- 環(huán)形圖 -->
            <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
  }

  // 獲取數(shù)據(jù)
  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,
        '論文數(shù)量': item.lw,
        '專利數(shù)量': item.zl,
        '著作數(shù)量': item.zz
      }))
      trend5YearData.value = {
        legend: true, // 是否顯示圖例
        dimensions: ['year', '論文數(shù)量', '專利數(shù)量', '著作數(shù)量'],
        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
  // 數(shù)據(jù)
  departmentAchiev({ collegeCode: id.value, time: select }).then(({ data }) => {
    if (data) {
      const source = data.map(item => ({
        name: item.name,
        '獲獎成果': item.hjCount,
        '論文數(shù)量': item.lwCount,
        '專利數(shù)量': item.zlCount,
        '著作數(shù)量': item.zzCount
      }))
      departmentAchievData.value = {
        // 配置項
        legend: true, // 是否顯示圖例
        rotate: 45,
        // 數(shù)據(jù)
        dimensions: ['name', '獲獎成果', '論文數(shù)量', '專利數(shù)量', '著作數(shù)量'],
        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
  // 數(shù)據(jù)
  trendOverview({ collegeCode: id.value, time: select }).then(({ data }) => {
    if (data) {
      trendOverviewData.value = {
        tipFormat: ({ data: { name, count, ratio } }) => {
          return `${name}:<br/>${count}  |  ${ratio}`
        },
        formatter: '秘症\n\nffubfs6%',
        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
  // 數(shù)據(jù)
  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

  // 數(shù)據(jù)
  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
  // 數(shù)據(jù)
  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
  // 數(shù)據(jù)
  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>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末式矫,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子聪廉,更是在濱河造成了極大的恐慌瞬痘,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件板熊,死亡現(xiàn)場離奇詭異框全,居然都是意外死亡,警方通過查閱死者的電腦和手機干签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門津辩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人容劳,你說我怎么就攤上這事丹泉。” “怎么了鸭蛙?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長筋岛。 經(jīng)常有香客問我娶视,道長,這世上最難降的妖魔是什么睁宰? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任肪获,我火速辦了婚禮,結(jié)果婚禮上柒傻,老公的妹妹穿的比我還像新娘孝赫。我一直安慰自己,他們只是感情好红符,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著致开,像睡著了一般双戳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上塘辅,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音沮榜,去河邊找鬼蟆融。 笑死山憨,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的棚亩。 我是一名探鬼主播讥蟆,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拯杠?” 一聲冷哼從身側(cè)響起潭陪,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎黎炉,沒想到半個月后淀弹,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡沐序,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片到逊。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖件缸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情痊末,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站炒刁,受9級特大地震影響翔始,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肤晓,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盈匾。 院中可真熱鬧,春花似錦窿撬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辆布,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惭蹂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留商架,地道東北人灿巧。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓饿肺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汰聋。 傳聞我的和親對象是個殘疾皇子玄妈,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容