Files
qnc-webview-tob/src/views/Report.vue

702 lines
33 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
const featureMap = {
G09SC02: {
name: '婚姻状态',
component: defineAsyncComponent(() => import('@/ui/CMarriage.vue')),
remark: "查询结果为“未婚或尚未登记结婚”时,表示婚姻登记处暂无相关的登记记录。婚姻状态信息由婚姻登记处逐级上报,可能存在数据遗漏或更新滞后。当前可查询的婚姻状态包括:未婚或尚未登记结婚、已婚、离异、离异冷静期。如您对查询结果有疑问,请联系客服反馈。"
},
G27BJ05: {
name: '借贷申请记录',
component: defineAsyncComponent(() =>
import('@/ui/CBankLoanApplication.vue')
),
},
G28BJ05: {
name: '借贷行为记录',
component: defineAsyncComponent(() =>
import('@/ui/CBankLoanBehavior.vue')
),
},
G26BJ05: {
name: '违约失信',
component: defineAsyncComponent(() =>
import('@/ui/CSpecialList.vue')
),
},
G34BJ03: {
name: '不良记录',
component: defineAsyncComponent(() => import('@/ui/CBad.vue')),
},
G35SC01: {
name: '司法涉诉',
component: defineAsyncComponent(() => import('@/ui/CLawsuit.vue')),
},
G22SC01: {
name: '司法涉诉',
component: defineAsyncComponent(() => import('@/ui/CLawsuitPP.vue')),
},
G05HZ01: {
name: '股东人企关系',
component: defineAsyncComponent(() => import('@/ui/CRelatedEnterprises.vue')),
},
Q23SC01: {
name: '企业涉诉',
component: defineAsyncComponent(() => import('@/ui/CLawsuit.vue')),
},
G15BJ02: {
name: '手机三要素',
component: defineAsyncComponent(() => import('@/ui/CPhoneThreeElements.vue')),
},
KZEYS: {
name: '身份证二要素',
component: defineAsyncComponent(() => import('@/ui/CIDCardTwoElements.vue')),
},
G17BJ02: {
name: '手机号二要素',
component: defineAsyncComponent(() => import('@/ui/CPhoneTwoElements.vue')),
},
G10SC02: {
name: '双人婚姻核验',
component: defineAsyncComponent(() => import('@/ui/CDualMarriage.vue')),
},
P_C_B332: {
name: '人车核验',
component: defineAsyncComponent(() => import('@/ui/CP_C_B332.vue')),
},
FIN019: {
name: '银行卡黑名单',
component: defineAsyncComponent(() => import('@/ui/CFIN019.vue')),
},
G20GZ01: {
name: '银行卡四要素核验',
component: defineAsyncComponent(() => import('@/ui/CG20GZ01.vue')),
},
G03HZ01: {
name: '手机号码风险',
component: defineAsyncComponent(() => import('@/ui/CG03HZ01.vue')),
},
G19BJ02: {
name: '手机二次卡',
component: defineAsyncComponent(() => import('@/ui/CG19BJ02.vue')),
},
G02BJ02: {
name: '手机在网时长',
component: defineAsyncComponent(() => import('@/ui/CG02BJ02.vue')),
},
CAR061: {
name: '名下车辆',
component: defineAsyncComponent(() => import('@/ui/CCAR061.vue')),
},
IDV044: {
name: '婚姻状态',
component: defineAsyncComponent(() => import('@/ui/CIDV044.vue')),
remark: '查询结果为"未婚或尚未登记结婚"时,表示婚姻登记处暂无相关的登记记录。婚姻状态信息由婚姻登记处逐级上报,可能存在数据遗漏或更新滞后。当前可查询的婚姻状态包括:未婚或尚未登记结婚、已婚、离异、离异冷静期。如您对查询结果有疑问,请联系客服反馈。'
},
};
import LEmpty from "@/components/LEmpty.vue";
import LPendding from "@/components/LPendding.vue";
import LTitle from "@/components/LTitle.vue";
const route = useRoute();
const productId = ref(null);
const isDone = ref(false);
const active = ref(null)
const reportData = ref([])
const reportParams = ref({})
const reportName = ref("")
const reportDateTime = ref(null)
const orderId = ref(null);
const orderNo = ref("")
const isEmpty = ref(false)
const isPending = ref(false)
onBeforeMount(() => {
const query = new URLSearchParams(window.location.search);
orderNo.value = query.get("out_trade_no");
orderId.value = query.get("order_id");
if (!orderNo.value && !orderId.value) {
orderId.value = route.query.orderId;
orderNo.value = route.query.orderNo;
}
if (!orderId.value && !orderNo.value) return;
getReport()
});
// 计算综合评分的函数
const calculateScore = (reportData) => {
// 从0分开始
let score = 0;
// 最高分为90分
const maxScore = 90;
// 定义各接口的相对权重比例(而非固定分值)
const relativeWeights = {
// 关键风险指标(高优先级)
'G34BJ03': 250, // 不良记录
'G26BJ05': 100, // 违约异常
'G22SC01': 400, // 司法涉诉
'G35SC01': 20, // 司法涉诉(次要)
'Q23SC01': 50, // 企业涉诉
'FIN019': 100, // 银行卡黑名单
// 高风险指标(中优先级)
'G27BJ05': 40, // 借贷申请记录
'G28BJ05': 40, // 借贷行为记录
'G03HZ01': 70, // 手机号码风险
// 中风险指标(低优先级)
'G19BJ02': 50, // 手机二次卡
'G02BJ02': 50, // 手机在网时长
'G05HZ01': 50, // 人企关系
// 验证指标(最低优先级)
'G15BJ02': 25, // 手机三要素
'G17BJ02': 25, // 手机号二要素
'KZEYS': 25, // 身份证二要素
'G20GZ01': 25 // 银行卡四要素核验
};
// 找出当前报告中包含的接口
const availableAPIs = reportData.map(item => item.data.apiID).filter(id => relativeWeights[id]);
// 如果没有可评分的接口,返回默认分数
if (availableAPIs.length === 0) return 60; // 默认60分
// 计算当前报告中所有接口的相对权重总和
let totalWeight = 0;
availableAPIs.forEach(apiID => {
totalWeight += relativeWeights[apiID];
});
// 计算每个权重点对应的分数
const pointValue = maxScore / totalWeight;
// 基于当前报告中的接口计算实际权重
const actualWeights = {};
availableAPIs.forEach(apiID => {
// 将相对权重转换为实际分数权重
actualWeights[apiID] = relativeWeights[apiID] * pointValue;
});
// 遍历报告数据进行评分
reportData.forEach(item => {
const apiID = item.data.apiID;
const data = item.data.data;
// 如果没有定义权重,跳过
if (!actualWeights[apiID]) return;
// 根据不同的API ID计算分数只有没有风险或风险低时加分
switch (apiID) {
case 'G09SC02': // 婚姻状态
// 不计入风险
break;
case 'G27BJ05': // 借贷申请记录
if (data) {
// 检查所有字段是否都为0没有风险
let noRisk = true;
for (const key in data) {
if (data[key] !== 0 && data[key] !== '0') {
noRisk = false;
break;
}
}
if (noRisk || !Object.keys(data).length) {
score += actualWeights[apiID];
}
} else {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G28BJ05': // 借贷行为记录
if (data) {
// 检查所有字段是否都为0没有风险
let noRisk = true;
for (const key in data) {
if (data[key] !== 0 && data[key] !== '0') {
noRisk = false;
break;
}
}
if (noRisk || !Object.keys(data).length) {
score += actualWeights[apiID];
}
} else {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G26BJ05': // 违约异常
if (data) {
// 检查除特定字段外的其他字段是否都为0没有风险
const excludeFields = ['swift_number', 'code', 'flag_specialList_c'];
let noRisk = true;
for (const key in data) {
if (!excludeFields.includes(key) && data[key] !== 0 && data[key] !== '0') {
noRisk = false;
break;
}
}
if (noRisk || !Object.keys(data).length) {
score += actualWeights[apiID];
}
} else {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G34BJ03': // 不良记录
if (data && data.risk_level) {
// 根据风险等级加分
switch (data.risk_level) {
case 'A': // 无风险
score += actualWeights[apiID];
break;
case 'F': // 低风险
score += actualWeights[apiID] * 0.7;
break;
case 'C': // 中风险
case 'D': // 中风险
score += actualWeights[apiID] * 0.3;
break;
case 'B': // 高风险
case 'E': // 高风险
// 不加分
break;
}
} else {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G35SC01': // 司法涉诉
case 'G22SC01': // 司法涉诉
case 'Q23SC01': // 企业涉诉
if (data) {
let hasRisk = false;
// 检查各种涉诉信息 - 处理嵌套数据结构
// entout是一个单元素数组数组中第一个元素是JSON对象对象中有entout属性
if (data.entout && Array.isArray(data.entout) && data.entout.length > 0 &&
data.entout[0] && data.entout[0].entout &&
((Array.isArray(data.entout[0].entout) && data.entout[0].entout.length > 0) ||
(typeof data.entout[0].entout === 'object' && Object.keys(data.entout[0].entout).length > 0))) {
hasRisk = true;
}
// 处理sxbzxr(失信被执行人)嵌套结构
if (data.sxbzxr && Array.isArray(data.sxbzxr) && data.sxbzxr.length > 0 &&
data.sxbzxr[0] && data.sxbzxr[0].sxbzxr &&
((Array.isArray(data.sxbzxr[0].sxbzxr) && data.sxbzxr[0].sxbzxr.length > 0) ||
(typeof data.sxbzxr[0].sxbzxr === 'object' && Object.keys(data.sxbzxr[0].sxbzxr).length > 0))) {
hasRisk = true;
}
// 处理xgbzxr(限制高消费被执行人)嵌套结构
if (data.xgbzxr && Array.isArray(data.xgbzxr) && data.xgbzxr.length > 0 &&
data.xgbzxr[0] && data.xgbzxr[0].xgbzxr &&
((Array.isArray(data.xgbzxr[0].xgbzxr) && data.xgbzxr[0].xgbzxr.length > 0) ||
(typeof data.xgbzxr[0].xgbzxr === 'object' && Object.keys(data.xgbzxr[0].xgbzxr).length > 0))) {
hasRisk = true;
}
if (!hasRisk) {
score += actualWeights[apiID];
}
} else {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G03HZ01': // 手机号码风险
if (data && data.filterType) {
// 根据filterType判断风险等级
switch (data.filterType) {
case '0': // 安全
score += actualWeights[apiID];
break;
case '3': // 低危
score += actualWeights[apiID] * 0.7;
break;
case '2': // 中危
score += actualWeights[apiID] * 0.3;
break;
case '1': // 高危
// 不加分
break;
}
} else {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G19BJ02': // 手机二次卡
if (data && data.is_second_card === false) {
score += actualWeights[apiID];
} else if (!data) {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G02BJ02': // 手机在网时长
if (data && data.online_months >= 6) {
score += actualWeights[apiID];
} else if (!data) {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'G15BJ02': // 手机三要素
case 'G17BJ02': // 手机号二要素
case 'KZEYS': // 身份证二要素
case 'G20GZ01': // 银行卡四要素核验
if (data && data.is_consistent) {
score += actualWeights[apiID];
} else if (!data) {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
case 'FIN019': // 银行卡黑名单
if (data && data.is_blacklisted === false) {
score += actualWeights[apiID];
} else if (!data) {
// 无数据视为无风险
score += actualWeights[apiID];
}
break;
default:
// 未知接口类型不影响评分
break;
}
});
// 确保分数在0-90范围内并四舍五入
return Math.max(0, Math.min(maxScore, Math.round(score)));
};
const getReport = async () => {
let queryUrl = ""
if (orderNo.value) {
queryUrl = `/query/orderNo/${orderNo.value}`
} else if (orderId.value) {
queryUrl = `/query/orderId/${orderId.value}`
} else {
return
}
const { data, error } = await useApiFetch(queryUrl)
.get()
.json()
if (data.value && !error.value) {
if (data.value.code === 200) {
reportData.value = data.value.data.query_data.sort((a, b) => {
return a.feature.sort - b.feature.sort
});
productId.value = data.value.data.product_id;
reportParams.value = data.value.data.query_params
reportName.value = data.value.data.product_name
reportDateTime.value = data.value.data.create_time
// 计算综合评分
reportScore.value = calculateScore(reportData.value);
} else if (data.value.code === 200003) {
isEmpty.value = true
} else if (data.value.code === 200002) {
isPending.value = true
}
isDone.value = true
console.log(isDone.value)
}
}
// 对请求参数进行脱敏处理
const maskValue = computed(() => {
return (type, value) => {
if (!value) return value;
if (type === "name") {
// 姓名脱敏(保留首位)
if (value.length === 1) {
return "*"; // 只保留一个字,返回 "*"
} else if (value.length === 2) {
return value[0] + "*"; // 两个字,保留姓氏,第二个字用 "*" 替代
} else {
return value[0] + "*".repeat(value.length - 2) + value[value.length - 1]; // 两个字以上,保留第一个和最后一个字,其余的用 "*" 替代
}
} else if (type === "id_card") {
// 身份证号脱敏保留前6位和最后4位
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === 'mobile') {
if (value.length === 11) {
return value.substring(0, 3) + "****" + value.substring(7);
}
return value; // 如果手机号不合法或长度不为 11 位,直接返回原手机号
} else if (type === "bank_card") {
// 银行卡号脱敏保留前6位和后4位
return value.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
} else if (type === "ent_name") {
// 企业名称脱敏保留前3个字符和后3个字符中间部分用 "*" 替代)
if (value.length <= 6) {
return value[0] + "*".repeat(value.length - 1); // 少于6个字符时只保留第一个字符其他用 * 替代
} else {
return value.slice(0, 3) + "*".repeat(value.length - 6) + value.slice(-3); // 多于6个字符时保留前3和后3
}
} else if (type === "ent_code") {
// 企业代码脱敏保留前4个字符和后4个字符中间部分用 "*" 替代)
if (value.length <= 8) {
return value.slice(0, 4) + "*".repeat(value.length - 4); // 长度不超过8时保留前4个字符其他用 * 替代
} else {
return value.slice(0, 4) + "*".repeat(value.length - 8) + value.slice(-4); // 长度超过8时保留前4个字符和后4个字符
}
} else if (type === "car_license") {
// 车牌号脱敏保留前2个字符后2个字符其他部分用 "*" 替代)
if (value.length <= 4) {
return value[0] + "*".repeat(value.length - 1); // 如果车牌号长度小于等于4只保留首字符
} else {
// 如果车牌号较长保留前2个字符后2个字符其余部分用 "*" 替代
return value.slice(0, 2) + "*".repeat(value.length - 4) + value.slice(-2);
}
}
return value;
}
});
</script>
<template>
<div class="min-h-full from-blue-100 to-white bg-gradient-to-b">
<!-- <van-notice-bar color="#e03131" background="#ecf9ff" left-icon="info-o"
text="由于全能查APP暂未上线建议将此网站加入收藏夹或书签或可以百度搜索全能查以及在微信全能查小程序复制链接打开" /> -->
<template v-if="isDone">
<van-tabs v-model:active="active" scrollspy sticky :offset-top="46">
<div class="flex flex-col gap-y-4 p-4">
<LEmpty v-if="isEmpty" />
<template v-if="!isEmpty && !isPending">
<van-tab title="分析指数">
<div id="overdiv" class="title mb-4">分析指数</div>
<div class="card mb-4">
<div class="my-4">
<GaugeChart :score="reportScore" />
</div>
</div>
</van-tab>
<van-tab title="基本信息">
<div id="overdiv" class="title mb-4">基本信息</div>
<div class="card">
<div class="flex flex-col gap-y-2">
<LTitle title="报告信息" type="blue-green"></LTitle>
<div class="flex flex-col gap-2 my-2">
<div class="flex justify-between border-b pb-2 pl-2">
<span class="text-gray-700 font-bold">报告时间</span>
<span class="text-gray-600">{{ reportDateTime }}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2" v-if="!isEmpty">
<span class="text-gray-700 font-bold">报告项目</span>
<span class="text-gray-600">
{{ reportName }}</span>
</div>
</div>
<template v-if="Object.keys(reportParams).length != 0">
<LTitle title="报告对象" type="blue-green"></LTitle>
<div class="flex flex-col gap-2 my-2">
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.name">
<span class="text-gray-700 font-bold">姓名</span>
<span class="text-gray-600">{{ maskValue("name", reportParams?.name)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.id_card">
<span class="text-gray-700 font-bold">身份证号</span>
<span class="text-gray-600">
{{ maskValue("id_card", reportParams?.id_card) }}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.nameMan">
<span class="text-gray-700 font-bold">男方姓名</span>
<span class="text-gray-600">{{ maskValue("name", reportParams?.nameMan)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.idCardMan">
<span class="text-gray-700 font-bold">男方身份证号</span>
<span class="text-gray-600">{{ maskValue("id_card",
reportParams?.idCardMan)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.nameWoman">
<span class="text-gray-700 font-bold">女方姓名</span>
<span class="text-gray-600">{{ maskValue("name",
reportParams?.nameWoman)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.idCardWoman">
<span class="text-gray-700 font-bold">女方身份证号</span>
<span class="text-gray-600">{{ maskValue("id_card",
reportParams?.idCardWoman)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.bank_card">
<span class="text-gray-700 font-bold">银行卡号</span>
<span class="text-gray-600">{{ maskValue("bank_card",
reportParams?.bank_card)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.mobile">
<span class="text-gray-700 font-bold">手机号</span>
<span class="text-gray-600">{{ maskValue("mobile", reportParams?.mobile)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.verification_code">
<span class="text-gray-700 font-bold">验证码</span>
<span class="text-gray-600">{{ maskValue("code",
reportParams?.verification_code)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.car_license">
<span class="text-gray-700 font-bold">车牌号</span>
<span class="text-gray-600">{{ maskValue("car_license",
reportParams?.car_license)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.ent_name">
<span class="text-gray-700 font-bold">企业名称</span>
<span class="text-gray-600">{{ maskValue("ent_name",
reportParams?.ent_name)
}}</span>
</div>
<div class="flex justify-between border-b pb-2 pl-2"
v-if="reportParams?.ent_code">
<span class="text-gray-700 font-bold">企业代码</span>
<span class="text-gray-600">{{ maskValue("ent_code",
reportParams?.ent_code)
}}</span>
</div>
<div class="flex flex-col gap-4">
<div class="flex items-center bg-blue-100 rounded-xl px-4 py-2 flex-1">
<div
class="bg-green-500 w-12 h-12 text-white text-xl flex items-center justify-center rounded-full mr-4">
</div>
<div>
<div class="font-bold text-lg">身份证检查结果</div>
<div class="text-sm text-gray-600">身份证信息核验通过</div>
</div>
</div>
<div class="flex items-center bg-blue-100 rounded-xl px-4 py-2 flex-1">
<div
class="bg-green-500 w-12 h-12 text-white text-xl flex items-center justify-center rounded-full mr-4">
</div>
<div>
<div class="font-bold text-lg">手机号检测结果</div>
<div class="text-sm text-gray-600">被查询人姓名与运营商提供的一致</div>
<div class="text-sm text-gray-600">被查询人身份证与运营商提供的一致</div>
</div>
</div>
</div>
</div>
</template>
</div>
</div>
<LRemark content="如查询的姓名/身份证与运营商提供的不一致,可能会存在报告内容不匹配的情况" />
</van-tab>
</template>
<LPendding v-if="isPending" />
<van-tab v-for="(item, index) in reportData" :key="index"
:title="featureMap[item.data.apiID]?.name">
<div id="lawsuit" class="title mb-4">{{ featureMap[item.data.apiID]?.name }}</div>
<component :is="featureMap[item.data.apiID]?.component" :data="item.data.data"
:params="reportParams" :isMerriageProduct="item.data.apiID === 'G09SC02'">
</component>
<LRemark v-if="featureMap[item.data.apiID]?.remark"
:content="featureMap[item.data.apiID]?.remark" />
</van-tab>
<div class="card">
<div>
<div class="text-bold text-blue-500 mb-2">报告说明</div>
<div>
&nbsp; &nbsp;本报告的数据由用户本人明确授权后我们才向相关合法存有用户个人数据的机构调取本报告相关内容本平台只做大数据的获取与分析仅向用户个人展示参考
</div>
<p>
&nbsp; &nbsp; 报告有效期<strong class="text-red-500">30</strong>过期自动删除
</p>
<p>
&nbsp; &nbsp;
若您的数据不全面可能是数据具有延迟性或者合作信息机构未获取到您的数据若数据有错误请联系客服
</p>
<p> &nbsp;
&nbsp;本产品所有数据均来自第三方可能部分数据未公开数据更新延迟或信息受到限制贵司不对数据的准确性真实性完整性做任何承诺用户需根据实际情况结合报告内容自行判断与决策
</p>
</div>
</div>
</div>
</van-tabs>
</template>
</div>
<div class="disclaimer">
<div class="flex flex-col items-center">
<div class="flex items-center">
<img class="w-4 h-4 mr-2" src="@/assets/images/public_security_record_icon.png" alt="公安备案" />
<text>琼公网安备46010002000443号</text>
</div>
<div>
<a class="text-blue-500" href="https://beian.miit.gov.cn">
琼ICP备2024038584号-2
</a>
</div>
</div>
<div>
海南省学宇思科技有限公司版权所有
</div>
</div>
</template>
<style lang="scss" scoped>
.title {
@apply mx-auto mt-2 w-64 border rounded-3xl bg-gradient-to-r from-blue-400 via-green-500 to-teal-500 py-2 text-center text-white font-bold;
}
.a {
color: #e03131;
}
.disclaimer {
/* margin-top: 24px; */
padding: 10px;
font-size: 12px;
color: #999;
text-align: center;
border-top: 1px solid #e0e0e0;
padding-bottom: 60px;
background: #ffffff;
}
</style>