Files
tydata-webview-v2/report-viewer/src/ui/DWBG6A2C/components/RiskListSection.vue
2026-07-08 21:29:20 +08:00

415 lines
12 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="card">
<div class="rounded-lg border border-gray-200 pb-4 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/fxmd.png" alt="风险名单" class="w-8 h-8 object-contain" />
</div>
<span class="font-bold text-gray-800">风险名单</span>
</div>
<div class="mt-4">
<!-- 名单核查概览 -->
<div class="mb-4">
<div class="mb-3">
<LTitle title="名单核查概览" />
</div>
<div class="px-4">
<div class="grid grid-cols-2 gap-4">
<div class="rounded-lg border p-2 text-center" :class="getOverviewCardStyle()">
<div class="text-xl font-bold mb-1" :class="getOverviewTextStyle()">
{{ riskCount }}
</div>
<div class="text-sm font-medium text-gray-700">命中名单数</div>
</div>
<div class="rounded-lg border p-2 text-center" :class="getOverviewCardStyle()">
<div class="text-xl font-bold mb-1" :class="getOverviewTextStyle()">
{{ riskCount > 0 ? '有风险' : '无风险' }}
</div>
<div class="text-sm font-medium text-gray-700">综合评估</div>
</div>
</div>
</div>
</div>
<!-- 详细风险名单项目 -->
<div class="space-y-3 p-4">
<div v-for="(item, key) in riskListItems" :key="key"
class="relative rounded-lg border p-3 transition-all duration-300" :class="getRiskListItemCardStyle(item)">
<!-- 右上角命中状态标签 -->
<div class="absolute top-0 right-0">
<div class="px-2 py-1 text-xs text-white rounded-bl-lg rounded-tr-lg" :class="getRiskListTagClass(item)">
{{ item.hasRisk ? '命中' : '未命中' }}
</div>
</div>
<div class="flex items-center">
<div class="w-10 h-10 flex items-center justify-center mr-3">
<img :src="getRiskListItemIcon(item)" :alt="item.name" class="w-10 h-10 object-contain" />
</div>
<div class="flex-1">
<div class="flex items-center mb-1">
<div class="font-bold text-gray-800">{{ item.name }}</div>
<!-- 风险等级标签 -->
<div class="ml-2">
<div class="px-2 py-1 text-xs font-medium rounded-md" :class="getSeverityBadgeStyle(item.severity)">
{{ getSeverityText(item.severity) }}
</div>
</div>
</div>
<div class="text-sm text-[#999999]">{{ item.description }}</div>
</div>
</div>
</div>
</div>
<!-- 高风险警告 -->
<div v-if="hasHighRiskItems" class="px-4">
<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-[#EB3C3C]">高风险名单警告</div>
</div>
<div class="ml-6 text-sm mb-1 text-[#EB3C3C]">
命中多个高风险名单建议立即停止业务并进行详细核实
</div>
<div class="ml-6 text-sm text-[#EB3C3C]">
命中名单{{ highRiskItemNames.join('、') }}
</div>
</div>
<!-- 安全提示 -->
<div v-if="!riskCount" class="px-4 mt-4">
<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">
未命中任何风险名单各项指标正常
</div>
</div>
</div>
</div>
</div>
<!-- 温馨提示 -->
<LRemark
content="风险名单展示申请人是否命中各类风险名单,包括黑名单、灰名单和其他风险标识。数据来源于各类风险数据库和监管机构。命中风险名单可能涉及欺诈、赖账、违法违规等行为。不同类型的风险名单有不同的风险等级和处理建议。建议对命中任何风险名单的申请进行重点审查,并根据具体情况调整风险策略。" />
</template>
<script setup>
import { computed } from 'vue'
import LTitle from '@/components/LTitle.vue'
import LRemark from '@/components/LRemark.vue'
import { useRiskNotifier } from '@/composables/useRiskNotifier'
const props = defineProps({
data: {
type: Object,
required: true,
},
apiId: {
type: String,
default: '',
},
index: {
type: Number,
default: 0,
},
notifyRiskStatus: {
type: Function,
default: () => { },
},
})
// 从data中解构出需要的字段
const riskList = computed(() => {
return props.data?.riskList || {}
})
// 风险名单项目配置
const riskListConfig = {
creditLeaseRisk: {
name: '信用租赁风险名单',
description: '命中信用租赁相关风险名单',
icon: '🏠',
severity: 'high',
color: 'red'
},
bankOverdueRecord: {
name: '银行逾期记录',
description: '命中银行逾期记录名单',
icon: '🏦',
severity: 'high',
color: 'red'
},
riskPhoneNumber: {
name: '通信风险手机号库',
description: '命中风险手机号码库',
icon: '📱',
severity: 'medium',
color: 'orange'
},
highRiskArea: {
name: '高风险集中地区',
description: '命中高风险地区名单',
icon: '🗺️',
severity: 'medium',
color: 'orange'
},
creditOverdueRecord: {
name: '信贷逾期记录',
description: '命中信贷逾期记录名单',
icon: '💰',
severity: 'high',
color: 'red'
},
vehicleLeaseViolation: {
name: '车辆租赁违约',
description: '命中车辆租赁违约名单',
icon: '🚗',
severity: 'medium',
color: 'orange'
},
courtViolator: {
name: '法院被执行名单',
description: '命中法院被执行人名单',
icon: '⚖️',
severity: 'high',
color: 'red'
},
industryBlacklist: {
name: '行业黑名单',
description: '命中行业相关黑名单',
icon: '🚫',
severity: 'high',
color: 'red'
},
identityFake: {
name: '身份伪冒风险',
description: '命中身份伪冒风险名单',
icon: '🎭',
severity: 'high',
color: 'red'
},
groupFraud: {
name: '团伙欺诈名单',
description: '命中团伙欺诈相关名单',
icon: '👥',
severity: 'high',
color: 'red'
},
taxDebt: {
name: '欠税名单',
description: '命中税务欠税名单',
icon: '💸',
severity: 'medium',
color: 'orange'
},
mediumRelationRisk: {
name: '中介关联风险',
description: '命中中介关联风险名单',
icon: '🔗',
severity: 'high',
color: 'red'
},
phoneNumberStatus: {
name: '手机号码状态异常',
description: '手机号码状态存在异常',
icon: '📞',
severity: 'low',
color: 'yellow'
}
}
// 处理风险名单数据
const riskListItems = computed(() => {
const items = {}
Object.keys(riskListConfig).forEach(key => {
const value = riskList.value?.[key] || 0
const hasRisk = value > 0
items[key] = {
...riskListConfig[key],
hasRisk,
value
}
})
return items
})
// 计算风险项数量
const riskCount = computed(() => {
return Object.values(riskListItems.value).filter(item => item.hasRisk).length
})
// 是否有高风险项目
const hasHighRiskItems = computed(() => {
return Object.values(riskListItems.value).some(item =>
item.hasRisk && item.severity === 'high'
)
})
// 高风险项目名称
const highRiskItemNames = computed(() => {
return Object.values(riskListItems.value)
.filter(item => item.hasRisk && item.severity === 'high')
.map(item => item.name)
})
// 是否有中风险项目
const hasMediumRiskItems = computed(() => {
return Object.values(riskListItems.value).some(item =>
item.hasRisk && item.severity === 'medium'
)
})
// 获取标题图标
const getTitleIcon = () => {
if (hasHighRiskItems.value || hasMediumRiskItems.value) {
return new URL('@/assets/images/report/gfx.png', import.meta.url).href
}
return new URL('@/assets/images/report/qsgg.png', import.meta.url).href
}
// 获取概览卡片样式 - 根据风险情况动态变化
const getOverviewCardStyle = () => {
if (riskCount.value === 0) {
return 'border-[#4CAF508F] bg-[#4CAF501A]' // 无风险:绿色
} else if (hasHighRiskItems.value) {
return 'border-[#EB3C3C8F] bg-[#EB3C3C1A]' // 高风险:红色
} else {
return 'border-[#D6943E8F] bg-[#D6943E1A]' // 中风险:橙色
}
}
// 获取概览文字样式 - 根据风险情况动态变化
const getOverviewTextStyle = () => {
if (riskCount.value === 0) {
return 'text-[#4CAF50]' // 无风险:绿色
} else if (hasHighRiskItems.value) {
return 'text-[#EB3C3C]' // 高风险:红色
} else {
return 'text-[#D6943E]' // 中风险:橙色
}
}
// 获取风险名单项目卡片整体样式
const getRiskListItemCardStyle = (item) => {
if (item.hasRisk) {
if (item.severity === 'high') {
return 'bg-[#F9ECEC] border-[#F0CACA]'
} else if (item.severity === 'medium') {
return 'bg-[#F9F5ED] border-[#FFB74D8F]'
} else {
return 'bg-[#FFF9E6] border-[#FFE6A0]'
}
}
return 'bg-[#ECF9EF] border-[#CAECD3]'
}
// 获取风险名单项目图标
const getRiskListItemIcon = (item) => {
if (item.hasRisk) {
if (item.severity === 'high') {
return new URL('@/assets/images/report/gfx.png', import.meta.url).href
} else if (item.severity === 'medium') {
return new URL('@/assets/images/report/zfx.png', import.meta.url).href
} else {
return new URL('@/assets/images/report/zfx.png', import.meta.url).href
}
}
return new URL('@/assets/images/report/zq.png', import.meta.url).href
}
// 获取风险名单标签样式(命中/未命中)
const getRiskListTagClass = (item) => {
if (item.hasRisk) {
if (item.severity === 'high') {
return 'bg-[#E53935]' // 高风险命中:红色
} else if (item.severity === 'medium') {
return 'bg-[#FF8C00]' // 中风险命中:橙色
} else {
return 'bg-[#FFA726]' // 低风险命中:浅橙色
}
}
return 'bg-[#4CAF50]' // 未命中:绿色
}
// 获取严重程度标签样式
const getSeverityBadgeStyle = (severity) => {
const styleMap = {
'high': 'text-[#EB3C3C] bg-[#EB3C3C1A]',
'medium': 'text-[#D6943E] bg-[#D6943E1A]',
'low': 'text-[#FFA726] bg-[#FFA7261A]'
}
return styleMap[severity] || 'text-[#999999] bg-[#9999991A]'
}
// 获取严重程度文本
const getSeverityText = (severity) => {
const textMap = {
'high': '高',
'medium': '中',
'low': '低'
}
return textMap[severity] || '未知'
}
// 计算风险评分0-100分分数越高越安全
const riskScore = computed(() => {
// 计算高风险数量、中风险数量和低风险数量
let highRiskCount = 0;
let mediumRiskCount = 0;
let lowRiskCount = 0;
Object.values(riskListItems.value).forEach(item => {
if (item.hasRisk) {
if (item.severity === 'high') {
highRiskCount++;
} else if (item.severity === 'medium') {
mediumRiskCount++;
} else if (item.severity === 'low') {
lowRiskCount++;
}
}
});
// 没有风险 -> 100分
if (highRiskCount === 0 && mediumRiskCount === 0 && lowRiskCount === 0) {
return 100;
}
// 计算风险分数
let score = 100;
// 每个高风险扣18分风险名单风险很高
score -= highRiskCount * 18;
// 每个中风险扣10分
score -= mediumRiskCount * 10;
// 每个低风险扣5分
score -= lowRiskCount * 5;
// 确保分数在合理范围内
return Math.max(20, Math.min(100, score));
});
// 使用 composable 通知父组件风险评分
useRiskNotifier(props, riskScore);
// 暴露给父组件
defineExpose({
riskScore
});
</script>
<style lang="scss" scoped>
// 组件样式</style>