Files
report_viewer/src/ui/9B3F-在线示例留存/components/CreditPanoramaSection.vue

463 lines
12 KiB
Vue
Raw Normal View History

2026-06-10 21:03:31 +08:00
<template>
<div class="panorama-report">
<div class="panorama-main-title"><span>🛡</span> 信用全景扫描</div>
<!-- 第一行指数 + 机构借贷情况 -->
<div class="panorama-row">
<div class="panorama-card">
<div class="score-grid">
<div v-for="item in CREDIT_PANORAMA_SCORES" :key="item.key" class="score-item">
<div class="score-label">{{ item.label }}</div>
<div class="circle-index">
<span :class="['score-value', { muted: formatModelScore(data[item.key]) === '未命中' }]">
{{ formatModelScore(data[item.key]) }}
</span>
</div>
<div class="score-hint">350-950指数越大逾期率越低</div>
</div>
</div>
</div>
<div class="panorama-card">
<div class="block-title"><span>🏦</span> 机构借贷情况</div>
<div class="mini-grid">
<div v-for="item in CREDIT_PANORAMA_INSTITUTIONS" :key="item.key" class="mini-box">
<div class="mini-box-head">
<span>{{ item.label }}</span>
<span
class="gamma-tag"
:class="{
'gamma-tag--danger': getPanoramaRiskTag(data[item.key]).danger,
'gamma-tag--warn': getPanoramaRiskTag(data[item.key]).warn,
}"
>
{{ getPanoramaRiskTag(data[item.key]).label }}
</span>
</div>
<div class="mini-box-value">{{ formatPanoramaCount(data[item.key], item.unit) }}</div>
</div>
</div>
</div>
</div>
<!-- 第二行近期贷款申请 + 还款历史 -->
<div class="panorama-row">
<div class="panorama-card">
<div class="block-title"><span></span> 近期贷款申请</div>
<div class="list-rows">
<div v-for="item in CREDIT_PANORAMA_RECENT_LOAN" :key="item.key" class="list-row">
<div class="list-row-left">
<span
class="gamma-tag tag-inline"
:class="{
'gamma-tag--danger': getPanoramaRiskTag(data[item.key]).danger,
'gamma-tag--warn': getPanoramaRiskTag(data[item.key]).warn,
}"
>
{{ getPanoramaRiskTag(data[item.key]).label }}
</span>
<span>{{ item.label }}</span>
</div>
<span class="list-row-value">{{ formatPanoramaCount(data[item.key], item.unit) }}</span>
</div>
</div>
</div>
<div class="panorama-card">
<div class="block-title"><span>📋</span> 还款历史</div>
<div class="stat-grid-2">
<div class="stat-box">
<div class="stat-label">历史贷款机构成功还款笔数</div>
<div class="stat-value">{{ formatPanoramaCount(data.xyp_cpl0014, ' 笔') }}</div>
</div>
<div class="stat-box">
<div class="stat-label">历史贷款机构交易失败笔数</div>
<div class="stat-value">{{ formatPanoramaCount(data.xyp_cpl0015, ' 笔') }}</div>
</div>
</div>
<div class="list-rows">
<div class="list-row">
<span>90天还款成功率</span>
<span class="list-row-value">{{ formatPanoramaRatio(data.xyp_cpl0080) }}</span>
</div>
<div class="list-row">
<span>近90天内还款中成功还款总金额比例</span>
<span class="list-row-value">{{ formatPanoramaRatio(data.xyp_cpl0079) }}</span>
</div>
<div class="list-row">
<span>近5次还款中成功还款总金额比例</span>
<span class="list-row-value">{{ formatPanoramaRatio(data.xyp_cpl0073) }}</span>
</div>
<div class="list-row">
<span>近5次还款中还款成功笔数比例</span>
<span class="list-row-value">{{ formatPanoramaRatio(data.xyp_cpl0074) }}</span>
</div>
</div>
</div>
</div>
<!-- 第三行交易失败 + 交易失败后还款 -->
<div class="panorama-row">
<div class="panorama-card">
<div class="block-title"><span></span> 交易失败情况</div>
<div class="list-rows">
<div v-for="item in CREDIT_PANORAMA_FAIL_COUNTS" :key="item.key" class="list-row">
<div class="list-row-left">
<span
class="gamma-tag tag-inline"
:class="{
'gamma-tag--danger': getPanoramaRiskTag(data[item.key]).danger,
'gamma-tag--warn': getPanoramaRiskTag(data[item.key]).warn,
}"
>
{{ getPanoramaRiskTag(data[item.key]).label }}
</span>
<span>{{ item.label }}</span>
</div>
<span class="list-row-value">{{ formatPanoramaCount(data[item.key], item.unit) }}</span>
</div>
</div>
</div>
<div class="panorama-card">
<div class="block-title"><span></span> 交易失败后还款情况</div>
<div class="list-rows">
<div v-for="item in CREDIT_PANORAMA_FAIL_RECOVERY" :key="item.key" class="list-row">
<div class="list-row-left">
<span
class="gamma-tag tag-inline"
:class="{
'gamma-tag--danger': getPanoramaRiskTag(data[item.key]).danger,
'gamma-tag--warn': getPanoramaRiskTag(data[item.key]).warn,
}"
>
{{ getPanoramaRiskTag(data[item.key]).label }}
</span>
<span>{{ item.label }}</span>
</div>
<span class="list-row-value">{{ formatPanoramaCount(data[item.key], item.unit) }}</span>
</div>
</div>
</div>
</div>
<!-- 第四行逾期情况 + 贷款整体情况 -->
<div class="panorama-row">
<div class="panorama-card">
<div class="block-title"><span>🚨</span> 逾期情况</div>
<div class="stat-grid-2">
<div class="stat-box">
<div class="stat-label">当前逾期机构数</div>
<div class="stat-value">{{ formatPanoramaCount(data.xyp_cpl0071, '家') }}</div>
</div>
<div class="stat-box">
<div class="stat-label">当前逾期金额</div>
<div class="stat-value">{{ formatPanoramaAmount(data.xyp_cpl0072) }}</div>
</div>
</div>
<div class="list-rows">
<div v-for="item in CREDIT_PANORAMA_OVERDUE_FLAGS" :key="item.key" class="list-row flag-row">
<div class="list-row-left">
<span class="success-check"></span>
<span>{{ item.label }}</span>
</div>
<span
class="list-row-value"
:class="{ 'gamma-text-danger': data[item.key] === '1' }"
>
{{ formatPanoramaYesNo(data[item.key]) }}
</span>
</div>
</div>
</div>
<div class="panorama-card">
<div class="block-title"><span>📈</span> 贷款整体情况</div>
<div class="list-rows">
<div v-for="item in CREDIT_PANORAMA_LOAN_OVERVIEW" :key="item.key" class="list-row flag-row">
<div class="list-row-left">
<span class="success-check"></span>
<span>{{ item.label }}</span>
</div>
<span class="list-row-value">
{{
item.type === 'amount'
? formatPanoramaAmount(data[item.key])
: formatPanoramaCount(data[item.key], item.unit)
}}
</span>
</div>
</div>
</div>
</div>
<!-- 第五行贷款分周期汇总 -->
<div class="panorama-card panorama-card--full">
<div class="block-title"><span>📊</span> 贷款分周期汇总</div>
<div class="table-wrap">
<table class="gamma-table panorama-table">
<thead>
<tr>
<th>周期</th>
<th>贷款机构数</th>
<th>还款成功笔数</th>
<th>还款成功总金额()</th>
<th>交易失败笔数</th>
<th>交易失败总金额()</th>
</tr>
</thead>
<tbody>
<tr v-for="row in CREDIT_PANORAMA_PERIOD_ROWS" :key="row.label">
<td>{{ row.label }}</td>
<td>{{ panoramaTableCell(data, row.instKey) }}</td>
<td>{{ panoramaTableCell(data, row.successKey) }}</td>
<td>{{ panoramaTableCell(data, row.successAmtKey, 'amount') }}</td>
<td>{{ panoramaTableCell(data, row.failKey) }}</td>
<td>{{ panoramaTableCell(data, row.failAmtKey, 'amount') }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script setup>
import {
CREDIT_PANORAMA_SCORES,
CREDIT_PANORAMA_INSTITUTIONS,
CREDIT_PANORAMA_RECENT_LOAN,
CREDIT_PANORAMA_FAIL_COUNTS,
CREDIT_PANORAMA_FAIL_RECOVERY,
CREDIT_PANORAMA_OVERDUE_FLAGS,
CREDIT_PANORAMA_LOAN_OVERVIEW,
CREDIT_PANORAMA_PERIOD_ROWS,
formatModelScore,
formatPanoramaCount,
formatPanoramaAmount,
formatPanoramaRatio,
formatPanoramaYesNo,
getPanoramaRiskTag,
panoramaTableCell,
} from '../reportHelper';
defineProps({
data: { type: Object, default: () => ({}) },
});
</script>
<style lang="scss" scoped>
.panorama-report {
background: #fff;
border-radius: 8px;
padding: 24px;
margin-bottom: 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.panorama-main-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 20px;
display: flex;
align-items: center;
gap: 8px;
color: #333;
padding-left: 10px;
border-left: 4px solid #d4af37;
}
.panorama-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.panorama-card {
background: #fff;
border: 1px solid #eee;
border-radius: 12px;
padding: 20px;
&--full {
margin-bottom: 0;
}
}
.block-title {
font-size: 15px;
font-weight: 600;
margin-bottom: 16px;
display: flex;
align-items: center;
gap: 6px;
color: #333;
}
.score-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.score-item {
text-align: center;
}
.score-label {
font-size: 14px;
font-weight: 500;
color: #444;
margin-bottom: 12px;
}
.circle-index {
width: 120px;
height: 120px;
border: 4px solid #f0f0f0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 10px;
}
.score-value {
font-size: 20px;
font-weight: 600;
color: #2da44e;
&.muted {
font-size: 16px;
color: #999;
}
}
.score-hint {
font-size: 12px;
color: #999;
line-height: 1.4;
}
.mini-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.mini-box {
border: 1px solid #eee;
border-radius: 8px;
padding: 12px;
}
.mini-box-head {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
font-size: 13px;
color: #444;
}
.mini-box-value {
margin-top: 6px;
font-size: 13px;
color: #666;
}
.list-rows {
display: flex;
flex-direction: column;
gap: 12px;
}
.list-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
font-size: 13px;
color: #444;
}
.list-row-left {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
min-width: 0;
}
.tag-inline {
flex-shrink: 0;
}
.list-row-value {
flex-shrink: 0;
color: #666;
}
.stat-grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 16px;
}
.stat-box {
border: 1px solid #eee;
border-radius: 8px;
padding: 16px;
text-align: center;
}
.stat-label {
font-size: 12px;
color: #999;
margin-bottom: 6px;
}
.stat-value {
font-size: 16px;
font-weight: 500;
color: #444;
}
.success-check {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
background: #2da44e;
border-radius: 50%;
color: #fff;
font-size: 10px;
flex-shrink: 0;
}
.flag-row .list-row-left span:last-child {
line-height: 1.4;
}
.table-wrap {
overflow-x: auto;
}
.panorama-table {
min-width: 720px;
}
@media (max-width: 992px) {
.panorama-row {
grid-template-columns: 1fr;
}
.score-grid {
grid-template-columns: 1fr;
}
}
</style>