Files
report_viewer/src/components/BaseReport.vue

775 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>
import ShareReportButton from "./ShareReportButton.vue";
import TitleBanner from "./TitleBanner.vue";
import VerificationCard from "./VerificationCard.vue";
import StyledTabs from "./StyledTabs.vue";
import { splitDWBG8B4DForTabs } from '@/ui/CDWBG8B4D/utils/simpleSplitter.js';
import { splitDWBG6A2CForTabs } from '@/ui/DWBG6A2C/utils/simpleSplitter.js';
import { splitCQYGL3F8EForTabs } from '@/ui/CQYGL3F8E/utils/simpleSplitter.js';
const props = defineProps({
isShare: {
type: Boolean,
default: false,
},
orderId: {
type: String,
required: false,
default: "",
},
orderNo: {
type: String,
default: "",
},
feature: {
type: String,
required: true,
},
reportData: {
type: Array,
required: true,
},
reportParams: {
type: Object,
required: true,
},
reportName: {
type: String,
required: true,
},
reportDateTime: {
type: [String, null],
required: false,
default: null,
},
isEmpty: {
type: Boolean,
required: true,
},
isDone: {
type: Boolean,
required: true,
},
isExample: {
type: Boolean,
default: false,
},
});
// 使用toRefs将props转换为组件内的ref
const {
feature,
reportData,
reportParams,
reportName,
reportDateTime,
isEmpty,
isDone,
isExample,
} = toRefs(props);
const active = ref(null);
const reportScore = ref(0); // 默认分数
const trapezoidBgImage = ref(''); // 牌匾背景图片
// 动态加载牌匾背景图片
const loadTrapezoidBackground = async () => {
try {
const bgModule = await import("@/assets/images/report/title_inquire_bg.png");
trapezoidBgImage.value = bgModule.default;
} catch (error) {
console.warn(`Failed to load trapezoid background image:`, error);
}
};
// 在组件挂载时加载牌匾背景图
onMounted(async () => {
await loadTrapezoidBackground();
});
// 处理数据拆分支持DWBG8B4D、DWBG6A2C、CJRZQ5E9F和CQYGL3F8E
const processedReportData = computed(() => {
let data = reportData.value;
// 拆分DWBG8B4D数据
data = splitDWBG8B4DForTabs(data);
// 拆分DWBG6A2C数据
data = splitDWBG6A2CForTabs(data);
// 拆分CQYGL3F8E数据
data = splitCQYGL3F8EForTabs(data);
// 过滤掉在featureMap中没有对应的项
return data.filter(item => featureMap[item.data.apiID]);
});
// 牌匾背景图片样式
const trapezoidBgStyle = computed(() => {
if (trapezoidBgImage.value) {
return {
backgroundImage: `url(${trapezoidBgImage.value})`,
};
}
return {};
});
const featureMap = {
// 司南报告
DWBG6A2C: {
name: "司南报告",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/index.vue")),
remark: '司南报告提供全面的个人信用风险评估,包括身份核验、风险名单、借贷行为、履约情况等多维度分析。'
},
// 司南报告拆分模块
DWBG6A2C_StandLiveInfo: {
name: "身份信息核验",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/StandLiveInfoSection.vue")),
},
DWBG6A2C_RiskPoint: {
name: "命中风险标注",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/RiskPointSection.vue")),
},
DWBG6A2C_SecurityInfo: {
name: "公安重点人员核验",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/SecurityInfoSection.vue")),
},
DWBG6A2C_AntiFraudInfo: {
name: "涉赌涉诈人员核验",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/AntiFraudInfoSection.vue")),
},
DWBG6A2C_RiskList: {
name: "风险名单",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/RiskListSection.vue")),
},
DWBG6A2C_ApplicationStatistics: {
name: "历史借贷行为",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/ApplicationStatisticsSection.vue")),
},
DWBG6A2C_LendingStatistics: {
name: "近24个月放款情况",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/LendingStatisticsSection.vue")),
},
DWBG6A2C_PerformanceStatistics: {
name: "履约情况",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/PerformanceStatisticsSection.vue")),
},
DWBG6A2C_OverdueRecord: {
name: "历史逾期记录",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/OverdueRecordSection.vue")),
},
DWBG6A2C_CreditDetail: {
name: "授信详情",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/CreditDetailSection.vue")),
},
DWBG6A2C_RentalBehavior: {
name: "租赁行为",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/RentalBehaviorSection.vue")),
},
DWBG6A2C_RiskSupervision: {
name: "关联风险监督",
component: defineAsyncComponent(() => import("@/ui/DWBG6A2C/components/RiskSupervisionSection.vue")),
},
// 司法涉诉
FLXG0V4B: {
name: "司法涉诉",
component: defineAsyncComponent(() =>
import("@/ui/CFLXG0V4B/index.vue")
),
},
// 个人涉诉
FLXG7E8F: {
name: "个人涉诉",
component: defineAsyncComponent(() =>
import("@/ui/FLXG7E8F/index.vue")
),
remark: '个人涉诉风险展示申请人相关的诉讼情况,包括民事诉讼、刑事诉讼、行政诉讼、执行案件、失信被执行人、限制消费等。数据来源于各级法院的公开判决书和法官网等权威渠道。'
},
// 人企关系加强版
QYGL3F8E: {
name: "人企关系加强版",
component: defineAsyncComponent(() =>
import("@/ui/CQYGL3F8E/index.vue")
),
remark: '人企关系加强版提供全面的企业关联分析,包括投资企业记录、高管任职记录和涉诉风险等多维度信息。'
},
// 人企关系加强版拆分模块
CQYGL3F8E_Investment: {
name: "投资企业记录",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/Investment.vue")),
},
CQYGL3F8E_SeniorExecutive: {
name: "高管任职记录",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/SeniorExecutive.vue")),
},
CQYGL3F8E_Lawsuit: {
name: "涉诉风险",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/Lawsuit.vue")),
},
CQYGL3F8E_InvestHistory: {
name: "对外投资历史",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/InvestHistory.vue")),
},
CQYGL3F8E_FinancingHistory: {
name: "融资历史",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/FinancingHistory.vue")),
},
CQYGL3F8E_Punishment: {
name: "行政处罚",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/Punishment.vue")),
},
CQYGL3F8E_Abnormal: {
name: "经营异常",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/Abnormal.vue")),
},
CQYGL3F8E_TaxRisk: {
name: "税务风险",
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/TaxRisk/index.vue")),
},
// 信贷表现
JRZQ4B6C: {
name: "信贷表现",
component: defineAsyncComponent(() => import("@/ui/JRZQ4B6C/index.vue")),
remark: '信贷表现主要为企业在背景调查过程中探查用户近期信贷表现时提供参考,帮助企业对其内部员工、外部业务进行个人信用过滤。数据来源于多个征信机构,可能存在数据延迟或不完整的情况。'
},
// 收入评估
JRZQ09J8: {
name: "收入评估",
component: defineAsyncComponent(() => import("@/ui/JRZQ09J8/index.vue")),
remark: '基于全国社会保险信息系统的缴费基数数据进行收入水平评估。评级反映相对收入水平,实际收入可能因地区差异而有所不同,建议结合其他收入证明材料进行综合评估。'
},
// 个人消费能力等级
JRZQ8B3C: {
name: "个人消费能力等级",
component: defineAsyncComponent(() => import("@/ui/JRZQ8B3C/index.vue")),
remark: '基于个人收入指数进行消费能力等级评估,展示用户的月消费能力范围。消费能力等级反映用户的消费水平,等级越高对应的月消费能力越强。数据来源于个人收入指数评分,实际消费能力可能因个人消费习惯、地区差异等因素而有所不同,建议结合其他消费行为数据进行综合评估。'
},
// 名下车辆详细版
QCXG9P1C: {
name: "名下车辆",
component: defineAsyncComponent(() => import("@/ui/CQCXG9P1C.vue")),
},
// 网络社交异常
IVYZ8I9J: {
name: "网络社交异常",
component: defineAsyncComponent(() => import("@/ui/IVYZ8I9J.vue")),
remark: '网络社交异常通过分析用户在互联网上的行为特征,检测资料包装中介、异常行业、虚假资料、羊毛党、身份信息存疑、严重异常行为、失信行为、支付异常行为、其他异常行为和上网环境异常等多维度风险。'
},
// 多头借贷行业风险版
DWBG7F3A: {
name: "多头借贷行业风险版",
component: defineAsyncComponent(() => import("@/ui/DWBG7F3A/index.vue")),
remark: '多头借贷行业风险版提供全面的多头借贷风险评估,包括多头共债子分、多头申请、多头逾期、圈团风险和可疑欺诈风险等多维度分析。'
},
JRZQ5E9F: {
name: "贷款风险评估",
component: defineAsyncComponent(() => import("@/ui/CJRZQ5E9F/index.vue")),
remark: '贷款风险评估提供全面的个人贷款风险分析,包括风险概览、信用评分、贷款行为分析、机构分析等多维度评估。'
},
FLXG3D56: {
name: "违约失信",
component: defineAsyncComponent(() => import("@/ui/CFLXG3D56.vue")),
remark: '违约失信用于检测个人在法院失信、银行风险、非银机构风险等多个维度的不良记录,包括法院失信被执行人、银行不良记录、非银机构逾期、失联等各类风险名单。'
},
FLXGDEA9: {
name: "本人不良",
component: defineAsyncComponent(() => import("@/ui/CFLXGDEA9.vue")),
remark: '本人不良记录查询结果来源于公安部门等权威机构,包括各类违法犯罪前科记录。查询结果仅供参考,具体信息以相关部门官方记录为准。'
},
IVYZ81NC:{
name: "婚姻状态",
component: defineAsyncComponent(() => import("@/ui/CIVYZ81NC.vue")),
remark: '查询结果为"未婚或尚未登记结婚"时,表示婚姻登记处暂无相关的登记记录。婚姻状态信息由婚姻登记处逐级上报,可能存在数据遗漏或更新滞后。当前可查询的婚姻状态包括:未婚或尚未登记结婚、已婚、离异。如您对查询结果有疑问,请联系客服反馈。',
},
IVYZ5733:{
name: "婚姻状态",
component: defineAsyncComponent(() => import("@/ui/CIVYZ5733.vue")),
remark: '查询结果为"未婚或尚未登记结婚"时,表示婚姻登记处暂无相关的登记记录。婚姻状态信息由婚姻登记处逐级上报,可能存在数据遗漏或更新滞后。当前可查询的婚姻状态包括:未婚或尚未登记结婚、已婚、离异。如您对查询结果有疑问,请联系客服反馈。',
},
JRZQ0A03:{
name: "借贷申请记录",
component: defineAsyncComponent(() => import("@/ui/CJRZQ0A03.vue")),
remark: '借贷申请记录通过分析用户在不同时间段的借贷申请行为、机构类型分布、申请频率等数据,评估用户的借贷意向强度和风险特征,帮助识别潜在的过度借贷风险。'
},
JRZQ4AA8:{
name: "偿债压力指数",
component: defineAsyncComponent(() => import("@/ui/CJRZQ4AA8.vue")),
remark: '偿债压力指数通过分析用户的债务规模、还款能力、收入水平等因素,计算并评估用户的偿债压力等级,帮助金融机构评估用户的还款能力和违约风险。'
},
JRZQ8203:{
name: "借贷行为记录",
component: defineAsyncComponent(() => import("@/ui/CJRZQ8203.vue")),
remark: '借贷行为记录通过分析用户的借款行为、还款行为、时间趋势等多维度数据,全面评估用户的借贷行为特征和信用表现,帮助识别异常借贷模式和潜在风险。'
},
QCXG7A2B:{
name: "名下车辆",
component: defineAsyncComponent(() => import("@/ui/CQCXG7A2B.vue")),
remark: '名下车辆查询结果来源于车辆管理部门等权威机构,包括各类车辆登记记录。查询结果仅供参考,具体信息以相关部门官方记录为准。'
},
IVYZ3P9M: {
name: "学历信息",
component: defineAsyncComponent(() => import("@/ui/IVYZ3P9M.vue")),
remark: '学历信息展示学生姓名、身份证号、学校、专业、入学与毕业时间、学历层次以及学习形式等字段,可结合字典编码了解具体含义。',
},
// 特殊名单验证B
JRZQ8A2D: {
name: "特殊名单验证",
component: defineAsyncComponent(() => import("@/ui/JRZQ8A2D.vue")),
remark: '特殊名单验证用于验证个人在各类金融机构和法院系统的信用状况,包括法院失信、银行风险、非银机构风险等多个维度。'
},
// 全景雷达
JRZQ7F1A: {
name: "全景雷达",
component: defineAsyncComponent(() => import("@/ui/JRZQ7F1A/index.vue")),
remark: '全景雷达提供全面的信用评估,包括申请行为详情、放款还款详情和信用详情。通过多维度的数据分析,全面展示申请人的信用状况和借贷行为。'
},
// 借贷意向验证A
JRZQ6F2A: {
name: "借贷意向验证",
component: defineAsyncComponent(() => import("@/ui/JRZQ6F2A/index.vue")),
remark: '借贷意向验证提供全面的借贷申请行为分析,包括申请次数、申请总次数(银行+非银)和申请机构总数(银行+非银)等多维度数据。通过不同时间段的统计分析,全面展示申请人的借贷申请行为。'
},
// 支付行为指数
JRZQ3C9R: {
name: "支付行为指数",
component: defineAsyncComponent(() => import("@/ui/JRZQ3C9R/index.vue")),
remark: '支付行为指数基于近两年的查验记录、还款成功与失败表现以及余额不足情况,对用户支付与还款习惯进行量化评分,用于评估其支付稳定性与违约风险。'
},
// 手机携号转网
YYSY7D3E: {
name: "手机携号转网",
component: defineAsyncComponent(() => import("@/ui/YYSY7D3E/index.vue")),
remark: '手机携号转网查询用于检测用户手机号码是否发生过携号转网操作,以及转网前后的运营商信息。携号转网可能影响用户身份验证和信用评估。'
},
// 手机在网时长
YYSY8B1C: {
name: "手机在网时长",
component: defineAsyncComponent(() => import("@/ui/YYSY8B1C/index.vue")),
remark: '手机在网时长查询用于检测用户手机号码的在网使用时长。在网时长越长,通常表示用户身份越稳定,信用风险越低。需要注意的是,如果手机号码存在携号转网的情况,那么在网时长会从转网的时候重新计算,转网前的在网时长不计入当前在网时长。建议结合手机携号转网查询结果进行综合评估。'
},
// 谛听多维报告
DWBG8B4D: {
name: "谛听多维报告",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/index.vue")),
},
// 谛听多维报告拆分模块
DWBG8B4D_Overview: {
name: "报告概览",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/ReportOverview.vue")),
},
DWBG8B4D_ElementVerification: {
name: "要素核查",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/ElementVerification.vue")),
},
DWBG8B4D_Identity: {
name: "运营商核验",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/Identity.vue")),
},
DWBG8B4D_RiskWarning: {
name: "公安重点人员检验",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/RiskWarning.vue")),
},
DWBG8B4D_OverdueRisk: {
name: "逾期风险综述",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/OverdueRiskSection.vue")),
},
DWBG8B4D_LoanEvaluation: {
name: "借贷评估",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/LoanEvaluationSection.vue")),
},
DWBG8B4D_LeasingRisk: {
name: "租赁风险评估",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/LeasingRiskSection.vue")),
},
DWBG8B4D_RiskSupervision: {
name: "关联风险监督",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/RiskSupervisionSection.vue")),
},
DWBG8B4D_RiskWarningTab: {
name: "规则风险提示",
component: defineAsyncComponent(() => import("@/ui/CDWBG8B4D/components/RiskWarningTab.vue")),
},
};
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;
};
});
// ==================== 新评分系统 ====================
// Feature 风险等级配置(权重越高表示风险越大,最终分数越高越安全)
const featureRiskLevels = {
// 🔴 高风险类
'FLXG0V4B': 20, // 司法涉诉
'FLXG7E8F': 20, // 个人涉诉
// 🟠 中高风险类 - 权重 7
'JRZQ4B6C': 7, // 信贷表现
'IVYZ8I9J': 7, // 网络社交异常
'JRZQ8A2D': 9, // 特殊名单验证
'JRZQ7F1A': 8, // 全景雷达
'JRZQ6F2A': 7, // 借贷意向验证A
'JRZQ5E9F': 8, // 借选指数评估
'JRZQ0A03': 7, // 借贷意向验证
'JRZQ8203': 7, // 借贷行为验证
'JRZQ4AA8': 6, // 偿债压力指数
'FLXG3D56': 9, // 特殊名单验证
'JRZQ3C9R': 7, // 支付行为指数
'YYSY7D3E': 5, // 手机携号转网
'YYSY8B1C': 5, // 手机在网时长
// 🟡 中风险类 - 权重 5
'QYGL3F8E': 5, // 人企关系加强版
'QCXG9P1C': 5, // 名下车辆
'QCXG7A2B': 3, // 名下车辆(简化版)
'JRZQ09J8': 5, // 收入评估
'JRZQ8B3C': 5, // 个人消费能力等级
'IVYZ3P9M': 4, // 学籍学历核验(实时版)
'FLXGDEA9': 15, // 公安不良人员名单(加强版)
'IVYZ81NC': 3, // 单人婚姻查询(登记时间版)
'IVYZ5733': 3, // 单人婚姻状态A
// 📊 复合报告类 - 按子模块动态计算
'DWBG8B4D': 0, // 谛听多维报告(由子模块计算)
'DWBG6A2C': 0, // 司南报告(由子模块计算)
'DWBG7F3A': 8, // 多头借贷行业风险版
// 谛听多维报告子模块
'DWBG8B4D_Overview': 10,
'DWBG8B4D_ElementVerification': 4,
'DWBG8B4D_Identity': 4,
'DWBG8B4D_RiskWarning': 10,
'DWBG8B4D_OverdueRisk': 9,
'DWBG8B4D_LoanEvaluation': 7,
'DWBG8B4D_LeasingRisk': 6,
'DWBG8B4D_RiskSupervision': 8,
'DWBG8B4D_RiskWarningTab': 9,
// 司南报告子模块
'DWBG6A2C_StandLiveInfo': 4,
'DWBG6A2C_RiskPoint': 9,
'DWBG6A2C_SecurityInfo': 15,
'DWBG6A2C_AntiFraudInfo': 15,
'DWBG6A2C_RiskList': 12,
'DWBG6A2C_ApplicationStatistics': 7,
'DWBG6A2C_LendingStatistics': 6,
'DWBG6A2C_PerformanceStatistics': 7,
'DWBG6A2C_OverdueRecord': 9,
'DWBG6A2C_CreditDetail': 5,
'DWBG6A2C_RentalBehavior': 5,
'DWBG6A2C_RiskSupervision': 8,
// 人企关系加强版子模块
'CQYGL3F8E_Investment': 4,
'CQYGL3F8E_SeniorExecutive': 4,
'CQYGL3F8E_Lawsuit': 8,
'CQYGL3F8E_InvestHistory': 3,
'CQYGL3F8E_FinancingHistory': 3,
'CQYGL3F8E_Punishment': 7,
'CQYGL3F8E_Abnormal': 6,
'CQYGL3F8E_TaxRisk': 7,
};
// 存储每个组件的 ref 引用
const componentRefs = ref({});
// 存储每个组件的风险评分(由组件主动通知)
const componentRiskScores = ref({});
// 提供方法让子组件通知自己的风险评分0-100分分数越高越安全
const notifyRiskStatus = (apiID, index, riskScore) => {
const key = `${apiID}_${index}`;
componentRiskScores.value[key] = riskScore;
};
// 暴露给子组件
defineExpose({
notifyRiskStatus
});
// 计算综合评分的函数(分数越高越安全)
const calculateScore = () => {
// 收集实际存在的 features 及其风险权重
const presentFeatures = [];
processedReportData.value.forEach((item, index) => {
const apiID = item.data?.apiID;
if (!apiID) return;
// 获取风险权重(如果不在配置中,默认为 3
const weight = featureRiskLevels[apiID] ?? 3;
// 跳过权重为 0 的复合报告主模块(它们由子模块计算)
if (weight === 0) return;
presentFeatures.push({
apiID,
index,
weight
});
});
if (presentFeatures.length === 0) return 100; // 无有效特征时返回满分(最安全)
// 累计总风险分数
let totalRiskScore = 0;
const riskDetails = []; // 用于调试
presentFeatures.forEach(({ apiID, index, weight }) => {
// 从组件风险评分中获取评分0-100分分数越高越安全
const key = `${apiID}_${index}`;
const componentScore = componentRiskScores.value[key] ?? 100; // 默认100分最安全
// 将组件评分转换为风险分数0-100 -> 100-0
const componentRisk = 100 - componentScore;
// 计算该模块的风险贡献(固定分值,不按占比)
// 使用权重系数放大高风险模块的影响
// 高风险模块权重10如果风险分数是0扣20分权重10 × 系数2
// 中风险模块权重7如果风险分数是0扣14分权重7 × 系数2
// 低风险模块权重3如果风险分数是0扣6分权重3 × 系数2
const weightMultiplier = 1.5; // 权重系数,可以调整这个值来控制影响程度
const riskContribution = (componentRisk / 100) * weight * weightMultiplier;
riskDetails.push({
apiID,
index,
weight,
componentScore,
componentRisk,
riskContribution,
hasStatus: key in componentRiskScores.value
});
// 累加风险分数
totalRiskScore += riskContribution;
});
// 将总风险分数限制在 0-90 范围内确保最低分为10分
const finalRiskScore = Math.max(0, Math.min(90, Math.round(totalRiskScore)));
// 转换为安全分数分数越高越安全100 - 风险分数)
// 最终分数范围10-100分
const safetyScore = 100 - finalRiskScore;
return safetyScore;
};
// 监听 reportData 和 componentRiskScores 变化并计算评分
watch([reportData, componentRiskScores], () => {
reportScore.value = calculateScore();
// 将评分系统数据整理到一个对象中
const scoreData = {
timestamp: new Date().toISOString(),
finalScore: reportScore.value,
reportModules: processedReportData.value.map((item, index) => ({
apiID: item.data.apiID,
name: featureMap[item.data.apiID]?.name || '未知',
index: index,
riskScore: componentRiskScores.value[`${item.data.apiID}_${index}`] ?? '未上报',
weight: featureRiskLevels[item.data.apiID] ?? 0
})),
componentScores: componentRiskScores.value,
riskLevels: featureRiskLevels
};
}, { immediate: true, deep: true });
</script>
<template>
<div class="min-h-full bg-[#f6f8fe]">
<template v-if="isDone">
<!-- Tabs 区域 -->
<StyledTabs v-model:active="active" scrollspy sticky>
<div class="flex flex-col gap-y-4 p-4">
<LEmpty v-if="isEmpty" />
<van-tab title="分析指数">
<div class="relative mb-4">
<!-- 产品卡片牌匾效果 - 使用背景图片 -->
<div class="absolute -top-[12px] left-1/2 transform -translate-x-1/2 w-[140px]">
<div class="trapezoid-bg-image flex items-center justify-center"
:style="trapezoidBgStyle">
<div class="text-xl whitespace-nowrap">分析指数</div>
</div>
</div>
<div class="card mb-4 mt-6 !pt-10">
<div class="my-4">
<GaugeChart :score="reportScore" />
</div>
</div>
</div>
</van-tab>
<van-tab title="基本信息">
<TitleBanner id="basic" class="mb-4">基本信息</TitleBanner>
<VerificationCard :report-params="reportParams" :report-date-time="reportDateTime"
:report-name="reportName" :is-empty="isEmpty" :is-share="isShare" :order-id="orderId"
:order-no="orderNo" />
<LRemark content="如查询的姓名/身份证与运营商提供的不一致,可能会存在报告内容不匹配的情况" />
</van-tab>
<van-tab v-for="(item, index) in processedReportData" :key="`${item.data.apiID}_${index}`"
:title="featureMap[item.data.apiID]?.name">
<TitleBanner :id="item.data.apiID" class="mb-4">
{{ featureMap[item.data.apiID]?.name }}
</TitleBanner>
<component :is="featureMap[item.data.apiID]?.component" :ref="el => {
if (el) {
const refKey = `${item.data.apiID}_${index}`;
componentRefs[refKey] = el;
}
}" :data="item.data.data" :params="reportParams" :api-id="item.data.apiID" :index="index"
:notify-risk-status="notifyRiskStatus">
</component>
<LRemark v-if="featureMap[item.data.apiID]?.remark"
:content="featureMap[item.data.apiID]?.remark" />
</van-tab>
<ShareReportButton v-if="!isShare" class="h-12 text-3xl mt-8" :order-id="orderId"
:order-no="orderNo" :is-example="isExample" />
<span class="mb-4 text-center text-sm text-gray-500">分享当前{{ isExample ? '示例' : '报告' }}链接</span>
<div class="card">
<div>
<div class="text-bold text-center text-[#333333] mb-2">
免责声明
</div>
<p class="text-[#999999]">
&nbsp; &nbsp;
1本份报告是在取得您个人授权后我们才向合法存有您以上个人信息的机构去调取相关内容我们不会以任何形式对您的报告进行存储除您和您授权的人外不会提供给任何人和机构进行查看
</p>
<p class="text-[#999999]">
&nbsp; &nbsp; 2本报告自生成之日起有效期 30
过期自动删除如果您对本份报告存有异议可能是合作机构数据有延迟或未能获取到您的相关数据出于合作平台数据隐私的保护本平台将不做任何解释
</p>
<p class="text-[#999999]">
&nbsp; &nbsp; 3若以上数据有错误请联系平台客服
</p>
</div>
</div>
</div>
</StyledTabs>
</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>琼公网安备46010002000584号</text>
</div>
<div>
<a class="text-blue-500" href="https://beian.miit.gov.cn">
琼ICP备2024048057号-2
</a>
</div>
</div>
<div>海南天远大数据科技有限公司版权所有</div>
</div>
</template>
<style lang="scss" scoped>
.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;
}
:deep(.card) {
@apply p-3;
box-shadow: 0px 0px 24px 0px #3F3F3F0F;
}
/* 梯形背景图片样式 */
.trapezoid-bg-image {
background-size: contain;
background-repeat: no-repeat;
background-position: center;
height: 44px;
}
</style>