This commit is contained in:
2025-12-18 15:39:43 +08:00
parent d576d8e734
commit 0190e21287
48 changed files with 41428 additions and 379 deletions

369
src/ui/CJRZQ4AA8.vue Normal file
View File

@@ -0,0 +1,369 @@
<script setup>
import { computed, ref, onMounted, onUnmounted, watch } from "vue";
import * as echarts from "echarts";
import { useRiskNotifier } from "@/composables/useRiskNotifier";
const props = defineProps({
data: {
type: Object,
default: () => ({}),
},
apiId: {
type: String,
default: '',
},
index: {
type: Number,
default: 0,
},
notifyRiskStatus: {
type: Function,
default: () => { },
},
});
// 计算得分如果没有数据则默认为0
const score = computed(() => {
return props.data?.score ? Number(props.data.score) : 0;
});
// 计算风险评分0-100分分数越高越安全
const riskScore = computed(() => {
// 还款压力分数越高风险越大,转换为安全分数
// 压力分数 0-20100分最安全
// 压力分数 20-5070分较安全
// 压力分数 50-8040分有风险
// 压力分数 80-10010分高风险
const pressure = score.value;
if (pressure <= 20) return 100;
if (pressure <= 50) return 70;
if (pressure <= 80) return 40;
return 10;
});
// 使用 composable 通知父组件风险评分
useRiskNotifier(props, riskScore);
// 暴露给父组件
defineExpose({
riskScore
});
// 根据分值确定压力等级
const pressureLevel = computed(() => {
if (score.value <= 20)
return {
level: "低",
color: "#67C23A",
text: "还款压力小",
bgGradient: "from-green-500 to-green-300",
lightBg: "bg-green-50",
borderColor: "border-green-200",
gradient: [
{ offset: 0, color: "#67C23A" },
{ offset: 1, color: "#85ce61" }
]
};
if (score.value <= 50)
return {
level: "中",
color: "#E6A23C",
text: "还款压力中等",
bgGradient: "from-yellow-500 to-yellow-300",
lightBg: "bg-yellow-50",
borderColor: "border-yellow-200",
gradient: [
{ offset: 0, color: "#E6A23C" },
{ offset: 1, color: "#ebb563" }
]
};
if (score.value <= 80)
return {
level: "高",
color: "#E53E3E",
text: "还款压力较大",
bgGradient: "from-orange-500 to-red-400",
lightBg: "bg-red-50",
borderColor: "border-red-200",
gradient: [
{ offset: 0, color: "#E53E3E" },
{ offset: 1, color: "#fc8181" }
]
};
return {
level: "极高",
color: "#FF0000",
text: "还款压力非常大",
bgGradient: "from-red-600 to-red-500",
lightBg: "bg-red-50",
borderColor: "border-red-300",
gradient: [
{ offset: 0, color: "#FF0000" },
{ offset: 1, color: "#ff3333" }
]
};
});
// 计算进度条宽度百分比
const progressWidth = computed(() => {
return `${score.value}%`;
});
// 计算评分对应的Tailwind文本颜色类
const scoreColorClass = computed(() => {
if (score.value <= 20) return "text-green-500";
if (score.value <= 50) return "text-yellow-500";
if (score.value <= 80) return "text-orange-500";
return "text-red-600";
});
// 获取图标路径(根据压力等级)
const getIconPath = () => {
// 低压力使用 zq
if (score.value <= 20) {
return new URL('@/assets/images/report/zq.png', import.meta.url).href
}
// 中等压力使用 zfx
if (score.value <= 50) {
return new URL('@/assets/images/report/zfx.png', import.meta.url).href
}
// 高压力和极高压力使用 gfx
return new URL('@/assets/images/report/gfx.png', import.meta.url).href
};
// 获取边框颜色
const getBorderColor = () => {
if (score.value <= 20) return '#bbf7d0'; // 绿色
if (score.value <= 50) return '#fef3c7'; // 黄色
if (score.value <= 80) return '#fecaca'; // 红色
return '#fecaca'; // 极高压力也是红色
};
// ECharts 仪表盘
const chartRef = ref(null);
let chartInstance = null;
const initChart = () => {
if (!chartRef.value) return;
chartInstance = echarts.init(chartRef.value);
updateChart();
};
const updateChart = () => {
if (!chartInstance) return;
const risk = pressureLevel.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"
},
{
offset: 1,
color: risk.color + "25"
}
])]
]
}
},
axisTick: {
show: true,
distance: -30,
length: 6,
splitNumber: 10,
lineStyle: {
color: risk.color,
width: 1,
opacity: 0.5
}
},
splitLine: {
show: true,
distance: -36,
length: 12,
splitNumber: 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: score.value
}
],
title: {
fontSize: 14,
color: risk.color,
offsetCenter: [0, "10%"],
formatter: risk.level + "级还款压力"
}
}
]
};
chartInstance.setOption(option);
};
watch(
() => score.value,
() => {
updateChart();
}
);
onMounted(() => {
initChart();
window.addEventListener("resize", () => {
if (chartInstance) {
chartInstance.resize();
}
});
});
onUnmounted(() => {
if (chartInstance) {
chartInstance.dispose();
chartInstance = null;
}
window.removeEventListener("resize", chartInstance?.resize);
});
</script>
<template>
<div class="card">
<div class="rounded-lg border border-gray-200 pb-2 mb-4">
<!-- 标题栏 -->
<div class="flex items-center mb-4 p-4">
<div class="w-8 h-8 flex items-center justify-center mr-2">
<img src="@/assets/images/report/hkylfx.png" alt="还款压力分析" class="w-8 h-8 object-contain" />
</div>
<span class="font-bold text-gray-800">还款压力分析</span>
</div>
<div class="px-4 pb-4">
<!-- 仪表盘图表 -->
<div class="mb-6">
<div ref="chartRef" :style="{ width: '100%', height: '200px' }"></div>
</div>
<!-- 压力等级显示 -->
<div class="mb-6">
<div class="space-y-3 p-4 rounded-lg border" :class="pressureLevel.lightBg"
:style="{ borderColor: getBorderColor() }">
<div class="flex items-start">
<div class="mr-3 mt-1">
<img :src="getIconPath()" alt="还款压力" class="w-10 h-10 object-contain" />
</div>
<div class="flex-1">
<h4 class="font-semibold text-gray-800 mb-2">{{ pressureLevel.text }}</h4>
<p class="text-gray-400 text-sm">
分值越高表示还款压力越大建议关注债务比例
</p>
</div>
</div>
</div>
</div>
<!-- 财务建议 -->
<div class="mb-6">
<div class="flex items-center mb-3">
<div class="w-4 h-4 flex items-center justify-center mr-2">
<img src="@/assets/images/report/wxts_icon.png" alt="财务建议" class="w-4 h-4 object-contain" />
</div>
<div class="font-bold text-gray-800">财务建议</div>
</div>
<div class="ml-6 text-sm text-gray-600 space-y-1">
<p v-if="score > 50">
建议合理规划财务控制债务比例增加收入来源避免过度负债
</p>
<p v-if="score > 50" class="mt-1">
可尝试分期付款或延长还款周期减轻每月还款压力
</p>
<p v-else>
当前还款压力在可控范围内继续保持良好的财务习惯
</p>
<p v-if="score <= 50" class="mt-1">
建议定期检查收支平衡确保及时还款维持良好信用记录
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
/* 样式已通过 Tailwind CSS 类实现 */
</style>