Files
ycc-proxy-webview/src/components/GaugeChart.vue
2025-11-27 13:19:45 +08:00

303 lines
9.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<div class="px-4 text-sm text-gray-500">
分析指数是根据网络行为大数据出具的分析评估参考分数分数越高越好该指数仅对本报告有效不代表对报告查询人的综合定性评价
</div>
<div ref="chartRef" :style="{ width: '100%', height: '200px' }"></div>
<div class="risk-description">
{{ riskDescription }}
</div>
<div class="risk-legend mt-6">
<div v-for="item in legendItems" :key="item.level" class="risk-legend__item">
<span class="risk-legend__pill" :style="{ backgroundColor: item.color, color: item.textColor }">
{{ item.range }}
</span>
<span class="risk-legend__text">{{ item.level }}</span>
</div>
</div>
</div>
</template>
<script setup>
import * as echarts from "echarts";
import { ref, onMounted, onUnmounted, watch, computed } from "vue";
const props = defineProps({
score: {
type: Number,
required: true,
},
});
// 根据分数计算风险等级和颜色(分数越高越安全)
const riskLevel = computed(() => {
const score = props.score;
if (score >= 75 && score <= 100) {
return {
level: "无任何风险",
color: "#52c41a",
gradient: [
{ offset: 0, color: "#52c41a" },
{ offset: 1, color: "#7fdb42" }
]
};
} else if (score >= 50 && score < 75) {
return {
level: "风险指数较低",
color: "#faad14",
gradient: [
{ offset: 0, color: "#faad14" },
{ offset: 1, color: "#ffc53d" }
]
};
} else if (score >= 25 && score < 50) {
return {
level: "风险指数较高",
color: "#fa8c16",
gradient: [
{ offset: 0, color: "#fa8c16" },
{ offset: 1, color: "#ffa940" }
]
};
} else {
return {
level: "高风险警告",
color: "#f5222d",
gradient: [
{ offset: 0, color: "#f5222d" },
{ offset: 1, color: "#ff4d4f" }
]
};
}
});
// 评分解释文本(分数越高越安全)
const riskDescription = computed(() => {
const score = props.score;
if (score >= 75 && score <= 100) {
return "根据综合分析,当前报告未检测到明显风险因素,各项指标表现正常,总体状况良好。";
} else if (score >= 50 && score < 75) {
return "根据综合分析,当前报告存在少量风险信号,建议关注相关指标变化,保持警惕。";
} else if (score >= 25 && score < 50) {
return "根据综合分析,当前报告风险指数较高,多项指标显示异常,建议进一步核实相关情况。";
} else {
return "根据综合分析,当前报告显示高度风险状态,多项重要指标严重异常,请立即采取相应措施。";
}
});
const chartRef = ref(null);
let chartInstance = null;
const legendItems = [
{ level: "高风险", color: "#f5222d", range: "0-24", textColor: "#ffffff" },
{ level: "一般", color: "#fa8c16", range: "25-49", textColor: "#ffffff" },
{ level: "良好", color: "#faad14", range: "50-74", textColor: "#ffffff" },
{ level: "优秀", color: "#52c41a", range: "75-100", textColor: "#ffffff" }
];
const initChart = () => {
if (!chartRef.value) return;
// 初始化ECharts实例
chartInstance = echarts.init(chartRef.value);
updateChart();
};
const updateChart = () => {
if (!chartInstance) return;
// 获取当前风险等级信息
const risk = riskLevel.value;
// 配置项
const option = {
series: [
{
type: "gauge",
startAngle: 180,
endAngle: 0,
min: 0,
max: 100,
radius: "100%",
center: ["50%", "80%"],
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, risk.gradient),
shadowBlur: 6,
shadowColor: risk.color,
},
progress: {
show: true,
width: 20,
roundCap: true,
clip: false
},
axisLine: {
roundCap: true,
lineStyle: {
width: 20,
color: [
[1, new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{
offset: 0,
color: risk.color + "30" // 使用风险颜色透明度20%
},
{
offset: 1,
color: risk.color + "25" // 使用风险颜色透明度10%
}
])]
]
}
},
axisTick: {
show: true,
distance: -30,
length: 6,
splitNumber: 10, // 每1分一个小刻度
lineStyle: {
color: risk.color,
width: 1,
opacity: 0.5
}
},
splitLine: {
show: true,
distance: -36,
length: 12,
splitNumber: 9, // 9个大刻度100分分成9个区间
lineStyle: {
color: risk.color,
width: 2,
opacity: 0.5
}
},
axisLabel: {
show: false,
},
anchor: {
show: false
},
pointer: {
icon: "triangle",
iconStyle: {
color: risk.color,
borderColor: risk.color,
borderWidth: 1
},
offsetCenter: ["7%", "-67%"],
length: "10%",
width: 15
},
detail: {
valueAnimation: true,
fontSize: 30,
fontWeight: "bold",
color: risk.color,
offsetCenter: [0, "-25%"],
formatter: function (value) {
return `{value|${value}分}\n{level|${risk.level}}`;
},
rich: {
value: {
fontSize: 30,
fontWeight: 'bold',
color: risk.color,
padding: [0, 0, 5, 0]
},
level: {
fontSize: 14,
fontWeight: 'normal',
color: risk.color,
padding: [5, 0, 0, 0]
}
}
},
data: [
{
value: props.score
}
],
title: {
fontSize: 14,
color: risk.color,
offsetCenter: [0, "10%"],
formatter: risk.level
}
}
]
};
// 使用配置项设置图表
chartInstance.setOption(option);
};
// 监听分数变化
watch(
() => props.score,
() => {
updateChart();
}
);
onMounted(() => {
initChart();
// 处理窗口大小变化
window.addEventListener("resize", () => {
if (chartInstance) {
chartInstance.resize();
}
});
});
// 在组件销毁前清理
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener("resize", chartInstance?.resize);
});
</script>
<style scoped>
.risk-description {
margin-bottom: 4px;
padding: 0 12px;
color: #666666;
font-size: 12px;
line-height: 1.5;
text-align: center;
}
.risk-legend {
display: flex;
justify-content: center;
gap: 16px;
padding: 0 16px 12px;
font-size: 12px;
color: #666666;
}
.risk-legend__item {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
justify-content: center;
}
.risk-legend__pill {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 2px 12px;
border-radius: 9999px;
font-size: 12px;
font-weight: 600;
min-width: 64px;
}
.risk-legend__text {
white-space: nowrap;
}
</style>