饼图组件(3D)优化折线展示效果
This commit is contained in:
parent
df80a4bb84
commit
2b1875c2da
@ -52,6 +52,11 @@ export default {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 是否展示折线(按需调整grid3D.viewControl.beta可以调整折线显示效果)
|
||||
isShowLine: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ['click'],
|
||||
setup(props, { emit }) {
|
||||
@ -180,6 +185,19 @@ export default {
|
||||
startValue = endValue;
|
||||
legendData.push(series[i].name);
|
||||
}
|
||||
|
||||
// 添加折线显示lable
|
||||
if (props.isShowLine) {
|
||||
series.forEach((item, index) => {
|
||||
let {
|
||||
itemStyle: { color },
|
||||
pieData: { startRatio, endRatio, value },
|
||||
} = item;
|
||||
|
||||
addLabelLine(series, startRatio, endRatio, value, k, index, color);
|
||||
});
|
||||
}
|
||||
|
||||
const option = Object.assign(
|
||||
{
|
||||
tooltip: {
|
||||
@ -289,6 +307,100 @@ export default {
|
||||
function onClick(params) {
|
||||
emit('click', params);
|
||||
}
|
||||
|
||||
//添加label指示线
|
||||
/**
|
||||
* @param {*} series 配置
|
||||
* @param {*} startRatio 扇形起始位置比例
|
||||
* @param {*} endRatio 扇形结束位置比例
|
||||
* @param {*} value 数值
|
||||
* @param {*} k 辅助参数,饼图半径相关
|
||||
* @param {*} i 在series中索引
|
||||
* @param {*} color 指示线颜色
|
||||
*/
|
||||
function addLabelLine(series, startRatio, endRatio, value, k, i, color = '#fff') {
|
||||
// 计算扇形中心弧度
|
||||
const midRadian = (startRatio + endRatio) * Math.PI;
|
||||
const radius = 1 + k; // 外径
|
||||
|
||||
// 1. 计算起点位置(扇形边缘)
|
||||
const startPos = [Math.cos(midRadian) * radius, Math.sin(midRadian) * radius, 0.1 * value];
|
||||
|
||||
// 2. 智能方向判断(通用逻辑)
|
||||
const isRightHalf = Math.cos(midRadian) > 0;
|
||||
const isTopHalf = Math.sin(midRadian) > 0;
|
||||
|
||||
// 3. 动态计算折线长度(根据数据条数自适应)
|
||||
const baseLength = 0.5 + 0.05 * (1 - series.length / 10); // 动态基准长度
|
||||
const lineSegment1 = baseLength * 0.8; // 第一段长度(径向)
|
||||
const lineSegment2 = baseLength * 1.2; // 第二段长度(水平)
|
||||
|
||||
// 4. 两段式折线计算
|
||||
// 第一段:径向延伸
|
||||
const turningPos = [startPos[0] + Math.cos(midRadian) * lineSegment1, startPos[1] + Math.sin(midRadian) * lineSegment1, startPos[2]];
|
||||
|
||||
// 第二段:水平延伸(带防重叠偏移)
|
||||
const endPos = [
|
||||
turningPos[0] + (isRightHalf ? lineSegment2 : -lineSegment2),
|
||||
turningPos[1] + (isTopHalf ? -0.1 : 0.1) * (1 + i * 0.03), // 动态垂直偏移
|
||||
turningPos[2],
|
||||
];
|
||||
|
||||
// 5. 文字位置二次偏移(确保在饼图两侧)
|
||||
const textPos = [
|
||||
endPos[0] * (isRightHalf ? 1.1 : 0.9), // 左右强化偏移
|
||||
endPos[1] + (isTopHalf ? -0.05 : 0.05),
|
||||
endPos[2],
|
||||
];
|
||||
|
||||
// 添加到series(白色折线+透明文字)
|
||||
series.push(
|
||||
{
|
||||
type: 'line3D',
|
||||
lineStyle: {
|
||||
color: '#ffffff', // 强制白色折线
|
||||
width: 1.8,
|
||||
opacity: 0.8,
|
||||
},
|
||||
data: [startPos, turningPos],
|
||||
zlevel: 10,
|
||||
},
|
||||
{
|
||||
type: 'line3D',
|
||||
lineStyle: {
|
||||
color: '#ffffff', // 白色折线
|
||||
width: 1.8,
|
||||
opacity: 0.6,
|
||||
},
|
||||
data: [turningPos, endPos],
|
||||
zlevel: 10,
|
||||
},
|
||||
{
|
||||
type: 'scatter3D',
|
||||
label: {
|
||||
show: true,
|
||||
position: isRightHalf ? 'right' : 'left',
|
||||
distance: 5,
|
||||
textStyle: {
|
||||
color: color, // 文字颜色保持与扇区一致
|
||||
fontSize: 14,
|
||||
// fontWeight: 'bold',
|
||||
backgroundColor: 'transparent', // 完全透明
|
||||
},
|
||||
formatter: `{b}\n${value} ${props.option.legendSuffix ?? ''}`, // 通用数据展示
|
||||
},
|
||||
symbolSize: 0,
|
||||
data: [
|
||||
{
|
||||
name: series[i].name,
|
||||
value: textPos, // 使用计算后的文字位置
|
||||
},
|
||||
],
|
||||
zlevel: 11,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return { chartRef };
|
||||
},
|
||||
};
|
||||
|
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="inputs">
|
||||
<h2 class="inputs-title">全县投入品数量:291.85万吨</h2>
|
||||
<new-hyaline-cake :chart-data="state.data" :option="state.option" :width="'100%'" :height="'188px'" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, watch } from 'vue';
|
||||
const state = reactive({
|
||||
option: {
|
||||
k: 0,
|
||||
opacity: 0.8,
|
||||
itemGap: 0.1,
|
||||
// legendSuffix: '万吨',
|
||||
itemHeight: 200,
|
||||
startAngle: 60,
|
||||
grid3D: {
|
||||
show: false,
|
||||
boxHeight: 2,
|
||||
top: '-20',
|
||||
bottom: '10',
|
||||
// left: '-20%',
|
||||
viewControl: {
|
||||
//3d效果可以放大、旋转等,请自己去查看官方配置
|
||||
alpha: 40, //角度(这个很重要 调节角度的)
|
||||
beta: -40,
|
||||
distance: 260, //调整视角到主体的距离,类似调整zoom(这是整体大小)
|
||||
rotateSensitivity: 0, //设置旋转灵敏度,为0无法旋转
|
||||
zoomSensitivity: 0, //设置缩放灵敏度,为0无法缩放
|
||||
panSensitivity: 0, //设置平移灵敏度,0无法平移
|
||||
autoRotate: false, //自动旋转
|
||||
autoRotateAfterStill: 2, //在鼠标静止操作后恢复自动旋转的时间间隔,在开启 autoRotate 后有效
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
top: 'bottom',
|
||||
orient: 'horizontal',
|
||||
itemWidth: 12, // 矩形宽度
|
||||
itemHeight: 12, // 矩形高度
|
||||
itemGap: 20,
|
||||
icon: 'rect',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 16,
|
||||
},
|
||||
formatter: (name) => name,
|
||||
},
|
||||
pie2dConfig: {
|
||||
center: ['50%', '40%'],
|
||||
label: {
|
||||
show: true,
|
||||
position: 'outside',
|
||||
formatter: (params) => {
|
||||
const total = 221.8 + 70.01; // 或动态计算
|
||||
const percent = ((params.value / total) * 100).toFixed(0);
|
||||
return `{name|${params.name}}\n{value|${params.value} 万吨\n(${percent}%)}`;
|
||||
},
|
||||
rich: {
|
||||
name: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff',
|
||||
lineHeight: 20,
|
||||
align: 'center',
|
||||
},
|
||||
value: {
|
||||
fontSize: 16,
|
||||
color: '#79F5AF',
|
||||
lineHeight: 18,
|
||||
align: 'center',
|
||||
},
|
||||
},
|
||||
color: '#fff',
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 40,
|
||||
length2: 40,
|
||||
lineStyle: { color: '#fff', width: 2 },
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: 221.8, name: '合资公司' },
|
||||
{ value: 70.01, name: '其它实体', floatZ: 1 },
|
||||
],
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.inputs {
|
||||
&-title {
|
||||
width: 270px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin: 24px auto 0;
|
||||
background-image: url('@/assets/images/inputs/bg_title.png');
|
||||
background-position: center bottom;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100%;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,30 +1,33 @@
|
||||
<template>
|
||||
<div class="inputs">
|
||||
<h2 class="inputs-title">全县投入品数量:291.85万吨</h2>
|
||||
<new-hyaline-cake :chart-data="state.data" :option="state.option" :width="'100%'" :height="'188px'" />
|
||||
<h2 class="inputs-title">全县投入品数量:291.81万吨</h2>
|
||||
<!-- <div style="height: 50px"></div> -->
|
||||
<custom-echart-hyaline-cake :chart-data="state.data" :option="state.option" :width="'100%'" :height="'188px'" :is-show-line="true" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive, watch } from 'vue';
|
||||
import { ref, reactive, watch, computed } from 'vue';
|
||||
const state = reactive({
|
||||
option: {
|
||||
k: 0,
|
||||
k: 1,
|
||||
opacity: 0.8,
|
||||
itemGap: 0.1,
|
||||
// legendSuffix: '万吨',
|
||||
legendSuffix: '万吨',
|
||||
itemHeight: 200,
|
||||
startAngle: 60,
|
||||
grid3D: {
|
||||
show: false,
|
||||
show: true,
|
||||
boxHeight: 2,
|
||||
top: '-20',
|
||||
bottom: '10',
|
||||
top: '-10%',
|
||||
bottom: '0',
|
||||
// left: '-20%',
|
||||
viewControl: {
|
||||
//3d效果可以放大、旋转等,请自己去查看官方配置
|
||||
alpha: 40, //角度(这个很重要 调节角度的)
|
||||
beta: -40,
|
||||
distance: 260, //调整视角到主体的距离,类似调整zoom(这是整体大小)
|
||||
//由于3d饼图加折线存在隐性问题,根据数据条目的多少对这个参数进行调整能更好的展示
|
||||
//2条数据 -20;3条数据 -10;4条数据 0;5条数据 10;
|
||||
beta: -20,
|
||||
distance: 300, //调整视角到主体的距离,类似调整zoom(这是整体大小),数值越大,饼图越小
|
||||
rotateSensitivity: 0, //设置旋转灵敏度,为0无法旋转
|
||||
zoomSensitivity: 0, //设置缩放灵敏度,为0无法缩放
|
||||
panSensitivity: 0, //设置平移灵敏度,0无法平移
|
||||
@ -34,7 +37,7 @@ const state = reactive({
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
top: 'bottom',
|
||||
top: '90%',
|
||||
orient: 'horizontal',
|
||||
itemWidth: 12, // 矩形宽度
|
||||
itemHeight: 12, // 矩形高度
|
||||
@ -47,44 +50,13 @@ const state = reactive({
|
||||
},
|
||||
formatter: (name) => name,
|
||||
},
|
||||
pie2dConfig: {
|
||||
center: ['50%', '40%'],
|
||||
label: {
|
||||
show: true,
|
||||
position: 'outside',
|
||||
formatter: (params) => {
|
||||
const total = 221.8 + 70.01; // 或动态计算
|
||||
const percent = ((params.value / total) * 100).toFixed(0);
|
||||
return `{name|${params.name}}\n{value|${params.value} 万吨\n(${percent}%)}`;
|
||||
},
|
||||
rich: {
|
||||
name: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: '#ffffff',
|
||||
lineHeight: 20,
|
||||
align: 'center',
|
||||
},
|
||||
value: {
|
||||
fontSize: 16,
|
||||
color: '#79F5AF',
|
||||
lineHeight: 18,
|
||||
align: 'center',
|
||||
},
|
||||
},
|
||||
color: '#fff',
|
||||
},
|
||||
labelLine: {
|
||||
show: true,
|
||||
length: 40,
|
||||
length2: 40,
|
||||
lineStyle: { color: '#fff', width: 2 },
|
||||
},
|
||||
},
|
||||
},
|
||||
data: [
|
||||
{ value: 221.8, name: '合资公司' },
|
||||
{ value: 70.01, name: '其它实体', floatZ: 1 },
|
||||
{ value: 70.01, name: '其它实体' },
|
||||
// { value: 55.03, name: '模拟一' },
|
||||
// { value: 33.06, name: '模拟二' },
|
||||
// { value: 47.6, name: '模拟三' },
|
||||
],
|
||||
});
|
||||
</script>
|
||||
@ -94,7 +66,7 @@ const state = reactive({
|
||||
width: 270px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
margin: 24px auto 0;
|
||||
margin: 10px auto 0;
|
||||
background-image: url('@/assets/images/inputs/bg_title.png');
|
||||
background-position: center bottom;
|
||||
background-repeat: no-repeat;
|
||||
|
1
sub-operation-service/components.d.ts
vendored
1
sub-operation-service/components.d.ts
vendored
@ -12,7 +12,6 @@ declare module 'vue' {
|
||||
BreadComp: typeof import('./src/components/breadComp.vue')['default']
|
||||
CenterMap: typeof import('./src/components/centerMap.vue')['default']
|
||||
CodeDialog: typeof import('./src/components/code-dialog/index.vue')['default']
|
||||
copy: typeof import('./src/components/custom-scroll-title copy/index.vue')['default']
|
||||
CostomImg: typeof import('./src/components/costomImg.vue')['default']
|
||||
CustomBack: typeof import('./src/components/customBack.vue')['default']
|
||||
CustomCarouselPicture: typeof import('./src/components/custom-carousel-picture/index.vue')['default']
|
||||
|
Loading…
x
Reference in New Issue
Block a user