Files
qncV4uni-app/src/report-ui/vehicles/CQCXG6B4E.vue

494 lines
16 KiB
Vue
Raw Normal View History

2026-05-16 15:47:07 +08:00
<template>
<div class="card">
<div class="header-box">
<div class="header-left">
<h3 class="header-title">车辆出险记录核验</h3>
<p class="header-desc">综合车辆出险脱保重大事故等信息评估风险</p>
</div>
</div>
<template v-if="hasData">
<div class="risk-band" :class="riskLevelClass">
<div class="risk-band-label">风险等级</div>
<div class="risk-band-text">{{ riskLevelText }}</div>
</div>
<!-- 顶部车辆与价格概览 -->
<div class="summary-card">
<div class="summary-main">
<div class="summary-left">
<div class="plate" v-if="data.LicensePlate">{{ data.LicensePlate }}</div>
<div class="car-type">{{ data.CarType || '未知车型' }}</div>
</div>
<div class="summary-right">
<div class="summary-label">二手车价格参考</div>
<div class="summary-price">{{ usedCarPriceText }}</div>
<div class="summary-sub">新车购置价{{ newCarPriceText }}</div>
</div>
</div>
<div class="summary-meta">
<div class="meta-line">
<span>燃料</span><span class="strong">{{ data.FuelType || '未知' }}</span>
<span class="dot"></span>
<span>发动机号</span><span class="strong code">{{ data.EngineNumber || '-' }}</span>
</div>
<div class="meta-line">
<span>初登日期</span><span class="strong">{{ data.DebutDate || '-' }}</span>
<span class="dot"></span>
<span>车龄</span><span class="strong">{{ carAgeText }}</span>
</div>
</div>
</div>
<!-- 核心风险指标 -->
<div class="detail-card">
<h4 class="section-title">核心风险指标</h4>
<div class="field-grid">
<div class="field">
<div class="field-label">是否高风险车辆</div>
<div class="field-value" :class="flagClass(data.IfHighriskVehicle === '1')">
{{ yesNoText(data.IfHighriskVehicle, '高风险车辆') }}
</div>
</div>
<div class="field">
<div class="field-label">是否营运车辆</div>
<div class="field-value" :class="flagClass(data.IsOperation === '1')">
{{ yesNoText(data.IsOperation, '营运车辆') }}
</div>
</div>
<div class="field">
<div class="field-label">是否投保车损险</div>
<div class="field-value" :class="flagClass(data.IfCarDamage === '1')">
{{ yesNoText(data.IfCarDamage, '已投保车损险', '未投保车损险') }}
</div>
</div>
<div class="field">
<div class="field-label">是否连续投保</div>
<div class="field-value" :class="flagClass(data.IsConInsure === '1')">
{{ yesNoText(data.IsConInsure, '连续投保', '非连续投保') }}
</div>
</div>
<div class="field">
<div class="field-label">历史是否脱保</div>
<div class="field-value" :class="flagClass(data.IfTuoBao === '1')">
{{ yesNoText(data.IfTuoBao, '有脱保记录', '无脱保记录') }}
</div>
</div>
<div class="field">
<div class="field-label">历史最大脱保时间</div>
<div class="field-value">{{ data.TuoBaoTime || '-' }}</div>
</div>
<div class="field">
<div class="field-label">最高车损险损失比例</div>
<div class="field-value">{{ data.CompensationRatioo || '-' }}</div>
</div>
<div class="field">
<div class="field-label">车损险综合评分</div>
<div class="field-value strong">{{ data.Total || '-' }}</div>
</div>
</div>
</div>
<!-- 出险与事故情况 -->
<div class="detail-card">
<h4 class="section-title">出险与事故情况</h4>
<div class="field-grid">
<div class="field">
<div class="field-label">商业险出险</div>
<div class="field-value">{{ formatDangerCount(data.CommercialPolicyDangerCount, '商业险') }}</div>
</div>
<div class="field">
<div class="field-label">交强险出险</div>
<div class="field-value">{{ formatDangerCount(data.CompulsoryPolicyDangerCount, '交强险') }}</div>
</div>
<div class="field">
<div class="field-label">三者险出险次数</div>
<div class="field-value">{{ formatDangerCount(data.ThreeRisksDangerCount, '三者险') }}</div>
</div>
<div class="field">
<div class="field-label">全损情况</div>
<div class="field-value">{{ totalLossText }}</div>
</div>
<div class="field field-span">
<div class="field-label">重大事故标志</div>
<div class="field-value">{{ formatMajorAccident(data.MajorAccident) }}</div>
</div>
<div class="field">
<div class="field-label">事故次数</div>
<div class="field-value">{{ data.IsMajorAccidentData || '-' }}</div>
</div>
<div class="field">
<div class="field-label">事故等级</div>
<div class="field-value">{{ data.IsMajorAccidentLevel || '-' }}</div>
</div>
<div class="field field-span">
<div class="field-label">损失部位</div>
<div class="field-value">{{ formatLossPart(data.LossPart) }}</div>
</div>
</div>
</div>
<!-- 保单与责任险可保情况 -->
<div class="detail-card">
<h4 class="section-title">保单与责任险承保情况</h4>
<div class="field-grid">
<div class="field">
<div class="field-label">商业险保单倒计时</div>
<div class="field-value">{{ formatPolicyTime(data.CommercialPolicyTime, '商业险') }}</div>
</div>
<div class="field">
<div class="field-label">交强险保单倒计时</div>
<div class="field-value">{{ formatPolicyTime(data.CompulsoryPolicyTime, '交强险') }}</div>
</div>
<div class="field">
<div class="field-label">商业险过户次数</div>
<div class="field-value">{{ formatTransferCount(data.CommercialPolicyTransferCount) }}</div>
</div>
<div class="field">
<div class="field-label">交强险过户次数</div>
<div class="field-value">{{ formatTransferCount(data.CompulsoryPolicyTransferCount) }}</div>
</div>
<div class="field">
<div class="field-label">是否可投保责任险</div>
<div class="field-value" :class="flagClass(data.IsLiabilityAvailable === 'Y')">
{{ ynText(data.IsLiabilityAvailable, '可投保', '不可投保') }}
</div>
</div>
<div class="field">
<div class="field-label">是否可承保延保</div>
<div class="field-value" :class="flagClass(data.IsExtendAvailable === 'Y')">
{{ ynText(data.IsExtendAvailable, '可承保', '不可承保') }}
</div>
</div>
</div>
</div>
</template>
<div v-else class="empty">
<div class="icon"></div>
<div class="title">暂无出险记录</div>
<div class="sub">未查询到车辆出险记录或返回数据为空</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { useRiskNotifier } from '@/composables/useRiskNotifier';
const props = defineProps({
data: { type: Object, default: () => ({}) },
params: { type: Object, default: () => ({}) },
apiId: { type: String, default: '' },
index: { type: Number, default: 0 },
notifyRiskStatus: { type: Function, default: () => { } },
});
const data = computed(() => props.data || {});
const hasData = computed(() => !!data.value && Object.keys(data.value).length > 0);
const usedCarPriceText = computed(() => {
const v = data.value.UsedCarPrice;
if (!v) return '-';
return `${v}`;
});
const newCarPriceText = computed(() => {
const v = data.value.PurchasePrice;
if (!v) return '-';
return `${v}`;
});
const carAgeText = computed(() => {
const m = data.value.CarAge;
if (!m) return '-';
return `${m} 个月`;
});
const totalLossText = computed(() => {
const v = data.value.TotalLoss;
if (v === '1') return '存在全损记录';
if (v === '0') return '无全损记录';
return '-';
});
// 简单按高风险车辆/重大事故等情况给出一个文字风险等级
const riskLevelText = computed(() => {
if (data.value.IfHighriskVehicle === '1') return '高风险';
if (data.value.IsMajorAccidentLevel && data.value.IsMajorAccidentLevel !== '一般') return '较高风险';
if (data.value.IsMajorAccidentData && data.value.IsMajorAccidentData !== '0') return '有事故记录';
return '风险可控';
});
const riskLevelClass = computed(() => {
const t = riskLevelText.value;
if (t === '高风险') return 'risk-high';
if (t === '较高风险' || t === '有事故记录') return 'risk-mid';
return 'risk-low';
});
const flagClass = (flag) => {
return flag ? 'flag-yes' : 'flag-no';
};
const yesNoText = (val, yesText, noText = '否') => {
if (val === '1') return yesText;
if (val === '0') return noText;
return '-';
};
const ynText = (val, yesText, noText) => {
if (val === 'Y') return yesText;
if (val === 'N') return noText;
return '-';
};
const formatPolicyTime = (val, label) => {
if (!val || val === 'NULL') {
return `当期无${label}保单`;
}
const parts = String(val).split(':');
if (parts.length < 2) return val;
const daysRaw = parts[1];
if (!daysRaw || daysRaw.toLowerCase() === 'null') {
return `${label}保单已过期`;
}
const days = Number(daysRaw);
if (Number.isNaN(days)) return val;
if (days < 0) return `${label}保单已过期`;
return `${label}保单剩余 ${days}`;
};
const formatDangerCount = (val, label) => {
if (!val) return '-';
const parts = String(val).split(':');
const countRaw = parts[1] ?? '';
if (!countRaw || countRaw.toLowerCase() === 'null') {
return `${label}暂无出险记录`;
}
const count = Number(countRaw);
if (Number.isNaN(count)) return val;
if (count === 0) return `${label}暂无出险记录`;
return `${label}出险 ${count}`;
};
const formatTransferCount = (val) => {
if (!val) return '-';
const parts = String(val).split(':');
const countRaw = parts[1] ?? '';
if (!countRaw || countRaw.toLowerCase() === 'null') return '-';
const count = Number(countRaw);
if (Number.isNaN(count)) return val;
return `${count}`;
};
const formatMajorAccident = (val) => {
if (!val) return '-';
const map = {
A: '碰撞',
B: '火自燃',
C: '水淹',
D: '盗抢',
};
const list = [];
String(val)
.split(',')
.forEach((pair) => {
const [k, v] = pair.split(':');
if (v === '1' && map[k]) {
list.push(map[k]);
}
});
if (!list.length) return '无重大事故记录';
return `重大事故类型:${list.join('、')}`;
};
const formatLossPart = (val) => {
if (!val) return '-';
const partMap = {
1: '正前方',
2: '正后方',
3: '顶部',
4: '底部',
5: '前方左侧',
6: '后方左侧',
7: '中间左侧',
8: '前方右侧',
9: '后方右侧',
10: '中间右侧',
11: '内部',
12: '其它',
13: '不详',
};
const items = [];
String(val)
.split(',')
.forEach((pair) => {
const [kRaw, vRaw] = pair.split(':');
const key = Number(kRaw);
const count = Number(vRaw);
if (!Number.isNaN(key) && !Number.isNaN(count) && count > 0) {
const label = partMap[key] || `部位${key}`;
items.push(`${label}${count}`);
}
});
if (!items.length) return '暂无损失部位信息';
return items.join('、');
};
const riskScore = computed(() => 100);
useRiskNotifier(props, riskScore);
defineExpose({ riskScore });
</script>
<style scoped>
.card {
@apply bg-white rounded-2xl p-6 shadow-sm border border-gray-100;
}
.header-box {
@apply flex items-center mb-3 px-5 py-4 rounded-2xl bg-gradient-to-r from-rose-50 via-orange-50 to-amber-50;
}
.header-left {
@apply flex flex-col;
}
.header-title {
@apply text-2xl font-semibold m-0 text-rose-900;
}
.header-desc {
@apply text-base mt-3 m-0 text-rose-800 opacity-90;
}
.risk-band {
@apply mb-4 px-4 py-3 rounded-2xl flex items-center justify-between;
}
.risk-band.risk-high {
@apply bg-red-50 border border-red-100;
}
.risk-band.risk-mid {
@apply bg-amber-50 border border-amber-100;
}
.risk-band.risk-low {
@apply bg-emerald-50 border border-emerald-100;
}
.risk-band-label {
@apply text-sm text-gray-600;
}
.risk-band-text {
@apply text-xl font-bold;
}
.summary-card {
@apply rounded-2xl border border-amber-100 bg-amber-50/60 px-5 py-4 mb-4;
}
.summary-main {
@apply flex items-start justify-between mb-3 gap-4;
}
.summary-left {
@apply flex flex-col gap-2;
}
.plate {
@apply inline-flex items-center px-4 py-2 rounded-full bg-slate-900 text-white text-xl font-semibold tracking-widest;
}
.car-type {
@apply text-lg font-medium text-gray-800;
}
.summary-right {
@apply text-right;
}
.summary-label {
@apply text-sm text-gray-500;
}
.summary-price {
@apply text-2xl font-bold text-amber-800 mt-1;
}
.summary-sub {
@apply text-sm text-amber-700 opacity-90;
}
.summary-meta {
@apply space-y-1 text-base text-gray-800;
}
.meta-line {
@apply flex flex-wrap items-center gap-2;
}
.meta-line .dot {
@apply w-1 h-1 rounded-full bg-gray-400;
}
.strong {
@apply font-semibold;
}
.code {
@apply font-mono tracking-wide;
}
.detail-card {
@apply rounded-2xl border border-gray-100 bg-gray-50/60 px-5 py-4 mb-4;
}
.section-title {
@apply text-base font-semibold text-gray-800 mb-3;
}
.field-grid {
@apply grid gap-y-3 gap-x-6;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.field-label {
@apply text-sm text-gray-500 mb-1;
}
.field-value {
@apply text-base text-gray-900;
}
.field-span {
grid-column: 1 / -1;
}
.flag-yes {
@apply text-red-700;
}
.flag-no {
@apply text-emerald-700;
}
.empty {
@apply text-center py-10 text-gray-500;
}
.empty .icon {
@apply text-3xl mb-2;
}
.empty .title {
@apply text-lg font-medium mb-1;
}
.empty .sub {
@apply text-sm;
}
</style>