饼图组件(3D)优化折线展示效果
This commit is contained in:
parent
df80a4bb84
commit
2b1875c2da
@ -52,6 +52,11 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
// 是否展示折线(按需调整grid3D.viewControl.beta可以调整折线显示效果)
|
||||||
|
isShowLine: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['click'],
|
emits: ['click'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
@ -180,6 +185,19 @@ export default {
|
|||||||
startValue = endValue;
|
startValue = endValue;
|
||||||
legendData.push(series[i].name);
|
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(
|
const option = Object.assign(
|
||||||
{
|
{
|
||||||
tooltip: {
|
tooltip: {
|
||||||
@ -289,6 +307,100 @@ export default {
|
|||||||
function onClick(params) {
|
function onClick(params) {
|
||||||
emit('click', 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 };
|
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>
|
<template>
|
||||||
<div class="inputs">
|
<div class="inputs">
|
||||||
<h2 class="inputs-title">全县投入品数量:291.85万吨</h2>
|
<h2 class="inputs-title">全县投入品数量:291.81万吨</h2>
|
||||||
<new-hyaline-cake :chart-data="state.data" :option="state.option" :width="'100%'" :height="'188px'" />
|
<!-- <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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, watch } from 'vue';
|
import { ref, reactive, watch, computed } from 'vue';
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
option: {
|
option: {
|
||||||
k: 0,
|
k: 1,
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
itemGap: 0.1,
|
itemGap: 0.1,
|
||||||
// legendSuffix: '万吨',
|
legendSuffix: '万吨',
|
||||||
itemHeight: 200,
|
itemHeight: 200,
|
||||||
startAngle: 60,
|
startAngle: 60,
|
||||||
grid3D: {
|
grid3D: {
|
||||||
show: false,
|
show: true,
|
||||||
boxHeight: 2,
|
boxHeight: 2,
|
||||||
top: '-20',
|
top: '-10%',
|
||||||
bottom: '10',
|
bottom: '0',
|
||||||
// left: '-20%',
|
// left: '-20%',
|
||||||
viewControl: {
|
viewControl: {
|
||||||
//3d效果可以放大、旋转等,请自己去查看官方配置
|
//3d效果可以放大、旋转等,请自己去查看官方配置
|
||||||
alpha: 40, //角度(这个很重要 调节角度的)
|
alpha: 40, //角度(这个很重要 调节角度的)
|
||||||
beta: -40,
|
//由于3d饼图加折线存在隐性问题,根据数据条目的多少对这个参数进行调整能更好的展示
|
||||||
distance: 260, //调整视角到主体的距离,类似调整zoom(这是整体大小)
|
//2条数据 -20;3条数据 -10;4条数据 0;5条数据 10;
|
||||||
|
beta: -20,
|
||||||
|
distance: 300, //调整视角到主体的距离,类似调整zoom(这是整体大小),数值越大,饼图越小
|
||||||
rotateSensitivity: 0, //设置旋转灵敏度,为0无法旋转
|
rotateSensitivity: 0, //设置旋转灵敏度,为0无法旋转
|
||||||
zoomSensitivity: 0, //设置缩放灵敏度,为0无法缩放
|
zoomSensitivity: 0, //设置缩放灵敏度,为0无法缩放
|
||||||
panSensitivity: 0, //设置平移灵敏度,0无法平移
|
panSensitivity: 0, //设置平移灵敏度,0无法平移
|
||||||
@ -34,7 +37,7 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
show: true,
|
show: true,
|
||||||
top: 'bottom',
|
top: '90%',
|
||||||
orient: 'horizontal',
|
orient: 'horizontal',
|
||||||
itemWidth: 12, // 矩形宽度
|
itemWidth: 12, // 矩形宽度
|
||||||
itemHeight: 12, // 矩形高度
|
itemHeight: 12, // 矩形高度
|
||||||
@ -47,44 +50,13 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
formatter: (name) => name,
|
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: [
|
data: [
|
||||||
{ value: 221.8, name: '合资公司' },
|
{ 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>
|
</script>
|
||||||
@ -94,7 +66,7 @@ const state = reactive({
|
|||||||
width: 270px;
|
width: 270px;
|
||||||
height: 40px;
|
height: 40px;
|
||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
margin: 24px auto 0;
|
margin: 10px auto 0;
|
||||||
background-image: url('@/assets/images/inputs/bg_title.png');
|
background-image: url('@/assets/images/inputs/bg_title.png');
|
||||||
background-position: center bottom;
|
background-position: center bottom;
|
||||||
background-repeat: no-repeat;
|
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']
|
BreadComp: typeof import('./src/components/breadComp.vue')['default']
|
||||||
CenterMap: typeof import('./src/components/centerMap.vue')['default']
|
CenterMap: typeof import('./src/components/centerMap.vue')['default']
|
||||||
CodeDialog: typeof import('./src/components/code-dialog/index.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']
|
CostomImg: typeof import('./src/components/costomImg.vue')['default']
|
||||||
CustomBack: typeof import('./src/components/customBack.vue')['default']
|
CustomBack: typeof import('./src/components/customBack.vue')['default']
|
||||||
CustomCarouselPicture: typeof import('./src/components/custom-carousel-picture/index.vue')['default']
|
CustomCarouselPicture: typeof import('./src/components/custom-carousel-picture/index.vue')['default']
|
||||||
|
Loading…
x
Reference in New Issue
Block a user