version temp2
This commit is contained in:
@@ -1,95 +1,36 @@
|
||||
<template>
|
||||
<div class="time-trend-analysis">
|
||||
<LTitle title="时间趋势分析" />
|
||||
<div class="section-spacing"></div>
|
||||
<div class="rounded-lg border border-[#99999933] mb-4">
|
||||
<!-- 标题栏 -->
|
||||
<div class="flex items-center mb-4 p-4">
|
||||
<div class="w-8 h-8 flex items-center justify-center mr-2">
|
||||
<img src="@/assets/images/report/zwsc.png" alt="贷款行为分析" class="w-8 h-8 object-contain" />
|
||||
</div>
|
||||
<span class="font-bold text-gray-800">贷款行为分析</span>
|
||||
</div>
|
||||
|
||||
<!-- 交易金额趋势 -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center mr-2">
|
||||
<svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M3 3a1 1 0 000 2v8a2 2 0 002 2h2.586l-1.293 1.293a1 1 0 101.414 1.414L10 15.414l2.293 2.293a1 1 0 001.414-1.414L12.414 15H15a2 2 0 002-2V5a1 1 0 100-2H3zm11.707 4.707a1 1 0 00-1.414-1.414L10 9.586 8.707 8.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-base font-semibold text-gray-800">交易金额趋势</h3>
|
||||
</div>
|
||||
<LTitle title="交易金额趋势" class="mb-2" />
|
||||
|
||||
<!-- 图表展示 -->
|
||||
<div class="bg-gradient-to-br from-blue-50 to-indigo-50 rounded-lg p-4 border border-blue-200 mb-6">
|
||||
<div class="text-sm font-medium text-gray-800 mb-4 text-center">交易金额趋势图</div>
|
||||
<div class="relative h-48 mb-4">
|
||||
<!-- Y轴标签 -->
|
||||
<div class="absolute left-0 top-0 h-full flex flex-col justify-between text-xs text-gray-500 pr-2">
|
||||
<span>{{ formatAmount(maxAmountTrend) }}</span>
|
||||
<span>{{ formatAmount(Math.round(maxAmountTrend * 0.75)) }}</span>
|
||||
<span>{{ formatAmount(Math.round(maxAmountTrend * 0.5)) }}</span>
|
||||
<span>{{ formatAmount(Math.round(maxAmountTrend * 0.25)) }}</span>
|
||||
<span>0元</span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<div class="ml-8 h-full relative border-l border-b border-gray-300">
|
||||
<!-- 水平网格线 -->
|
||||
<div class="absolute inset-0">
|
||||
<div class="absolute top-0 w-full border-t border-gray-200"></div>
|
||||
<div class="absolute top-1/4 w-full border-t border-gray-200"></div>
|
||||
<div class="absolute top-2/4 w-full border-t border-gray-200"></div>
|
||||
<div class="absolute top-3/4 w-full border-t border-gray-200"></div>
|
||||
</div>
|
||||
|
||||
<!-- 柱状图 -->
|
||||
<div class="absolute bottom-0 w-full h-full flex items-end justify-around px-2">
|
||||
<div v-for="(point, index) in amountTrendPoints" :key="point.label"
|
||||
class="flex flex-col items-center w-full max-w-16">
|
||||
<!-- 金额柱子 -->
|
||||
<div class="w-8 bg-blue-500 rounded-t transition-all duration-500 hover:bg-blue-600 relative group"
|
||||
:style="`height: ${Math.max((point.value / (maxAmountTrend || 1)) * 180, 4)}px`">
|
||||
<!-- 悬停提示 -->
|
||||
<div
|
||||
class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
|
||||
金额: {{ formatAmount(point.value) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 时间标签 -->
|
||||
<div class="text-xs text-gray-600 mt-2 text-center transform -rotate-45 origin-center">
|
||||
{{ point.label.replace('最近', '近').replace('天', '日') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- X轴单位标签 -->
|
||||
<div class="text-center mb-4">
|
||||
<div class="text-xs text-gray-600 font-medium">时间维度</div>
|
||||
</div>
|
||||
|
||||
<!-- 图例 -->
|
||||
<div class="flex justify-center items-center space-x-4 text-xs">
|
||||
<div class="flex items-center">
|
||||
<div class="w-3 h-3 bg-blue-500 rounded mr-2"></div>
|
||||
<span class="text-gray-600">交易金额</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ECharts 图表 -->
|
||||
<div class="mb-6">
|
||||
<div ref="amountChartRef" :style="{ width: '100%', height: '300px' }"></div>
|
||||
</div>
|
||||
|
||||
<!-- 数值统计 -->
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div class="bg-[#ECF2FD] rounded-lg border border-[#CADAF9] p-4 mx-4">
|
||||
<div class="flex justify-between gap-4">
|
||||
<div class="flex-1 text-center">
|
||||
<div class="text-sm font-bold text-blue-600">{{ formatAmount(maxAmountTrend) }}</div>
|
||||
<div class="text-xs text-gray-600">峰值</div>
|
||||
<div class="text-[#999999]">峰值</div>
|
||||
<div class="text-[#2B79EE] font-bold">{{ formatAmount(maxAmountTrend) }}</div>
|
||||
</div>
|
||||
<div class="flex-1 text-center">
|
||||
<div class="text-sm font-bold text-green-600">{{ formatAmount(minAmountTrend) }}</div>
|
||||
<div class="text-xs text-gray-600">低值</div>
|
||||
<div class="text-[#999999]">低值</div>
|
||||
<div class="text-[#2B79EE] font-bold">{{ formatAmount(minAmountTrend) }}</div>
|
||||
</div>
|
||||
<div class="flex-1 text-center">
|
||||
<div class="text-sm font-bold text-purple-600">{{ formatAmount(avgAmountTrend) }}</div>
|
||||
<div class="text-xs text-gray-600">均值</div>
|
||||
<div class="text-[#999999]">均值</div>
|
||||
<div class="text-[#2B79EE] font-bold">{{ formatAmount(avgAmountTrend) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,90 +38,27 @@
|
||||
|
||||
<!-- 交易笔数趋势 -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center mr-2">
|
||||
<svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-base font-semibold text-gray-800">交易笔数趋势</h3>
|
||||
</div>
|
||||
<LTitle title="交易笔数趋势" class="mb-2" />
|
||||
|
||||
<!-- 图表展示 -->
|
||||
<div class="bg-gradient-to-br from-green-50 to-emerald-50 rounded-lg p-4 border border-green-200 mb-6">
|
||||
<div class="text-sm font-medium text-gray-800 mb-4 text-center">交易笔数趋势图</div>
|
||||
<div class="relative h-48 mb-4">
|
||||
<!-- Y轴标签 -->
|
||||
<div class="absolute left-0 top-0 h-full flex flex-col justify-between text-xs text-gray-500 pr-2">
|
||||
<span>{{ maxCountTrend }}笔</span>
|
||||
<span>{{ Math.round(maxCountTrend * 0.75) }}笔</span>
|
||||
<span>{{ Math.round(maxCountTrend * 0.5) }}笔</span>
|
||||
<span>{{ Math.round(maxCountTrend * 0.25) }}笔</span>
|
||||
<span>0笔</span>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 图表区域 -->
|
||||
<div class="ml-8 h-full relative border-l border-b border-gray-300">
|
||||
<!-- 水平网格线 -->
|
||||
<div class="absolute inset-0">
|
||||
<div class="absolute top-0 w-full border-t border-gray-200"></div>
|
||||
<div class="absolute top-1/4 w-full border-t border-gray-200"></div>
|
||||
<div class="absolute top-2/4 w-full border-t border-gray-200"></div>
|
||||
<div class="absolute top-3/4 w-full border-t border-gray-200"></div>
|
||||
</div>
|
||||
|
||||
<!-- 柱状图 -->
|
||||
<div class="absolute bottom-0 w-full h-full flex items-end justify-around px-2">
|
||||
<div v-for="(item, index) in countTrendData" :key="item.label"
|
||||
class="flex flex-col items-center w-full max-w-16">
|
||||
<!-- 笔数柱子 -->
|
||||
<div class="w-8 bg-green-500 rounded-t transition-all duration-500 hover:bg-green-600 relative group"
|
||||
:style="`height: ${Math.max((item.value / (maxCountTrend || 1)) * 180, 4)}px`">
|
||||
<!-- 悬停提示 -->
|
||||
<div
|
||||
class="absolute bottom-full left-1/2 transform -translate-x-1/2 mb-2 px-2 py-1 bg-gray-800 text-white text-xs rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap">
|
||||
笔数: {{ item.value }}笔
|
||||
</div>
|
||||
</div>
|
||||
<!-- 时间标签 -->
|
||||
<div class="text-xs text-gray-600 mt-2 text-center transform -rotate-45 origin-center">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- X轴单位标签 -->
|
||||
<div class="text-center mb-4">
|
||||
<div class="text-xs text-gray-600 font-medium">时间维度</div>
|
||||
</div>
|
||||
|
||||
<!-- 图例 -->
|
||||
<div class="flex justify-center items-center space-x-4 text-xs">
|
||||
<div class="flex items-center">
|
||||
<div class="w-3 h-3 bg-green-500 rounded mr-2"></div>
|
||||
<span class="text-gray-600">交易笔数</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ECharts 图表 -->
|
||||
<div class="mb-6">
|
||||
<div ref="countChartRef" :style="{ width: '100%', height: '300px' }"></div>
|
||||
</div>
|
||||
|
||||
<!-- 统计摘要 -->
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div class="bg-[#ECF2FD] rounded-lg border border-[#CADAF9] p-4 mx-4">
|
||||
<div class="flex justify-between gap-4">
|
||||
<div class="flex-1 text-center">
|
||||
<div class="text-sm font-bold text-green-600">{{ maxCountTrend }}</div>
|
||||
<div class="text-xs text-gray-600">峰值</div>
|
||||
<div class="text-[#999999]">峰值</div>
|
||||
<div class="text-[#2B79EE] font-bold">{{ maxCountTrend }} 笔</div>
|
||||
</div>
|
||||
<div class="flex-1 text-center">
|
||||
<div class="text-sm font-bold text-blue-600">{{ minCountTrend }}</div>
|
||||
<div class="text-xs text-gray-600">低值</div>
|
||||
<div class="text-[#999999]">低值</div>
|
||||
<div class="text-[#2B79EE] font-bold">{{ minCountTrend }} 笔</div>
|
||||
</div>
|
||||
<div class="flex-1 text-center">
|
||||
<div class="text-sm font-bold text-purple-600">{{ avgCountTrend.toFixed(0) }}</div>
|
||||
<div class="text-xs text-gray-600">均值</div>
|
||||
<div class="text-[#999999]">均值</div>
|
||||
<div class="text-[#2B79EE] font-bold">{{ avgCountTrend.toFixed(0) }} 笔</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,34 +66,24 @@
|
||||
|
||||
<!-- 还款成功率趋势 -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-6 h-6 bg-orange-500 rounded-full flex items-center justify-center mr-2">
|
||||
<svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-base font-semibold text-gray-800">还款成功率趋势</h3>
|
||||
</div>
|
||||
<LTitle title="还款成功率趋势" class="mb-2" />
|
||||
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-50 rounded-lg p-3" v-for="rate in successRateTrend" :key="rate.period">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="text-sm font-semibold text-gray-800">{{ rate.period }}</span>
|
||||
<span class="font-bold" :class="getRateClass(rate.rate)">
|
||||
{{ (rate.rate * 100).toFixed(1) }}%
|
||||
</span>
|
||||
</div>
|
||||
<div class="h-2 bg-gray-200 rounded-full overflow-hidden mb-2">
|
||||
<div class="h-full rounded-full transition-all duration-300"
|
||||
:style="`width: ${Math.max(rate.rate * 100, 2)}%`" :class="getRateBarClass(rate.rate)"></div>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs text-gray-600">
|
||||
<span>成功: {{ rate.success }}笔</span>
|
||||
<span>失败: {{ rate.failure }}笔</span>
|
||||
</div>
|
||||
<div class="space-y-3 px-4">
|
||||
<div class="bg-[#ECF2FD] rounded-lg border border-[#CADAF9] p-4" v-for="rate in successRateTrend"
|
||||
:key="rate.period">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span class="text-base font-bold text-[#333333]">{{ rate.period }}</span>
|
||||
<span class="text-base font-bold text-[#333333]">
|
||||
{{ (rate.rate * 100).toFixed(1) }}%
|
||||
</span>
|
||||
</div>
|
||||
<div class="h-2 bg-[#DBE6FC] rounded-full overflow-hidden mb-3">
|
||||
<div class="h-full rounded-full transition-all duration-300"
|
||||
:style="`width: ${Math.max(rate.rate * 100, 2)}%; background-color: #5079EA;`"></div>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs text-[#999999]">
|
||||
<span>成功: {{ rate.success }}笔</span>
|
||||
<span>失败: {{ rate.failure }}笔</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,55 +91,36 @@
|
||||
|
||||
<!-- 机构数量变化趋势 -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-6 h-6 bg-purple-500 rounded-full flex items-center justify-center mr-2">
|
||||
<svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M4 4a2 2 0 012-2h8a2 2 0 012 2v12a1 1 0 110 2h-3a1 1 0 01-1-1v-6a1 1 0 00-1-1H9a1 1 0 00-1 1v6a1 1 0 01-1 1H4a1 1 0 110-2V4zm3 1h2v2H7V5zm2 4H7v2h2V9zm2-4h2v2h-2V5zm2 4h-2v2h2V9z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-base font-semibold text-gray-800">机构数量变化</h3>
|
||||
</div>
|
||||
<LTitle title="机构数量变化" class="mb-2" />
|
||||
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div class="space-y-4">
|
||||
<div class="bg-gray-50 rounded-lg p-3" v-for="item in institutionTrendData" :key="item.period">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span class="text-sm font-semibold text-gray-800">{{ item.period }}</span>
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs font-medium">总数: {{ item.total
|
||||
}}</span>
|
||||
<span class="px-2 py-1 bg-green-100 text-green-800 rounded text-xs font-medium" v-if="item.new > 0">新增:
|
||||
{{ item.new }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-3 px-4">
|
||||
<div class="bg-[#ECF2FD] rounded-lg border border-[#CADAF9] p-4" v-for="item in institutionTrendData"
|
||||
:key="item.period">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span class="text-base font-bold text-[#333333]">{{ item.period }}</span>
|
||||
<span class="text-base font-bold text-[#333333]">总数: {{ item.total }}</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="h-2 bg-gray-200 rounded-full overflow-hidden flex">
|
||||
<div class="bg-blue-500 transition-all duration-300"
|
||||
:style="`width: ${Math.max(item.totalPercentage, 2)}%`"></div>
|
||||
<div class="bg-green-500 transition-all duration-300"
|
||||
:style="`width: ${Math.max(item.newPercentage, 1)}%`" v-if="item.new > 0"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-2 bg-[#DBE6FC] rounded-full overflow-hidden mb-3">
|
||||
<div class="h-full rounded-full transition-all duration-300"
|
||||
:style="`width: ${Math.max(item.totalPercentage, 2)}%; background-color: #5079EA;`"></div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2" :class="item.trendClass">
|
||||
<svg v-if="item.trendText === '增长趋势'" class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else-if="item.trendText === '下降趋势'" class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<span class="text-xs font-medium">{{ item.trendText }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-xs text-[#999999]">
|
||||
<svg v-if="item.trendText === '增长趋势'" class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M3.293 9.707a1 1 0 010-1.414l6-6a1 1 0 011.414 0l6 6a1 1 0 01-1.414 1.414L11 5.414V17a1 1 0 11-2 0V5.414L4.707 9.707a1 1 0 01-1.414 0z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else-if="item.trendText === '下降趋势'" class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M16.707 10.293a1 1 0 010 1.414l-6 6a1 1 0 01-1.414 0l-6-6a1 1 0 111.414-1.414L9 14.586V3a1 1 0 012 0v11.586l4.293-4.293a1 1 0 011.414 0z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else class="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<span>{{ item.trendText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -279,46 +128,58 @@
|
||||
|
||||
<!-- 新增机构金额分析 -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-6 h-6 bg-indigo-500 rounded-full flex items-center justify-center mr-2">
|
||||
<svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M8.433 7.418c.155-.103.346-.196.567-.267v1.698a2.305 2.305 0 01-.567-.267C8.07 8.34 8 8.114 8 8c0-.114.07-.34.433-.582zM11 12.849v-1.698c.22.071.412.164.567.267.364.243.433.468.433.582 0 .114-.07.34-.433.582a2.305 2.305 0 01-.567.267z" />
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a1 1 0 10-2 0v.092a4.535 4.535 0 00-1.676.662C6.602 6.234 6 7.009 6 8c0 .99.602 1.765 1.324 2.246.48.32 1.054.545 1.676.662v1.941c-.391-.127-.68-.317-.843-.504a1 1 0 10-1.51 1.31c.562.649 1.413 1.076 2.353 1.253V15a1 1 0 102 0v-.092a4.535 4.535 0 001.676-.662C13.398 13.766 14 12.991 14 12c0-.99-.602-1.765-1.324-2.246A4.535 4.535 0 0011 9.092V7.151c.391.127.68.317.843.504a1 1 0 101.511-1.31c-.563-.649-1.413-1.076-2.354-1.253V5z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-base font-semibold text-gray-800">新增机构金额分析</h3>
|
||||
</div>
|
||||
<LTitle title="新增机构金额分析" class="mb-2" />
|
||||
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div class="bg-gray-50 rounded-lg p-3" v-for="item in newInstitutionAmounts" :key="item.period">
|
||||
<div class="flex justify-between items-center mb-3">
|
||||
<span class="text-sm font-semibold text-gray-800">{{ item.period }}</span>
|
||||
<span class="text-sm font-bold text-indigo-600">{{ formatAmount(item.totalAmount) }}</span>
|
||||
</div>
|
||||
<!-- 标签页布局 -->
|
||||
<div class="">
|
||||
<div class="space-y-4">
|
||||
<div class="performance-item">
|
||||
<div class="loan-evaluation-wrap">
|
||||
<!-- 标签页 -->
|
||||
<div class="mb-3">
|
||||
<van-tabs v-model:active="activeAmountPeriod" line-width="20" line-height="2" color="#a22525"
|
||||
class="loan-evaluation-tabs">
|
||||
<van-tab v-for="item in newInstitutionAmounts" :key="item.period" :name="item.period"
|
||||
:title="item.period" />
|
||||
</van-tabs>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2 mb-3">
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-gray-600">最大单笔</span>
|
||||
<span class="font-medium text-gray-800">{{ formatAmount(item.maxAmount) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-gray-600">最小单笔</span>
|
||||
<span class="font-medium text-gray-800">{{ formatAmount(item.minAmount) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs">
|
||||
<span class="text-gray-600">平均金额</span>
|
||||
<span class="font-medium text-gray-800">{{ formatAmount(item.avgAmount) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 内容显示 -->
|
||||
<div class="loan-evaluation-content">
|
||||
<div class="space-y-3">
|
||||
<!-- 新增金额 -->
|
||||
<div class="space-y-2">
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">新增</span>
|
||||
<span class="text-sm font-bold text-gray-800">{{ formatAmount(currentAmountData.totalAmount)
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="h-2 rounded-full bg-gray-200">
|
||||
<div class="h-2 rounded-full transition-all duration-500"
|
||||
:style="`width: ${Math.max(currentAmountData.percentage, 2)}%; background-color: #10b981;`">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-1.5 bg-gray-200 rounded-full overflow-hidden">
|
||||
<div
|
||||
class="h-full bg-gradient-to-r from-indigo-500 to-indigo-600 rounded-full transition-all duration-300"
|
||||
:style="`width: ${Math.max(item.percentage, 2)}%`"></div>
|
||||
<!-- 最大单笔 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">最大单笔</span>
|
||||
<span class="text-sm font-bold text-gray-800">{{ formatAmount(currentAmountData.maxAmount) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 最小单笔 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">最小单笔</span>
|
||||
<span class="text-sm font-bold text-gray-800">{{ formatAmount(currentAmountData.minAmount) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- 平均金额 -->
|
||||
<div class="flex justify-between items-center">
|
||||
<span class="text-sm text-gray-600">平均金额</span>
|
||||
<span class="text-sm font-bold text-gray-800">{{ formatAmount(currentAmountData.avgAmount) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -327,85 +188,58 @@
|
||||
|
||||
<!-- 风险指标时间分布 -->
|
||||
<div class="mb-6">
|
||||
<div class="flex items-center mb-4">
|
||||
<div class="w-6 h-6 bg-red-500 rounded-full flex items-center justify-center mr-2">
|
||||
<svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-base font-semibold text-gray-800">风险时间分布</h3>
|
||||
</div>
|
||||
<LTitle title="风险指标时间分布" class="mb-2" />
|
||||
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-4">
|
||||
<div class="px-4">
|
||||
<!-- 风险事件列表 -->
|
||||
<div class="space-y-3 mb-4" v-if="riskTimeline.length > 0">
|
||||
<div class="flex items-center gap-3 p-2 rounded-lg" v-for="event in riskTimeline" :key="event.id" :class="event.riskLevel === 'high-risk' ? 'bg-red-50 border border-red-200' :
|
||||
event.riskLevel === 'medium-risk' ? 'bg-yellow-50 border border-yellow-200' :
|
||||
'bg-green-50 border border-green-200'">
|
||||
<div class="w-6 h-6 rounded-full flex items-center justify-center" :class="event.riskLevel === 'high-risk' ? 'bg-red-500' :
|
||||
event.riskLevel === 'medium-risk' ? 'bg-yellow-500' :
|
||||
'bg-green-500'">
|
||||
<svg v-if="event.riskLevel === 'high-risk'" class="w-3 h-3 text-white" fill="currentColor"
|
||||
viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else-if="event.riskLevel === 'medium-risk'" class="w-3 h-3 text-white" fill="currentColor"
|
||||
viewBox="0 0 20 20">
|
||||
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" />
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM4.332 8.027a6.012 6.012 0 011.912-2.706C6.512 5.73 6.974 6 7.5 6A1.5 1.5 0 019 7.5V8a2 2 0 004 0v-.5A1.5 1.5 0 0114.5 6c.526 0 .988-.27 1.256-.679a6.012 6.012 0 011.912 2.706A8.03 8.03 0 0118 10a8.03 8.03 0 01-.332 2.027 6.012 6.012 0 01-1.912 2.706c-.268.41-.73.679-1.256.679A1.5 1.5 0 0113 13.5V13a2 2 0 10-4 0v.5A1.5 1.5 0 017.5 14c-.526 0-.988.27-1.256.679a6.012 6.012 0 01-1.912-2.706A8.03 8.03 0 014 10a8.03 8.03 0 01.332-2.027z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg v-else class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
<div class="flex items-center gap-3 p-3 rounded-lg" v-for="event in riskTimeline" :key="event.id"
|
||||
:class="getRiskEventCardClass(event.riskLevel)">
|
||||
<div class="w-10 h-10 flex-shrink-0">
|
||||
<img :src="getRiskEventIcon(event.riskLevel)" :alt="event.title" class="w-10 h-10 object-contain" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<h4 class="text-sm font-semibold text-gray-800">{{ event.title }}</h4>
|
||||
<p class="text-xs text-gray-600">{{ event.description }}</p>
|
||||
<h4 class="text-sm font-bold text-[#333333]">{{ event.title }}</h4>
|
||||
<p class="text-xs text-[#999999] mt-1">{{ event.description }}</p>
|
||||
</div>
|
||||
<span class="text-xs text-gray-500">{{ event.timeAgo }}</span>
|
||||
<span class="text-xs text-[#999999] whitespace-nowrap ml-2">{{ event.timeAgo }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计摘要 -->
|
||||
<div class="bg-gray-50 rounded-lg p-3 border-t border-gray-200">
|
||||
<div class="bg-[#F9F9F9] rounded-lg border p-4 border-[#EEEEEE]">
|
||||
<div class="flex justify-between text-center">
|
||||
<div class="flex-1">
|
||||
<div class="text-lg font-bold text-red-600">{{ riskEventCounts.high }}</div>
|
||||
<div class="text-xs text-gray-600">高风险事件</div>
|
||||
<div class="text-lg font-bold text-red-600">{{ riskEventCounts.high }}</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-lg font-bold text-yellow-600">{{ riskEventCounts.medium }}</div>
|
||||
<div class="text-xs text-gray-600">中风险事件</div>
|
||||
<div class="text-lg font-bold text-yellow-600">{{ riskEventCounts.medium }}</div>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<div class="text-lg font-bold text-green-600">{{ riskEventCounts.low }}</div>
|
||||
<div class="text-xs text-gray-600">低风险事件</div>
|
||||
<div class="text-lg font-bold text-green-600">{{ riskEventCounts.low }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 温馨提示 -->
|
||||
<LRemark content="时间趋势分析通过可视化图表展示申请人的交易金额趋势、申请频率趋势和风险变化趋势。图表数据基于历史申请记录生成,可以直观反映申请行为的时间规律性。建议重点关注异常波动时期,如短期内交易金额大幅增长或申请频率异常增高的情况。趋势分析有助于识别周期性风险模式和预测未来风险走向。数据统计周期通常为近12个月。" />
|
||||
<LRemark
|
||||
content="时间趋势分析通过可视化图表展示申请人的交易金额趋势、申请频率趋势和风险变化趋势。图表数据基于历史申请记录生成,可以直观反映申请行为的时间规律性。建议重点关注异常波动时期,如短期内交易金额大幅增长或申请频率异常增高的情况。趋势分析有助于识别周期性风险模式和预测未来风险走向。数据统计周期通常为近12个月。" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LTitle from '@/components/LTitle.vue'
|
||||
import LRemark from '@/components/LRemark.vue'
|
||||
import * as echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'TimeTrendAnalysis',
|
||||
@@ -419,6 +253,13 @@ export default {
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
amountChartInstance: null,
|
||||
countChartInstance: null,
|
||||
activeAmountPeriod: '最近90天新增'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 交易金额趋势点
|
||||
amountTrendPoints() {
|
||||
@@ -613,21 +454,21 @@ export default {
|
||||
newInstitutionAmounts() {
|
||||
const amounts = [
|
||||
{
|
||||
period: '最近90天新增',
|
||||
period: '最近90天',
|
||||
totalAmount: this.parseIntervalValue(this.data.xyp_t01aczfzz),
|
||||
maxAmount: this.parseIntervalValue(this.data.xyp_t01aazfzz),
|
||||
minAmount: this.parseIntervalValue(this.data.xyp_t01abzfzz),
|
||||
avgAmount: this.parseIntervalValue(this.data.xyp_t01adzfbz)
|
||||
},
|
||||
{
|
||||
period: '最近180天新增',
|
||||
period: '最近180天',
|
||||
totalAmount: this.parseIntervalValue(this.data.xyp_t01aczgzz),
|
||||
maxAmount: this.parseIntervalValue(this.data.xyp_t01aazgzc),
|
||||
minAmount: this.parseIntervalValue(this.data.xyp_t01abzgzc),
|
||||
avgAmount: this.parseIntervalValue(this.data.xyp_t01adzgzc)
|
||||
},
|
||||
{
|
||||
period: '最近360天新增',
|
||||
period: '最近360天',
|
||||
totalAmount: this.parseIntervalValue(this.data.xyp_t01achzzc),
|
||||
maxAmount: this.parseIntervalValue(this.data.xyp_t01aazhzz),
|
||||
minAmount: 0, // 没有对应的最小值字段
|
||||
@@ -650,10 +491,227 @@ export default {
|
||||
else counts.low++
|
||||
})
|
||||
return counts
|
||||
},
|
||||
|
||||
// 当前选中的金额数据
|
||||
currentAmountData() {
|
||||
return this.newInstitutionAmounts.find(item => item.period === this.activeAmountPeriod) || this.newInstitutionAmounts[0]
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.initAmountChart()
|
||||
this.initCountChart()
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
},
|
||||
|
||||
beforeUnmount() {
|
||||
if (this.amountChartInstance) {
|
||||
this.amountChartInstance.dispose()
|
||||
this.amountChartInstance = null
|
||||
}
|
||||
if (this.countChartInstance) {
|
||||
this.countChartInstance.dispose()
|
||||
this.countChartInstance = null
|
||||
}
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
},
|
||||
|
||||
watch: {
|
||||
amountTrendPoints() {
|
||||
this.updateAmountChart()
|
||||
},
|
||||
countTrendData() {
|
||||
this.updateCountChart()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
initAmountChart() {
|
||||
if (!this.$refs.amountChartRef) return
|
||||
this.amountChartInstance = echarts.init(this.$refs.amountChartRef)
|
||||
this.updateAmountChart()
|
||||
},
|
||||
|
||||
updateAmountChart() {
|
||||
if (!this.amountChartInstance) return
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
formatter: (params) => {
|
||||
const data = params[0]
|
||||
const point = this.amountTrendPoints[data.dataIndex]
|
||||
return `${data.name}<br/>金额: ${this.formatAmount(point.value)}`
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
top: '5%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: this.amountTrendPoints.map(p => p.label.replace('最近', '近').replace('天', '日')),
|
||||
axisLabel: {
|
||||
rotate: 45,
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#e0e0e0'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: (value) => {
|
||||
return this.formatAmount(value)
|
||||
},
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#e0e0e0'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '交易金额',
|
||||
type: 'bar',
|
||||
data: this.amountTrendPoints.map(p => p.value),
|
||||
barWidth: '25%',
|
||||
barMinHeight: 2,
|
||||
itemStyle: {
|
||||
color: '#10b981',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: (params) => {
|
||||
return this.formatAmount(params.value)
|
||||
},
|
||||
fontSize: 11,
|
||||
color: '#333'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.amountChartInstance.setOption(option)
|
||||
},
|
||||
|
||||
initCountChart() {
|
||||
if (!this.$refs.countChartRef) return
|
||||
this.countChartInstance = echarts.init(this.$refs.countChartRef)
|
||||
this.updateCountChart()
|
||||
},
|
||||
|
||||
updateCountChart() {
|
||||
if (!this.countChartInstance) return
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
formatter: (params) => {
|
||||
const data = params[0]
|
||||
const item = this.countTrendData[data.dataIndex]
|
||||
return `${data.name}<br/>笔数: ${item.value}笔`
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
top: '5%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: this.countTrendData.map(d => d.label),
|
||||
axisLabel: {
|
||||
rotate: 45,
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#e0e0e0'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
formatter: '{value}笔',
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#e0e0e0'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '交易笔数',
|
||||
type: 'bar',
|
||||
data: this.countTrendData.map(d => d.value),
|
||||
barWidth: '25%',
|
||||
barMinHeight: 2,
|
||||
itemStyle: {
|
||||
color: '#10b981',
|
||||
borderRadius: [4, 4, 0, 0]
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: (params) => {
|
||||
return `${params.value}笔`
|
||||
},
|
||||
fontSize: 11,
|
||||
color: '#333'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.countChartInstance.setOption(option)
|
||||
},
|
||||
|
||||
handleResize() {
|
||||
if (this.amountChartInstance) {
|
||||
this.amountChartInstance.resize()
|
||||
}
|
||||
if (this.countChartInstance) {
|
||||
this.countChartInstance.resize()
|
||||
}
|
||||
},
|
||||
|
||||
parseIntervalValue(value) {
|
||||
if (!value || value === '' || value === '-1') return 0
|
||||
const num = parseInt(value)
|
||||
@@ -696,7 +754,17 @@ export default {
|
||||
return 'bg-red-500'
|
||||
},
|
||||
|
||||
getRiskEventCardClass(riskLevel) {
|
||||
if (riskLevel === 'high-risk') return 'bg-[#FFF0F0] border border-red-200'
|
||||
if (riskLevel === 'medium-risk') return 'bg-[#FFF8E7] border border-[#F5D980]'
|
||||
return 'bg-[#ECF9EF] border border-[#CAECD3]'
|
||||
},
|
||||
|
||||
getRiskEventIcon(riskLevel) {
|
||||
if (riskLevel === 'high-risk') return new URL('@/assets/images/report/gfx.png', import.meta.url).href
|
||||
if (riskLevel === 'medium-risk') return new URL('@/assets/images/report/zfx.png', import.meta.url).href
|
||||
return new URL('@/assets/images/report/zq.png', import.meta.url).href
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -721,6 +789,50 @@ export default {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 标签页样式 */
|
||||
.loan-evaluation-tabs {}
|
||||
|
||||
.loan-evaluation-tabs :deep(.van-tabs__wrap) {
|
||||
height: 32px !important;
|
||||
background-color: transparent !important;
|
||||
padding: 0 !important;
|
||||
border-bottom: 1px solid #DDDDDD !important;
|
||||
}
|
||||
|
||||
.loan-evaluation-tabs :deep(.van-tabs__nav) {
|
||||
background-color: transparent !important;
|
||||
gap: 0;
|
||||
height: 32px !important;
|
||||
}
|
||||
|
||||
.loan-evaluation-tabs :deep(.van-tab) {
|
||||
color: #999999 !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.loan-evaluation-tabs :deep(.van-tab--active) {
|
||||
color: var(--van-theme-primary) !important;
|
||||
background-color: unset !important;
|
||||
}
|
||||
|
||||
.loan-evaluation-tabs :deep(.van-tabs__line) {
|
||||
height: 2px !important;
|
||||
border-radius: 1px !important;
|
||||
}
|
||||
|
||||
/* 内容区域样式 */
|
||||
.loan-evaluation-wrap {
|
||||
@apply mx-4 my-1;
|
||||
border: 1px solid #DDDDDD;
|
||||
background-color: #F9F9F9;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.loan-evaluation-content {
|
||||
padding: 8px 16px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.time-trend-analysis {
|
||||
|
||||
Reference in New Issue
Block a user