first commit
This commit is contained in:
459
src/ui/JRZQ7F1A/components/ApplyReportSection.vue
Normal file
459
src/ui/JRZQ7F1A/components/ApplyReportSection.vue
Normal file
@@ -0,0 +1,459 @@
|
||||
<template>
|
||||
<div class="apply-report-section bg-white rounded-lg border border-gray-200">
|
||||
<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/sjqsfx.png" alt="申请行为详情" class="w-8 h-8 object-contain" />
|
||||
</div>
|
||||
<span class="font-bold text-gray-800">申请行为详情</span>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
<!-- 核心指标 -->
|
||||
<div class="grid grid-cols-2 gap-4 mb-6">
|
||||
<!-- 申请准入分 -->
|
||||
<div class="rounded-lg border border-[#2B79EE8F] bg-[#2B79EE1A] p-4">
|
||||
<div class="text-sm font-medium text-gray-700 mb-2 text-center">申请准入分</div>
|
||||
<div class="h-40">
|
||||
<v-chart class="chart-container" :option="applyScoreGaugeOption" autoresize />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 申请准入置信度 -->
|
||||
<div class="rounded-lg border border-[#2B79EE8F] bg-[#2B79EE1A] p-4">
|
||||
<div class="text-sm font-medium text-gray-700 mb-2 text-center">申请准入置信度</div>
|
||||
<div class="h-40">
|
||||
<v-chart class="chart-container" :option="applyConfidenceGaugeOption" autoresize />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 申请命中情况 -->
|
||||
<LTitle title="申请命中情况" />
|
||||
<div class="grid grid-cols-2 gap-4 mb-6 mt-3">
|
||||
<div class="bg-gray-50 rounded-lg p-3">
|
||||
<div class="text-sm text-gray-600 mb-1">申请命中机构数</div>
|
||||
<div class="text-lg font-bold text-gray-800">{{ getValue(data.A22160003, '0') }} 家</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 rounded-lg p-3">
|
||||
<div class="text-sm text-gray-600 mb-1">申请命中消金类机构数</div>
|
||||
<div class="text-lg font-bold text-gray-800">{{ getValue(data.A22160004, '0') }} 家</div>
|
||||
</div>
|
||||
<div class="bg-gray-50 rounded-lg p-3">
|
||||
<div class="text-sm text-gray-600 mb-1">申请命中网络贷款类机构数</div>
|
||||
<div class="text-lg font-bold text-gray-800">{{ getValue(data.A22160005, '0') }} 家</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 机构查询统计 -->
|
||||
<LTitle title="机构查询统计" />
|
||||
<div class="mt-3">
|
||||
<div class="bg-gray-50 rounded-lg p-4 mb-4">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span class="text-sm text-gray-600">机构总查询次数</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ getValue(data.A22160006, '0') }} 次</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span class="text-sm text-gray-600">最近一次查询时间</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ formatTime(data.A22160007) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="bg-blue-50 rounded-lg p-3 text-center border border-[#2B79EE8F]">
|
||||
<div class="text-xl font-bold text-[#2B79EE] mb-1">{{ getValue(data.A22160008, '0') }}</div>
|
||||
<div class="text-xs text-gray-600">近1个月查询笔数</div>
|
||||
</div>
|
||||
<div class="bg-blue-50 rounded-lg p-3 text-center border border-[#2B79EE8F]">
|
||||
<div class="text-xl font-bold text-[#2B79EE] mb-1">{{ getValue(data.A22160009, '0') }}</div>
|
||||
<div class="text-xs text-gray-600">近3个月查询笔数</div>
|
||||
</div>
|
||||
<div class="bg-blue-50 rounded-lg p-3 text-center border border-[#2B79EE8F]">
|
||||
<div class="text-xl font-bold text-[#2B79EE] mb-1">{{ getValue(data.A22160010, '0') }}</div>
|
||||
<div class="text-xs text-gray-600">近6个月查询笔数</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 查询次数趋势图 -->
|
||||
<div class="mb-4 mt-4">
|
||||
<LTitle title="查询次数趋势" />
|
||||
<div class="h-64 mt-3">
|
||||
<v-chart class="chart-container" :option="queryTrendChartOption" autoresize />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<Remark
|
||||
content="申请行为详情展示申请人在各金融机构的申请记录和查询行为,包括申请准入分、命中机构数、机构查询次数等指标。申请行为反映了申请人的借贷需求和信用活跃度。频繁的申请可能暗示资金需求紧张或信用状况不佳。建议关注申请命中机构数和查询次数,过多的申请可能影响信用评分。" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import LTitle from '@/components/LTitle.vue'
|
||||
import Remark from '@/components/Remark.vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { BarChart, GaugeChart } from 'echarts/charts'
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent
|
||||
} from 'echarts/components'
|
||||
import { formatConfidence, formatTime, getValue } from '../utils/formatUtils'
|
||||
|
||||
// 注册ECharts组件
|
||||
use([
|
||||
CanvasRenderer,
|
||||
BarChart,
|
||||
GaugeChart,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent
|
||||
])
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
// 申请准入分仪表盘配置(1-1000分,分数越高风险越大)
|
||||
const applyScoreGaugeOption = computed(() => {
|
||||
const score = parseInt(getValue(props.data.A22160001, '0')) || 0
|
||||
// 转换为0-100的百分比(用于仪表盘显示)
|
||||
const percentage = (score / 1000) * 100
|
||||
|
||||
// 根据分数确定颜色(分数越高风险越大)
|
||||
let color = '#52c41a' // 绿色 - 低风险
|
||||
if (score > 800) {
|
||||
color = '#f5222d' // 红色 - 高风险
|
||||
} else if (score > 600) {
|
||||
color = '#fa8c16' // 橙色 - 中高风险
|
||||
} else if (score > 400) {
|
||||
color = '#faad14' // 黄色 - 中等风险
|
||||
}
|
||||
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
startAngle: 180,
|
||||
endAngle: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
radius: '75%',
|
||||
center: ['50%', '75%'],
|
||||
itemStyle: {
|
||||
color: color,
|
||||
shadowBlur: 4,
|
||||
shadowColor: color,
|
||||
shadowOffsetX: 0,
|
||||
shadowOffsetY: 0,
|
||||
},
|
||||
progress: {
|
||||
show: true,
|
||||
width: 14,
|
||||
roundCap: true,
|
||||
clip: false,
|
||||
},
|
||||
axisLine: {
|
||||
roundCap: true,
|
||||
lineStyle: {
|
||||
width: 14,
|
||||
color: [
|
||||
[percentage / 100, color],
|
||||
[1, '#e5e7eb']
|
||||
]
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: true,
|
||||
distance: -28,
|
||||
length: 6,
|
||||
splitNumber: 10,
|
||||
lineStyle: {
|
||||
width: 1.5,
|
||||
color: '#999',
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
distance: -34,
|
||||
length: 10,
|
||||
splitNumber: 5,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: '#999',
|
||||
opacity: 0.7
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
show: true,
|
||||
distance: -18,
|
||||
fontSize: 10,
|
||||
color: '#666',
|
||||
formatter: function(value) {
|
||||
if (value === 0) return '0'
|
||||
if (value === 25) return '250'
|
||||
if (value === 50) return '500'
|
||||
if (value === 75) return '750'
|
||||
if (value === 100) return '1000'
|
||||
return ''
|
||||
}
|
||||
},
|
||||
pointer: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
offsetCenter: [0, '-5%'],
|
||||
formatter: () => `${score}`,
|
||||
rich: {
|
||||
value: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
lineHeight: 28
|
||||
},
|
||||
unit: {
|
||||
fontSize: 16,
|
||||
color: '#666',
|
||||
padding: [0, 0, 0, 4]
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: percentage
|
||||
}
|
||||
],
|
||||
title: {
|
||||
show: true,
|
||||
fontSize: 13,
|
||||
color: '#666',
|
||||
offsetCenter: [0, '25%'],
|
||||
formatter: () => '申请准入分'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 申请准入置信度仪表盘配置(50-100)
|
||||
const applyConfidenceGaugeOption = computed(() => {
|
||||
const confidence = parseInt(getValue(props.data.A22160002, '0')) || 0
|
||||
// 转换为0-100的百分比(置信度范围是50-100,所以需要映射)
|
||||
const percentage = confidence >= 50 ? ((confidence - 50) / 50) * 100 : 0
|
||||
|
||||
// 根据置信度确定颜色
|
||||
let color = '#52c41a' // 绿色 - 高置信度
|
||||
if (confidence < 60) {
|
||||
color = '#faad14' // 黄色 - 中等置信度
|
||||
} else if (confidence < 70) {
|
||||
color = '#fa8c16' // 橙色 - 较低置信度
|
||||
}
|
||||
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
startAngle: 180,
|
||||
endAngle: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
radius: '85%',
|
||||
center: ['50%', '75%'],
|
||||
itemStyle: {
|
||||
color: color,
|
||||
shadowBlur: 8,
|
||||
shadowColor: color,
|
||||
},
|
||||
progress: {
|
||||
show: true,
|
||||
width: 18,
|
||||
roundCap: true,
|
||||
},
|
||||
axisLine: {
|
||||
roundCap: true,
|
||||
lineStyle: {
|
||||
width: 18,
|
||||
color: [
|
||||
[percentage / 100, color],
|
||||
[1, '#e5e7eb']
|
||||
]
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: true,
|
||||
distance: -25,
|
||||
length: 6,
|
||||
splitNumber: 5,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: '#999',
|
||||
opacity: 0.5
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
distance: -30,
|
||||
length: 10,
|
||||
splitNumber: 4,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: '#999',
|
||||
opacity: 0.7
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
show: true,
|
||||
distance: -15,
|
||||
fontSize: 11,
|
||||
color: '#666',
|
||||
formatter: function(value) {
|
||||
// 将百分比映射回置信度值
|
||||
const confValue = Math.round(50 + (value / 100) * 50)
|
||||
if (value === 0) return '50'
|
||||
if (value === 25) return '62'
|
||||
if (value === 50) return '75'
|
||||
if (value === 75) return '87'
|
||||
if (value === 100) return '100'
|
||||
return ''
|
||||
}
|
||||
},
|
||||
pointer: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
offsetCenter: [0, '-5%'],
|
||||
formatter: () => `${confidence}%`,
|
||||
rich: {
|
||||
value: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
lineHeight: 28
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: percentage
|
||||
}
|
||||
],
|
||||
title: {
|
||||
show: true,
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
offsetCenter: [0, '25%'],
|
||||
formatter: () => '申请准入置信度'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 查询次数趋势图配置
|
||||
const queryTrendChartOption = computed(() => {
|
||||
const periods = ['近1个月', '近3个月', '近6个月']
|
||||
const queryData = [
|
||||
parseInt(getValue(props.data.A22160008, '0')) || 0,
|
||||
parseInt(getValue(props.data.A22160009, '0')) || 0,
|
||||
parseInt(getValue(props.data.A22160010, '0')) || 0
|
||||
]
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
formatter: function (params) {
|
||||
let result = params[0].name + '<br/>'
|
||||
params.forEach(item => {
|
||||
result += `${item.seriesName}: ${item.value} 次<br/>`
|
||||
})
|
||||
return result
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: periods,
|
||||
axisLabel: {
|
||||
fontSize: 12,
|
||||
color: '#6b7280'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#e5e7eb'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
fontSize: 11,
|
||||
color: '#6b7280',
|
||||
formatter: '{value} 次'
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f3f4f6'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '查询笔数',
|
||||
type: 'bar',
|
||||
data: queryData,
|
||||
barWidth: '40%',
|
||||
itemStyle: {
|
||||
color: '#2B79EE',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#1e5bb8'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
fontSize: 11,
|
||||
color: '#333'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
1004
src/ui/JRZQ7F1A/components/BehaviorReportSection.vue
Normal file
1004
src/ui/JRZQ7F1A/components/BehaviorReportSection.vue
Normal file
File diff suppressed because it is too large
Load Diff
456
src/ui/JRZQ7F1A/components/CurrentReportSection.vue
Normal file
456
src/ui/JRZQ7F1A/components/CurrentReportSection.vue
Normal file
@@ -0,0 +1,456 @@
|
||||
<template>
|
||||
<div class="current-report-section bg-white rounded-lg border border-gray-200">
|
||||
<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/gl.png" alt="信用详情" class="w-8 h-8 object-contain" />
|
||||
</div>
|
||||
<span class="font-bold text-gray-800">信用详情</span>
|
||||
</div>
|
||||
|
||||
<div class="p-4">
|
||||
<!-- 网络贷款类信用 -->
|
||||
<LTitle title="网络贷款类信用" />
|
||||
<div class="mt-3 mb-6">
|
||||
<div class="space-y-4 mb-4">
|
||||
<!-- 网贷授信额度 -->
|
||||
<div class="rounded-lg border border-[#2B79EE8F] bg-[#2B79EE1A] p-4 text-center">
|
||||
<div class="text-xl font-bold text-[#2B79EE] mb-1">
|
||||
{{ formatCreditAmount(data.C22180001) }}
|
||||
</div>
|
||||
<div class="text-sm font-medium text-gray-700 mb-1">网贷授信额度</div>
|
||||
</div>
|
||||
|
||||
<!-- 网贷额度置信度 -->
|
||||
<div class="rounded-lg border border-[#2B79EE8F] bg-[#2B79EE1A] p-4">
|
||||
<div class="text-sm font-medium text-gray-700 mb-2 text-center">网贷额度置信度</div>
|
||||
<div class="h-40">
|
||||
<v-chart class="chart-container" :option="p2pConfidenceGaugeOption" autoresize />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 space-y-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">网络贷款类机构数</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ getValue(data.C22180003, '0') }} 家</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">网络贷款类产品数</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ getValue(data.C22180004, '0') }} 个</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">网络贷款机构最大授信额度</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ formatCreditAmount(data.C22180005) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">网络贷款机构平均授信额度</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ formatCreditAmount(data.C22180006) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 授信额度对比图 -->
|
||||
<div class="mb-6">
|
||||
<LTitle title="授信额度对比" />
|
||||
<div class="h-64 mt-3">
|
||||
<v-chart class="chart-container" :option="creditAmountChartOption" autoresize />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消金贷款类信用 -->
|
||||
<LTitle title="消金贷款类信用" />
|
||||
<div class="mt-3 mb-6">
|
||||
<div class="space-y-4 mb-4">
|
||||
<!-- 消金建议授信额度 -->
|
||||
<div class="rounded-lg border border-[#1FBE5D8F] bg-[#1FBE5D1A] p-4 text-center">
|
||||
<div class="text-xl font-bold text-[#1FBE5D] mb-1">
|
||||
{{ formatCreditAmount(data.C22180011) }}
|
||||
</div>
|
||||
<div class="text-sm font-medium text-gray-700 mb-1">消金建议授信额度</div>
|
||||
</div>
|
||||
|
||||
<!-- 消金额度置信度 -->
|
||||
<div class="rounded-lg border border-[#1FBE5D8F] bg-[#1FBE5D1A] p-4">
|
||||
<div class="text-sm font-medium text-gray-700 mb-2 text-center">消金额度置信度</div>
|
||||
<div class="h-40">
|
||||
<v-chart class="chart-container" :option="consumerConfidenceGaugeOption" autoresize />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 space-y-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">消金贷款类机构数</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ getValue(data.C22180007, '0') }} 家</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">消金贷款类产品数</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ getValue(data.C22180008, '0') }} 个</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">消金贷款类机构最大授信额度</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ formatCreditAmount(data.C22180009) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">消金贷款类机构平均授信额度</span>
|
||||
<span class="text-lg font-bold text-gray-800">{{ formatCreditAmount(data.C22180010) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<Remark
|
||||
content="信用详情展示申请人在网络贷款和消金贷款领域的授信情况,包括授信额度、机构数、产品数等指标。授信额度反映了金融机构对申请人信用状况的评估,额度越高通常表示信用状况越好。建议关注授信额度置信度,置信度越高表示评估结果越可靠。同时需要结合申请人的实际借贷行为和还款记录进行综合评估。" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import LTitle from '@/components/LTitle.vue'
|
||||
import Remark from '@/components/Remark.vue'
|
||||
import VChart from 'vue-echarts'
|
||||
import { use } from 'echarts/core'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import { BarChart, GaugeChart } from 'echarts/charts'
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent
|
||||
} from 'echarts/components'
|
||||
import { formatCreditAmount, formatConfidence, getValue } from '../utils/formatUtils'
|
||||
|
||||
// 注册ECharts组件
|
||||
use([
|
||||
CanvasRenderer,
|
||||
BarChart,
|
||||
GaugeChart,
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent
|
||||
])
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
// 网贷额度置信度仪表盘配置(50-100)
|
||||
const p2pConfidenceGaugeOption = computed(() => {
|
||||
const confidence = parseInt(getValue(props.data.C22180002, '0')) || 0
|
||||
const percentage = confidence >= 50 ? ((confidence - 50) / 50) * 100 : 0
|
||||
|
||||
// 根据置信度确定颜色
|
||||
let color = '#2B79EE' // 蓝色 - 高置信度
|
||||
if (confidence < 60) {
|
||||
color = '#faad14' // 黄色 - 中等置信度
|
||||
} else if (confidence < 70) {
|
||||
color = '#fa8c16' // 橙色 - 较低置信度
|
||||
}
|
||||
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
startAngle: 180,
|
||||
endAngle: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
radius: '90%',
|
||||
center: ['50%', '75%'],
|
||||
itemStyle: {
|
||||
color: color,
|
||||
shadowBlur: 6,
|
||||
shadowColor: color,
|
||||
},
|
||||
progress: {
|
||||
show: true,
|
||||
width: 18,
|
||||
roundCap: true,
|
||||
},
|
||||
axisLine: {
|
||||
roundCap: true,
|
||||
lineStyle: {
|
||||
width: 18,
|
||||
color: [
|
||||
[percentage / 100, color],
|
||||
[1, '#e5e7eb']
|
||||
]
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
pointer: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
offsetCenter: [0, '-5%'],
|
||||
formatter: '{value}%',
|
||||
rich: {
|
||||
value: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
lineHeight: 28
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: percentage
|
||||
}
|
||||
],
|
||||
title: {
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
offsetCenter: [0, '25%'],
|
||||
formatter: () => `${confidence}%`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 消金额度置信度仪表盘配置(50-100)
|
||||
const consumerConfidenceGaugeOption = computed(() => {
|
||||
const confidence = parseInt(getValue(props.data.C22180012, '0')) || 0
|
||||
const percentage = confidence >= 50 ? ((confidence - 50) / 50) * 100 : 0
|
||||
|
||||
// 根据置信度确定颜色
|
||||
let color = '#1FBE5D' // 绿色 - 高置信度
|
||||
if (confidence < 60) {
|
||||
color = '#faad14' // 黄色 - 中等置信度
|
||||
} else if (confidence < 70) {
|
||||
color = '#fa8c16' // 橙色 - 较低置信度
|
||||
}
|
||||
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
type: 'gauge',
|
||||
startAngle: 180,
|
||||
endAngle: 0,
|
||||
min: 0,
|
||||
max: 100,
|
||||
radius: '90%',
|
||||
center: ['50%', '75%'],
|
||||
itemStyle: {
|
||||
color: color,
|
||||
shadowBlur: 6,
|
||||
shadowColor: color,
|
||||
},
|
||||
progress: {
|
||||
show: true,
|
||||
width: 18,
|
||||
roundCap: true,
|
||||
},
|
||||
axisLine: {
|
||||
roundCap: true,
|
||||
lineStyle: {
|
||||
width: 18,
|
||||
color: [
|
||||
[percentage / 100, color],
|
||||
[1, '#e5e7eb']
|
||||
]
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
pointer: {
|
||||
show: false
|
||||
},
|
||||
detail: {
|
||||
valueAnimation: true,
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
offsetCenter: [0, '-5%'],
|
||||
formatter: '{value}%',
|
||||
rich: {
|
||||
value: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: color,
|
||||
lineHeight: 28
|
||||
}
|
||||
}
|
||||
},
|
||||
data: [
|
||||
{
|
||||
value: percentage
|
||||
}
|
||||
],
|
||||
title: {
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
offsetCenter: [0, '25%'],
|
||||
formatter: () => `${confidence}%`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
// 授信额度对比图配置
|
||||
const creditAmountChartOption = computed(() => {
|
||||
const categories = ['网贷', '消金']
|
||||
const maxData = [
|
||||
parseInt(getValue(props.data.C22180005, '0')) || 0,
|
||||
parseInt(getValue(props.data.C22180009, '0')) || 0
|
||||
]
|
||||
const avgData = [
|
||||
parseInt(getValue(props.data.C22180006, '0')) || 0,
|
||||
parseInt(getValue(props.data.C22180010, '0')) || 0
|
||||
]
|
||||
|
||||
return {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
formatter: function (params) {
|
||||
let result = params[0].name + '<br/>'
|
||||
params.forEach(item => {
|
||||
const value = item.value >= 10000
|
||||
? `${(item.value / 10000).toFixed(2)}万元`
|
||||
: `${item.value}元`
|
||||
result += `${item.seriesName}: ${value}<br/>`
|
||||
})
|
||||
return result
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['最大授信额度', '平均授信额度'],
|
||||
top: '5%',
|
||||
textStyle: {
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
top: '20%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: categories,
|
||||
axisLabel: {
|
||||
fontSize: 12,
|
||||
color: '#6b7280'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#e5e7eb'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
fontSize: 11,
|
||||
color: '#6b7280',
|
||||
formatter: function (value) {
|
||||
if (value >= 10000) {
|
||||
return `${(value / 10000).toFixed(1)}万`
|
||||
}
|
||||
return value
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f3f4f6'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '最大授信额度',
|
||||
type: 'bar',
|
||||
data: maxData,
|
||||
barWidth: '30%',
|
||||
itemStyle: {
|
||||
color: '#2B79EE',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#1e5bb8'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
fontSize: 11,
|
||||
color: '#333',
|
||||
formatter: function (params) {
|
||||
const value = params.value
|
||||
return value >= 10000
|
||||
? `${(value / 10000).toFixed(1)}万`
|
||||
: value
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '平均授信额度',
|
||||
type: 'bar',
|
||||
data: avgData,
|
||||
barWidth: '30%',
|
||||
itemStyle: {
|
||||
color: '#1FBE5D',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#179e4d'
|
||||
}
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
fontSize: 11,
|
||||
color: '#333',
|
||||
formatter: function (params) {
|
||||
const value = params.value
|
||||
return value >= 10000
|
||||
? `${(value / 10000).toFixed(1)}万`
|
||||
: value
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
140
src/ui/JRZQ7F1A/index.vue
Normal file
140
src/ui/JRZQ7F1A/index.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="card shadow-sm rounded-xl overflow-hidden flex flex-col gap-4">
|
||||
<!-- 申请行为详情 -->
|
||||
<ApplyReportSection :data="applyData" />
|
||||
|
||||
<!-- 放款还款详情 -->
|
||||
<BehaviorReportSection :data="behaviorData" />
|
||||
|
||||
<!-- 信用详情 -->
|
||||
<CurrentReportSection :data="currentData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import ApplyReportSection from './components/ApplyReportSection.vue'
|
||||
import BehaviorReportSection from './components/BehaviorReportSection.vue'
|
||||
import CurrentReportSection from './components/CurrentReportSection.vue'
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({})
|
||||
},
|
||||
apiId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
notifyRiskStatus: {
|
||||
type: Function,
|
||||
default: () => { },
|
||||
},
|
||||
})
|
||||
|
||||
// 获取数据
|
||||
const rawData = computed(() => props.data?.data || props.data || {})
|
||||
|
||||
// 申请行为详情
|
||||
const applyData = computed(() => rawData.value.apply_report_detail || {})
|
||||
|
||||
// 放款还款详情
|
||||
const behaviorData = computed(() => rawData.value.behavior_report_detail || {})
|
||||
|
||||
// 信用详情
|
||||
const currentData = computed(() => rawData.value.current_report_detail || {})
|
||||
|
||||
// 计算风险评分(0-100分,分数越高越安全)
|
||||
const riskScore = computed(() => {
|
||||
const apply = applyData.value
|
||||
const behavior = behaviorData.value
|
||||
const current = currentData.value
|
||||
|
||||
// 检查是否有数据
|
||||
if (!apply || !behavior || !current || Object.keys(apply).length === 0) {
|
||||
return 100 // 无数据视为最安全
|
||||
}
|
||||
|
||||
let score = 100 // 初始满分
|
||||
|
||||
// 申请准入分评估(1-1000,分数越高风险越大)
|
||||
const applyScore = parseInt(apply.A22160001) || 0
|
||||
if (applyScore > 800) {
|
||||
score -= 30 // 高风险
|
||||
} else if (applyScore > 600) {
|
||||
score -= 20 // 中高风险
|
||||
} else if (applyScore > 400) {
|
||||
score -= 10 // 中等风险
|
||||
}
|
||||
|
||||
// 申请命中机构数评估
|
||||
const hitInstitutions = parseInt(apply.A22160003) || 0
|
||||
if (hitInstitutions > 20) {
|
||||
score -= 20 // 命中机构数过多
|
||||
} else if (hitInstitutions > 10) {
|
||||
score -= 10 // 命中机构数较多
|
||||
}
|
||||
|
||||
// 贷款行为分评估(1-1000,分数越高风险越大)
|
||||
const behaviorScore = parseInt(behavior.B22170001) || 0
|
||||
if (behaviorScore > 800) {
|
||||
score -= 25 // 高风险
|
||||
} else if (behaviorScore > 600) {
|
||||
score -= 15 // 中高风险
|
||||
} else if (behaviorScore > 400) {
|
||||
score -= 8 // 中等风险
|
||||
}
|
||||
|
||||
// 逾期评估
|
||||
const m0Overdue6m = parseInt(behavior.B22170025) || 0
|
||||
const m1Overdue6m = parseInt(behavior.B22170028) || 0
|
||||
if (m0Overdue6m > 5 || m1Overdue6m > 3) {
|
||||
score -= 20 // 逾期笔数过多
|
||||
} else if (m0Overdue6m > 2 || m1Overdue6m > 1) {
|
||||
score -= 10 // 逾期笔数较多
|
||||
}
|
||||
|
||||
// 失败扣款评估
|
||||
const failedDeduction6m = parseInt(behavior.B22170037) || 0
|
||||
if (failedDeduction6m > 10) {
|
||||
score -= 15 // 失败扣款过多
|
||||
} else if (failedDeduction6m > 5) {
|
||||
score -= 8 // 失败扣款较多
|
||||
}
|
||||
|
||||
// 正常还款比例评估
|
||||
const repaymentRatio = parseFloat(behavior.B22170034?.replace('%', '')) || 100
|
||||
if (repaymentRatio < 50) {
|
||||
score -= 25 // 还款比例过低
|
||||
} else if (repaymentRatio < 70) {
|
||||
score -= 15 // 还款比例较低
|
||||
} else if (repaymentRatio < 85) {
|
||||
score -= 8 // 还款比例中等
|
||||
}
|
||||
|
||||
// 确保分数在10-100范围内
|
||||
return Math.max(10, Math.min(100, score))
|
||||
})
|
||||
|
||||
// 使用 composable 通知父组件风险评分
|
||||
useRiskNotifier(props, riskScore)
|
||||
|
||||
// 暴露给父组件
|
||||
defineExpose({
|
||||
riskScore
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
</style>
|
||||
|
||||
138
src/ui/JRZQ7F1A/utils/formatUtils.js
Normal file
138
src/ui/JRZQ7F1A/utils/formatUtils.js
Normal file
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* 格式化金额区间
|
||||
* @param {string} amountRange - 金额区间字符串,如 "(0,500)", "[500,1000)", "[50000,+)" 等
|
||||
* @returns {string} 格式化后的金额区间
|
||||
*/
|
||||
export function formatAmountRange(amountRange) {
|
||||
if (!amountRange || amountRange === '0' || amountRange === '') {
|
||||
return '0元'
|
||||
}
|
||||
|
||||
// 处理 [50000,+) 这种格式
|
||||
if (amountRange.includes('[50000,+')) {
|
||||
return '50000元以上'
|
||||
}
|
||||
|
||||
// 处理区间格式,如 "(0,500)", "[500,1000)"
|
||||
const match = amountRange.match(/[\[\(](\d+),(\d+)[\)\]]/)
|
||||
if (match) {
|
||||
const min = parseInt(match[1])
|
||||
const max = parseInt(match[2])
|
||||
|
||||
// 如果最小值是0,显示为"0-最大值"
|
||||
if (min === 0) {
|
||||
return `0-${max}元`
|
||||
}
|
||||
|
||||
return `${min}-${max}元`
|
||||
}
|
||||
|
||||
// 如果无法解析,返回原值
|
||||
return amountRange
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化天数区间
|
||||
* @param {string} dayRange - 天数区间字符串,如 "(0,7]", "(7,15]", "(360,+)" 等
|
||||
* @returns {string} 格式化后的天数区间
|
||||
*/
|
||||
export function formatDayRange(dayRange) {
|
||||
if (!dayRange || dayRange === '0' || dayRange === '') {
|
||||
return '0天'
|
||||
}
|
||||
|
||||
// 处理 (360,+) 这种格式
|
||||
if (dayRange.includes('(360,+')) {
|
||||
return '360天以上'
|
||||
}
|
||||
|
||||
// 处理区间格式,如 "(0,7]", "(7,15]"
|
||||
const match = dayRange.match(/[\[\(](\d+),(\d+)[\)\]]/)
|
||||
if (match) {
|
||||
const min = parseInt(match[1])
|
||||
const max = parseInt(match[2])
|
||||
|
||||
// 如果最小值是0,显示为"0-最大值"
|
||||
if (min === 0) {
|
||||
return `0-${max}天`
|
||||
}
|
||||
|
||||
return `${min}-${max}天`
|
||||
}
|
||||
|
||||
// 如果无法解析,返回原值
|
||||
return dayRange
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间(YYYY-MM格式)
|
||||
* @param {string} time - 时间字符串,如 "2023-05"
|
||||
* @returns {string} 格式化后的时间,如 "2023年05月"
|
||||
*/
|
||||
export function formatTime(time) {
|
||||
if (!time || time === '0' || time === '') {
|
||||
return '—'
|
||||
}
|
||||
|
||||
// 如果是 YYYY-MM 格式
|
||||
if (time.match(/^\d{4}-\d{2}$/)) {
|
||||
return time.replace('-', '年') + '月'
|
||||
}
|
||||
|
||||
return time
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化置信度
|
||||
* @param {string} confidence - 置信度字符串,如 "76", "80"
|
||||
* @returns {string} 格式化后的置信度,如 "76%"
|
||||
*/
|
||||
export function formatConfidence(confidence) {
|
||||
if (!confidence || confidence === '0' || confidence === '') {
|
||||
return '—'
|
||||
}
|
||||
|
||||
const num = parseInt(confidence)
|
||||
if (isNaN(num)) {
|
||||
return confidence
|
||||
}
|
||||
|
||||
return `${num}%`
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化授信额度(单位:元)
|
||||
* @param {string} amount - 授信额度字符串,如 "12600", "6120"
|
||||
* @returns {string} 格式化后的授信额度,如 "12600元"
|
||||
*/
|
||||
export function formatCreditAmount(amount) {
|
||||
if (!amount || amount === '0' || amount === '') {
|
||||
return '0元'
|
||||
}
|
||||
|
||||
const num = parseInt(amount)
|
||||
if (isNaN(num)) {
|
||||
return amount
|
||||
}
|
||||
|
||||
// 如果大于10000,转换为万元
|
||||
if (num >= 10000) {
|
||||
return `${(num / 10000).toFixed(2)}万元`
|
||||
}
|
||||
|
||||
return `${num}元`
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值,如果为空则返回默认值
|
||||
* @param {any} value - 值
|
||||
* @param {any} defaultValue - 默认值
|
||||
* @returns {any} 值或默认值
|
||||
*/
|
||||
export function getValue(value, defaultValue = '—') {
|
||||
if (value === null || value === undefined || value === '' || value === '0') {
|
||||
return defaultValue
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user