|
|
@@ -9,7 +9,7 @@
|
|
|
显示全部
|
|
|
</el-checkbox>
|
|
|
</div>
|
|
|
- <div class="render_doughnut_charts" ref="chartsRef">
|
|
|
+ <div class="render_doughnut_charts" :style="{height:chartHeight}" ref="chartsRef">
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
@@ -19,44 +19,149 @@
|
|
|
<script setup lang="ts">
|
|
|
import {ref,onMounted, watch,computed} from 'vue'
|
|
|
import * as echarts from 'echarts';
|
|
|
+import type { PropType } from 'vue'
|
|
|
|
|
|
+interface PieItm {
|
|
|
+ name:String,
|
|
|
+ value:String
|
|
|
+}
|
|
|
+interface SeiresLabelFormatter {
|
|
|
+ formatter:Function | String,
|
|
|
+ rich:Object
|
|
|
|
|
|
+}
|
|
|
const props = defineProps({
|
|
|
+ chartHeight:{
|
|
|
+ type:String,
|
|
|
+ default:'270px'
|
|
|
+ },
|
|
|
+ //饼图数据
|
|
|
chartData:{
|
|
|
required:true,
|
|
|
- type:{
|
|
|
- series:Array
|
|
|
- }
|
|
|
+ type:Array<PieItm>
|
|
|
},
|
|
|
+ // 饼图扇形弧度
|
|
|
rotateAngle:{
|
|
|
type:[Number,null],
|
|
|
+ default:360
|
|
|
},
|
|
|
- randomActiveIndex:{
|
|
|
- type:Number,
|
|
|
- required:true
|
|
|
- },
|
|
|
+ // 是否显示图例
|
|
|
showLegend:{
|
|
|
type:Boolean,
|
|
|
- default:false
|
|
|
+ default:true
|
|
|
+ },
|
|
|
+ // 图例的位置
|
|
|
+ legendTop:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:'auto'
|
|
|
+ },
|
|
|
+ legendBottom:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:'auto'
|
|
|
+ },
|
|
|
+ legendLeft:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:'auto'
|
|
|
},
|
|
|
+ legendRight:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:'auto'
|
|
|
+ },
|
|
|
+ // 图例布局朝向
|
|
|
+ legendOrient:{
|
|
|
+ type:String,
|
|
|
+ default:'vertical'
|
|
|
+ },
|
|
|
+ // 是否一键隐藏图例或展示图例
|
|
|
showLegendOperationAll:{
|
|
|
type:Boolean,
|
|
|
default:false
|
|
|
},
|
|
|
+ // 图例展示文本
|
|
|
+ legendFormatter:{
|
|
|
+ type:Function
|
|
|
+ },
|
|
|
+
|
|
|
+ // 扇形图位置
|
|
|
+ pieTop:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:0
|
|
|
+ },
|
|
|
+ pieLeft:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:0
|
|
|
+ },
|
|
|
+ pieBottom:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:0
|
|
|
+ },
|
|
|
+ pieRight:{
|
|
|
+ type:[Number,String],
|
|
|
+ default:0
|
|
|
+ },
|
|
|
+
|
|
|
+ pieRadius:{
|
|
|
+ type:Array,
|
|
|
+ required:true
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ //展示扇区信息
|
|
|
showSeriesLabel:{
|
|
|
type:Boolean,
|
|
|
- default:false
|
|
|
- }
|
|
|
+ default:true
|
|
|
+ },
|
|
|
+ // 扇形的信息展示位置
|
|
|
+ seriesLabelPosition:{
|
|
|
+ type:String,
|
|
|
+ },
|
|
|
+ // 扇区信息的格式
|
|
|
+ seiresLabelFormatter: {
|
|
|
+ type:Object as PropType<SeiresLabelFormatter>
|
|
|
+ },
|
|
|
+
|
|
|
+ // 展示鼠标悬浮提示
|
|
|
+ showTooltip:{
|
|
|
+ type:Boolean,
|
|
|
+ default:true,
|
|
|
+ },
|
|
|
+ // 鼠标悬浮提示 展示的位置
|
|
|
+ tooltipPosition:{
|
|
|
+ type:[Array,Function],
|
|
|
+ default:()=>{
|
|
|
+ return function (point, params, dom, rect, size) {
|
|
|
+ // 固定在鼠标右侧
|
|
|
+ return [point[0], point[1]];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 图表鼠标悬浮提示信息
|
|
|
+ tooltipFormatter:{
|
|
|
+ type:[Function,String]
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+ //页面动态变化的标识
|
|
|
+ randomIndex:{
|
|
|
+ type:Number,
|
|
|
+ required:true
|
|
|
+ },
|
|
|
+
|
|
|
})
|
|
|
|
|
|
|
|
|
+
|
|
|
+
|
|
|
const chartsRef = ref(null)
|
|
|
const myChart = ref()
|
|
|
|
|
|
+/**
|
|
|
+ * 图例全选/取消全选
|
|
|
+ */
|
|
|
const showAll = ref(true)
|
|
|
const indeterminate = ref(false)
|
|
|
const selectedLegendList = ref([])
|
|
|
-
|
|
|
const HandleCheckAllOrNot = (val)=>{
|
|
|
console.log('当前的变更值',val)
|
|
|
showAll.value = val
|
|
|
@@ -83,42 +188,123 @@ const HandleCheckAllOrNot = (val)=>{
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-let baseOption = {
|
|
|
- tooltip: {
|
|
|
- trigger: 'item',
|
|
|
- borderWidth:0,
|
|
|
- showContent:true,
|
|
|
- textStyle:{
|
|
|
- color:'#666'
|
|
|
+
|
|
|
+// 基础配置
|
|
|
+const getBaseOption = () => ({
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ borderWidth:0,
|
|
|
+ textStyle:{
|
|
|
+ color:'#666'
|
|
|
+ },
|
|
|
},
|
|
|
- formatter: function (params) {
|
|
|
- const {name,marker,percent,value} = params
|
|
|
- return marker + name.split(':')[0] + `<span style=\"margin-left:10px;\">${value} %</span>` ;
|
|
|
+ legend: {
|
|
|
+ itemGap:15,
|
|
|
+ itemWidth:20,
|
|
|
+ padding:1,
|
|
|
+ itemHeight:10,
|
|
|
+ textStyle:{
|
|
|
+ fontSize:12,
|
|
|
+ },
|
|
|
},
|
|
|
- position:function (point, params, dom, rect, size) {
|
|
|
- // 固定在鼠标右侧
|
|
|
- return [point[0], point[1]];
|
|
|
- }
|
|
|
- },
|
|
|
- legend: {
|
|
|
- show:props.showLegend,
|
|
|
- top: '0%',
|
|
|
- left:props.showLegendOperationAll ? 92 : 'left',
|
|
|
- itemGap:15,
|
|
|
- itemWidth:20,
|
|
|
- padding:1,
|
|
|
- itemHeight:10,
|
|
|
- formatter:function (name) {
|
|
|
- return name;
|
|
|
+ color:['#5470C6','#3BA272','#FAC858','#995FB3','#EE6666','#72C0DD','#90CB75','#EA7ACB','#FC8451','#65789B',
|
|
|
+ '#178BD3','#26A616','#A6129E','#D3A141','#234098','#047640','#B43535','#848BDC','#D6AF83','#84313D'],
|
|
|
+
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '',
|
|
|
+ type: 'pie',
|
|
|
+ startAngle:90,
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ labelLine: {
|
|
|
+ length:15,
|
|
|
+ length2:10,
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ scale:false,
|
|
|
+ },
|
|
|
+ data: [],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '',
|
|
|
+ type: 'pie',
|
|
|
+ startAngle:90,
|
|
|
+ endAngle:'auto',
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ label: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: false,
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ scale:false,
|
|
|
+ label: {
|
|
|
+ show: false,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data:[],
|
|
|
+ emptyCircleStyle:{
|
|
|
+ color:'#F4F5F8',
|
|
|
+ opacity:1
|
|
|
+ },
|
|
|
+ }
|
|
|
+ ]
|
|
|
+});
|
|
|
+// 动态配置
|
|
|
+const getDynamicOption = ()=>({
|
|
|
+ tooltip: {
|
|
|
+ showContent:props.showTooltip,
|
|
|
+ formatter: props.tooltipFormatter,
|
|
|
+ position:props.tooltipPosition
|
|
|
},
|
|
|
- textStyle:{
|
|
|
- fontSize:12,
|
|
|
+ legend: {
|
|
|
+ show: props.showLegend,
|
|
|
+ top: props.legendTop,
|
|
|
+ right:props.legendRight,
|
|
|
+ left:props.legendLeft,
|
|
|
+ bottom:props.legendBottom,
|
|
|
+ orient:props.legendOrient,
|
|
|
+ // 自定义图例展示内容:名称:占比xx%
|
|
|
+ formatter: props.legendFormatter,
|
|
|
},
|
|
|
- },
|
|
|
- color:['#5470C6','#3BA272','#FAC858','#995FB3','#EE6666','#72C0DD','#90CB75','#EA7ACB','#FC8451','#65789B',
|
|
|
- '#178BD3','#26A616','#A6129E','#D3A141','#234098','#047640','#B43535','#848BDC','#D6AF83','#84313D'],
|
|
|
-};
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '',
|
|
|
+ radius: props.pieRadius,
|
|
|
+ endAngle:endAngle.value,
|
|
|
+ top:props.pieTop,
|
|
|
+ left:props.pieLeft,
|
|
|
+ right:props.pieRight,
|
|
|
+ bottom:props.pieBottom,
|
|
|
+ label: {
|
|
|
+ show: props.showSeriesLabel,
|
|
|
+ position:props.seriesLabelPosition,
|
|
|
+ ...props.seiresLabelFormatter,
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: props.showSeriesLabel,
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ disabled: !props.showTooltip,
|
|
|
+ label: {
|
|
|
+ show: props.showSeriesLabel,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ data: props?.chartData || [],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '',
|
|
|
+ radius: props.pieRadius,
|
|
|
+ top:props.pieTop,
|
|
|
+ left:props.pieLeft,
|
|
|
+ right:props.pieRight,
|
|
|
+ bottom:props.pieBottom,
|
|
|
+ }
|
|
|
+ ]
|
|
|
+})
|
|
|
|
|
|
+// 饼图的扇区结束位置
|
|
|
const endAngle = computed(()=>{
|
|
|
console.log('旋转角度',props.rotateAngle)
|
|
|
if(props.rotateAngle && props.rotateAngle !== 360){
|
|
|
@@ -132,101 +318,24 @@ const endAngle = computed(()=>{
|
|
|
}
|
|
|
})
|
|
|
|
|
|
-const UpdateChart = (dynamicOption,baseOption) =>{
|
|
|
- myChart.value.setOption({
|
|
|
- ...baseOption,
|
|
|
- ...dynamicOption
|
|
|
-
|
|
|
- });
|
|
|
+const UpdateChart = () =>{
|
|
|
+ if(!myChart.value) return
|
|
|
+ console.log('labelformater值',props.seiresLabelFormatter)
|
|
|
+ myChart.value.setOption(getBaseOption());
|
|
|
+ myChart.value.setOption(getDynamicOption());
|
|
|
}
|
|
|
|
|
|
const HandleInitChart = ()=>{
|
|
|
- // 基于准备好的dom,初始化echarts实例
|
|
|
- myChart.value = echarts.init(chartsRef.value);
|
|
|
+ console.log('准备初始化了--')
|
|
|
|
|
|
- let dynamicOption = {
|
|
|
- series: [
|
|
|
- {
|
|
|
- name: '',
|
|
|
- type: 'pie',
|
|
|
- radius: props.showSeriesLabel ? ['40%', '80%'] : ['50%', '90%'],
|
|
|
- startAngle:90,
|
|
|
- endAngle:endAngle.value,
|
|
|
- avoidLabelOverlap: false,
|
|
|
- top:35,
|
|
|
- left:60,
|
|
|
- right:60,
|
|
|
- bottom:10,
|
|
|
- label: {
|
|
|
- show: props.showSeriesLabel,
|
|
|
- position: 'outside',
|
|
|
- color:'#666666',
|
|
|
- formatter:(params)=>{
|
|
|
- // console.log('获取到的参数',params)
|
|
|
- const {name,value,percent} = params
|
|
|
- return [
|
|
|
- `{a|${name.split(':')[0]}:}`,
|
|
|
- `{b|${value}%}`,
|
|
|
- ].join('\n')
|
|
|
- },
|
|
|
- rich:{
|
|
|
- b:{
|
|
|
- align:'left',
|
|
|
- lineHeight: 20,
|
|
|
- },
|
|
|
- a:{
|
|
|
- // lineHeight: 20,
|
|
|
- }
|
|
|
- },
|
|
|
- },
|
|
|
- labelLine: {
|
|
|
- show: props.showSeriesLabel,
|
|
|
- length:15,
|
|
|
- length2:10,
|
|
|
- },
|
|
|
- emphasis: {
|
|
|
- scale:false,
|
|
|
- label: {
|
|
|
- show: true,
|
|
|
- // fontSize: 40,
|
|
|
- // fontWeight: 'bold'
|
|
|
- }
|
|
|
- },
|
|
|
- data:props?.chartData?.series[0]?.data || [],
|
|
|
- },
|
|
|
- {
|
|
|
- name: '',
|
|
|
- type: 'pie',
|
|
|
- radius: props.showSeriesLabel ? ['40%', '80%'] : ['50%', '90%'],
|
|
|
- startAngle:90,
|
|
|
- endAngle:'auto',
|
|
|
- avoidLabelOverlap: false,
|
|
|
- top:35,
|
|
|
- left:60,
|
|
|
- right:60,
|
|
|
- bottom:10,
|
|
|
- label: {
|
|
|
- show: false,
|
|
|
- },
|
|
|
- labelLine: {
|
|
|
- show: false,
|
|
|
- },
|
|
|
- emphasis: {
|
|
|
- scale:false,
|
|
|
- label: {
|
|
|
- show: false,
|
|
|
- }
|
|
|
- },
|
|
|
- data:[],
|
|
|
- emptyCircleStyle:{
|
|
|
- color:'#F4F5F8',
|
|
|
- opacity:1
|
|
|
- },
|
|
|
- }
|
|
|
- ]
|
|
|
+ if(myChart.value){
|
|
|
+ myChart.value?.dispose();
|
|
|
+ myChart.value = null
|
|
|
}
|
|
|
- UpdateChart(dynamicOption,baseOption)
|
|
|
|
|
|
+ // 基于准备好的dom,初始化echarts实例
|
|
|
+ myChart.value = echarts.init(chartsRef.value);
|
|
|
+ UpdateChart()
|
|
|
|
|
|
myChart.value.on('legendselectchanged', function (params) {
|
|
|
console.log('图例状态切换了',params);
|
|
|
@@ -251,21 +360,29 @@ const HandleInitChart = ()=>{
|
|
|
}
|
|
|
|
|
|
|
|
|
-watch([()=>props.randomActiveIndex,()=>props.rotateAngle],([newVal,newRotate])=>{
|
|
|
- // 监听到父组件页面的容器尺寸变化后,要重新init
|
|
|
- if(myChart.value){
|
|
|
- myChart.value.dispose();
|
|
|
- myChart.value = null
|
|
|
+watch([()=>props.randomIndex,()=>props.rotateAngle],([newRandom,newRotate],[oldRandom,oldRotate])=>{
|
|
|
+ if(!myChart.value) return
|
|
|
+ // rotateAngle 实际发生变化时需要重建以应用 endAngle 变更
|
|
|
+ if(newRotate !== oldRotate){
|
|
|
+ HandleInitChart()
|
|
|
+ return
|
|
|
}
|
|
|
- HandleInitChart()
|
|
|
+ console.log('执行到了--')
|
|
|
+ // 仅容器尺寸/布局变化时调整大小
|
|
|
+ try{ myChart.value.resize() }catch(e){}
|
|
|
})
|
|
|
|
|
|
watch(()=>props.chartData,(newVal)=>{
|
|
|
- // 数据变换后要重新渲染
|
|
|
- UpdateChart(newVal,{})
|
|
|
+ // 数据变换后只更新 series.data
|
|
|
+ if(!myChart.value) return
|
|
|
+ try{
|
|
|
+ // 同步更新 series 数据并刷新 legend 和 label 的展示(使百分比同步)
|
|
|
+ myChart.value.setOption(getDynamicOption())
|
|
|
+ }catch(e){
|
|
|
+ HandleInitChart()
|
|
|
+ }
|
|
|
},{deep:true})
|
|
|
|
|
|
-
|
|
|
onMounted(()=>{
|
|
|
HandleInitChart()
|
|
|
})
|
|
|
@@ -278,7 +395,6 @@ onMounted(()=>{
|
|
|
.render_doughnut_charts{
|
|
|
position: relative;
|
|
|
width: 100%;
|
|
|
- height: 270px;
|
|
|
}
|
|
|
|
|
|
.operation{
|