Files
report_viewer/src/ui/CJRZQ5E9F/components/RiskAdvice.vue
2025-12-18 15:39:43 +08:00

415 lines
14 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 class="rounded-lg border border-[#99999933] mb-4">
<!-- 标题栏 -->
<div class="flex items-center p-4">
<div class="w-8 h-8 flex items-center justify-center mr-2">
<img src="@/assets/images/report/zyjy.png" alt="专业建议" class="w-8 h-8 object-contain" />
</div>
<span class="font-bold text-gray-800">专业建议</span>
</div>
<!-- 风险评估结论 -->
<div class="mb-6 px-4">
<div class="rounded-xl p-4 relative border" :class="overallRiskLevel.bgClass">
<!-- 风险分标签 -->
<div
class="absolute top-0 right-0 px-3 py-1 rounded-bl-lg rounded-tr-lg text-sm font-bold text-white whitespace-nowrap"
:class="getRiskBadgeClass()">
风险分{{ overallRiskScore }}
</div>
<div class="flex items-center gap-4 mb-3">
<div class="w-10 h-10 flex-shrink-0">
<img :src="getRiskIcon()" :alt="overallRiskLevel.title" class="w-10 h-10 object-contain" />
</div>
<div class="flex-1">
<h3 class="text-base font-bold text-[#333333]">{{ overallRiskLevel.title }}</h3>
<p class="text-sm text-[#999999]">{{ overallRiskLevel.subtitle }}</p>
</div>
</div>
<div class="text-sm text-[#333333] leading-relaxed">
{{ overallRiskLevel.description }}
</div>
</div>
</div>
<!-- 关键建议 -->
<div class="mb-6">
<LTitle title="关键建议" class="mb-2" />
<div class="space-y-3 px-4">
<div class="rounded-xl p-4 relative" v-for="recommendation in keyRecommendations" :key="recommendation.id"
:class="getRecommendationCardClass(recommendation.priority)">
<!-- 优先级标签 -->
<div class="absolute top-0 right-0 px-2 py-1 rounded-bl-lg rounded-tr-lg text-xs font-bold text-white"
:class="getRecommendationBadgeClass(recommendation.priority)">
{{ recommendation.priorityText }}
</div>
<div class="flex items-center gap-3">
<div class="w-10 h-10 flex-shrink-0">
<img :src="getRecommendationIcon(recommendation.priority)" :alt="recommendation.title"
class="w-10 h-10 object-contain" />
</div>
<div class="flex-1 min-w-0">
<h4 class="text-base font-bold text-[#333333] mb-2">{{ recommendation.title }}</h4>
<p class="text-sm text-[#999999] mb-3 leading-relaxed">{{ recommendation.description }}</p>
<div class="flex flex-wrap gap-2" v-if="recommendation.actions.length > 0">
<span class="inline-flex items-center px-3 py-1 rounded-xl text-sm"
:class="getRecommendationActionClass(recommendation.priority)"
v-for="action in recommendation.actions.slice(0, 3)" :key="action">
{{ action }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 温馨提示 -->
<!-- <div class="px-4 pb-4">
<LRemark
content="专业建议基于综合风险评估结果,为不同风险等级的申请人提供针对性的审核廊议和风险管控措施。建议内容包括关键建议、风险管控措施、注意事项和后续跟进等方面。系统会根据当前风险等级动态调整建议内容,但最终决策仍需结合具体业务情况和风险政策进行综合判断。建议定期复评风险状况和调整风险管控策略。" />
</div> -->
</div>
</template>
<script>
import LTitle from '@/components/LTitle.vue'
import LRemark from '@/components/LRemark.vue'
export default {
name: 'RiskAdvice',
components: {
LTitle,
LRemark
},
props: {
data: {
type: Object,
default: () => ({})
}
},
computed: {
// 综合风险评估
overallRiskScore() {
const creditScore = parseFloat(this.data.xyp_cpl0081) || 0
const amountIndex = parseFloat(this.data.xyp_cpl0082) || 0
const countIndex = parseFloat(this.data.xyp_cpl0083) || 0
// 风险分数 (0-100, 分数越高风险越低)
const avgRisk = (creditScore + amountIndex + countIndex) / 3
return Math.round((1 - avgRisk) * 100)
},
overallRiskLevel() {
const score = this.overallRiskScore
const hasCurrentOverdue = this.data.xyp_cpl0044 === '1'
const hasRecentOverdue = this.data.xyp_cpl0028 === '1' || this.data.xyp_cpl0029 === '1'
if (hasCurrentOverdue || score < 30) {
return {
title: '高风险用户',
subtitle: '需要立即关注',
description: '当前信用状况较差,建议立即处理逾期问题并暂停新申请。',
bgClass: 'bg-red-50 border-red-200',
iconBg: 'bg-red-500',
iconComponent: 'ExclamationTriangleIcon'
}
} else if (hasRecentOverdue || score < 60) {
return {
title: '中风险用户',
subtitle: '需要改善',
description: '信用状况一般,建议优化还款表现并控制申请频率。',
bgClass: 'bg-yellow-50 border-yellow-200',
iconBg: 'bg-yellow-500',
iconComponent: 'ExclamationCircleIcon'
}
} else {
return {
title: '低风险用户',
subtitle: '状况良好',
description: '信用状况良好,建议继续保持良好的还款习惯。',
bgClass: 'bg-green-50 border-green-200',
iconBg: 'bg-green-500',
iconComponent: 'CheckCircleIcon'
}
}
},
// 关键建议
keyRecommendations() {
const recommendations = []
// 当前逾期处理
if (this.data.xyp_cpl0044 === '1') {
recommendations.push({
id: 'handle_overdue',
title: '立即处理逾期',
description: '尽快联系机构协商还款,避免影响征信。',
priority: 'urgent',
priorityText: '紧急',
iconComponent: 'ExclamationTriangleIcon',
iconBg: 'bg-red-500',
borderClass: 'border-l-red-500',
badgeClass: 'bg-red-100 text-red-800',
actions: [
'联系机构协商',
'优先还小额',
'制定还款计划'
]
})
}
// 高频申请警告
const recent1Day = this.parseIntervalValue(this.data.xyp_cpl0070)
const recent7Day = this.parseIntervalValue(this.data.xyp_cpl0009)
if (recent1Day > 0 || recent7Day > 5) {
recommendations.push({
id: 'reduce_applications',
title: '控制申请频率',
description: '近期申请过频建议暂停新申请3-6个月。',
priority: 'high',
priorityText: '重要',
iconComponent: 'PauseCircleIcon',
iconBg: 'bg-orange-500',
borderClass: 'border-l-orange-500',
badgeClass: 'bg-orange-100 text-orange-800',
actions: [
'暂停新申请',
'整理现有贷款',
'制定资金规划'
]
})
}
// 还款表现改善
const successRate = parseFloat(this.data.xyp_cpl0080) || 0
const recent5SuccessRate = parseFloat(this.data.xyp_cpl0074) || 0
const recent20SuccessRate = parseFloat(this.data.xyp_t0400002) || 0
if (successRate < 0.8 || recent5SuccessRate < 0.8 || recent20SuccessRate < 0.8) {
recommendations.push({
id: 'improve_repayment',
title: '提升还款表现',
description: `还款成功率偏低,建议设置自动还款。`,
priority: 'high',
priorityText: '重要',
iconComponent: 'CalendarIcon',
iconBg: 'bg-blue-500',
borderClass: 'border-l-blue-500',
badgeClass: 'bg-blue-100 text-blue-800',
actions: [
'设置自动还款',
'确保账户余额',
'按时还款'
]
})
}
// 机构数量管理
const totalInstitutions = this.parseIntervalValue(this.data.xyp_cpl0001)
if (totalInstitutions > 10) {
recommendations.push({
id: 'manage_institutions',
title: '优化机构数量',
description: '机构数量较多,建议优先结清小额贷款。',
priority: 'medium',
priorityText: '建议',
iconComponent: 'AdjustmentsIcon',
iconBg: 'bg-purple-500',
borderClass: 'border-l-purple-500',
badgeClass: 'bg-purple-100 text-purple-800',
actions: [
'结清小额贷款',
'合并同类贷款',
'控制新增机构'
]
})
}
// 交易失败后恢复分析
const consumerFailureRecoveryDays = this.parseIntervalValue(this.data.xyp_cpl0054)
const smallLoanFailureRecoveryDays = this.parseIntervalValue(this.data.xyp_cpl0055)
const overallFailureRecoveryDays = this.parseIntervalValue(this.data.xyp_cpl0056)
if (consumerFailureRecoveryDays > 30 || smallLoanFailureRecoveryDays > 30 || overallFailureRecoveryDays > 30) {
recommendations.push({
id: 'improve_recovery_time',
title: '快速恢复能力',
description: '失败后恢复较慢,建议建立应急资金。',
priority: 'medium',
priorityText: '建议',
iconComponent: 'ClockIcon',
iconBg: 'bg-indigo-500',
borderClass: 'border-l-indigo-500',
badgeClass: 'bg-indigo-100 text-indigo-800',
actions: [
'建立应急资金',
'优化资金流',
'快速处理失败'
]
})
}
// 信用修复
const settledInstitutions = this.parseIntervalValue(this.data.xyp_cpl0002)
if (settledInstitutions > 0) {
recommendations.push({
id: 'credit_repair',
title: '继续信用修复',
description: '已有良好结清记录,建议继续保持。',
priority: 'medium',
priorityText: '建议',
iconComponent: 'TrendingUpIcon',
iconBg: 'bg-green-500',
borderClass: 'border-l-green-500',
badgeClass: 'bg-green-100 text-green-800',
actions: [
'保持还款记录',
'结清剩余贷款',
'稳定收入来源'
]
})
}
return recommendations
},
// 改善步骤
improvementSteps() {
const steps = []
if (this.data.xyp_cpl0044 === '1') {
steps.push({
id: 'immediate_action',
title: '立即行动期',
description: '处理逾期问题,停止新申请',
duration: '1-2周',
impact: '高',
badgeClass: 'bg-red-100 text-red-800'
})
}
steps.push({
id: 'stabilization',
title: '稳定期',
description: '建立稳定还款计划,按时还款',
duration: '3-6个月',
impact: '中',
badgeClass: 'bg-yellow-100 text-yellow-800'
})
steps.push({
id: 'optimization',
title: '优化期',
description: '减少机构数量,优化债务结构',
duration: '6-12个月',
impact: '中',
badgeClass: 'bg-yellow-100 text-yellow-800'
})
steps.push({
id: 'recovery',
title: '恢复期',
description: '建立良好信用记录,恢复信用状况',
duration: '12-24个月',
impact: '高',
badgeClass: 'bg-green-100 text-green-800'
})
return steps
}
},
methods: {
parseIntervalValue(value) {
if (!value || value === '' || value === '-1') return 0
const num = parseInt(value)
if (isNaN(num)) return 0
// 根据区间映射返回大致范围的中值
switch (num) {
case 1: return 1
case 2: return 3
case 3: return 7
case 4: return 15
case 5: return 25
default: return num
}
},
getRiskIcon() {
// 根据风险等级返回对应的图标
if (this.overallRiskLevel.iconComponent === 'ExclamationTriangleIcon') {
return new URL('@/assets/images/report/gfx.png', import.meta.url).href
} else if (this.overallRiskLevel.iconComponent === 'ExclamationCircleIcon') {
return new URL('@/assets/images/report/zfx.png', import.meta.url).href
} else {
return new URL('@/assets/images/report/zq.png', import.meta.url).href
}
},
getRiskBadgeClass() {
// 根据风险等级返回徽章样式
if (this.overallRiskLevel.iconComponent === 'ExclamationTriangleIcon') {
return 'bg-[#D44643]'
} else if (this.overallRiskLevel.iconComponent === 'ExclamationCircleIcon') {
return 'bg-[#F5A623]'
} else {
return 'bg-[#5EBC62]'
}
},
getRecommendationCardClass(priority) {
// 根据优先级返回卡片样式
if (priority === 'urgent') {
return 'bg-[#FFF0F0] border border-[#F0CACA]'
} else if (priority === 'high') {
return 'bg-[#ECF2FD] border border-[#CADAF9]'
} else {
return 'bg-[#F0FFF0] border border-green-200'
}
},
getRecommendationIcon(priority) {
// 根据优先级返回图标
if (priority === 'urgent') {
return new URL('@/assets/images/report/gfx.png', import.meta.url).href
} else if (priority === 'high') {
return new URL('@/assets/images/report/wxts_icon.png', import.meta.url).href
} else {
return new URL('@/assets/images/report/zq.png', import.meta.url).href
}
},
getRecommendationBadgeClass(priority) {
// 根据优先级返回徽章样式
if (priority === 'urgent') {
return 'bg-[#D44643]'
} else if (priority === 'high') {
return 'bg-[#5079EA]'
} else {
return 'bg-[#5EBC62]'
}
},
getRecommendationActionClass(priority) {
// 根据优先级返回操作按钮样式
if (priority === 'urgent') {
return 'bg-[#F0CACA] text-[#D44643]'
} else if (priority === 'high') {
return 'bg-[#DBE6FC] text-[#2B79EE]'
} else {
return 'bg-green-200 text-[#5EBC62]'
}
}
}
}
</script>
<style scoped>
/* 组件样式已使用 Tailwind CSS */
</style>