This commit is contained in:
Mrx
2026-05-13 11:04:32 +08:00
parent 53284b6979
commit 79a47c6957

View File

@@ -1,481 +1,524 @@
<script setup lang="ts"> <script setup>
import { computed } from 'vue'; import { computed, ref } from 'vue';
import { useRiskNotifier } from '@/composables/useRiskNotifier';
// 接收父组件传入的 props
const props = defineProps({ const props = defineProps({
data: Object, data: { type: Object, default: () => ({}) },
params: Object, params: { type: Object, default: () => ({}) },
apiId: { type: String, default: '' },
index: { type: Number, default: 0 },
notifyRiskStatus: { type: Function, default: () => { } },
}); });
// 定义组件名称,用于在控制台输出调试信息 const periodTab = ref('threeYears'); // 近三年 | 近五年
const componentName = 'IVYZ0S0D';
// 将 props.data 赋值给 reportData 变量 // 支持 data 或 data.result接口返回的 result 对象)
let reportData: any = props.data || {}; const result = computed(() => props.data?.result ?? props.data ?? {});
// 如果 reportData 不为空,则将其赋值给变量 const getStatusText = (value) => {
if (reportData) {
console.log(`${componentName} 组件接收到的数据:`, reportData);
} else {
console.log(`${componentName} 组件未接收到数据`);
}
// 获取状态描述文本
const getStatusText = (value: number) => {
if (value === 1) return '未命中'; if (value === 1) return '未命中';
if (value === 2) return '命中'; if (value === 2) return '命中';
return '未知'; return '';
}; };
// 获取通知函状态描述文本 // 获取通知函期间描述文本(支持数字或字符串如 "2"
const getNoticeLetterStatusText = (value: number) => { const getNoticeLetterPeriodText = (period) => {
if (value === 1) return '未命中'; const p = Number(period);
if (value === 2) return '命中'; const periodMap = { 0: '没有被发送通知函', 1: '近2年内', 2: '2-4年', 3: '5年以上' };
return '未知'; return periodMap[p] ?? '—';
}; };
// 获取通知函期间描述文本
const getNoticeLetterPeriodText = (period: number) => {
const periodMap: Record<number, string> = {
0: '没有被发送通知函',
1: '近2年内',
2: '2-4年',
3: '5年以上'
};
return periodMap[period] || '未知期间';
};
// 获取背景颜色
const getBackgroundColor = (value: number) => {
if (value === 1) return '#e8f5e8'; // 浅绿色
if (value === 2) return '#ffe8e8'; // 浅红色
return '#f5f5f5'; // 默认灰色
};
// 获取边框颜色
const getBorderColor = (value: number) => {
if (value === 1) return '#4caf50'; // 绿色边框
if (value === 2) return '#f44336'; // 红色边框
return '#ccc'; // 默认灰色边框
};
// 判断是否应该隐藏该条目(如果是带时间范围的"未命中"
const shouldHideItem = (itemText: string) => {
// 检查是否包含时间范围关键词并且结果是"未命中"
const timeRangeKeywords = ['近2年', '近3年', '近4年', '近5年', '2-4年', '5年以上'];
const isTimeRangeItem = timeRangeKeywords.some(keyword => itemText.includes(keyword));
const isNoRisk = itemText.includes('未命中');
// 如果是时间范围项目且结果是"未命中",则隐藏
return isTimeRangeItem && isNoRisk;
};
// 获取风险类型数组 - 所有模块都显示
const riskTypes = computed(() => {
const risks: {title: string, value: number, details: string | string[], bgColor: string, borderColor: string}[] = [];
// 总体风险
if (reportData.risk_flag !== undefined) {
risks.push({
title: '总体风险',
value: reportData.risk_flag,
details: getStatusText(reportData.risk_flag),
bgColor: getBackgroundColor(reportData.risk_flag),
borderColor: getBorderColor(reportData.risk_flag)
});
}
// 失信风险
if (reportData.dishonesty && reportData.dishonesty.dishonesty !== undefined) {
risks.push({
title: '失信风险',
value: reportData.dishonesty.dishonesty,
details: getStatusText(reportData.dishonesty.dishonesty),
bgColor: getBackgroundColor(reportData.dishonesty.dishonesty),
borderColor: getBorderColor(reportData.dishonesty.dishonesty)
});
}
// 高消费限制风险
if (reportData.high_consumption && reportData.high_consumption.high_consumption !== undefined) {
risks.push({
title: '高消费限制风险',
value: reportData.high_consumption.high_consumption,
details: getStatusText(reportData.high_consumption.high_consumption),
bgColor: getBackgroundColor(reportData.high_consumption.high_consumption),
borderColor: getBorderColor(reportData.high_consumption.high_consumption)
});
}
// 劳动争议风险
if (reportData.labor_disputes) {
let details: string[] = [];
if (reportData.labor_disputes.labor_disputes !== undefined) {
details.push(`当前: ${getStatusText(reportData.labor_disputes.labor_disputes)}`);
}
if (reportData.labor_disputes.labor_disputes_3y !== undefined) {
const detail = `近3年: ${getStatusText(reportData.labor_disputes.labor_disputes_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.labor_disputes.labor_disputes_5y !== undefined) {
const detail = `近5年: ${getStatusText(reportData.labor_disputes.labor_disputes_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.labor_disputes.labor_contract !== undefined) {
details.push(`劳动合同: ${getStatusText(reportData.labor_disputes.labor_contract)}`);
}
if (reportData.labor_disputes.labor_relation !== undefined) {
details.push(`劳动关系: ${getStatusText(reportData.labor_disputes.labor_relation)}`);
}
if (reportData.labor_disputes.labor_relation_3y !== undefined) {
const detail = `近3年劳动关系: ${getStatusText(reportData.labor_disputes.labor_relation_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.labor_disputes.labor_relation_5y !== undefined) {
const detail = `近5年劳动关系: ${getStatusText(reportData.labor_disputes.labor_relation_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (details.length > 0) {
risks.push({
title: '劳动争议风险',
value: Math.max(...Object.values(reportData.labor_disputes).filter(v => typeof v === 'number')),
details: details,
bgColor: getBackgroundColor(Math.max(...Object.values(reportData.labor_disputes).filter(v => typeof v === 'number'))),
borderColor: getBorderColor(Math.max(...Object.values(reportData.labor_disputes).filter(v => typeof v === 'number')))
});
}
}
// 社会保险纠纷风险
if (reportData.social_insurance) {
let details: string[] = [];
if (reportData.social_insurance.social_insurance !== undefined) {
details.push(`社保纠纷: ${getStatusText(reportData.social_insurance.social_insurance)}`);
}
if (reportData.social_insurance.pension !== undefined) {
details.push(`养老纠纷: ${getStatusText(reportData.social_insurance.pension)}`);
}
if (reportData.social_insurance.pension_3y !== undefined) {
const detail = `近3年养老: ${getStatusText(reportData.social_insurance.pension_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.pension_5y !== undefined) {
const detail = `近5年养老: ${getStatusText(reportData.social_insurance.pension_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.injury_insurance !== undefined) {
details.push(`工伤纠纷: ${getStatusText(reportData.social_insurance.injury_insurance)}`);
}
if (reportData.social_insurance.injury_insurance_3y !== undefined) {
const detail = `近3年工伤: ${getStatusText(reportData.social_insurance.injury_insurance_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.injury_insurance_5y !== undefined) {
const detail = `近5年工伤: ${getStatusText(reportData.social_insurance.injury_insurance_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.unemployment_insurance !== undefined) {
details.push(`失业纠纷: ${getStatusText(reportData.social_insurance.unemployment_insurance)}`);
}
if (reportData.social_insurance.unemployment_insurance_3y !== undefined) {
const detail = `近3年失业: ${getStatusText(reportData.social_insurance.unemployment_insurance_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.unemployment_insurance_5y !== undefined) {
const detail = `近5年失业: ${getStatusText(reportData.social_insurance.unemployment_insurance_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.medical_insurance !== undefined) {
details.push(`医疗纠纷: ${getStatusText(reportData.social_insurance.medical_insurance)}`);
}
if (reportData.social_insurance.medical_insurance_3y !== undefined) {
const detail = `近3年医疗: ${getStatusText(reportData.social_insurance.medical_insurance_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.medical_insurance_5y !== undefined) {
const detail = `近5年医疗: ${getStatusText(reportData.social_insurance.medical_insurance_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.maternity_insurance !== undefined) {
details.push(`生育纠纷: ${getStatusText(reportData.social_insurance.maternity_insurance)}`);
}
if (reportData.social_insurance.maternity_insurance_3y !== undefined) {
const detail = `近3年生育: ${getStatusText(reportData.social_insurance.maternity_insurance_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.social_insurance.maternity_insurance_5y !== undefined) {
const detail = `近5年生育: ${getStatusText(reportData.social_insurance.maternity_insurance_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (details.length > 0) {
risks.push({
title: '社会保险纠纷风险',
value: Math.max(...Object.values(reportData.social_insurance).filter(v => typeof v === 'number')),
details: details,
bgColor: getBackgroundColor(Math.max(...Object.values(reportData.social_insurance).filter(v => typeof v === 'number'))),
borderColor: getBorderColor(Math.max(...Object.values(reportData.social_insurance).filter(v => typeof v === 'number')))
});
}
}
// 福利待遇纠纷
if (reportData.welfare_disputes && reportData.welfare_disputes.welfare !== undefined) {
risks.push({
title: '福利待遇纠纷',
value: reportData.welfare_disputes.welfare,
details: getStatusText(reportData.welfare_disputes.welfare),
bgColor: getBackgroundColor(reportData.welfare_disputes.welfare),
borderColor: getBorderColor(reportData.welfare_disputes.welfare)
});
}
// 人事争议类纠纷
if (reportData.personnel_disputes) {
let details: string[] = [];
if (reportData.personnel_disputes.personnel_dispute !== undefined) {
details.push(`人事争议: ${getStatusText(reportData.personnel_disputes.personnel_dispute)}`);
}
if (reportData.personnel_disputes.resignation_dispute !== undefined) {
details.push(`辞职争议: ${getStatusText(reportData.personnel_disputes.resignation_dispute)}`);
}
if (reportData.personnel_disputes.resignation_dispute_3y !== undefined) {
const detail = `近3年辞职: ${getStatusText(reportData.personnel_disputes.resignation_dispute_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.personnel_disputes.resignation_dispute_5y !== undefined) {
const detail = `近5年辞职: ${getStatusText(reportData.personnel_disputes.resignation_dispute_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.personnel_disputes.dismissal_dispute !== undefined) {
details.push(`辞退争议: ${getStatusText(reportData.personnel_disputes.dismissal_dispute)}`);
}
if (reportData.personnel_disputes.dismissal_dispute_3y !== undefined) {
const detail = `近3年辞退: ${getStatusText(reportData.personnel_disputes.dismissal_dispute_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.personnel_disputes.dismissal_dispute_5y !== undefined) {
const detail = `近5年辞退: ${getStatusText(reportData.personnel_disputes.dismissal_dispute_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (details.length > 0) {
risks.push({
title: '人事争议类纠纷',
value: Math.max(...Object.values(reportData.personnel_disputes).filter(v => typeof v === 'number')),
details: details,
bgColor: getBackgroundColor(Math.max(...Object.values(reportData.personnel_disputes).filter(v => typeof v === 'number'))),
borderColor: getBorderColor(Math.max(...Object.values(reportData.personnel_disputes).filter(v => typeof v === 'number')))
});
}
}
// 仲裁相关案件
if (reportData.arbitration) {
let details: string[] = [];
if (reportData.arbitration.arbitration_confirmation !== undefined) {
details.push(`仲裁确认: ${getStatusText(reportData.arbitration.arbitration_confirmation)}`);
}
if (reportData.arbitration.arbitration_confirmation_3y !== undefined) {
const detail = `近3年仲裁确认: ${getStatusText(reportData.arbitration.arbitration_confirmation_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.arbitration.arbitration_confirmation_5y !== undefined) {
const detail = `近5年仲裁确认: ${getStatusText(reportData.arbitration.arbitration_confirmation_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.arbitration.arbitration_revocation !== undefined) {
details.push(`仲裁撤销: ${getStatusText(reportData.arbitration.arbitration_revocation)}`);
}
if (reportData.arbitration.arbitration_revocation_3y !== undefined) {
const detail = `近3年仲裁撤销: ${getStatusText(reportData.arbitration.arbitration_revocation_3y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (reportData.arbitration.arbitration_revocation_5y !== undefined) {
const detail = `近5年仲裁撤销: ${getStatusText(reportData.arbitration.arbitration_revocation_5y)}`;
if (!shouldHideItem(detail)) {
details.push(detail);
}
}
if (details.length > 0) {
risks.push({
title: '仲裁相关案件',
value: Math.max(...Object.values(reportData.arbitration).filter(v => typeof v === 'number')),
details: details,
bgColor: getBackgroundColor(Math.max(...Object.values(reportData.arbitration).filter(v => typeof v === 'number'))),
borderColor: getBorderColor(Math.max(...Object.values(reportData.arbitration).filter(v => typeof v === 'number')))
});
}
}
// 通知函触达
if (reportData.notice_letter && reportData.notice_letter.notice_letter !== undefined) {
let statusText = getNoticeLetterStatusText(reportData.notice_letter.notice_letter);
let periodText = '';
if (reportData.notice_letter.notice_letter_period !== undefined) {
periodText = `期间: ${getNoticeLetterPeriodText(reportData.notice_letter.notice_letter_period)}`;
}
const detailParts = [`状态: ${statusText}`];
if (periodText) {
detailParts.push(periodText);
}
risks.push({
title: '通知函触达',
value: reportData.notice_letter.notice_letter,
details: detailParts,
bgColor: getBackgroundColor(reportData.notice_letter.notice_letter),
borderColor: getBorderColor(reportData.notice_letter.notice_letter)
});
}
return risks;
});
// 检查是否至少有一个数据类别有内容 // 检查是否至少有一个数据类别有内容
const hasAnyData = computed(() => { const hasAnyData = computed(() => {
return riskTypes.value.length > 0; const r = result.value;
return Object.keys(r).length > 0;
}); });
// 汇总数据 - 按分类分组 { key, title, rows }
const summaryGroups = computed(() => {
const groups = [];
const basic = result.value.basic_info;
if (basic?.risk_flag !== undefined) {
groups.push({ key: 'basic', title: '基础风险', rows: [{ label: '该人员是否有风险', value: basic.risk_flag }] });
}
const dishonesty = result.value.dishonesty?.dishonesty;
const highConsumption = result.value.high_consumption?.high_consumption;
if (dishonesty !== undefined || highConsumption !== undefined) {
const rows = [];
if (dishonesty !== undefined) rows.push({ label: '失信人员风险', value: dishonesty });
if (highConsumption !== undefined) rows.push({ label: '限制高消费人员风险', value: highConsumption });
groups.push({ key: 'credit', title: '失信限高', rows });
}
const labor = result.value.labor_disputes;
if (labor) {
const items = [['劳动争议', labor.labor_disputes], ['劳动合同纠纷', labor.labor_contract], ['劳动关系纠纷', labor.labor_relation], ['追索劳动报酬纠纷', labor.wage_claim], ['经济补偿金纠纷', labor.compensation], ['集体合同纠纷', labor.collective_contract], ['劳务派遣合同纠纷', labor.dispatch_contract], ['非全日制用工纠纷', labor.part_time], ['竞业限制纠纷', labor.non_compete]];
const rows = items.filter((item) => item[1] !== undefined).map((item) => ({ label: item[0], value: item[1] }));
if (rows.length) groups.push({ key: 'labor', title: '劳动争议', rows });
}
const social = result.value.social_insurance;
if (social) {
const items = [['社会保险纠纷', social.social_insurance], ['养老保险待遇纠纷', social.pension], ['工伤保险待遇纠纷', social.injury_insurance], ['医疗保险待遇纠纷', social.medical_insurance], ['生育保险待遇纠纷', social.maternity_insurance], ['商业保险待遇纠纷', social.commercial_insurance]];
const rows = items.filter((item) => item[1] !== undefined).map((item) => ({ label: item[0], value: item[1] }));
if (rows.length) groups.push({ key: 'social', title: '社会保险', rows });
}
if (result.value.welfare_disputes?.welfare !== undefined) {
groups.push({ key: 'welfare', title: '福利待遇', rows: [{ label: '福利待遇纠纷', value: result.value.welfare_disputes.welfare }] });
}
const personnel = result.value.personnel_disputes;
if (personnel) {
const items = [['人事争议类纠纷', personnel.personnel_dispute], ['辞职争议纠纷', personnel.resignation_dispute], ['辞退争议纠纷', personnel.dismissal_dispute], ['聘用合同争议纠纷', personnel.employment_contract]];
const rows = items.filter((item) => item[1] !== undefined).map((item) => ({ label: item[0], value: item[1] }));
if (rows.length) groups.push({ key: 'personnel', title: '人事争议', rows });
}
const arb = result.value.arbitration;
if (arb && (arb.arbitration_confirmation !== undefined || arb.arbitration_revocation !== undefined)) {
const rows = [];
if (arb.arbitration_confirmation !== undefined) rows.push({ label: '申请仲裁确认', value: arb.arbitration_confirmation });
if (arb.arbitration_revocation !== undefined) rows.push({ label: '撤销仲裁裁决', value: arb.arbitration_revocation });
groups.push({ key: 'arbitration', title: '仲裁流程', rows });
}
const notice = result.value.notice_letter;
if (notice?.notice_letter !== undefined) {
const rows = [{ label: '通知函触达', value: notice.notice_letter }];
if (notice.notice_letter_period !== undefined && notice.notice_letter === 2) rows.push({ label: '通知函发送时间', value: null, period: notice.notice_letter_period });
groups.push({ key: 'notice', title: '通知函触达', rows });
}
return groups;
});
const summaryRows = computed(() => summaryGroups.value.flatMap((g) => g.rows));
// 真正的风险项(文档:失信限高、劳动争议、社会保险、福利待遇、人事争议、仲裁流程、通知函触达)
// 排除 basic_info.risk_flag汇总结论和 notice_letter_period非风险项
const riskItemRows = computed(() =>
summaryGroups.value
.filter((g) => g.key !== 'basic')
.flatMap((g) => g.rows)
.filter((r) => r.value === 1 || r.value === 2)
);
// 近三年/近五年 - 按分类分组
const periodGroups = computed(() => {
const suffix = periodTab.value === 'threeYears' ? '_3y' : '_5y';
const groups = [];
const labor = result.value.labor_disputes;
if (labor) {
const keys = ['labor_disputes', 'labor_relation', 'wage_claim', 'compensation', 'collective_contract', 'dispatch_contract', 'part_time', 'non_compete'];
const labels = ['劳动争议', '劳动关系', '追索劳动报酬', '经济补偿金', '集体合同', '劳务派遣', '非全日制用工', '竞业限制'];
const rows = keys.map((k, i) => ({ label: labels[i], value: labor[k + suffix] })).filter((r) => r.value === 2).map((r) => ({ label: r.label, value: 2 }));
if (rows.length) groups.push({ key: 'labor', title: '劳动争议', rows });
}
const social = result.value.social_insurance;
if (social) {
const keys = ['pension', 'injury_insurance', 'medical_insurance', 'maternity_insurance', 'commercial_insurance'];
const labels = ['养老保险', '工伤保险', '医疗保险', '生育保险', '商业保险'];
const rows = keys.map((k, i) => ({ label: labels[i], value: social[k + suffix] })).filter((r) => r.value === 2).map((r) => ({ label: r.label, value: 2 }));
if (rows.length) groups.push({ key: 'social', title: '社会保险', rows });
}
const personnel = result.value.personnel_disputes;
if (personnel) {
const keys = ['resignation_dispute', 'dismissal_dispute', 'employment_contract'];
const labels = ['辞职争议', '辞退争议', '聘用合同'];
const rows = keys.map((k, i) => ({ label: labels[i], value: personnel[k + suffix] })).filter((r) => r.value === 2).map((r) => ({ label: r.label, value: 2 }));
if (rows.length) groups.push({ key: 'personnel', title: '人事争议', rows });
}
const arb = result.value.arbitration;
if (arb) {
const rows = [];
if (arb[`arbitration_confirmation${suffix}`] === 2) rows.push({ label: '申请仲裁确认', value: 2 });
if (arb[`arbitration_revocation${suffix}`] === 2) rows.push({ label: '撤销仲裁裁决', value: 2 });
if (rows.length) groups.push({ key: 'arbitration', title: '仲裁流程', rows });
}
return groups;
});
const periodRows = computed(() => periodGroups.value.flatMap((g) => g.rows));
// 用于 riskScore需要近三年+近五年全部数据
const recentThreeYearsRows = computed(() => {
const r = result.value;
const rows = [];
const labor = r.labor_disputes;
if (labor) {
[['labor_disputes_3y'], ['labor_relation_3y'], ['wage_claim_3y'], ['compensation_3y'], ['collective_contract_3y'], ['dispatch_contract_3y'], ['part_time_3y'], ['non_compete_3y']].forEach(([k]) => { if (labor[k] === 2) rows.push({ value: 2 }); });
}
const social = r.social_insurance;
if (social) {
['pension_3y', 'injury_insurance_3y', 'medical_insurance_3y', 'maternity_insurance_3y', 'commercial_insurance_3y'].forEach((k) => { if (social[k] === 2) rows.push({ value: 2 }); });
}
const personnel = r.personnel_disputes;
if (personnel) {
['resignation_dispute_3y', 'dismissal_dispute_3y', 'employment_contract_3y'].forEach((k) => { if (personnel[k] === 2) rows.push({ value: 2 }); });
}
const arb = r.arbitration;
if (arb) {
if (arb.arbitration_confirmation_3y === 2) rows.push({ value: 2 });
if (arb.arbitration_revocation_3y === 2) rows.push({ value: 2 });
}
return rows;
});
const recentFiveYearsRows = computed(() => {
const r = result.value;
const rows = [];
const labor = r.labor_disputes;
if (labor) {
[['labor_disputes_5y'], ['labor_relation_5y'], ['wage_claim_5y'], ['compensation_5y'], ['collective_contract_5y'], ['dispatch_contract_5y'], ['part_time_5y'], ['non_compete_5y']].forEach(([k]) => { if (labor[k] === 2) rows.push({ value: 2 }); });
}
const social = r.social_insurance;
if (social) {
['pension_5y', 'injury_insurance_5y', 'medical_insurance_5y', 'maternity_insurance_5y', 'commercial_insurance_5y'].forEach((k) => { if (social[k] === 2) rows.push({ value: 2 }); });
}
const personnel = r.personnel_disputes;
if (personnel) {
['resignation_dispute_5y', 'dismissal_dispute_5y', 'employment_contract_5y'].forEach((k) => { if (personnel[k] === 2) rows.push({ value: 2 }); });
}
const arb = r.arbitration;
if (arb) {
if (arb.arbitration_confirmation_5y === 2) rows.push({ value: 2 });
if (arb.arbitration_revocation_5y === 2) rows.push({ value: 2 });
}
return rows;
});
// 头部风险总结:风险类型、建议、命中统计(仅真正的风险项)
const riskSummary = computed(() => {
const basic = result.value.basic_info;
const hasRisk = basic?.risk_flag === 2;
// 真正的风险项:总项数、命中数(文档:失信限高、劳动争议、社会保险、福利待遇、人事争议、仲裁流程、通知函触达)
const totalItems = riskItemRows.value.length;
const hitItems = riskItemRows.value.filter((r) => r.value === 2).length;
// 命中的风险分类(汇总中 value=2 的 group
const riskCategories = summaryGroups.value
.filter((g) => g.key !== 'basic' && g.rows.some((r) => r.value === 2))
.map((g) => g.title);
// 精简建议(取主要类型合并为一句)
const suggestionMap = {
'失信限高': '征信修复与限高事项',
'劳动争议': '劳动纠纷与薪酬离职',
'社会保险': '社保缴纳与补缴',
'福利待遇': '福利待遇合规',
'人事争议': '辞职辞退与聘用合同',
'仲裁流程': '仲裁案件进展',
'通知函触达': '仲裁调解涉诉通知',
};
const suggestionParts = riskCategories.map((c) => suggestionMap[c]).filter(Boolean);
const suggestion = suggestionParts.length ? `建议关注${suggestionParts.slice(0, 3).join('、')}` : '';
return {
hasRisk,
label: hasRisk ? '有风险' : '无风险',
riskCategories,
suggestion,
totalItems,
hitItems,
};
});
// 风险评分 0-100越高越安全供 BaseReport 分析指数)
// 基于真正的风险项riskItemRows与展示逻辑一致通过 useRiskNotifier 递交 BaseReport
const riskScore = computed(() => {
const basic = result.value.basic_info;
if (!basic || basic.risk_flag === 1) return 100;
const hitItems = riskItemRows.value.filter((r) => r.value === 2).length;
const score = 100 - hitItems * 2; // 每命中 1 项扣 2 分
return Math.max(0, Math.min(100, score));
});
useRiskNotifier(props, riskScore);
defineExpose({ riskScore });
// 借鉴司法涉诉概览:风险图标与背景样式
const getRiskIcon = () => {
if (riskSummary.value.hasRisk) return new URL('@/assets/images/report/gfx.png', import.meta.url).href;
return new URL('@/assets/images/report/zq.png', import.meta.url).href;
};
</script> </script>
<template> <template>
<div class="ivyz0s0d-container"> <div class="report-wrap">
<!-- 风险卡片网格 --> <!-- 头部风险总结底色固定白色命中项用内嵌 card 展现 -->
<div v-if="hasAnyData" class="risk-cards-grid"> <div v-if="hasAnyData" class="risk-summary card">
<div <div class="flex items-center mb-4">
v-for="(risk, index) in riskTypes" <div class="w-12 h-12 mr-3 flex-shrink-0">
:key="index" <img :src="getRiskIcon()" alt="风险" class="w-12 h-12 object-contain" />
class="risk-card" </div>
:style="{ backgroundColor: risk.bgColor, borderLeft: `4px solid ${risk.borderColor}` }" <div class="text-gray-700 text-[15px] leading-relaxed">
> <template v-if="riskSummary.hasRisk">
<div class="risk-card__content"> <span v-if="riskSummary.riskCategories.length">涉及{{ riskSummary.riskCategories.join('')
<h4 class="risk-card__title">{{ risk.title }}</h4> }}</span>
<div class="risk-card__status"> <span v-if="riskSummary.suggestion">{{ riskSummary.suggestion }}</span>
<!-- details 是字符串时显示单行 --> </template>
<p v-if="typeof risk.details === 'string'" class="risk-detail-item">{{ risk.details }}</p> <template v-else>未检测到相关风险</template>
<!-- details 是数组时每项占一行 --> </div>
<p </div>
v-else <!-- 命中项仅显示总项数 / 命中数真正的风险项失信限高劳动争议社会保险福利待遇人事争议仲裁流程通知函触达 -->
v-for="(detail, idx) in risk.details" <div v-if="riskSummary.totalItems > 0" class="inner-card p-4 rounded-xl text-center"
:key="idx" :class="riskSummary.hitItems > 0 ? 'inner-card-risk' : 'inner-card-safe'">
class="risk-detail-item" <div class="text-2xl font-bold mb-1"
>{{ detail }}</p> :class="riskSummary.hitItems > 0 ? 'text-[#EB3C3C]' : 'text-[#10b981]'">
</div> {{ riskSummary.hitItems }}/{{ riskSummary.totalItems }}
</div>
<div class="text-sm font-medium text-gray-800">命中项</div>
</div>
</div> </div>
</div>
</div>
<!-- 无数据提示 --> <!-- Card 1: 汇总 -->
<div v-if="!hasAnyData" class="no-data"> <section class="card">
<p>暂无相关风险数据</p> <header class="card-header">
<span class="card-title">风险概览</span>
<span class="card-subtitle">综合评估结果</span>
</header>
<div v-if="hasAnyData" class="group-list">
<div v-for="(group, gi) in summaryGroups" :key="group.key" class="group-box">
<div class="group-header">
<span class="group-title">{{ group.title }}</span>
</div>
<div class="group-body">
<div v-for="(row, ri) in group.rows" :key="ri" class="data-row">
<span class="data-label">{{ row.label }}</span>
<span v-if="row.period !== undefined" class="data-value data-value-text">{{
getNoticeLetterPeriodText(row.period) }}</span>
<span v-else
:class="['data-value', 'data-badge', row.value === 2 ? 'badge-risk' : 'badge-safe']">
<span class="badge-dot" :class="row.value === 2 ? 'dot-risk' : 'dot-safe'" />
{{ getStatusText(row.value) }}
</span>
</div>
</div>
</div>
</div>
<div v-else class="empty-state">
<span class="empty-icon"></span>
<span class="empty-text">暂无相关风险数据</span>
</div>
</section>
<!-- Card 2: 近三年 / 近五年 -->
<section class="card">
<header class="card-header">
<span class="card-title">时间维度</span>
<span class="card-subtitle">按周期查看命中情况</span>
</header>
<div class="period-tabs">
<button type="button" :class="['period-tab', periodTab === 'threeYears' && 'active']"
@click="periodTab = 'threeYears'">近三年</button>
<button type="button" :class="['period-tab', periodTab === 'fiveYears' && 'active']"
@click="periodTab = 'fiveYears'">近五年</button>
</div>
<div v-if="periodGroups.length" class="group-list">
<div v-for="(group, gi) in periodGroups" :key="group.key" class="group-box">
<div class="group-header">
<span class="group-title">{{ group.title }}</span>
</div>
<div class="group-body">
<div v-for="(row, ri) in group.rows" :key="ri" class="data-row">
<span class="data-label">{{ row.label }}</span>
<span class="data-value data-badge badge-risk">
<span class="badge-dot dot-risk" />
{{ getStatusText(row.value) }}
</span>
</div>
</div>
</div>
</div>
<div v-else class="empty-state">
<span class="empty-icon"></span>
<span class="empty-text">暂无{{ periodTab === 'threeYears' ? '近三年' : '近五年' }}命中数据</span>
</div>
</section>
</div> </div>
</div>
</template> </template>
<style scoped> <style lang="scss" scoped>
.ivyz0s0d-container { .report-wrap {
padding: 20px; display: flex;
font-family: Arial, sans-serif; flex-direction: column;
gap: 16px;
} }
.risk-cards-grid { .card {
display: grid; background: #fff;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); border-radius: 12px;
gap: 15px; padding: 16px;
margin-bottom: 20px; border: 1px solid rgba(0, 0, 0, 0.06);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
} }
.risk-card { .inner-card {
border: 1px solid #e0e0e0; border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 15px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
} }
.risk-card:hover { .inner-card-risk {
transform: translateY(-2px); background: rgba(235, 60, 60, 0.1);
box-shadow: 0 4px 8px rgba(0,0,0,0.15); border-color: rgba(235, 60, 60, 0.3);
} }
.risk-card__content { .inner-card-safe {
display: flex; background: rgba(16, 185, 129, 0.1);
flex-direction: column; border-color: rgba(16, 185, 129, 0.3);
} }
.risk-card__title { .card-header {
margin: 0 0 8px 0; display: flex;
font-size: 16px; flex-direction: column;
font-weight: bold; gap: 2px;
color: #333; margin-bottom: 14px;
padding-bottom: 12px;
border-bottom: 1px solid #f1f5f9;
} }
.risk-card__status { .card-title {
margin: 0; font-size: 16px;
font-size: 14px; font-weight: 600;
color: #666; color: #1e293b;
letter-spacing: 0.02em;
} }
.risk-detail-item { .card-subtitle {
margin: 0 0 4px 0; font-size: 13px;
line-height: 1.4; color: #94a3b8;
} }
.risk-detail-item:last-child { .period-tabs {
margin-bottom: 0; display: flex;
gap: 8px;
margin-bottom: 14px;
padding: 4px;
background: #f8fafc;
border-radius: 10px;
} }
.no-data { .period-tab {
text-align: center; flex: 1;
color: #999; padding: 10px 16px;
font-style: italic; font-size: 15px;
padding: 20px; color: #64748b;
background: transparent;
border: none;
border-radius: 8px;
cursor: pointer;
}
.period-tab.active {
color: #1e293b;
font-weight: 500;
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.group-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.group-box {
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 10px;
overflow: hidden;
}
.group-header {
padding: 12px 16px;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
}
.group-title {
font-size: 16px;
font-weight: 600;
color: #475569;
letter-spacing: 0.03em;
}
.group-body {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.data-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 0;
font-size: 16px;
}
.data-row:not(:last-child) {
border-bottom: 1px solid #f1f5f9;
}
.data-label {
color: #475569;
flex: 1;
margin-right: 12px;
font-size: 16px;
line-height: 1.5;
}
.data-value {
flex-shrink: 0;
}
.data-value-text {
color: #64748b;
font-size: 15px;
}
.data-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 12px;
border-radius: 12px;
font-size: 14px;
font-weight: 500;
}
.badge-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
.badge-risk {
background: #fff1f2;
color: #be123c;
}
.dot-risk {
background: #e11d48;
}
.badge-safe {
background: #f0fdf4;
color: #047857;
}
.dot-safe {
background: #10b981;
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 28px 16px;
}
.empty-icon {
font-size: 20px;
color: #cbd5e1;
font-weight: 300;
}
.empty-text {
font-size: 15px;
color: #94a3b8;
} }
</style> </style>