1308 lines
45 KiB
Vue
1308 lines
45 KiB
Vue
|
|
<script setup>
|
|||
|
|
import * as echarts from 'echarts' // 引入 ECharts
|
|||
|
|
import LTable from '@/components/LTable.vue'
|
|||
|
|
import LTitle from '@/components/LTitle.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: () => { },
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
const { data } = props
|
|||
|
|
|
|||
|
|
// 图表相关
|
|||
|
|
const chartInstance = ref(null) // ECharts 实例
|
|||
|
|
const chartRef = ref(null) // 图表容器 DOM
|
|||
|
|
const orgPieChartRef = ref(null) // 机构类型饼图容器
|
|||
|
|
const orgPieChartInstance = ref(null) // 机构类型饼图实例
|
|||
|
|
const chartData = ref({})
|
|||
|
|
|
|||
|
|
// 颜色配置
|
|||
|
|
const opts = ref({
|
|||
|
|
color: ['#4B96FF', '#3CA272', '#EE6666', '#FAC858', '#73C0DE', '#B280E9', '#FF8463'],
|
|||
|
|
idColor: '#4B96FF', // 身份证数据颜色
|
|||
|
|
cellColor: '#3CA272', // 手机号数据颜色
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 表格数据
|
|||
|
|
const tableData = ref({
|
|||
|
|
id: [],
|
|||
|
|
cell: [],
|
|||
|
|
})
|
|||
|
|
const dateTableData = ref({
|
|||
|
|
id: [],
|
|||
|
|
cell: [],
|
|||
|
|
})
|
|||
|
|
const totalStatsData = ref({
|
|||
|
|
id: {
|
|||
|
|
totalApplyCount: [],
|
|||
|
|
totalOrgCount: [],
|
|||
|
|
},
|
|||
|
|
cell: {
|
|||
|
|
totalApplyCount: [],
|
|||
|
|
totalOrgCount: [],
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 按钮选项
|
|||
|
|
const timeOptions = ref([
|
|||
|
|
{ label: '近7日', value: 0 },
|
|||
|
|
{ label: '近1月', value: 1 },
|
|||
|
|
{ label: '近3月', value: 2 },
|
|||
|
|
{ label: '近6月', value: 3 },
|
|||
|
|
{ label: '近1年', value: 4 },
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
const dataSourceOptions = ref([
|
|||
|
|
{ label: '身份证匹配数据', value: 'id' },
|
|||
|
|
{ label: '手机号匹配数据', value: 'cell' },
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
const selectedTimeOption = ref(0)
|
|||
|
|
const dateSelectedOption = ref(0)
|
|||
|
|
const selectedDataSource = ref('id')
|
|||
|
|
const selectedStatType = ref('apply') // 'apply' 或 'org'
|
|||
|
|
const statTabOptions = ref([
|
|||
|
|
{ label: '申请次数', value: 'apply' },
|
|||
|
|
{ label: '申请机构数', value: 'org' },
|
|||
|
|
])
|
|||
|
|
|
|||
|
|
// 时间维度映射
|
|||
|
|
const timeDimensions = {
|
|||
|
|
d7: '近7日',
|
|||
|
|
m1: '近1月',
|
|||
|
|
m3: '近3月',
|
|||
|
|
m6: '近6月',
|
|||
|
|
m12: '近1年',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 机构类型映射,更加用户友好的描述
|
|||
|
|
const orgMappings = {
|
|||
|
|
bank: '银行借贷',
|
|||
|
|
mc: '小额贷款',
|
|||
|
|
cf: '消费分期',
|
|||
|
|
ca: '现金分期',
|
|||
|
|
rel: '信用卡相关',
|
|||
|
|
af: '汽车金融',
|
|||
|
|
other: '其他借贷',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 详细机构类型分组
|
|||
|
|
const tableGroup = {
|
|||
|
|
bank: [{ code: 'bank', name: '银行借贷机构' }],
|
|||
|
|
mc: [
|
|||
|
|
{ code: 'nbank_mc', name: '小贷机构' },
|
|||
|
|
{ code: 'nbank_nsloan', name: '持牌网络小贷' },
|
|||
|
|
{ code: 'nbank_sloan', name: '持牌小贷机构' },
|
|||
|
|
{ code: 'pdl', name: '线上小额现金贷' },
|
|||
|
|
],
|
|||
|
|
cf: [
|
|||
|
|
{ code: 'nbank_cf', name: '消费类分期机构' },
|
|||
|
|
{ code: 'coon', name: '线上消费分期' },
|
|||
|
|
{ code: 'cooff', name: '线下消费分期' },
|
|||
|
|
],
|
|||
|
|
ca: [
|
|||
|
|
{ code: 'nbank_ca', name: '现金类分期机构' },
|
|||
|
|
{ code: 'caon', name: '线上现金分期' },
|
|||
|
|
{ code: 'caoff', name: '线下现金分期' },
|
|||
|
|
{ code: 'nbank_com', name: '代偿类分期机构' },
|
|||
|
|
],
|
|||
|
|
rel: [{ code: 'rel', name: '信用卡(类信用卡)' }],
|
|||
|
|
af: [
|
|||
|
|
{ code: 'af', name: '汽车金融' },
|
|||
|
|
{ code: 'nbank_autofin', name: '持牌汽车金融机构' },
|
|||
|
|
],
|
|||
|
|
other: [
|
|||
|
|
{ code: 'nbank_p2p', name: '改制机构' },
|
|||
|
|
{ code: 'nbank_cons', name: '持牌消费金融机构' },
|
|||
|
|
{ code: 'nbank_finlea', name: '持牌融资租赁机构' },
|
|||
|
|
{ code: 'nbank_oth', name: '其他非银借贷类型申请' },
|
|||
|
|
{ code: 'nbank_else', name: '其他非银类型申请' },
|
|||
|
|
{ code: 'oth', name: '其他银行借贷类型申请' },
|
|||
|
|
{ code: 'else', name: '其他银行类型申请' },
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 特殊时段类型
|
|||
|
|
const dateGroup = {
|
|||
|
|
week: [
|
|||
|
|
{ code: 'bank_week', name: '周末银行' },
|
|||
|
|
{ code: 'nbank_week', name: '周末非银' },
|
|||
|
|
],
|
|||
|
|
night: [
|
|||
|
|
{ code: 'bank_night', name: '夜间银行' },
|
|||
|
|
{ code: 'nbank_night', name: '夜间非银' },
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化图表
|
|||
|
|
function initChart() {
|
|||
|
|
if (!chartRef.value) return
|
|||
|
|
|
|||
|
|
if (!chartInstance.value) {
|
|||
|
|
chartInstance.value = echarts.init(chartRef.value)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const option = {
|
|||
|
|
color: opts.value.color,
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'axis',
|
|||
|
|
axisPointer: {
|
|||
|
|
type: 'shadow',
|
|||
|
|
shadowStyle: {
|
|||
|
|
color: 'rgba(0,0,0,0.05)',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
textStyle: {
|
|||
|
|
fontSize: 12,
|
|||
|
|
},
|
|||
|
|
backgroundColor: 'rgba(50,50,50,0.9)',
|
|||
|
|
borderRadius: 4,
|
|||
|
|
shadowColor: 'rgba(0,0,0,0.3)',
|
|||
|
|
shadowBlur: 10,
|
|||
|
|
},
|
|||
|
|
grid: {
|
|||
|
|
left: '3%',
|
|||
|
|
right: '4%',
|
|||
|
|
bottom: '10%',
|
|||
|
|
|
|||
|
|
containLabel: true,
|
|||
|
|
},
|
|||
|
|
xAxis: {
|
|||
|
|
type: 'category',
|
|||
|
|
data: chartData.value.categories || [],
|
|||
|
|
axisLabel: {
|
|||
|
|
interval: 0,
|
|||
|
|
rotate: 0,
|
|||
|
|
fontSize: 12,
|
|||
|
|
color: '#666',
|
|||
|
|
},
|
|||
|
|
axisLine: {
|
|||
|
|
lineStyle: {
|
|||
|
|
color: '#ddd',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
yAxis: {
|
|||
|
|
type: 'value',
|
|||
|
|
name: '数量',
|
|||
|
|
nameTextStyle: {
|
|||
|
|
color: '#666',
|
|||
|
|
fontSize: 12,
|
|||
|
|
},
|
|||
|
|
splitLine: {
|
|||
|
|
lineStyle: {
|
|||
|
|
type: 'dashed',
|
|||
|
|
color: '#eee',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
data: chartData.value.series?.map(item => item.name) || [],
|
|||
|
|
bottom: '0%',
|
|||
|
|
textStyle: {
|
|||
|
|
fontSize: 12,
|
|||
|
|
},
|
|||
|
|
selectedMode: true,
|
|||
|
|
},
|
|||
|
|
series: (chartData.value.series || []).map(item => ({
|
|||
|
|
name: item.name,
|
|||
|
|
type: 'bar',
|
|||
|
|
data: item.data || [],
|
|||
|
|
barMaxWidth: 50,
|
|||
|
|
barGap: '30%',
|
|||
|
|
barMinHeight: 3,
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
position: 'top',
|
|||
|
|
formatter: '{c}',
|
|||
|
|
fontSize: 12,
|
|||
|
|
fontWeight: 'bold',
|
|||
|
|
},
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: [3, 3, 0, 0],
|
|||
|
|
shadowColor: 'rgba(0,0,0,0.2)',
|
|||
|
|
shadowBlur: 8,
|
|||
|
|
shadowOffsetX: 2,
|
|||
|
|
shadowOffsetY: 2,
|
|||
|
|
},
|
|||
|
|
})),
|
|||
|
|
}
|
|||
|
|
chartInstance.value.setOption(option)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化机构类型饼图
|
|||
|
|
function initOrgPieChart() {
|
|||
|
|
if (!orgPieChartRef.value) return
|
|||
|
|
|
|||
|
|
if (!orgPieChartInstance.value) {
|
|||
|
|
orgPieChartInstance.value = echarts.init(orgPieChartRef.value)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 从当前显示的tableData中获取数据
|
|||
|
|
const currentData = transformedTableData.value?.[selectedDataSource.value]?.[selectedStatType.value] || []
|
|||
|
|
|
|||
|
|
if (!currentData || currentData.length === 0) {
|
|||
|
|
// 如果没有数据,设置一个空的饼图
|
|||
|
|
const option = {
|
|||
|
|
title: {
|
|||
|
|
text: '暂无数据',
|
|||
|
|
left: 'center',
|
|||
|
|
top: 'center',
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['40%', '70%'],
|
|||
|
|
data: [],
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
orgPieChartInstance.value.setOption(option)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const pieData = currentData.map(item => ({
|
|||
|
|
name: item.label,
|
|||
|
|
value: item.value,
|
|||
|
|
}))
|
|||
|
|
|
|||
|
|
const option = {
|
|||
|
|
color: opts.value.color,
|
|||
|
|
tooltip: {
|
|||
|
|
trigger: 'item',
|
|||
|
|
formatter: '{a} <br/>{b}: {c} ({d}%)',
|
|||
|
|
},
|
|||
|
|
legend: {
|
|||
|
|
orient: 'vertical',
|
|||
|
|
right: 0,
|
|||
|
|
top: 'center',
|
|||
|
|
data: pieData.map(item => item.name),
|
|||
|
|
},
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: selectedStatType.value === 'apply' ? '申请次数' : '申请机构数',
|
|||
|
|
type: 'pie',
|
|||
|
|
radius: ['35%', '60%'], // 缩小饼图尺寸
|
|||
|
|
avoidLabelOverlap: false,
|
|||
|
|
label: {
|
|||
|
|
show: false,
|
|||
|
|
position: 'center',
|
|||
|
|
},
|
|||
|
|
emphasis: {
|
|||
|
|
label: {
|
|||
|
|
show: true,
|
|||
|
|
fontSize: '14',
|
|||
|
|
fontWeight: 'bold',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
labelLine: {
|
|||
|
|
show: false,
|
|||
|
|
},
|
|||
|
|
data: pieData,
|
|||
|
|
center: ['32%', '50%'], // 将饼图位置向左移动
|
|||
|
|
itemStyle: {
|
|||
|
|
borderRadius: 8, // 添加圆角
|
|||
|
|
borderWidth: 2, // 添加边框
|
|||
|
|
borderColor: '#fff', // 边框颜色
|
|||
|
|
shadowBlur: 10, // 添加阴影
|
|||
|
|
shadowColor: 'rgba(0, 0, 0, 0.2)', // 阴影颜色
|
|||
|
|
},
|
|||
|
|
// 添加3D效果
|
|||
|
|
// roseType: 'radius',
|
|||
|
|
zlevel: 1,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
orgPieChartInstance.value.setOption(option)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 将表格数据转换为按时间维度展示的格式
|
|||
|
|
const transformedTableData = computed(() => {
|
|||
|
|
const result = {
|
|||
|
|
id: {
|
|||
|
|
// 申请次数数据
|
|||
|
|
apply: [],
|
|||
|
|
// 机构数数据
|
|||
|
|
org: [],
|
|||
|
|
},
|
|||
|
|
cell: {
|
|||
|
|
apply: [],
|
|||
|
|
org: [],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 遍历所有机构类型
|
|||
|
|
Object.keys(orgMappings).forEach(orgType => {
|
|||
|
|
// ID数据 - 申请次数
|
|||
|
|
const idApplyItem = {
|
|||
|
|
label: orgMappings[orgType],
|
|||
|
|
d7: tableData.value?.id?.[0]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m1: tableData.value?.id?.[1]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m3: tableData.value?.id?.[2]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m6: tableData.value?.id?.[3]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m12: tableData.value?.id?.[4]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
value: tableData.value?.id?.[selectedTimeOption.value]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
}
|
|||
|
|
result.id.apply.push(idApplyItem)
|
|||
|
|
|
|||
|
|
// ID数据 - 机构数
|
|||
|
|
const idOrgItem = {
|
|||
|
|
label: orgMappings[orgType],
|
|||
|
|
d7: tableData.value?.id?.[0]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m1: tableData.value?.id?.[1]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m3: tableData.value?.id?.[2]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m6: tableData.value?.id?.[3]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m12: tableData.value?.id?.[4]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
value: tableData.value?.id?.[selectedTimeOption.value]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
}
|
|||
|
|
result.id.org.push(idOrgItem)
|
|||
|
|
|
|||
|
|
// Cell数据 - 申请次数
|
|||
|
|
const cellApplyItem = {
|
|||
|
|
label: orgMappings[orgType],
|
|||
|
|
d7: tableData.value?.cell?.[0]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m1: tableData.value?.cell?.[1]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m3: tableData.value?.cell?.[2]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m6: tableData.value?.cell?.[3]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
m12: tableData.value?.cell?.[4]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
value: tableData.value?.cell?.[selectedTimeOption.value]?.find(i => i.name === orgType)?.totalApplyCount || 0,
|
|||
|
|
}
|
|||
|
|
result.cell.apply.push(cellApplyItem)
|
|||
|
|
|
|||
|
|
// Cell数据 - 机构数
|
|||
|
|
const cellOrgItem = {
|
|||
|
|
label: orgMappings[orgType],
|
|||
|
|
d7: tableData.value?.cell?.[0]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m1: tableData.value?.cell?.[1]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m3: tableData.value?.cell?.[2]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m6: tableData.value?.cell?.[3]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
m12: tableData.value?.cell?.[4]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
value: tableData.value?.cell?.[selectedTimeOption.value]?.find(i => i.name === orgType)?.totalOrgCount || 0,
|
|||
|
|
}
|
|||
|
|
result.cell.org.push(cellOrgItem)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
return result
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 特殊时段申请数据转换
|
|||
|
|
const transformedDateTableData = computed(() => {
|
|||
|
|
const result = {
|
|||
|
|
id: {
|
|||
|
|
apply: [],
|
|||
|
|
org: [],
|
|||
|
|
},
|
|||
|
|
cell: {
|
|||
|
|
apply: [],
|
|||
|
|
org: [],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 特殊时段名称映射
|
|||
|
|
const dateTypeMappings = {
|
|||
|
|
week: '周末申请',
|
|||
|
|
night: '夜间申请',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 遍历所有特殊时段类型
|
|||
|
|
Object.keys(dateTypeMappings).forEach(dateType => {
|
|||
|
|
// ID数据 - 申请次数
|
|||
|
|
const idApplyItem = {
|
|||
|
|
label: dateTypeMappings[dateType],
|
|||
|
|
d7: dateTableData.value?.id?.[0]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m1: dateTableData.value?.id?.[1]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m3: dateTableData.value?.id?.[2]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m6: dateTableData.value?.id?.[3]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m12: dateTableData.value?.id?.[4]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
}
|
|||
|
|
result.id.apply.push(idApplyItem)
|
|||
|
|
|
|||
|
|
// ID数据 - 机构数
|
|||
|
|
const idOrgItem = {
|
|||
|
|
label: dateTypeMappings[dateType],
|
|||
|
|
d7: dateTableData.value?.id?.[0]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m1: dateTableData.value?.id?.[1]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m3: dateTableData.value?.id?.[2]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m6: dateTableData.value?.id?.[3]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m12: dateTableData.value?.id?.[4]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
}
|
|||
|
|
result.id.org.push(idOrgItem)
|
|||
|
|
|
|||
|
|
// Cell数据 - 申请次数
|
|||
|
|
const cellApplyItem = {
|
|||
|
|
label: dateTypeMappings[dateType],
|
|||
|
|
d7: dateTableData.value?.cell?.[0]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m1: dateTableData.value?.cell?.[1]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m3: dateTableData.value?.cell?.[2]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m6: dateTableData.value?.cell?.[3]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
m12: dateTableData.value?.cell?.[4]?.find(i => i.name === dateType)?.totalApplyCount || 0,
|
|||
|
|
}
|
|||
|
|
result.cell.apply.push(cellApplyItem)
|
|||
|
|
|
|||
|
|
// Cell数据 - 机构数
|
|||
|
|
const cellOrgItem = {
|
|||
|
|
label: dateTypeMappings[dateType],
|
|||
|
|
d7: dateTableData.value?.cell?.[0]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m1: dateTableData.value?.cell?.[1]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m3: dateTableData.value?.cell?.[2]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m6: dateTableData.value?.cell?.[3]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
m12: dateTableData.value?.cell?.[4]?.find(i => i.name === dateType)?.totalOrgCount || 0,
|
|||
|
|
}
|
|||
|
|
result.cell.org.push(cellOrgItem)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 添加首次和最近申请数据
|
|||
|
|
if (dateTableData.value?.id?.[4]?.some(i => i.name === 'first_apply')) {
|
|||
|
|
const firstApply = dateTableData.value.id[4].find(i => i.name === 'first_apply')
|
|||
|
|
const lastApply = dateTableData.value.id[4].find(i => i.name === 'last_apply')
|
|||
|
|
|
|||
|
|
result.id.apply.push({
|
|||
|
|
label: '最早非银申请',
|
|||
|
|
m12: firstApply?.totalApplyCount || '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
result.id.apply.push({
|
|||
|
|
label: '最近非银申请',
|
|||
|
|
m12: lastApply?.totalApplyCount || '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
result.id.org.push({
|
|||
|
|
label: '最早非银申请',
|
|||
|
|
m12: '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
result.id.org.push({
|
|||
|
|
label: '最近非银申请',
|
|||
|
|
m12: '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (dateTableData.value?.cell?.[4]?.some(i => i.name === 'first_apply')) {
|
|||
|
|
const firstApply = dateTableData.value.cell[4].find(i => i.name === 'first_apply')
|
|||
|
|
const lastApply = dateTableData.value.cell[4].find(i => i.name === 'last_apply')
|
|||
|
|
|
|||
|
|
result.cell.apply.push({
|
|||
|
|
label: '最早非银申请',
|
|||
|
|
m12: firstApply?.totalApplyCount || '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
result.cell.apply.push({
|
|||
|
|
label: '最近非银申请',
|
|||
|
|
m12: lastApply?.totalApplyCount || '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
result.cell.org.push({
|
|||
|
|
label: '最早非银申请',
|
|||
|
|
m12: '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
result.cell.org.push({
|
|||
|
|
label: '最近非银申请',
|
|||
|
|
m12: '-',
|
|||
|
|
specialDisplay: true,
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 更新图表
|
|||
|
|
function updateChart() {
|
|||
|
|
if (chartInstance.value) {
|
|||
|
|
initChart()
|
|||
|
|
}
|
|||
|
|
if (orgPieChartInstance.value) {
|
|||
|
|
initOrgPieChart()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算总体统计数据
|
|||
|
|
function calculateTotalStats(data) {
|
|||
|
|
// 获取时间维度列表
|
|||
|
|
const timeKeys = Object.keys(timeDimensions)
|
|||
|
|
|
|||
|
|
// 初始化结果对象
|
|||
|
|
const result = {
|
|||
|
|
id: {
|
|||
|
|
totalApplyCount: [],
|
|||
|
|
totalOrgCount: [],
|
|||
|
|
},
|
|||
|
|
cell: {
|
|||
|
|
totalApplyCount: [],
|
|||
|
|
totalOrgCount: [],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 机构类型
|
|||
|
|
const orgTypes = ['bank', 'nbank']
|
|||
|
|
|
|||
|
|
// 遍历每个时间维度
|
|||
|
|
for (const timeKey of timeKeys) {
|
|||
|
|
// ID数据
|
|||
|
|
let idTotalApplyCount = 0
|
|||
|
|
let idTotalOrgCount = 0
|
|||
|
|
|
|||
|
|
// Cell数据
|
|||
|
|
let cellTotalApplyCount = 0
|
|||
|
|
let cellTotalOrgCount = 0
|
|||
|
|
|
|||
|
|
// 遍历每种机构类型并累加
|
|||
|
|
orgTypes.forEach(orgType => {
|
|||
|
|
// ID数据统计
|
|||
|
|
const idApplyCountKey = `als_${timeKey}_id_${orgType}_allnum`
|
|||
|
|
const idOrgCountKey = `als_${timeKey}_id_${orgType}_orgnum`
|
|||
|
|
if (data?.[idApplyCountKey] !== undefined && data?.[idOrgCountKey] !== undefined) {
|
|||
|
|
idTotalApplyCount += Number(data?.[idApplyCountKey] || 0)
|
|||
|
|
idTotalOrgCount += Number(data?.[idOrgCountKey] || 0)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Cell数据统计
|
|||
|
|
const cellApplyCountKey = `als_${timeKey}_cell_${orgType}_allnum`
|
|||
|
|
const cellOrgCountKey = `als_${timeKey}_cell_${orgType}_orgnum`
|
|||
|
|
if (data?.[cellApplyCountKey] !== undefined && data?.[cellOrgCountKey] !== undefined) {
|
|||
|
|
cellTotalApplyCount += Number(data?.[cellApplyCountKey] || 0)
|
|||
|
|
cellTotalOrgCount += Number(data?.[cellOrgCountKey] || 0)
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 添加到结果对象
|
|||
|
|
result.id.totalApplyCount.push(idTotalApplyCount)
|
|||
|
|
result.id.totalOrgCount.push(idTotalOrgCount)
|
|||
|
|
result.cell.totalApplyCount.push(cellTotalApplyCount)
|
|||
|
|
result.cell.totalOrgCount.push(cellTotalOrgCount)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算各借贷类型统计数据
|
|||
|
|
function typeTotalStats(data) {
|
|||
|
|
// 时间维度列表
|
|||
|
|
const timeKeys = Object.keys(timeDimensions)
|
|||
|
|
|
|||
|
|
// 初始化结果数组 - 按数据来源分组
|
|||
|
|
const result = {
|
|||
|
|
id: [],
|
|||
|
|
cell: [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 遍历每个时间维度
|
|||
|
|
for (const timeKey of timeKeys) {
|
|||
|
|
const idTableDataEntry = []
|
|||
|
|
const cellTableDataEntry = []
|
|||
|
|
|
|||
|
|
// 遍历每种借贷类型并累加
|
|||
|
|
Object.keys(orgMappings).forEach(groupOrgType => {
|
|||
|
|
const orgTypeArray = tableGroup[groupOrgType]
|
|||
|
|
|
|||
|
|
// ID数据统计
|
|||
|
|
let idTotalApplyCount = 0
|
|||
|
|
let idTotalOrgCount = 0
|
|||
|
|
|
|||
|
|
// Cell数据统计
|
|||
|
|
let cellTotalApplyCount = 0
|
|||
|
|
let cellTotalOrgCount = 0
|
|||
|
|
|
|||
|
|
for (const i of orgTypeArray) {
|
|||
|
|
// ID数据统计
|
|||
|
|
const idApplyCountKey = `als_${timeKey}_id_${i.code}_allnum`
|
|||
|
|
const idOrgCountKey = `als_${timeKey}_id_${i.code}_orgnum`
|
|||
|
|
idTotalApplyCount += Number(data?.[idApplyCountKey] || 0)
|
|||
|
|
idTotalOrgCount += Number(data?.[idOrgCountKey] || 0)
|
|||
|
|
|
|||
|
|
// Cell数据统计
|
|||
|
|
const cellApplyCountKey = `als_${timeKey}_cell_${i.code}_allnum`
|
|||
|
|
const cellOrgCountKey = `als_${timeKey}_cell_${i.code}_orgnum`
|
|||
|
|
cellTotalApplyCount += Number(data?.[cellApplyCountKey] || 0)
|
|||
|
|
cellTotalOrgCount += Number(data?.[cellOrgCountKey] || 0)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加到ID表格数据
|
|||
|
|
idTableDataEntry.push({
|
|||
|
|
label: orgMappings[groupOrgType],
|
|||
|
|
name: groupOrgType,
|
|||
|
|
totalApplyCount: idTotalApplyCount,
|
|||
|
|
totalOrgCount: idTotalOrgCount,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 添加到Cell表格数据
|
|||
|
|
cellTableDataEntry.push({
|
|||
|
|
label: orgMappings[groupOrgType],
|
|||
|
|
name: groupOrgType,
|
|||
|
|
totalApplyCount: cellTotalApplyCount,
|
|||
|
|
totalOrgCount: cellTotalOrgCount,
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 添加到结果
|
|||
|
|
result.id.push(idTableDataEntry)
|
|||
|
|
result.cell.push(cellTableDataEntry)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算特殊时段统计数据
|
|||
|
|
function dateTotalStats(data) {
|
|||
|
|
// 时间维度列表
|
|||
|
|
const timeKeys = Object.keys(timeDimensions)
|
|||
|
|
|
|||
|
|
// 特殊时段名称映射
|
|||
|
|
const dateTypeMappings = {
|
|||
|
|
week: '周末申请',
|
|||
|
|
night: '夜间申请 (晚8点至次日早8点)',
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化结果数组 - 按数据来源分组
|
|||
|
|
const result = {
|
|||
|
|
id: [],
|
|||
|
|
cell: [],
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 遍历每个时间维度
|
|||
|
|
for (const timeKey of timeKeys) {
|
|||
|
|
const idTableDataEntry = []
|
|||
|
|
const cellTableDataEntry = []
|
|||
|
|
|
|||
|
|
// 遍历每种特殊时段类型
|
|||
|
|
Object.keys(dateTypeMappings).forEach(dateType => {
|
|||
|
|
const typeArray = dateGroup[dateType]
|
|||
|
|
|
|||
|
|
// ID数据统计
|
|||
|
|
let idTotalApplyCount = 0
|
|||
|
|
let idTotalOrgCount = 0
|
|||
|
|
|
|||
|
|
// Cell数据统计
|
|||
|
|
let cellTotalApplyCount = 0
|
|||
|
|
let cellTotalOrgCount = 0
|
|||
|
|
|
|||
|
|
for (const i of typeArray) {
|
|||
|
|
// ID数据统计
|
|||
|
|
const idApplyCountKey = `als_${timeKey}_id_${i.code}_allnum`
|
|||
|
|
const idOrgCountKey = `als_${timeKey}_id_${i.code}_orgnum`
|
|||
|
|
idTotalApplyCount += Number(data?.[idApplyCountKey] || 0)
|
|||
|
|
idTotalOrgCount += Number(data?.[idOrgCountKey] || 0)
|
|||
|
|
|
|||
|
|
// Cell数据统计
|
|||
|
|
const cellApplyCountKey = `als_${timeKey}_cell_${i.code}_allnum`
|
|||
|
|
const cellOrgCountKey = `als_${timeKey}_cell_${i.code}_orgnum`
|
|||
|
|
cellTotalApplyCount += Number(data?.[cellApplyCountKey] || 0)
|
|||
|
|
cellTotalOrgCount += Number(data?.[cellOrgCountKey] || 0)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加到ID表格数据
|
|||
|
|
idTableDataEntry.push({
|
|||
|
|
label: dateTypeMappings[dateType],
|
|||
|
|
name: dateType,
|
|||
|
|
totalApplyCount: idTotalApplyCount,
|
|||
|
|
totalOrgCount: idTotalOrgCount,
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 添加到Cell表格数据
|
|||
|
|
cellTableDataEntry.push({
|
|||
|
|
label: dateTypeMappings[dateType],
|
|||
|
|
name: dateType,
|
|||
|
|
totalApplyCount: cellTotalApplyCount,
|
|||
|
|
totalOrgCount: cellTotalOrgCount,
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 添加最早和最近申请时间(如果存在数据)
|
|||
|
|
if (timeKey === 'm12') {
|
|||
|
|
// 最早申请日期(间隔天数)
|
|||
|
|
const idFirstApplyKey = 'als_fst_id_nbank_inteday'
|
|||
|
|
const cellFirstApplyKey = 'als_fst_cell_nbank_inteday'
|
|||
|
|
|
|||
|
|
// 最近申请日期(间隔天数)
|
|||
|
|
const idLastApplyKey = 'als_lst_id_nbank_inteday'
|
|||
|
|
const cellLastApplyKey = 'als_lst_cell_nbank_inteday'
|
|||
|
|
|
|||
|
|
if (data?.[idFirstApplyKey]) {
|
|||
|
|
idTableDataEntry.push({
|
|||
|
|
label: '最早非银借贷申请',
|
|||
|
|
name: 'first_apply',
|
|||
|
|
totalApplyCount: `${data[idFirstApplyKey]}天前`,
|
|||
|
|
totalOrgCount: '-',
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data?.[idLastApplyKey]) {
|
|||
|
|
idTableDataEntry.push({
|
|||
|
|
label: '最近非银借贷申请',
|
|||
|
|
name: 'last_apply',
|
|||
|
|
totalApplyCount: `${data[idLastApplyKey]}天前`,
|
|||
|
|
totalOrgCount: '-',
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data?.[cellFirstApplyKey]) {
|
|||
|
|
cellTableDataEntry.push({
|
|||
|
|
label: '最早非银借贷申请',
|
|||
|
|
name: 'first_apply',
|
|||
|
|
totalApplyCount: `${data[cellFirstApplyKey]}天前`,
|
|||
|
|
totalOrgCount: '-',
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data?.[cellLastApplyKey]) {
|
|||
|
|
cellTableDataEntry.push({
|
|||
|
|
label: '最近非银借贷申请',
|
|||
|
|
name: 'last_apply',
|
|||
|
|
totalApplyCount: `${data[cellLastApplyKey]}天前`,
|
|||
|
|
totalOrgCount: '-',
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加到结果
|
|||
|
|
result.id.push(idTableDataEntry)
|
|||
|
|
result.cell.push(cellTableDataEntry)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算总结统计数据
|
|||
|
|
function calculateSummaryStats(data) {
|
|||
|
|
// 获取最大借贷次数和最大机构数
|
|||
|
|
const maxData = {
|
|||
|
|
id: {
|
|||
|
|
maxApply: 0,
|
|||
|
|
maxOrg: 0,
|
|||
|
|
avgMonthlyApply: 0,
|
|||
|
|
},
|
|||
|
|
cell: {
|
|||
|
|
maxApply: 0,
|
|||
|
|
maxOrg: 0,
|
|||
|
|
avgMonthlyApply: 0,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否有最大月申请次数数据
|
|||
|
|
if (data?.als_m12_id_max_monnum) {
|
|||
|
|
maxData.id.maxApply = Number(data.als_m12_id_max_monnum)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data?.als_m12_cell_max_monnum) {
|
|||
|
|
maxData.cell.maxApply = Number(data.als_m12_cell_max_monnum)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查是否有平均月申请次数数据
|
|||
|
|
if (data?.als_m12_id_avg_monnum) {
|
|||
|
|
maxData.id.avgMonthlyApply = Number(data.als_m12_id_avg_monnum)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data?.als_m12_cell_avg_monnum) {
|
|||
|
|
maxData.cell.avgMonthlyApply = Number(data.als_m12_cell_avg_monnum)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取总申请月份数
|
|||
|
|
if (data?.als_m12_id_nbank_tot_mons) {
|
|||
|
|
maxData.id.totalMonths = Number(data.als_m12_id_nbank_tot_mons)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (data?.als_m12_cell_nbank_tot_mons) {
|
|||
|
|
maxData.cell.totalMonths = Number(data.als_m12_cell_nbank_tot_mons)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return maxData
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查某种数据源是否有数据
|
|||
|
|
function hasDataSource(source) {
|
|||
|
|
if (source === 'id') {
|
|||
|
|
// 检查ID数据源
|
|||
|
|
return totalStatsData.value?.id?.totalApplyCount?.some(count => count > 0) || false
|
|||
|
|
} else if (source === 'cell') {
|
|||
|
|
// 检查Cell数据源
|
|||
|
|
return totalStatsData.value?.cell?.totalApplyCount?.some(count => count > 0) || false
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 监听选项变化
|
|||
|
|
watch(selectedTimeOption, () => {
|
|||
|
|
// 更新饼图值
|
|||
|
|
transformedTableData.value[selectedDataSource.value][selectedStatType.value].forEach(item => {
|
|||
|
|
const timeKey = ['d7', 'm1', 'm3', 'm6', 'm12'][selectedTimeOption.value]
|
|||
|
|
item.value = item[timeKey]
|
|||
|
|
})
|
|||
|
|
updateChart()
|
|||
|
|
})
|
|||
|
|
watch(selectedDataSource, () => {
|
|||
|
|
updateChartData()
|
|||
|
|
// 完全重新渲染图表
|
|||
|
|
initChart()
|
|||
|
|
})
|
|||
|
|
watch(selectedStatType, updateChart)
|
|||
|
|
|
|||
|
|
// 窗口大小变化时重新调整图表大小
|
|||
|
|
function resizeCharts() {
|
|||
|
|
if (chartInstance.value) {
|
|||
|
|
chartInstance.value.resize()
|
|||
|
|
}
|
|||
|
|
if (orgPieChartInstance.value) {
|
|||
|
|
orgPieChartInstance.value.resize()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 初始化组件
|
|||
|
|
onMounted(() => {
|
|||
|
|
// 计算总体统计数据
|
|||
|
|
totalStatsData.value = calculateTotalStats(data)
|
|||
|
|
|
|||
|
|
// 计算各借贷类型统计数据
|
|||
|
|
tableData.value = typeTotalStats(data)
|
|||
|
|
|
|||
|
|
// 计算特殊时段统计数据
|
|||
|
|
dateTableData.value = dateTotalStats(data)
|
|||
|
|
|
|||
|
|
// 计算总结统计数据
|
|||
|
|
const summaryData = calculateSummaryStats(data)
|
|||
|
|
|
|||
|
|
// 检查哪个数据源有数据,并自动选择
|
|||
|
|
if (hasDataSource('id')) {
|
|||
|
|
selectedDataSource.value = 'id'
|
|||
|
|
} else if (hasDataSource('cell')) {
|
|||
|
|
selectedDataSource.value = 'cell'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 设置图表数据
|
|||
|
|
updateChartData()
|
|||
|
|
|
|||
|
|
nextTick(() => {
|
|||
|
|
// 初始化所有图表
|
|||
|
|
initChart()
|
|||
|
|
initOrgPieChart()
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 添加窗口大小变化监听
|
|||
|
|
window.addEventListener('resize', resizeCharts)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 更新图表数据
|
|||
|
|
function updateChartData() {
|
|||
|
|
chartData.value = {
|
|||
|
|
categories: ['近7日', '近1月', '近3月', '近6月', '近1年'],
|
|||
|
|
series: [
|
|||
|
|
{
|
|||
|
|
name: '申请次数',
|
|||
|
|
data: totalStatsData.value?.[selectedDataSource.value]?.totalApplyCount || [],
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
name: '申请的机构数',
|
|||
|
|
data: totalStatsData.value?.[selectedDataSource.value]?.totalOrgCount || [],
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算风险评分(0-100分,分数越高越安全)
|
|||
|
|
const riskScore = computed(() => {
|
|||
|
|
// 获取近1年的申请次数
|
|||
|
|
const idApplyCount = totalStatsData.value?.id?.totalApplyCount?.[4] || 0;
|
|||
|
|
const cellApplyCount = totalStatsData.value?.cell?.totalApplyCount?.[4] || 0;
|
|||
|
|
const totalApplyCount = Math.max(idApplyCount, cellApplyCount);
|
|||
|
|
|
|||
|
|
// 根据申请次数计算风险评分
|
|||
|
|
// 0次:100分(最安全)
|
|||
|
|
// 1-3次:80分(较安全)
|
|||
|
|
// 4-10次:60分(中等风险)
|
|||
|
|
// 11-20次:40分(较高风险)
|
|||
|
|
// 20次以上:20分(高风险)
|
|||
|
|
if (totalApplyCount === 0) return 100;
|
|||
|
|
if (totalApplyCount <= 3) return 80;
|
|||
|
|
if (totalApplyCount <= 10) return 60;
|
|||
|
|
if (totalApplyCount <= 20) return 40;
|
|||
|
|
return 20;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 使用 composable 通知父组件风险评分
|
|||
|
|
useRiskNotifier(props, riskScore);
|
|||
|
|
|
|||
|
|
// 暴露给父组件
|
|||
|
|
defineExpose({
|
|||
|
|
riskScore
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 组件销毁时清理资源
|
|||
|
|
onUnmounted(() => {
|
|||
|
|
window.removeEventListener('resize', resizeCharts)
|
|||
|
|
if (chartInstance.value) {
|
|||
|
|
chartInstance.value.dispose()
|
|||
|
|
}
|
|||
|
|
if (orgPieChartInstance.value) {
|
|||
|
|
orgPieChartInstance.value.dispose()
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
// 当数据源变化时,更新主图表数据
|
|||
|
|
watch(selectedDataSource, newValue => {
|
|||
|
|
updateChartData()
|
|||
|
|
|
|||
|
|
if (chartInstance.value) {
|
|||
|
|
chartInstance.value.setOption({
|
|||
|
|
xAxis: {
|
|||
|
|
data: chartData.value.categories,
|
|||
|
|
},
|
|||
|
|
series: chartData.value.series.map(item => ({
|
|||
|
|
name: item.name,
|
|||
|
|
data: item.data,
|
|||
|
|
})),
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div class="card">
|
|||
|
|
<div class="flex flex-col gap-y-6">
|
|||
|
|
<!-- 数据切换选项 -->
|
|||
|
|
<div class="p-6 bg-white rounded-lg shadow-sm border border-gray-100 relative overflow-hidden">
|
|||
|
|
<!-- 背景装饰元素 -->
|
|||
|
|
<div class="absolute top-0 right-0 w-32 h-32 bg-blue-50 rounded-full -mr-8 -mt-8 opacity-60"></div>
|
|||
|
|
<div class="absolute bottom-0 left-0 w-20 h-20 bg-green-50 rounded-full -ml-10 -mb-10 opacity-50"></div>
|
|||
|
|
|
|||
|
|
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 relative z-10">
|
|||
|
|
<div class="space-y-2">
|
|||
|
|
<h2 class="text-xl font-semibold text-gray-800 flex items-center">借贷申请分析报告</h2>
|
|||
|
|
<p class="text-sm text-gray-600 ml-6">本报告统计借贷申请记录和机构情况,帮助评估信贷风险</p>
|
|||
|
|
</div>
|
|||
|
|
<div class="flex flex-wrap gap-6 w-full">
|
|||
|
|
<template v-if="hasDataSource('id') || hasDataSource('cell')">
|
|||
|
|
<div class="flex-1 flex rounded-md shadow-sm relative">
|
|||
|
|
<!-- 图标装饰 -->
|
|||
|
|
<button v-if="hasDataSource('id')" type="button"
|
|||
|
|
class="flex-1 py-2 px-4 text-sm font-medium rounded-l-md border transition-all duration-200 flex items-center flex-shrink-0"
|
|||
|
|
:class="[
|
|||
|
|
selectedDataSource === 'id'
|
|||
|
|
? 'bg-gradient-to-r from-blue-500 to-blue-600 text-white border-blue-500 shadow-md'
|
|||
|
|
: 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50',
|
|||
|
|
]" @click="selectedDataSource = 'id'">
|
|||
|
|
<svg v-if="selectedDataSource === 'id'" class="w-4 h-4 mr-1" fill="none" stroke="currentColor"
|
|||
|
|
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|||
|
|
</svg>
|
|||
|
|
<svg v-else class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|||
|
|
xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M10 6H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V8a2 2 0 00-2-2h-5m-4 0V5a2 2 0 114 0v1m-4 0a2 2 0 104 0m-5 8a2 2 0 100-4 2 2 0 000 4zm0 0c1.306 0 2.417.835 2.83 2M9 14a3.001 3.001 0 00-2.83 2M15 11h3m-3 4h2">
|
|||
|
|
</path>
|
|||
|
|
</svg>
|
|||
|
|
身份证匹配
|
|||
|
|
</button>
|
|||
|
|
<button v-if="hasDataSource('cell')" type="button"
|
|||
|
|
class="flex-1 py-2 px-4 text-sm font-medium rounded-r-md border transition-all duration-200 flex items-center flex-shrink-0"
|
|||
|
|
:class="[
|
|||
|
|
selectedDataSource === 'cell'
|
|||
|
|
? 'bg-gradient-to-r from-green-500 to-green-600 text-white border-green-500 shadow-md'
|
|||
|
|
: 'bg-white text-gray-700 border-gray-300 hover:bg-gray-50',
|
|||
|
|
]" @click="selectedDataSource = 'cell'">
|
|||
|
|
<svg v-if="selectedDataSource === 'cell'" class="w-4 h-4 mr-1" fill="none" stroke="currentColor"
|
|||
|
|
viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|||
|
|
</svg>
|
|||
|
|
<svg v-else class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"
|
|||
|
|
xmlns="http://www.w3.org/2000/svg">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"></path>
|
|||
|
|
</svg>
|
|||
|
|
手机号匹配
|
|||
|
|
</button>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 数据类型说明 -->
|
|||
|
|
<div class="mt-4 bg-blue-50 p-3 rounded-lg text-xs text-gray-700">
|
|||
|
|
<p class="font-medium text-blue-800 mb-1">数据类型说明:</p>
|
|||
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-2">
|
|||
|
|
<div class="flex items-start">
|
|||
|
|
<span class="inline-block w-2 h-2 mt-1 mr-2 rounded-full flex-shrink-0 bg-blue-500"></span>
|
|||
|
|
<span><strong>身份证匹配:</strong>通过身份证号码匹配获取的借贷申请记录,能够反映与身份证关联的所有金融机构申请行为</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="flex items-start">
|
|||
|
|
<span class="inline-block w-2 h-2 mt-1 mr-2 rounded-full flex-shrink-0 bg-green-500"></span>
|
|||
|
|
<span><strong>手机号匹配:</strong>通过手机号码匹配获取的借贷申请记录,能够反映与手机号关联的所有金融机构申请行为</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<template v-if="hasDataSource(selectedDataSource)">
|
|||
|
|
<!-- 借贷申请总体情况概览 -->
|
|||
|
|
<div class="bg-gradient-to-br from-gray-50 to-gray-100 rounded-lg p-4 relative overflow-hidden">
|
|||
|
|
<!-- 装饰元素 -->
|
|||
|
|
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-400 to-green-400 opacity-70"></div>
|
|||
|
|
<div class="absolute bottom-4 right-4 opacity-10">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-32 w-32" fill="none" viewBox="0 0 24 24"
|
|||
|
|
stroke="currentColor">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1"
|
|||
|
|
d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="grid grid-cols-2 md:grid-cols-2 lg:grid-cols-4 gap-4 relative z-10">
|
|||
|
|
<div
|
|||
|
|
class="bg-white rounded-lg p-3 md:p-5 shadow-sm border border-gray-100 transform transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
|
|||
|
|
<div class="flex items-start">
|
|||
|
|
<div class="mr-2 md:mr-3 bg-blue-100 rounded-lg p-1 md:p-2 text-blue-600">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 md:h-6 md:w-6" fill="none" viewBox="0 0 24 24"
|
|||
|
|
stroke="currentColor">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<div class="text-xs md:text-sm text-gray-500">近1年总申请次数</div>
|
|||
|
|
<div class="text-lg md:text-2xl font-bold mt-1 text-gray-800">
|
|||
|
|
{{ totalStatsData[selectedDataSource]?.totalApplyCount[4] || 0 }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div
|
|||
|
|
class="bg-white rounded-lg p-3 md:p-5 shadow-sm border border-gray-100 transform transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
|
|||
|
|
<div class="flex items-start">
|
|||
|
|
<div class="mr-2 md:mr-3 bg-green-100 rounded-lg p-1 md:p-2 text-green-600">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 md:h-6 md:w-6" fill="none" viewBox="0 0 24 24"
|
|||
|
|
stroke="currentColor">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<div class="text-xs md:text-sm text-gray-500">近1年总申请机构数</div>
|
|||
|
|
<div class="text-lg md:text-2xl font-bold mt-1 text-gray-800">
|
|||
|
|
{{ totalStatsData[selectedDataSource]?.totalOrgCount[4] || 0 }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div
|
|||
|
|
class="bg-white rounded-lg p-3 md:p-5 shadow-sm border border-gray-100 transform transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
|
|||
|
|
<div class="flex items-start">
|
|||
|
|
<div class="mr-2 md:mr-3 bg-yellow-100 rounded-lg p-1 md:p-2 text-yellow-600">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 md:h-6 md:w-6" fill="none" viewBox="0 0 24 24"
|
|||
|
|
stroke="currentColor">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<div class="text-xs md:text-sm text-gray-500">单月最高申请次数</div>
|
|||
|
|
<div class="text-lg md:text-2xl font-bold mt-1 text-gray-800">
|
|||
|
|
{{ data[`als_m12_${selectedDataSource}_max_monnum`] || 0 }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div
|
|||
|
|
class="bg-white rounded-lg p-3 md:p-5 shadow-sm border border-gray-100 transform transition-transform duration-300 hover:shadow-md hover:-translate-y-1">
|
|||
|
|
<div class="flex items-start">
|
|||
|
|
<div class="mr-2 md:mr-3 bg-purple-100 rounded-lg p-1 md:p-2 text-purple-600">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 md:h-6 md:w-6" fill="none" viewBox="0 0 24 24"
|
|||
|
|
stroke="currentColor">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<div>
|
|||
|
|
<div class="text-xs md:text-sm text-gray-500">月均申请次数</div>
|
|||
|
|
<div class="text-lg md:text-2xl font-bold mt-1 text-gray-800">
|
|||
|
|
{{ data[`als_m12_${selectedDataSource}_avg_monnum`] || 0 }}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 申请次数和机构数趋势图 -->
|
|||
|
|
<div>
|
|||
|
|
<LTitle title="借贷申请趋势" />
|
|||
|
|
<div ref="chartRef" class="chart-container"></div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 借贷类型分析区域 -->
|
|||
|
|
<div>
|
|||
|
|
<LTitle title="借贷类型分析" />
|
|||
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|||
|
|
<!-- 借贷类型分布饼图 -->
|
|||
|
|
<div class="relative">
|
|||
|
|
<div ref="orgPieChartRef" class="chart-container-small"></div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 借贷类型表格 -->
|
|||
|
|
<div>
|
|||
|
|
<div class="mb-2 flex justify-between items-center">
|
|||
|
|
<LButtonGroup v-model="selectedStatType" :options="statTabOptions" />
|
|||
|
|
</div>
|
|||
|
|
<div class="overflow-x-auto">
|
|||
|
|
<LTable :data="transformedTableData?.[selectedDataSource]?.[selectedStatType] || []"
|
|||
|
|
class="w-full whitespace-nowrap">
|
|||
|
|
<template #header>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[25%]">借贷类别</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近7日</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近1月</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近3月</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近6月</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近1年</th>
|
|||
|
|
</template>
|
|||
|
|
<template #default="{ row }">
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm font-medium whitespace-nowrap">
|
|||
|
|
{{ row.label }}
|
|||
|
|
</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">{{ row.d7 }}</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">{{ row.m1 }}</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">{{ row.m3 }}</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">{{ row.m6 }}</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">{{ row.m12 }}</td>
|
|||
|
|
</template>
|
|||
|
|
</LTable>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 借贷类型解释 -->
|
|||
|
|
<div class="mt-4 bg-blue-50 p-3 rounded-lg text-sm">
|
|||
|
|
<p class="font-medium text-blue-800 mb-1">借贷类型说明:</p>
|
|||
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2">
|
|||
|
|
<div v-for="(desc, key) in orgMappings" :key="key" class="flex items-start">
|
|||
|
|
<span class="inline-block w-3 h-3 mt-1 mr-2 rounded-full flex-shrink-0"
|
|||
|
|
:style="{ backgroundColor: opts.color[Object.keys(orgMappings).indexOf(key)] }"></span>
|
|||
|
|
<span><strong>{{ desc }}:</strong>{{key === 'bank' ? '包含各类银行贷款' : tableGroup[key].map(i =>
|
|||
|
|
i.name).join('、')
|
|||
|
|
}}</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- 特殊时段申请记录 -->
|
|||
|
|
<div>
|
|||
|
|
<LTitle title="特殊时段申请记录" />
|
|||
|
|
<div class="text-xs text-gray-500 my-2">
|
|||
|
|
此表格展示在周末或夜间时段的借贷申请情况,这类时段的高频申请可能需要额外关注
|
|||
|
|
</div>
|
|||
|
|
<div class="mb-2">
|
|||
|
|
<LButtonGroup v-model="selectedStatType" :options="statTabOptions" />
|
|||
|
|
</div>
|
|||
|
|
<div class="overflow-x-auto">
|
|||
|
|
<LTable :data="transformedDateTableData?.[selectedDataSource]?.[selectedStatType] || []"
|
|||
|
|
class="w-full whitespace-nowrap">
|
|||
|
|
<template #header>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[25%]">时段类型</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近7日</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近1月</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近3月</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近6月</th>
|
|||
|
|
<th class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm w-[15%]">近1年</th>
|
|||
|
|
</template>
|
|||
|
|
<template #default="{ row }">
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm font-medium whitespace-nowrap">
|
|||
|
|
{{ row.label }}
|
|||
|
|
</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">
|
|||
|
|
{{ row.specialDisplay ? '-' : row.d7 }}
|
|||
|
|
</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">
|
|||
|
|
{{ row.specialDisplay ? '-' : row.m1 }}
|
|||
|
|
</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">
|
|||
|
|
{{ row.specialDisplay ? '-' : row.m3 }}
|
|||
|
|
</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">
|
|||
|
|
{{ row.specialDisplay ? '-' : row.m6 }}
|
|||
|
|
</td>
|
|||
|
|
<td class="border px-2 py-1 text-xs lg:px-3 lg:py-2 lg:text-sm text-center">{{ row.m12 }}</td>
|
|||
|
|
</template>
|
|||
|
|
</LTable>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- <div v-if="data.flag_applyloanstr || data.flag_datastrategy">
|
|||
|
|
<LTitle title="风险策略评估" />
|
|||
|
|
<div class="bg-gray-50 p-4 rounded-lg">
|
|||
|
|
<div class="flex flex-col gap-y-2">
|
|||
|
|
<div v-if="data.flag_applyloanstr === '1'" class="flex items-center">
|
|||
|
|
<span class="inline-block w-3 h-3 mr-2 rounded-full bg-yellow-500"></span>
|
|||
|
|
<span>根据申请记录分析,此用户存在多头申请特征</span>
|
|||
|
|
</div>
|
|||
|
|
<div v-if="data.flag_datastrategy === '1'" class="flex items-center">
|
|||
|
|
<span class="inline-block w-3 h-3 mr-2 rounded-full bg-red-500"></span>
|
|||
|
|
<span>根据数据策略评估,此用户存在潜在风险特征</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div> -->
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<template v-else>
|
|||
|
|
<div class="flex items-center justify-center h-60 bg-gray-50 rounded-lg">
|
|||
|
|
<div class="text-center">
|
|||
|
|
<div class="text-gray-400 text-4xl mb-2">
|
|||
|
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16 mx-auto" fill="none" viewBox="0 0 24 24"
|
|||
|
|
stroke="currentColor">
|
|||
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
|||
|
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|||
|
|
</svg>
|
|||
|
|
</div>
|
|||
|
|
<div class="text-gray-600 text-xl font-medium">暂无借贷申请数据</div>
|
|||
|
|
<div class="text-gray-500 mt-2">当前没有匹配到任何借贷申请记录</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.chart-container {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 350px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.chart-container-small {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 300px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.form-radio {
|
|||
|
|
@apply appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-blue-600 checked:border-blue-600 focus:outline-none transition duration-200 align-top bg-no-repeat bg-center bg-contain float-left cursor-pointer;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.card {
|
|||
|
|
@apply bg-white rounded-lg shadow-md p-4 md:p-6;
|
|||
|
|
}
|
|||
|
|
</style>
|