927 lines
34 KiB
Vue
927 lines
34 KiB
Vue
<script setup>
|
||
import * as echarts from "echarts"; // 引入 ECharts
|
||
import LTable from "@/components/LTable.vue";
|
||
import LTitle from "@/components/LTitle.vue";
|
||
import { ref, onMounted, watch, computed } from "vue";
|
||
|
||
const props = defineProps({
|
||
data: {
|
||
type: Object,
|
||
required: true,
|
||
},
|
||
mode: {
|
||
type: String,
|
||
default: "idOnly", // 'full' 或 'idOnly'
|
||
validator: (value) => ["full", "idOnly"].includes(value),
|
||
},
|
||
});
|
||
|
||
const { data, mode } = props;
|
||
|
||
// 数据类型切换
|
||
const dataType = ref("id"); // 'id' 或 'cell'
|
||
|
||
// 监听mode变化,如果是idOnly模式,强制selectedDataSource为"id"
|
||
watch(
|
||
() => props.mode,
|
||
(newMode) => {
|
||
if (newMode === "idOnly") {
|
||
dataType.value = "id";
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
);
|
||
|
||
// 图表实例
|
||
const borrowChartRef = ref(null);
|
||
const repayChartRef = ref(null);
|
||
const trendChartRef = ref(null);
|
||
const borrowChartInstance = ref(null);
|
||
const repayChartInstance = ref(null);
|
||
const trendChartInstance = ref(null);
|
||
|
||
// 表格数据
|
||
const borrowTable = ref([]);
|
||
const institutionTable = ref([]);
|
||
|
||
// 获取参考日期
|
||
const getReferenceDate = computed(() => {
|
||
const prefix = `tl_${dataType.value}`;
|
||
const dateStr = data[`${prefix}_eletail_lasttime`];
|
||
if (dateStr) {
|
||
// 将字符串转为日期对象
|
||
return new Date(dateStr);
|
||
}
|
||
// 如果没有日期信息,则使用当前日期
|
||
return new Date();
|
||
});
|
||
|
||
// 根据相对月份获取实际年月
|
||
function getActualMonthYear(monthsBack) {
|
||
const month = monthsBack.replace("m", "");
|
||
if (month === "12") {
|
||
return "近1年";
|
||
}
|
||
return `近${month}月`;
|
||
// const refDate = new Date(getReferenceDate.value);
|
||
// refDate.setMonth(refDate.getMonth() - monthsBack);
|
||
|
||
// const year = refDate.getFullYear();
|
||
// const month = refDate.getMonth() + 1; // JavaScript月份从0开始
|
||
|
||
// return `${year}年${month}月`;
|
||
}
|
||
function getActualMonthYearT(monthsBack) {
|
||
if (monthsBack === "t0") {
|
||
return "1年内";
|
||
}
|
||
return `近${monthsBack.replace("t", "")}月`;
|
||
}
|
||
// 金额转换函数
|
||
function getLevelAmount(level) {
|
||
const levelNum = Number(level) || 1;
|
||
const baseAmount = 3000;
|
||
return baseAmount * (levelNum - 1);
|
||
}
|
||
|
||
// 等级范围转换
|
||
function getLevelRange(level) {
|
||
const levelNum = Number(level) || 1;
|
||
const baseAmount = 3000;
|
||
const lowerLimit = baseAmount * (levelNum - 1);
|
||
const upperLimit = baseAmount * levelNum;
|
||
return `${lowerLimit}元 - ${upperLimit}元`;
|
||
}
|
||
|
||
// 计算借贷金额数据(按月)
|
||
const monthlyBorrowData = computed(() => {
|
||
const months = ["m1", "m3", "m6", "m9", "m12"];
|
||
|
||
const prefix = `tl_${dataType.value}`;
|
||
|
||
return months
|
||
.map((month, index) => {
|
||
const borrowKey = `${prefix}_${month}_nbank_passlendamt`;
|
||
const borrowAmount = getLevelAmount(data[borrowKey]);
|
||
console.log(borrowKey, borrowAmount);
|
||
return {
|
||
month: getActualMonthYear(month),
|
||
amount: borrowAmount,
|
||
displayAmount: formatAmount(borrowAmount),
|
||
level: data[borrowKey] || "0",
|
||
levelRange: getLevelRange(data[borrowKey]),
|
||
};
|
||
})
|
||
.reverse();
|
||
});
|
||
// 计算应还金额数据(按月)
|
||
const monthlyRepayData = computed(() => {
|
||
const months = ["m1", "m3", "m6", "m9", "m12"];
|
||
const prefix = `tl_${dataType.value}`;
|
||
|
||
return months
|
||
.map((month, index) => {
|
||
const repayKey = `${prefix}_${month}_nbank_reamt`;
|
||
const repayAmount = getLevelAmount(data[repayKey]);
|
||
return {
|
||
month: getActualMonthYear(month),
|
||
amount: repayAmount,
|
||
displayAmount: formatAmount(repayAmount),
|
||
level: data[repayKey] || "0",
|
||
levelRange: getLevelRange(data[repayKey]),
|
||
};
|
||
})
|
||
.reverse();
|
||
});
|
||
|
||
// 计算机构数和借还差值(按月)
|
||
const monthlyInstitutionData = computed(() => {
|
||
const months = ["m1", "m3", "m6", "m9", "m12"];
|
||
const prefix = `tl_${dataType.value}`;
|
||
|
||
return months
|
||
.map((month, index) => {
|
||
const orgKey = `${prefix}_${month}_nbank_passorg`;
|
||
const numKey = `${prefix}_${month}_nbank_passnum`;
|
||
const borrowKey = `${prefix}_${month}_nbank_passlendamt`;
|
||
const repayKey = `${prefix}_${month}_nbank_reamt`;
|
||
|
||
const orgCount = Number(data[orgKey] || 0);
|
||
const loanCount = Number(data[numKey] || 0);
|
||
const borrowAmount = getLevelAmount(data[borrowKey]);
|
||
const repayAmount = getLevelAmount(data[repayKey]);
|
||
|
||
let ratio = 0;
|
||
if (borrowAmount > 0) {
|
||
ratio = ((repayAmount / borrowAmount) * 100).toFixed(2);
|
||
}
|
||
|
||
return {
|
||
month: getActualMonthYear(month),
|
||
orgCount,
|
||
loanCount,
|
||
borrowAmount: formatAmount(borrowAmount),
|
||
repayAmount: formatAmount(repayAmount),
|
||
ratio: `${ratio}%`,
|
||
};
|
||
})
|
||
.reverse();
|
||
});
|
||
|
||
// 计算近期借贷趋势数据(3月、6月、9月、12月)
|
||
const recentBorrowTrends = computed(() => {
|
||
const months = ["t0"];
|
||
const prefix = `tl_${dataType.value}`;
|
||
|
||
return months
|
||
.map((month, index) => {
|
||
const orgKey = `${prefix}_${month}_nbank_org`;
|
||
const numKey = `${prefix}_${month}_nbank_num`;
|
||
const borrowKey = `${prefix}_${month}_nbank_lendamt`;
|
||
const repayKey = `${prefix}_${month}_nbank_reamt`;
|
||
|
||
const orgCount = Number(data[orgKey] || 0);
|
||
const loanCount = Number(data[numKey] || 0);
|
||
const borrowAmount = getLevelAmount(data[borrowKey]);
|
||
const repayAmount = getLevelAmount(data[repayKey]);
|
||
|
||
let ratio = 0;
|
||
if (borrowAmount > 0) {
|
||
ratio = ((repayAmount / borrowAmount) * 100).toFixed(2);
|
||
}
|
||
|
||
return {
|
||
month: getActualMonthYearT(month),
|
||
orgCount,
|
||
loanCount,
|
||
borrowAmount: formatAmount(borrowAmount),
|
||
repayAmount: formatAmount(repayAmount),
|
||
ratio: `${ratio}%`,
|
||
};
|
||
})
|
||
.reverse();
|
||
});
|
||
|
||
// 获取最近一次借贷信息
|
||
const lastLoanInfo = computed(() => {
|
||
const prefix = `tl_${dataType.value}`;
|
||
return {
|
||
time: data[`${prefix}_eletail_lasttime`] || "--",
|
||
type: getLoanTypeDesc(data[`${prefix}_eletail_lasttype`]),
|
||
count: Number(data[`${prefix}_eletail_num`] || 0),
|
||
orgCount: Number(data[`${prefix}_eletail_org`] || 0),
|
||
};
|
||
});
|
||
|
||
// 获取借贷类型描述
|
||
function getLoanTypeDesc(type) {
|
||
const typeMap = {
|
||
a: "传统银行",
|
||
b: "网络零售银行",
|
||
c: "持牌网络小贷",
|
||
d: "持牌小贷",
|
||
e: "持牌消费金融",
|
||
f: "持牌融资租赁",
|
||
g: "持牌汽车金融",
|
||
h: "其他",
|
||
};
|
||
return typeMap[type] || "未知";
|
||
}
|
||
|
||
// 金额格式化
|
||
function formatAmount(amount) {
|
||
return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||
}
|
||
|
||
// 借贷行为总结
|
||
const behaviorSummary = computed(() => {
|
||
const prefix = `tl_${dataType.value}`;
|
||
|
||
// 获取近一年数据
|
||
const yearData = recentBorrowTrends.value[0];
|
||
|
||
// 计算平均月申请次数
|
||
const avgMonthlyApplications = (yearData.loanCount / 12).toFixed(1);
|
||
|
||
// 计算平均月审批额度
|
||
const totalBorrowAmount = Number(yearData.borrowAmount.replace(/,/g, ""));
|
||
const avgMonthlyAmount = (totalBorrowAmount / 12).toFixed(0);
|
||
|
||
// 计算平均月应还金额
|
||
const totalRepayAmount = Number(yearData.repayAmount.replace(/,/g, ""));
|
||
const avgMonthlyRepay = (totalRepayAmount / 12).toFixed(0);
|
||
|
||
// 计算还款比例
|
||
const repayRatio =
|
||
totalBorrowAmount > 0
|
||
? ((totalRepayAmount / totalBorrowAmount) * 100).toFixed(1)
|
||
: 0;
|
||
|
||
// 风险评估
|
||
let riskLevel = "低";
|
||
let riskDesc = "借贷行为健康,借贷金额合理";
|
||
|
||
// 基于机构数评估
|
||
if (yearData.orgCount > 5) {
|
||
riskLevel = "高";
|
||
riskDesc = "多头借贷风险较高,借贷机构过多";
|
||
} else if (yearData.orgCount > 3) {
|
||
riskLevel = "中";
|
||
riskDesc = "存在多头借贷风险,借贷机构较多";
|
||
}
|
||
|
||
// 基于月均申请次数评估
|
||
if (avgMonthlyApplications > 3) {
|
||
riskLevel = riskLevel === "低" ? "中" : "高";
|
||
riskDesc += ",月均申请次数较多";
|
||
}
|
||
|
||
// 基于还款比例评估
|
||
if (repayRatio < 50) {
|
||
riskLevel = riskLevel === "低" ? "中" : "高";
|
||
riskDesc += ",还款比例较低";
|
||
}
|
||
|
||
return {
|
||
totalApplications: yearData.loanCount,
|
||
totalOrgs: yearData.orgCount,
|
||
totalAmount: formatAmount(totalBorrowAmount),
|
||
avgMonthlyApplications,
|
||
avgMonthlyAmount: formatAmount(avgMonthlyAmount),
|
||
avgMonthlyRepay: formatAmount(avgMonthlyRepay),
|
||
repayRatio: `${repayRatio}%`,
|
||
riskLevel,
|
||
riskDesc,
|
||
};
|
||
});
|
||
|
||
// 绘制借贷金额图表
|
||
function drawBorrowChart() {
|
||
if (!borrowChartRef.value) return;
|
||
|
||
if (!borrowChartInstance.value) {
|
||
borrowChartInstance.value = echarts.init(borrowChartRef.value);
|
||
}
|
||
|
||
const chartData = monthlyBorrowData.value;
|
||
const option = {
|
||
title: {
|
||
text: "月度审批额度(元)",
|
||
left: "center",
|
||
textStyle: {
|
||
fontWeight: "bold",
|
||
fontSize: 16,
|
||
},
|
||
},
|
||
tooltip: {
|
||
trigger: "axis",
|
||
formatter: function (params) {
|
||
const data = params[0].data;
|
||
return `${params[0].name}<br/>${params[0].seriesName}: ${data.displayAmount}<br/>等级: ${data.level} (${data.levelRange})`;
|
||
},
|
||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||
borderColor: "#5470C6",
|
||
borderWidth: 1,
|
||
textStyle: {
|
||
color: "#333",
|
||
},
|
||
shadowBlur: 10,
|
||
shadowColor: "rgba(0, 0, 0, 0.2)",
|
||
},
|
||
grid: {
|
||
left: "5%",
|
||
right: "5%",
|
||
bottom: "0%",
|
||
containLabel: true,
|
||
},
|
||
xAxis: {
|
||
type: "category",
|
||
data: chartData.map((item) => item.month),
|
||
axisLabel: {
|
||
interval: 0,
|
||
rotate: 45,
|
||
fontWeight: "bold",
|
||
margin: 15,
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: "#999",
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: "value",
|
||
name: "金额(元)",
|
||
nameTextStyle: {
|
||
fontWeight: "bold",
|
||
},
|
||
splitLine: {
|
||
lineStyle: {
|
||
type: "dashed",
|
||
opacity: 0.6,
|
||
},
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
name: "审批额度",
|
||
type: "bar",
|
||
data: chartData.map((item) => ({
|
||
value: item.amount,
|
||
displayAmount: item.displayAmount,
|
||
level: item.level,
|
||
levelRange: item.levelRange,
|
||
})),
|
||
itemStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: "#83bff6" },
|
||
{ offset: 0.5, color: "#5470C6" },
|
||
{ offset: 1, color: "#4662a4" },
|
||
]),
|
||
borderRadius: [5, 5, 0, 0],
|
||
},
|
||
emphasis: {
|
||
itemStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: "#5470C6" },
|
||
{ offset: 0.7, color: "#4662a4" },
|
||
{ offset: 1, color: "#3c5390" },
|
||
]),
|
||
},
|
||
},
|
||
barWidth: "60%",
|
||
showBackground: true,
|
||
backgroundStyle: {
|
||
color: "rgba(180, 180, 180, 0.1)",
|
||
},
|
||
},
|
||
],
|
||
animation: true,
|
||
};
|
||
|
||
borrowChartInstance.value.setOption(option);
|
||
}
|
||
|
||
// 绘制应还金额图表
|
||
function drawRepayChart() {
|
||
if (!repayChartRef.value) return;
|
||
|
||
if (!repayChartInstance.value) {
|
||
repayChartInstance.value = echarts.init(repayChartRef.value);
|
||
}
|
||
|
||
const chartData = monthlyRepayData.value;
|
||
const option = {
|
||
title: {
|
||
text: "月度应还金额(元)",
|
||
left: "center",
|
||
textStyle: {
|
||
fontWeight: "bold",
|
||
fontSize: 16,
|
||
},
|
||
},
|
||
tooltip: {
|
||
trigger: "axis",
|
||
formatter: function (params) {
|
||
const data = params[0].data;
|
||
return `${params[0].name}<br/>${params[0].seriesName}: ${data.displayAmount}<br/>等级: ${data.level} (${data.levelRange})`;
|
||
},
|
||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||
borderColor: "#91CC75",
|
||
borderWidth: 1,
|
||
textStyle: {
|
||
color: "#333",
|
||
},
|
||
shadowBlur: 10,
|
||
shadowColor: "rgba(0, 0, 0, 0.2)",
|
||
},
|
||
grid: {
|
||
left: "5%",
|
||
right: "5%",
|
||
bottom: "15%",
|
||
containLabel: true,
|
||
},
|
||
xAxis: {
|
||
type: "category",
|
||
data: chartData.map((item) => item.month),
|
||
axisLabel: {
|
||
interval: 0,
|
||
rotate: 45,
|
||
fontWeight: "bold",
|
||
margin: 15,
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: "#999",
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: "value",
|
||
name: "金额(元)",
|
||
nameTextStyle: {
|
||
fontWeight: "bold",
|
||
},
|
||
splitLine: {
|
||
lineStyle: {
|
||
type: "dashed",
|
||
opacity: 0.6,
|
||
},
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
name: "应还金额",
|
||
type: "bar",
|
||
data: chartData.map((item) => ({
|
||
value: item.amount,
|
||
displayAmount: item.displayAmount,
|
||
level: item.level,
|
||
levelRange: item.levelRange,
|
||
})),
|
||
itemStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: "#b8e986" },
|
||
{ offset: 0.5, color: "#91CC75" },
|
||
{ offset: 1, color: "#7cb362" },
|
||
]),
|
||
borderRadius: [5, 5, 0, 0],
|
||
},
|
||
emphasis: {
|
||
itemStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: "#91CC75" },
|
||
{ offset: 0.7, color: "#7cb362" },
|
||
{ offset: 1, color: "#6a9c53" },
|
||
]),
|
||
},
|
||
},
|
||
barWidth: "60%",
|
||
showBackground: true,
|
||
backgroundStyle: {
|
||
color: "rgba(180, 180, 180, 0.1)",
|
||
},
|
||
},
|
||
],
|
||
animation: true,
|
||
};
|
||
|
||
repayChartInstance.value.setOption(option);
|
||
}
|
||
|
||
// 绘制借贷应还趋势对比图
|
||
function drawTrendChart() {
|
||
if (!trendChartRef.value) return;
|
||
|
||
if (!trendChartInstance.value) {
|
||
trendChartInstance.value = echarts.init(trendChartRef.value);
|
||
}
|
||
|
||
const borrowData = monthlyBorrowData.value;
|
||
const repayData = monthlyRepayData.value;
|
||
|
||
const option = {
|
||
title: {
|
||
text: "审批额度与应还金额趋势对比",
|
||
left: "center",
|
||
textStyle: {
|
||
fontWeight: "bold",
|
||
fontSize: 16,
|
||
},
|
||
},
|
||
tooltip: {
|
||
trigger: "axis",
|
||
backgroundColor: "rgba(255, 255, 255, 0.8)",
|
||
borderColor: "#ccc",
|
||
borderWidth: 1,
|
||
textStyle: {
|
||
color: "#333",
|
||
},
|
||
shadowBlur: 10,
|
||
shadowColor: "rgba(0, 0, 0, 0.2)",
|
||
},
|
||
legend: {
|
||
data: ["审批额度", "应还金额"],
|
||
top: 30,
|
||
textStyle: {
|
||
fontWeight: "bold",
|
||
},
|
||
},
|
||
grid: {
|
||
left: "5%",
|
||
right: "5%",
|
||
bottom: "15%",
|
||
containLabel: true,
|
||
},
|
||
xAxis: {
|
||
type: "category",
|
||
data: borrowData.map((item) => item.month),
|
||
axisLabel: {
|
||
interval: 0,
|
||
rotate: 45,
|
||
fontWeight: "bold",
|
||
margin: 15,
|
||
},
|
||
axisLine: {
|
||
lineStyle: {
|
||
color: "#999",
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: "value",
|
||
name: "金额(元)",
|
||
nameTextStyle: {
|
||
fontWeight: "bold",
|
||
},
|
||
splitLine: {
|
||
lineStyle: {
|
||
type: "dashed",
|
||
opacity: 0.6,
|
||
},
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
name: "审批额度",
|
||
type: "line",
|
||
data: borrowData.map((item) => item.amount),
|
||
smooth: true,
|
||
symbol: "emptyCircle",
|
||
symbolSize: 8,
|
||
lineStyle: {
|
||
width: 3,
|
||
shadowColor: "rgba(0, 0, 0, 0.3)",
|
||
shadowBlur: 10,
|
||
shadowOffsetY: 8,
|
||
},
|
||
itemStyle: {
|
||
color: "#5470C6",
|
||
borderWidth: 2,
|
||
},
|
||
areaStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: "rgba(84, 112, 198, 0.5)" },
|
||
{ offset: 1, color: "rgba(84, 112, 198, 0.1)" },
|
||
]),
|
||
},
|
||
},
|
||
{
|
||
name: "应还金额",
|
||
type: "line",
|
||
data: repayData.map((item) => item.amount),
|
||
smooth: true,
|
||
symbol: "emptyCircle",
|
||
symbolSize: 8,
|
||
lineStyle: {
|
||
width: 3,
|
||
shadowColor: "rgba(0, 0, 0, 0.3)",
|
||
shadowBlur: 10,
|
||
shadowOffsetY: 8,
|
||
},
|
||
itemStyle: {
|
||
color: "#91CC75",
|
||
borderWidth: 2,
|
||
},
|
||
areaStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: "rgba(145, 204, 117, 0.5)" },
|
||
{ offset: 1, color: "rgba(145, 204, 117, 0.1)" },
|
||
]),
|
||
},
|
||
},
|
||
],
|
||
animation: true,
|
||
};
|
||
|
||
trendChartInstance.value.setOption(option);
|
||
}
|
||
|
||
// 监听数据类型变化
|
||
watch(dataType, () => {
|
||
drawBorrowChart();
|
||
drawRepayChart();
|
||
drawTrendChart();
|
||
});
|
||
|
||
// 初始化所有图表
|
||
function initCharts() {
|
||
drawBorrowChart();
|
||
drawRepayChart();
|
||
drawTrendChart();
|
||
}
|
||
|
||
// 窗口大小变化时重绘图表
|
||
function handleResize() {
|
||
if (borrowChartInstance.value) borrowChartInstance.value.resize();
|
||
if (repayChartInstance.value) repayChartInstance.value.resize();
|
||
if (trendChartInstance.value) trendChartInstance.value.resize();
|
||
}
|
||
|
||
onMounted(() => {
|
||
initCharts();
|
||
window.addEventListener("resize", handleResize);
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
window.removeEventListener("resize", handleResize);
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="card">
|
||
<div class="flex flex-col gap-y-4">
|
||
<!-- 数据类型切换 -->
|
||
<div class="p-6 bg-white rounded-lg shadow-sm border border-gray-100 relative overflow-hidden mb-4">
|
||
<!-- 背景装饰元素 -->
|
||
<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 v-if="mode === 'full'" class="flex flex-wrap gap-6 w-full md:w-auto">
|
||
<div class="flex-1 md:flex-none flex rounded-md shadow-sm relative">
|
||
<button type="button"
|
||
class="flex-1 py-2 px-4 text-sm font-medium rounded-l-md border transition-all duration-200 flex items-center"
|
||
:class="[
|
||
dataType === '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="dataType = 'id'">
|
||
<svg v-if="dataType === '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 type="button"
|
||
class="flex-1 py-2 px-4 text-sm font-medium rounded-r-md border transition-all duration-200 flex items-center"
|
||
:class="[
|
||
dataType === '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="dataType = 'cell'">
|
||
<svg v-if="dataType === '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>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 数据类型说明 -->
|
||
<div v-if="mode === 'full'" 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>
|
||
|
||
<!-- 借贷金额图表 -->
|
||
<LTitle title="近期审批额度" type="blue-green" />
|
||
<div ref="borrowChartRef" class="chart-container"></div>
|
||
|
||
<!-- 应还金额图表 -->
|
||
<!--<LTitle title="月度应还金额" type="blue-green" />
|
||
<div ref="repayChartRef" class="chart-container"></div> -->
|
||
|
||
<!-- 借贷应还趋势对比 -->
|
||
<!--<LTitle title="审批额度与应还金额趋势对比" type="blue-green" />
|
||
<div ref="trendChartRef" class="chart-container"></div> -->
|
||
|
||
<!-- 借贷机构数和借还差值比例表格 -->
|
||
<LTitle title="近期通过借贷审批情况" type="blue-green" />
|
||
<div class="overflow-x-auto">
|
||
<LTable :data="monthlyInstitutionData" type="blue-green">
|
||
<template #header>
|
||
<th class="border px-1 py-2 text-xs min-w-[25%]">
|
||
时间
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
借贷机构数
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
借贷次数
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
审批额度
|
||
</th>
|
||
<!-- <th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
应还金额
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
应还比例
|
||
</th> -->
|
||
</template>
|
||
<template #default="{ row }">
|
||
<td class="border px-1 py-2 text-xs">
|
||
{{ row.month }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.orgCount }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.loanCount }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.borrowAmount }}
|
||
</td>
|
||
<!-- <td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.repayAmount }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.ratio }}
|
||
</td> -->
|
||
</template>
|
||
</LTable>
|
||
</div>
|
||
|
||
<!-- 近期借贷趋势表格 -->
|
||
<LTitle title="近1年借贷情况" type="blue-green" />
|
||
<div class="overflow-x-auto">
|
||
<LTable :data="recentBorrowTrends" type="blue-green">
|
||
<template #header>
|
||
<th class="border px-1 py-2 text-xs min-w-[25%]">
|
||
时间
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
借贷机构数
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
借贷次数
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
审批额度
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
应还金额
|
||
</th>
|
||
<th class="border px-1 py-2 text-xs min-w-[15%]">
|
||
审批与应还比例
|
||
</th>
|
||
</template>
|
||
<template #default="{ row }">
|
||
<td class="border px-1 py-2 text-xs">
|
||
{{ row.month }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.orgCount }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.loanCount }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.borrowAmount }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.repayAmount }}
|
||
</td>
|
||
<td class="border px-1 py-2 text-xs text-center">
|
||
{{ row.ratio }}
|
||
</td>
|
||
</template>
|
||
</LTable>
|
||
</div>
|
||
|
||
<!-- 借贷行为总结 -->
|
||
<LTitle title="借贷行为总结分析" type="blue-green" />
|
||
<div class="summary-container bg-blue-50 p-4 rounded-md">
|
||
<div class="text-xs text-gray-500 mb-2">
|
||
数据时间范围: 近1年
|
||
</div>
|
||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
|
||
<div class="info-card p-3 bg-white rounded shadow-sm">
|
||
<div class="text-sm text-gray-500">总申请次数</div>
|
||
<div class="text-lg font-semibold">
|
||
{{ behaviorSummary.totalApplications }}次
|
||
</div>
|
||
</div>
|
||
<div class="info-card p-3 bg-white rounded shadow-sm">
|
||
<div class="text-sm text-gray-500">借贷机构数</div>
|
||
<div class="text-lg font-semibold">
|
||
{{ behaviorSummary.totalOrgs }}家
|
||
</div>
|
||
</div>
|
||
<div class="info-card p-3 bg-white rounded shadow-sm">
|
||
<div class="text-sm text-gray-500">总审批额度</div>
|
||
<div class="text-lg font-semibold">
|
||
{{ behaviorSummary.totalAmount }}元
|
||
</div>
|
||
</div>
|
||
<div class="info-card p-3 bg-white rounded shadow-sm">
|
||
<div class="text-sm text-gray-500">月均申请次数</div>
|
||
<div class="text-lg font-semibold">
|
||
{{ behaviorSummary.avgMonthlyApplications }}次
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="risk-assessment p-4 bg-white rounded-md">
|
||
<div class="text-lg font-bold mb-2">
|
||
风险评估:
|
||
<span :class="{
|
||
'text-red-500':
|
||
behaviorSummary.riskLevel === '高',
|
||
'text-yellow-500':
|
||
behaviorSummary.riskLevel === '中',
|
||
'text-green-500':
|
||
behaviorSummary.riskLevel === '低',
|
||
}">{{ behaviorSummary.riskLevel }}风险</span>
|
||
</div>
|
||
<div class="text-gray-700">
|
||
<p>
|
||
· 月均审批额度:
|
||
{{ behaviorSummary.avgMonthlyAmount }}元
|
||
</p>
|
||
<p>
|
||
· 月均应还金额:
|
||
{{ behaviorSummary.avgMonthlyRepay }}元
|
||
</p>
|
||
<p>· 还款比例: {{ behaviorSummary.repayRatio }}</p>
|
||
<p>· {{ behaviorSummary.riskDesc }}</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.chart-container {
|
||
width: 100%;
|
||
height: 300px;
|
||
margin-bottom: 20px;
|
||
}
|
||
</style>
|