第一种方式
// 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;
}
第二种 单个
//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>