问题描述:
下列的代码是我项目中一个3D饼图组件的代码部分,问题是3D饼图的样式更新问题。
若我直接修改第67行:state.isOpenFlag = isOpen.value这一行代码为state.isOpenFlag = true或者state.isOpenFlag = false时,3D饼图的样式会按照预期变化。但是当我用目前的赋值方式为它赋值:state.isOpenFlag = isOpen.value,并在watch中修改它的值时,样式没有任何变化。经输出调试,在watch中isOpenFlag的值是确实发生了改变的。
请问这个问题的原因是什么?要怎么修改呢?
<template>
<div v-if="!config.isHidden">
<div class='dataV-wrapper' :style='wrapperStyle'>
<tc-chart v-if="isShowCom" ref='chartRef'
:option='chartOptions' style='width: 100%;height: 100%'
:isOpenFlag="isOpenFlag"
:key="refreshKey"
theme='vab-echarts-theme'/>
</div>
</div>
</template>
<script>
import TcChart from '@/tc/components/TcChart'
import {computed, defineComponent, onBeforeUnmount, reactive, ref, toRefs, watch, nextTick } from 'vue'
import {CanvasRenderer} from 'echarts/renderers'
import {PieChart} from 'echarts/charts'
import {GridComponent, LegendComponent, TooltipComponent} from 'echarts/components'
import {getFieldMap, useDataCenter} from '@/tc/components/dataVComponents/mixins/data-center'
import {use} from 'echarts/core'
import store from '@/store'
use([
CanvasRenderer,
PieChart,
GridComponent,
TooltipComponent,
LegendComponent,
])
export default defineComponent({
name: 'VPie3D',
components: {
TcChart
},
props: {
com: {
type: Object,
required: true,
}
},
setup(props) {
// 组件开始,进入组件公共方法,入参为当前组件相关配置
useDataCenter(props.com)
const attr = computed(() => props.com.attr)
const config = computed(() => props.com.config)
const sourceConfig = computed(() => props.com.sourceConfig)
const timer = ref(null)
const state = reactive({
isShowCom: true,
isOpenFlag: false,
})
const isRefresh = computed(() => {
if (config.value.autoLoad) {
return config.value.autoLoad.isReFresh
} else {
return false
}
})
// 开启南丁格尔模式
const isOpen = ref(config.value.isOpen);
// var isOpen = false
console.log('isOpen_s', isOpen.value);
state.isOpenFlag = isOpen.value
// 组件相关配置数据
const wrapperStyle = computed(() => {
return {
width: `${attr.value.w}px`,
height: `${attr.value.h}px`
}
})
// 监听vuex中的组件数据,组件数据加载在组件公共方法中
const dv_data = computed(() => {
let item = store.getters['dataV_api/componentsData']
let curData
item.forEach(x => {
if (x.id === props.com.id) {
curData = x.data
}
})
return curData
})
// 获取fields
const dv_field = computed(() => {
return getFieldMap(props.com.sourceConfig.fields)
})
// 获取图标数据
const chartData = computed(() => {
let curData = []
// 根据字段映射数据
if (dv_data.value) {
dv_data.value.forEach(x => {
let curItem = {
name: x[dv_field.value.name],
value: x[dv_field.value.value]
}
curData.push(curItem)
})
return curData
} else {
return []
}
})
// 组件数据配置相关
const getSeries = () => {
const {global, label} = config.value
const curChartData = chartData.value
return {
type: 'surface',
label: {
show: label.show,
position: label.position,
...label.textStyle,
formatter: label.formatter || '{b}',
alignTo: label.alignTo,
bleedMargin: label.bleedMargin,
distanceToLabelLine: label.distanceToLabelLine,
},
itemStyle: {
borderRadius: global.borderRadius
},
center: global.center,
radius: global.radius,
data: curChartData
}
}
const filterStyle = computed(() => {
return {
'margin-left': `${config.value.filterStyle.marginLeft}px`,
'margin-top': `${config.value.marginTop}px`,
color: `${config.value.filterStyle.color}`,
'font-size': `${config.value.filterStyle.fontSize}px`,
}
})
const getParametricEquation = (startRatio, endRatio, isSelected, isHovered, k, h, value, maxValue, isOpenFlag) => {
console.log('getParametricEquation');
console.log('isOpenFlag', isOpenFlag);
const midRatio = (startRatio + endRatio) / 2
const startRadian = startRatio * Math.PI * 2
const endRadian = endRatio * Math.PI * 2
const midRadian = midRatio * Math.PI * 2
if (startRatio === 0 && endRatio === 1) {
isSelected = false
}
k = typeof k !== 'undefined' ? k : 1 / 3
const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0
const hoverRate = isHovered ? 1.05 : 1
// 调整扇形高度比例(仅在南丁格尔模式下)
const scaleHeight = isOpenFlag ? (value / maxValue) * h : h
// 调整扇形半径比例
const scaleRadius = isOpenFlag ? 1 + (value / maxValue) * 0.5 : 1
return {
u: { min: -Math.PI, max: Math.PI * 3, step: Math.PI / 32 },
v: { min: 0, max: Math.PI * 2, step: Math.PI / 20 },
x(u, v) {
if (u < startRadian) {
return offsetX + Math.cos(startRadian) * (scaleRadius + Math.cos(v) * k) * hoverRate
}
if (u > endRadian) {
return offsetX + Math.cos(endRadian) * (scaleRadius + Math.cos(v) * k) * hoverRate
}
return offsetX + Math.cos(u) * (scaleRadius + Math.cos(v) * k) * hoverRate
},
y(u, v) {
if (u < startRadian) {
return offsetY + Math.sin(startRadian) * (scaleRadius + Math.cos(v) * k) * hoverRate
}
if (u > endRadian) {
return offsetY + Math.sin(endRadian) * (scaleRadius + Math.cos(v) * k) * hoverRate
}
return offsetY + Math.sin(u) * (scaleRadius + Math.cos(v) * k) * hoverRate
},
z(u, v) {
if (u < -Math.PI * 0.5) {
return Math.sin(u)
}
if (u > Math.PI * 2.5) {
return Math.sin(u) * scaleHeight * 0.1
}
return Math.sin(v) > 0 ? 1 * scaleHeight * 0.1 : -1
}
}
}
const maxValue = Math.max(...chartData.value.map(item => item.value)) // 获取最大值
const getPie3D = (pieData, internalDiameterRatio, isOpenFlag=false) => {
console.log('getPie3D');
const series = []
let sumValue = 0
let startValue = 0
let endValue = 0
const legendData = []
const k = typeof internalDiameterRatio !== 'undefined' ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio) : 1 / 3
// 计算总值和生成系列数据
for (let i = 0; i < pieData.length; i += 1) {
sumValue += pieData[i].value
const seriesItem = {
name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
radius: ['40%', '60%'],
type: 'surface',
parametric: true,
wireframe: { show: false },
pieData: pieData[i],
pieStatus: { selected: false, hovered: false, k }
}
// 处理itemStyle属性
if (typeof pieData[i].itemStyle !== 'undefined') {
const { itemStyle } = pieData[i]
typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null
typeof pieData[i].itemStyle.opacity !== 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null
seriesItem.itemStyle = itemStyle
}
series.push(seriesItem)
}
// 计算每个系列的起始和结束比例
for (let i = 0; i < series.length; i += 1) {
endValue = startValue + series[i].pieData.value
series[i].pieData.startRatio = startValue / sumValue
series[i].pieData.endRatio = endValue / sumValue
series[i].parametricEquation = getParametricEquation(
series[i].pieData.startRatio,
series[i].pieData.endRatio,
false,
false,
k,
10,
series[i].pieData.value, // 传递每个扇形的值
maxValue, //传递最大值isOpen
isOpenFlag
)
startValue = endValue
legendData.push(series[i].name)
}
// 生成图表配置对象
const option = computed(() => {
const { global, tooltip, legend, color } = config.value
const [ legendTop, legendLeft ] = legend.position.split('-')
return {
legend: {
show: legend.show,
type: 'scroll',
top: legendTop,
left: legendLeft,
// right: 50,
// top: 60,
orient: legend.orient,
textStyle: {...legend.textStyle},
icon: legend.icon,
itemHeight: legend.symbol.width,
itemWidth: legend.symbol.height,
itemGap: legend.symbol.gap,
data: legendData,
},
color: color,
tooltip: {
show: tooltip.show,
textStyle: { ...tooltip.textStyle },
padding: [tooltip.background.padding.v, tooltip.background.padding.h],
backgroundColor: tooltip.background.color,
trigger: 'item',
formatter: params => {
if (params.seriesName !== 'mouseoutSeries') {
return `${params.marker}${params.seriesName}:${pieData[params.seriesIndex].value}人`
}
return ''
}
},
xAxis3D: { min: -1, max: 1 },
yAxis3D: { min: -1, max: 1 },
zAxis3D: { min: -1, max: 1 },
grid3D: {
show: false,
boxHeight: 25,
top: '5%',
// left: '-15%',
left: "center",
viewControl: {
alpha: 40,
beta: 30,
rotateSensitivity: 1,
zoomSensitivity: 0,
panSensitivity: 0,
autoRotateSpeed: 50,
autoRotate: true,
distance: 300
}
},
series,
// series: getSeries(),
}
})
return option
}
var chartOptions = ref({})
chartOptions = computed(() => getPie3D(chartData.value, 0, state.isOpenFlag)).value
const refreshKey = ref(0);
const chartRef = ref(null)
watch(config, (newVal,oldVal) => {
isOpen.value = config.value.isOpen
state.isOpenFlag = isOpen.value
nextTick(() => {
chartOptions = computed(() => getPie3D(chartData.value, 0, state.isOpenFlag)).value
chartRef.value.initOptionsWatcher()
chartRef.value.refresh()
refreshKey.value += 1;
})
console.log('isOpen_e', isOpen.value);
console.log('watch');
}, { deep: true });
watch(config.value.autoLoad, () => {
clearInterval(timer.value)
if (isReFresh.value === true) {
timer.value === setInterval(() => {
state.isShowCom = false
// 延时器
setTimeout(() => {
state.isShowCom = true
}, 100)
}, config.value.autoLoad.timer * 1000)
} else {
clearInterval(timer.value)
}
}, { immediate: true, deep: true})
// 页面离开的时候销毁定时器
onBeforeUnmount(() => {
clearInterval(timer.value)
})
return {
...toRefs(state),
config,
wrapperStyle,
filterStyle,
sourceConfig,
chartRef,
chartOptions,
refreshKey
}
},
})
</script>
<style scoped>
.age_distribution {
position: relative;
width: 100%;
height: 400px;
}
.age_distribution .title {
position: absolute;
top: -7px;
left: 50%;
transform: translateX(-50%);
font-size: 17px;
font-family: PingFang SC, PingFang SC;
color: #ffffff;
letter-spacing: 2px;
}
</style>