Files
report_viewer/src/ui/9B3F-在线示例留存/components/CreditPanoramaSection.vue
2026-06-10 21:03:31 +08:00

463 lines
12 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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="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>