f
This commit is contained in:
1
src/auto-imports.d.ts
vendored
1
src/auto-imports.d.ts
vendored
@@ -284,6 +284,7 @@ declare global {
|
||||
const useUserStore: typeof import('./stores/userStore.js')['useUserStore']
|
||||
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||
const useVehiclePayload: typeof import('./composables/useVehiclePayload.js')['useVehiclePayload']
|
||||
const useVibrate: typeof import('@vueuse/core')['useVibrate']
|
||||
const useVirtualList: typeof import('@vueuse/core')['useVirtualList']
|
||||
const useWakeLock: typeof import('@vueuse/core')['useWakeLock']
|
||||
|
||||
@@ -14,6 +14,8 @@ import { useAgentStore } from "@/stores/agentStore";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { showFailToast } from "vant";
|
||||
import { checkFeatureWhitelistStatus, offlineFeature, checkOrderAgent } from "@/api/agent";
|
||||
import { vehicleFeatureMap, vehicleFeatureRiskLevels } from "@/config/vehicleFeatureMap";
|
||||
import { normalizeVehicleReportData } from "@/utils/vehicleReportNormalize";
|
||||
|
||||
// 动态导入产品背景图片的函数
|
||||
const loadProductBackground = async (productType) => {
|
||||
@@ -226,8 +228,10 @@ const processedReportData = computed(() => {
|
||||
|
||||
// 拆分CQYGL3F8E数据
|
||||
data = splitCQYGL3F8EForTabs(data);
|
||||
// 车辆报告:解包嵌套 data,与微信小程序 normalizeVehicleQueryData 对齐
|
||||
data = normalizeVehicleReportData(data);
|
||||
// 过滤掉在featureMap中没有对应的项
|
||||
return data.filter(item => featureMap[item.data.apiID]);
|
||||
return data.filter(item => featureMap[item.data?.apiID]);
|
||||
});
|
||||
|
||||
// 获取产品背景图片
|
||||
@@ -553,14 +557,7 @@ const featureMap = {
|
||||
name: "税务风险",
|
||||
component: defineAsyncComponent(() => import("@/ui/CQYGL3F8E/components/TaxRisk/index.vue")),
|
||||
},
|
||||
QCXG7A2B: {
|
||||
name: "名下车辆",
|
||||
component: defineAsyncComponent(() => import("@/ui/CQCXG7A2B.vue")),
|
||||
},
|
||||
QCXG9P1C: {
|
||||
name: "名下车辆",
|
||||
component: defineAsyncComponent(() => import("@/ui/CQCXG9P1C.vue")),
|
||||
},
|
||||
...vehicleFeatureMap,
|
||||
BehaviorRiskScan: {
|
||||
name: "风险行为扫描",
|
||||
component: defineAsyncComponent(() =>
|
||||
@@ -917,7 +914,7 @@ const featureRiskLevels = {
|
||||
|
||||
// 🟢 中风险类 - 权重 8-12
|
||||
'QYGL3F8E': 10, // 人企关系加强版
|
||||
'QCXG7A2B': 10, // 名下车辆
|
||||
...vehicleFeatureRiskLevels,
|
||||
'JRZQ09J8': 10, // 收入评估
|
||||
'JRZQ3C9R': 10, // 支付行为指数
|
||||
// 'IVYZ0S0D': 10, // 个人仲裁信息
|
||||
|
||||
@@ -203,6 +203,19 @@ const getFeatureIcon = (apiId) => {
|
||||
JRZQ4AA8: "/inquire_icons/huankuanyali.svg",
|
||||
QCXG7A2B: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG9P1C: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG4D2E: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG5F3A: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG5U0Z: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXGGB2Q: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXGYTS2: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG1H7Y: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG4I1Z: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG1U4U: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG3Y6B: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG3Z3L: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXG6B4E: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXGP00W: "/inquire_icons/mingxiacheliang.svg",
|
||||
QCXGY7F2: "/inquire_icons/mingxiacheliang.svg",
|
||||
BehaviorRiskScan: "/inquire_icons/fengxianxingwei.svg",
|
||||
IVYZ5733: "/inquire_icons/hunyinzhuangtai.svg",
|
||||
IVYZ81NC: "/inquire_icons/hunyinzhuangtai.svg",
|
||||
|
||||
19
src/composables/useVehiclePayload.js
Normal file
19
src/composables/useVehiclePayload.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { computed } from 'vue'
|
||||
import { payloadAsArray, payloadAsObject, unwrapVehiclePayload } from '@/utils/vehiclePayload'
|
||||
|
||||
const defaultProps = {
|
||||
data: null,
|
||||
params: () => ({}),
|
||||
apiId: '',
|
||||
index: 0,
|
||||
notifyRiskStatus: () => {},
|
||||
}
|
||||
|
||||
export function useVehiclePayload(props = defaultProps) {
|
||||
const payload = computed(() => unwrapVehiclePayload(props.data))
|
||||
const obj = computed(() => payloadAsObject(props.data))
|
||||
const list = computed(() => payloadAsArray(props.data))
|
||||
const params = computed(() => props.params || {})
|
||||
|
||||
return { payload, obj, list, params }
|
||||
}
|
||||
41
src/config/vehicleFeatureMap.js
Normal file
41
src/config/vehicleFeatureMap.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import { VEHICLE_API_TITLES } from './vehicleReportRegistry'
|
||||
|
||||
/** 已单独实现 UI 的 apiID */
|
||||
const VEHICLE_COMPONENT_LOADERS = {
|
||||
QCXG7A2B: () => import('@/ui/CQCXG7A2B.vue'),
|
||||
QCXG9P1C: () => import('@/ui/CQCXG9P1C.vue'),
|
||||
QCXG4D2E: () => import('@/ui/CQCXG4D2E.vue'),
|
||||
QCXG5F3A: () => import('@/ui/CQCXG5F3A.vue'),
|
||||
QCXG5U0Z: () => import('@/ui/CQCXG5U0Z.vue'),
|
||||
QCXGGB2Q: () => import('@/ui/CQCXGGB2Q.vue'),
|
||||
QCXGYTS2: () => import('@/ui/CQCXGYTS2.vue'),
|
||||
QCXG1H7Y: () => import('@/ui/CQCXG1H7Y.vue'),
|
||||
QCXG4I1Z: () => import('@/ui/CQCXG4I1Z.vue'),
|
||||
QCXG1U4U: () => import('@/ui/CQCXG1U4U.vue'),
|
||||
QCXG3Y6B: () => import('@/ui/CQCXG3Y6B.vue'),
|
||||
QCXG3Z3L: () => import('@/ui/CQCXG3Z3L.vue'),
|
||||
QCXG6B4E: () => import('@/ui/CQCXG6B4E.vue'),
|
||||
QCXGP00W: () => import('@/ui/CQCXGP00W.vue'),
|
||||
QCXGY7F2: () => import('@/ui/CQCXGY7F2.vue'),
|
||||
}
|
||||
|
||||
const fallbackLoader = () => import('@/ui/CQCXGFallback.vue')
|
||||
|
||||
export const vehicleFeatureMap = Object.fromEntries(
|
||||
Object.entries(VEHICLE_API_TITLES).map(([apiId, name]) => {
|
||||
const loader = VEHICLE_COMPONENT_LOADERS[apiId] || fallbackLoader
|
||||
return [
|
||||
apiId,
|
||||
{
|
||||
name,
|
||||
component: defineAsyncComponent(loader),
|
||||
},
|
||||
]
|
||||
}),
|
||||
)
|
||||
|
||||
/** 车辆类模块默认低风险权重 */
|
||||
export const vehicleFeatureRiskLevels = Object.fromEntries(
|
||||
Object.keys(VEHICLE_API_TITLES).map(id => [id, 10]),
|
||||
)
|
||||
45
src/config/vehicleReportRegistry.js
Normal file
45
src/config/vehicleReportRegistry.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 车辆类报告模块:apiID(产品能力编码)→ 展示名称
|
||||
* 与 qncV4uni-app、后端 ProductFeature / 上游 api 约定对齐
|
||||
*/
|
||||
export const VEHICLE_API_TITLES = {
|
||||
QCXG9F5C: '疑似营运车辆注册平台数',
|
||||
QCXG3B8Z: '疑似运营车辆查询(月度里程)',
|
||||
QCXGP1W3: '疑似运营车辆查询(季度里程)',
|
||||
QCXGM7R9: '疑似运营车辆查询(半年度里程)',
|
||||
QCXGU2K4: '疑似运营车辆查询(年度里程)',
|
||||
QCXGY7F2: '二手车 VIN 估值',
|
||||
QCXG5U0Z: '车辆静态信息查询',
|
||||
QCXG3M7Z: '人车关系核验(ETC)',
|
||||
QCXG1U4U: '车辆里程记录(混合查询)',
|
||||
QCXG2T6S: '车辆里程记录(品牌查询)',
|
||||
QCXG3Y6B: '车辆维保简版查询',
|
||||
QCXG3Z3L: '车辆维保详细版查询',
|
||||
QCXG1H7Y: '车辆过户简版查询',
|
||||
QCXG4I1Z: '车辆过户详版查询',
|
||||
QCXGGB2Q: '车辆二要素核验简版',
|
||||
QCXGP00W: '车辆出险详版查询',
|
||||
QCXGYTS2: '人车核验(详版)',
|
||||
QCXGGJ3A: '车辆 VIN 码查询号牌简版',
|
||||
QCXGJJ2A: '车辆 VIN 码查车辆信息详版',
|
||||
QCXG5F3A: '名下车辆车牌查询 B',
|
||||
QCXG4D2E: '名下车辆数量查询',
|
||||
QCXG6B4E: '车辆出险记录核验',
|
||||
QCXG8A3D: '车辆七项信息核验',
|
||||
QCXG9P1C: '名下车辆车牌查询 A',
|
||||
QCXG7A2B: '名下车辆',
|
||||
}
|
||||
|
||||
export function getVehicleModuleTitle(apiId, featureName) {
|
||||
if (!apiId || apiId === '__UNLABELED__') {
|
||||
if (featureName?.trim())
|
||||
return featureName.trim()
|
||||
return '报告模块'
|
||||
}
|
||||
const t = VEHICLE_API_TITLES[apiId]
|
||||
if (t)
|
||||
return t
|
||||
if (featureName?.trim())
|
||||
return featureName.trim()
|
||||
return apiId
|
||||
}
|
||||
76
src/ui/CQCXG1H7Y.vue
Normal file
76
src/ui/CQCXG1H7Y.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj } = useVehiclePayload(props)
|
||||
|
||||
const hasData = computed(() => Object.keys(obj.value).length > 0)
|
||||
const flag = computed(() => obj.value.transferFlag)
|
||||
|
||||
const flagText = computed(() => {
|
||||
if (flag.value === '1' || flag.value === 1) return '已过户'
|
||||
if (flag.value === '0' || flag.value === 0) return '未过户'
|
||||
return '未知'
|
||||
})
|
||||
|
||||
const formattedTransferDate = computed(() => {
|
||||
const raw = obj.value.transferDate
|
||||
if (!raw) return '-'
|
||||
if (raw === '近一年内过户') return raw
|
||||
const s = String(raw)
|
||||
if (s.length === 6) return `${s.slice(0, 4)}年${s.slice(4, 6)}月`
|
||||
return s
|
||||
})
|
||||
|
||||
const transferNum = computed(() => {
|
||||
const n = obj.value.transferNum
|
||||
return n === '' || n == null ? '0' : String(n)
|
||||
})
|
||||
|
||||
const bandClass = computed(() => {
|
||||
if (flag.value === '1' || flag.value === 1) return 'bg-amber-50 border-amber-200'
|
||||
if (flag.value === '0' || flag.value === 0) return 'bg-green-50 border-green-200'
|
||||
return 'bg-gray-50 border-gray-200'
|
||||
})
|
||||
|
||||
const riskScore = computed(() => ((flag.value === '1' || flag.value === 1) ? 70 : 100))
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-sky-50 to-blue-50 rounded-lg p-4 border border-sky-100">
|
||||
<h3 class="text-lg font-bold text-sky-900">车辆过户简版查询</h3>
|
||||
<p class="text-sm text-sky-700 mt-1">最近是否发生过户及过户次数</p>
|
||||
<div class="mt-3 inline-block bg-white rounded-lg px-3 py-2 text-right">
|
||||
<div class="text-xs text-gray-500">是否过户</div>
|
||||
<div class="text-xl font-bold">{{ flagText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="hasData" :class="['rounded-lg p-4 border', bandClass]">
|
||||
<div class="font-semibold">最近过户情况</div>
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
{{ flag === 1 || flag === '1' ? '该车辆存在过户记录' : flag === 0 || flag === '0' ? '该车辆暂无过户记录' : '未能识别过户状态' }}
|
||||
</p>
|
||||
<div class="flex justify-between mt-3 text-sm">
|
||||
<span class="text-gray-500">最近过户时间</span>
|
||||
<span class="font-medium">{{ formattedTransferDate }}</span>
|
||||
</div>
|
||||
<div class="flex justify-between mt-2 text-sm">
|
||||
<span class="text-gray-500">累计过户次数</span>
|
||||
<span class="font-bold">{{ transferNum }} 次</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无过户信息</div>
|
||||
</div>
|
||||
</template>
|
||||
104
src/ui/CQCXG1U4U.vue
Normal file
104
src/ui/CQCXG1U4U.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { formatDateZh, formatMileageKm } from '@/utils/vehicleReportBlockMaps'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj } = useVehiclePayload(props)
|
||||
|
||||
const hasData = computed(() => Object.keys(obj.value).length > 0)
|
||||
const vin = computed(() => obj.value.vehicleInfo?.vin || '')
|
||||
const mileageList = computed(() => obj.value.mileageInfo?.mileageList || [])
|
||||
const adjustList = computed(() => obj.value.mileageInfo?.suspectedAdjustMileageList || [])
|
||||
const suspectedAdjust = computed(() => obj.value.mileageInfo?.suspectedAdjust)
|
||||
const imageUrl = computed(() => obj.value.imageUrl || '')
|
||||
|
||||
const latestRecord = computed(() => {
|
||||
const list = mileageList.value
|
||||
return list.length ? list[list.length - 1] : null
|
||||
})
|
||||
|
||||
const suspectedText = computed(() => {
|
||||
if (suspectedAdjust.value === 'true') return '存在异常里程行为'
|
||||
if (suspectedAdjust.value === 'false') return '未发现里程异常'
|
||||
return '未知'
|
||||
})
|
||||
|
||||
function sourceText(source) {
|
||||
if (source === '0') return '诊断里程'
|
||||
if (source === '1') return '维保里程'
|
||||
return '其他'
|
||||
}
|
||||
|
||||
const riskScore = computed(() => (suspectedAdjust.value === 'true' ? 45 : 95))
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-sky-50 to-blue-50 rounded-lg p-4 border border-sky-100">
|
||||
<h3 class="text-lg font-bold text-sky-900">车辆里程记录(混合查询)</h3>
|
||||
<p class="text-sm text-sky-700 mt-1">里程变化与调表嫌疑综合展示</p>
|
||||
</div>
|
||||
|
||||
<template v-if="hasData">
|
||||
<div
|
||||
class="rounded-lg p-4 border"
|
||||
:class="suspectedAdjust === 'true' ? 'bg-amber-50 border-amber-200' : suspectedAdjust === 'false' ? 'bg-green-50 border-green-200' : 'bg-gray-50 border-gray-200'"
|
||||
>
|
||||
<div class="flex justify-between gap-4 flex-wrap">
|
||||
<div>
|
||||
<div class="text-xs text-gray-500">VIN</div>
|
||||
<div class="font-mono text-sm">{{ vin || '-' }}</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="text-xs text-gray-500">最新里程</div>
|
||||
<div class="text-xl font-bold text-blue-600">{{ latestRecord ? formatMileageKm(latestRecord.mileage) : '-' }}</div>
|
||||
<div class="text-xs text-gray-500">最近:{{ latestRecord?.reportTime || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm mt-2">里程是否异常:{{ suspectedText }}</p>
|
||||
<img v-if="imageUrl" :src="imageUrl" class="w-full mt-2 rounded-lg" alt="里程凭证" />
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
||||
<h4 class="font-semibold mb-3">里程记录时间轴</h4>
|
||||
<div v-if="mileageList.length" class="space-y-3">
|
||||
<div v-for="(item, idx) in mileageList" :key="idx" class="flex gap-3">
|
||||
<div class="w-2 rounded-full shrink-0" :class="item.mileageStatus === '1' ? 'bg-red-500' : 'bg-blue-500'" />
|
||||
<div class="flex-1 text-sm">
|
||||
<div class="flex justify-between">
|
||||
<span>{{ formatDateZh(item.reportTime) }}</span>
|
||||
<span class="font-semibold text-blue-600">{{ formatMileageKm(item.mileage) }}</span>
|
||||
</div>
|
||||
<div class="text-gray-500 mt-1">
|
||||
来源:{{ sourceText(item.source) }}
|
||||
<span v-if="item.mileageStatus === '1'" class="ml-2 text-red-600 bg-red-50 px-1 rounded text-xs">异常里程</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-gray-500 text-sm">暂无里程记录</div>
|
||||
</div>
|
||||
|
||||
<div v-if="adjustList.length" class="bg-gray-50 rounded-lg p-4 border border-gray-200">
|
||||
<h4 class="font-semibold mb-3">疑似调表记录</h4>
|
||||
<div v-for="(item, idx) in adjustList" :key="idx" class="py-2 border-b border-gray-200 last:border-0 text-sm">
|
||||
<div>{{ formatDateZh(item.reportTime) }}</div>
|
||||
<div class="text-gray-600 mt-1">
|
||||
调整前 {{ formatMileageKm(item.beforeMileage) }} → 调整后 {{ formatMileageKm(item.afterMileage) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无里程数据</div>
|
||||
</div>
|
||||
</template>
|
||||
66
src/ui/CQCXG3Y6B.vue
Normal file
66
src/ui/CQCXG3Y6B.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { formatDateZh, formatMileageKm } from '@/utils/vehicleReportBlockMaps'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj, params } = useVehiclePayload(props)
|
||||
|
||||
const records = computed(() => (Array.isArray(obj.value.record) ? obj.value.record : []))
|
||||
const hasData = computed(() => Object.keys(obj.value).length > 0)
|
||||
const vin = computed(() => (records.value[0]?.vin) || params.value.vin_code || '')
|
||||
const lastRecord = computed(() => (records.value.length ? records.value[records.value.length - 1] : null))
|
||||
|
||||
const riskScore = computed(() => 95)
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-sky-50 to-blue-50 rounded-lg p-4 border border-sky-100">
|
||||
<h3 class="text-lg font-bold">车辆维保简版查询</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">按时间轴展示维保与材料明细</p>
|
||||
</div>
|
||||
|
||||
<template v-if="hasData">
|
||||
<div class="bg-gray-50 rounded-lg p-4 border text-sm">
|
||||
<div class="font-mono">VIN:{{ vin || '-' }}</div>
|
||||
<div class="text-gray-600 mt-1">维保 {{ records.length }} 条
|
||||
<span v-if="lastRecord"> · 最近 {{ formatDateZh(lastRecord.lastTime) }} · {{ formatMileageKm(lastRecord.mileage) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="records.length" class="space-y-3">
|
||||
<div v-for="(item, idx) in records" :key="idx" class="bg-white rounded-lg p-4 border border-gray-200">
|
||||
<div class="flex justify-between font-medium">
|
||||
<span>{{ formatDateZh(item.lastTime) }}</span>
|
||||
<span class="text-blue-600">{{ formatMileageKm(item.mileage) }}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 mt-1">{{ item.repairType || '维保' }} · VIN {{ item.vin || vin || '-' }}</p>
|
||||
<div v-if="item.details?.length" class="mt-2 text-sm">
|
||||
<div class="text-xs text-gray-500 mb-1">维修项目</div>
|
||||
<div v-for="(det, di) in item.details" :key="di" class="py-1">
|
||||
<span v-if="det.type" class="text-xs bg-blue-50 text-blue-700 px-1 rounded mr-1">{{ det.type }}</span>{{ det.content }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.materials?.length" class="mt-2 text-sm">
|
||||
<div class="text-xs text-gray-500 mb-1">使用材料</div>
|
||||
<div v-for="(m, mi) in item.materials" :key="mi" class="py-1">
|
||||
<span v-if="m.type" class="text-xs bg-gray-100 px-1 rounded mr-1">{{ m.type }}</span>{{ m.content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-gray-500 py-6">暂无维保记录</div>
|
||||
</template>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无维保数据</div>
|
||||
</div>
|
||||
</template>
|
||||
53
src/ui/CQCXG3Z3L.vue
Normal file
53
src/ui/CQCXG3Z3L.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { formatDateZh, formatMileageKm } from '@/utils/vehicleReportBlockMaps'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj } = useVehiclePayload(props)
|
||||
|
||||
const records = computed(() => (Array.isArray(obj.value.record) ? obj.value.record : []))
|
||||
const hasData = computed(() => Object.keys(obj.value).length > 0)
|
||||
|
||||
const riskScore = computed(() => 95)
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-sky-50 to-blue-50 rounded-lg p-4 border border-sky-100">
|
||||
<h3 class="text-lg font-bold">车辆维保详细版查询</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">品牌、VIN 及每次维保详细内容</p>
|
||||
</div>
|
||||
|
||||
<template v-if="hasData">
|
||||
<div class="bg-gray-50 rounded-lg p-4 border text-sm grid grid-cols-2 gap-2">
|
||||
<div><span class="text-gray-500">品牌</span><div class="font-medium">{{ obj.brandName || '未知' }}</div></div>
|
||||
<div><span class="text-gray-500">VIN</span><div class="font-mono">{{ obj.vin || '-' }}</div></div>
|
||||
<div class="col-span-2 text-gray-600">车牌 {{ obj.licensePlate || '未提供' }} · 发动机 {{ obj.engine || '-' }}</div>
|
||||
</div>
|
||||
|
||||
<div v-if="records.length" class="space-y-3">
|
||||
<div v-for="(item, idx) in records" :key="idx" class="bg-white rounded-lg p-4 border">
|
||||
<div class="flex justify-between font-medium">
|
||||
<span>{{ formatDateZh(item.date) }}</span>
|
||||
<span class="text-blue-600">{{ formatMileageKm(item.mileage) }}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500">{{ item.type || '维保' }}</p>
|
||||
<p v-if="item.content" class="text-sm mt-2">维修内容:{{ item.content }}</p>
|
||||
<p v-if="item.remark" class="text-xs text-gray-500 mt-1">{{ item.remark }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center text-gray-500 py-6">暂无维保记录</div>
|
||||
</template>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无维保数据</div>
|
||||
</div>
|
||||
</template>
|
||||
15
src/ui/CQCXG4D2E.vue
Normal file
15
src/ui/CQCXG4D2E.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup>
|
||||
import VehiclePlateList from '@/ui/vehicle/VehiclePlateList.vue'
|
||||
|
||||
defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VehiclePlateList v-bind="$props" title="名下车辆数量查询" />
|
||||
</template>
|
||||
84
src/ui/CQCXG4I1Z.vue
Normal file
84
src/ui/CQCXG4I1Z.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { payloadAsArray } from '@/utils/vehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: { default: null },
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const transfers = computed(() => {
|
||||
return payloadAsArray(props.data).map((item) => {
|
||||
const changeMonth = item.changeMonth
|
||||
let changeMonthFormatted = '-'
|
||||
if (changeMonth === '近一年内过户') changeMonthFormatted = changeMonth
|
||||
else if (typeof changeMonth === 'string' && changeMonth.length === 6) {
|
||||
changeMonthFormatted = `${changeMonth.slice(0, 4)}年${changeMonth.slice(4, 6)}月`
|
||||
}
|
||||
else if (changeMonth) changeMonthFormatted = String(changeMonth)
|
||||
|
||||
let intervalText = '-'
|
||||
if (item.transYear || item.transMonth) {
|
||||
const years = item.transYear ? `${item.transYear}年` : ''
|
||||
const months = item.transMonth ? `${item.transMonth}个月` : ''
|
||||
intervalText = `${years}${months}` || '-'
|
||||
}
|
||||
return { ...item, changeMonthFormatted, intervalText }
|
||||
})
|
||||
})
|
||||
|
||||
const totalTimes = computed(() => {
|
||||
if (!transfers.value.length) return ''
|
||||
const last = transfers.value[transfers.value.length - 1]
|
||||
return last.transTimeSum ?? ''
|
||||
})
|
||||
|
||||
const riskScore = computed(() => (transfers.value.length > 2 ? 65 : 90))
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-sky-50 to-blue-50 rounded-lg p-4 border border-sky-100 flex justify-between items-start gap-3">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold text-sky-900">车辆过户详版查询</h3>
|
||||
<p class="text-sm text-sky-700 mt-1">按时间轴展示过户与车牌变更</p>
|
||||
</div>
|
||||
<div v-if="totalTimes" class="bg-white rounded-lg px-3 py-2 text-right shrink-0">
|
||||
<div class="text-xs text-gray-500">总过户次数</div>
|
||||
<div class="text-lg font-bold">{{ totalTimes }} 次</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="transfers.length" class="space-y-3">
|
||||
<div
|
||||
v-for="(item, index) in transfers"
|
||||
:key="index"
|
||||
class="bg-gray-50 rounded-lg p-4 border border-gray-200"
|
||||
>
|
||||
<div class="flex justify-between mb-2">
|
||||
<span class="font-semibold">{{ item.changeMonthFormatted }}</span>
|
||||
<span class="text-sm text-blue-600">第 {{ item.transTimeSum }} 次过户</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3 text-sm">
|
||||
<div>
|
||||
<div class="text-gray-500 text-xs">过户前车牌</div>
|
||||
<div class="font-mono font-medium">{{ item.oldCp || '未知' }}</div>
|
||||
<div v-if="item.cityBefore" class="text-xs text-gray-500 mt-1">城市:{{ item.cityBefore }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-gray-500 text-xs">过户后车牌</div>
|
||||
<div class="font-mono font-medium">{{ item.newCp || '未知' }}</div>
|
||||
<div v-if="item.cityAfter" class="text-xs text-gray-500 mt-1">城市:{{ item.cityAfter }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.intervalText !== '-'" class="text-xs text-gray-500 mt-2">距上次过户:{{ item.intervalText }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无过户记录</div>
|
||||
</div>
|
||||
</template>
|
||||
15
src/ui/CQCXG5F3A.vue
Normal file
15
src/ui/CQCXG5F3A.vue
Normal file
@@ -0,0 +1,15 @@
|
||||
<script setup>
|
||||
import VehiclePlateList from '@/ui/vehicle/VehiclePlateList.vue'
|
||||
|
||||
defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VehiclePlateList v-bind="$props" title="名下车辆车牌查询 B" />
|
||||
</template>
|
||||
52
src/ui/CQCXG5U0Z.vue
Normal file
52
src/ui/CQCXG5U0Z.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
import { payloadAsArray } from '@/utils/vehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: { default: null },
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const records = computed(() => payloadAsArray(props.data))
|
||||
|
||||
const riskScore = computed(() => 100)
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-blue-50 to-indigo-50 rounded-lg p-4 border border-blue-100">
|
||||
<h3 class="text-lg font-bold text-gray-900">车辆静态信息查询</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">生产、排放、燃料等静态信息</p>
|
||||
</div>
|
||||
|
||||
<template v-if="records.length">
|
||||
<div
|
||||
v-for="(item, idx) in records"
|
||||
:key="idx"
|
||||
class="bg-gray-50 rounded-lg p-4 border border-gray-200"
|
||||
>
|
||||
<div class="flex flex-wrap items-center gap-2 mb-3">
|
||||
<span class="text-xs text-blue-700 bg-blue-50 px-2 py-0.5 rounded">车辆 {{ idx + 1 }}</span>
|
||||
<span class="font-semibold text-gray-900">{{ item.vType || '未知车型' }}</span>
|
||||
<span class="text-xs text-blue-600 bg-white px-2 py-0.5 rounded-full border">{{ item.vFuelType || '燃料未知' }}</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3 text-sm">
|
||||
<div><span class="text-gray-500 block text-xs">发动机号</span><span class="font-mono">{{ item.engineNO || '-' }}</span></div>
|
||||
<div><span class="text-gray-500 block text-xs">发动机型号</span><span class="font-mono">{{ item.engineType || '-' }}</span></div>
|
||||
<div><span class="text-gray-500 block text-xs">生产日期</span>{{ item.vScdate || '-' }}</div>
|
||||
<div><span class="text-gray-500 block text-xs">排放阶段</span>{{ item.dischargeStage || '-' }}</div>
|
||||
<div><span class="text-gray-500 block text-xs">车辆分类</span>{{ item.vClassification || '-' }}</div>
|
||||
<div class="col-span-2"><span class="text-gray-500 block text-xs">生产企业</span>{{ item.vManufacturer || '-' }}</div>
|
||||
<div class="col-span-2"><span class="text-gray-500 block text-xs">生产厂地址</span>{{ item.vSccdz || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无车辆静态信息</div>
|
||||
</div>
|
||||
</template>
|
||||
76
src/ui/CQCXG6B4E.vue
Normal file
76
src/ui/CQCXG6B4E.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj: data } = useVehiclePayload(props)
|
||||
const hasData = computed(() => Object.keys(data.value).length > 0)
|
||||
|
||||
function yesNoText(val, yesText, noText = '否') {
|
||||
if (val === '1') return yesText
|
||||
if (val === '0') return noText
|
||||
return '-'
|
||||
}
|
||||
|
||||
const riskLevelText = computed(() => {
|
||||
const d = data.value
|
||||
if (d.IfHighriskVehicle === '1') return '高风险'
|
||||
if (d.IsMajorAccidentLevel && d.IsMajorAccidentLevel !== '一般') return '较高风险'
|
||||
if (d.IsMajorAccidentData && d.IsMajorAccidentData !== '0') return '有事故记录'
|
||||
return '风险可控'
|
||||
})
|
||||
|
||||
const riskScore = computed(() => {
|
||||
const t = riskLevelText.value
|
||||
if (t === '高风险') return 30
|
||||
if (t === '较高风险' || t === '有事故记录') return 50
|
||||
return 90
|
||||
})
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-orange-50 to-red-50 rounded-lg p-4 border border-orange-100">
|
||||
<h3 class="text-lg font-bold">车辆出险记录核验</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">综合出险、脱保、重大事故等信息</p>
|
||||
</div>
|
||||
|
||||
<template v-if="hasData">
|
||||
<div
|
||||
class="rounded-lg p-4 text-center border"
|
||||
:class="riskLevelText === '高风险' ? 'bg-red-50 border-red-200' : riskLevelText.includes('风险') && riskLevelText !== '风险可控' ? 'bg-amber-50 border-amber-200' : 'bg-green-50 border-green-200'"
|
||||
>
|
||||
<div class="text-sm text-gray-500">风险等级</div>
|
||||
<div class="text-2xl font-bold">{{ riskLevelText }}</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 rounded-lg p-4 border text-sm space-y-2">
|
||||
<div v-if="data.LicensePlate" class="text-xl font-bold font-mono">{{ data.LicensePlate }}</div>
|
||||
<div>{{ data.CarType || '未知车型' }}</div>
|
||||
<div class="text-gray-600">二手车参考价 {{ data.UsedCarPrice ? `${data.UsedCarPrice} 元` : '-' }} · 新车 {{ data.PurchasePrice ? `${data.PurchasePrice} 元` : '-' }}</div>
|
||||
<div class="text-gray-500 text-xs">车龄 {{ data.CarAge ? `${data.CarAge} 个月` : '-' }} · 初登 {{ data.DebutDate || '-' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg border divide-y text-sm">
|
||||
<div class="px-4 py-2 font-semibold bg-gray-50">核心风险指标</div>
|
||||
<div class="flex justify-between px-4 py-2"><span class="text-gray-500">高风险车辆</span><span>{{ yesNoText(data.IfHighriskVehicle, '是') }}</span></div>
|
||||
<div class="flex justify-between px-4 py-2"><span class="text-gray-500">营运车辆</span><span>{{ yesNoText(data.IsOperation, '是') }}</span></div>
|
||||
<div class="flex justify-between px-4 py-2"><span class="text-gray-500">车损险</span><span>{{ yesNoText(data.IfCarDamage, '已投保', '未投保') }}</span></div>
|
||||
<div class="flex justify-between px-4 py-2"><span class="text-gray-500">连续投保</span><span>{{ yesNoText(data.IsConInsure, '是') }}</span></div>
|
||||
<div class="flex justify-between px-4 py-2"><span class="text-gray-500">历史脱保</span><span>{{ yesNoText(data.IfTuoBao, '有', '无') }}</span></div>
|
||||
<div class="flex justify-between px-4 py-2"><span class="text-gray-500">全损情况</span><span>{{ data.TotalLoss === '1' ? '存在全损' : data.TotalLoss === '0' ? '无全损' : '-' }}</span></div>
|
||||
<div class="flex justify-between px-4 py-2"><span class="text-gray-500">综合评分</span><span class="font-bold">{{ data.Total || '-' }}</span></div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无出险核验数据</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,65 +1,32 @@
|
||||
<template>
|
||||
<div class="card">
|
||||
<!-- 名下车辆信息展示 -->
|
||||
<div class="bg-yellow-100 text-yellow-700 p-4 rounded-lg">
|
||||
<h3 class="text-xl font-semibold">名下车辆</h3>
|
||||
<p class="text-sm">此人名下拥有车辆:{{ data?.carNum }} 辆</p>
|
||||
</div>
|
||||
|
||||
<!-- 校验对象展示 -->
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, watch, computed } from 'vue';
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier';
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
// 接收父组件传入的 props
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
notifyRiskStatus: {
|
||||
type: Function,
|
||||
default: () => { },
|
||||
},
|
||||
});
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
// 脱敏函数:姓名脱敏(保留首位)
|
||||
const maskName = (name) => {
|
||||
if (!name) return '';
|
||||
return name.length > 1 ? name[0] + "*".repeat(name.length - 1) : "*";
|
||||
};
|
||||
const { obj } = useVehiclePayload(props)
|
||||
|
||||
// 脱敏函数:身份证号脱敏(保留前6位和最后4位)
|
||||
const maskIdCard = (idCard) => {
|
||||
if (!idCard) return '';
|
||||
return idCard.replace(/^(.{6})(?:\d+)(.{4})$/, "$1****$2");
|
||||
};
|
||||
const carNum = computed(() => {
|
||||
const n = Number(obj.value.carNum)
|
||||
return Number.isFinite(n) ? n : null
|
||||
})
|
||||
|
||||
// 计算风险评分(0-100分,分数越高越安全)
|
||||
const riskScore = computed(() => {
|
||||
// 名下车辆不算风险,始终返回100分(最安全)
|
||||
return 100;
|
||||
});
|
||||
|
||||
// 使用 composable 通知父组件风险评分
|
||||
useRiskNotifier(props, riskScore);
|
||||
|
||||
// 暴露给父组件
|
||||
defineExpose({
|
||||
riskScore
|
||||
});
|
||||
const riskScore = computed(() => 100)
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 */
|
||||
</style>
|
||||
<template>
|
||||
<div class="card">
|
||||
<div class="bg-yellow-100 text-yellow-800 p-4 rounded-lg border border-yellow-200">
|
||||
<h3 class="text-xl font-semibold">名下车辆</h3>
|
||||
<p class="text-sm mt-2">此人名下拥有车辆:{{ carNum != null ? `${carNum} 辆` : '—' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,130 +1,15 @@
|
||||
<template>
|
||||
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
|
||||
<!-- 车辆总数统计 -->
|
||||
<div class="flex justify-between items-center mb-6 pb-4 border-b border-gray-100">
|
||||
<div class="flex items-center gap-3">
|
||||
<div>
|
||||
<div class="text-lg font-semibold text-gray-900">个人名下车辆</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-blue-50 text-blue-700 px-4 py-2 rounded-full text-sm font-medium">
|
||||
共 {{ vehicleCount }} 辆
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 车辆列表 -->
|
||||
<div class="space-y-3" v-if="vehicleList && vehicleList.length > 0">
|
||||
<div class="bg-gray-50 rounded-lg p-4 border border-gray-200 hover:bg-blue-50 hover:border-blue-200 transition-colors duration-200"
|
||||
v-for="(vehicle, index) in vehicleList" :key="index">
|
||||
<div class="space-y-3">
|
||||
<div class="text-xl font-bold text-gray-900 font-mono tracking-wider">
|
||||
{{ vehicle.plateNum }}
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="inline-flex items-center gap-1 px-3 py-1 rounded text-sm font-medium text-white"
|
||||
:class="getPlateColorClass(vehicle.plateColor)">
|
||||
<span>🏷️</span>
|
||||
<span>{{ getPlateColorText(vehicle.plateColor) }}</span>
|
||||
</div>
|
||||
<div class="text-sm text-gray-600">
|
||||
<span class="text-gray-500">车辆类型:</span>
|
||||
<span class="font-medium text-gray-900 ml-1">{{ getVehicleTypeText(vehicle.vehicleType)
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 无数据状态 -->
|
||||
<div class="text-center py-12 text-gray-500" v-else>
|
||||
<div class="text-4xl mb-3">🚫</div>
|
||||
<div class="text-lg font-medium mb-1">暂无车辆信息</div>
|
||||
<div class="text-sm">No vehicle records found</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineProps, computed } from 'vue';
|
||||
|
||||
// 接收父组件传入的 props
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
});
|
||||
|
||||
// 车牌颜色映射
|
||||
const plateColorMap = {
|
||||
0: '蓝色 - 普通燃油车',
|
||||
1: '黄色 - 大型车/货车',
|
||||
2: '黑色 - 外籍车辆/港澳台车',
|
||||
3: '白色 - 警车/军车/武警车',
|
||||
4: '渐变绿色 - 新能源汽车',
|
||||
5: '黄绿双拼色 - 大型新能源汽车',
|
||||
6: '蓝白渐变色 - 临时牌照',
|
||||
7: '临时牌照 - 临时行驶车辆',
|
||||
11: '绿色 - 新能源汽车',
|
||||
12: '红色 - 教练车/试验车'
|
||||
};
|
||||
|
||||
// 车辆类型映射
|
||||
const vehicleTypeMap = {
|
||||
1: '一型客车',
|
||||
2: '二型客车',
|
||||
3: '三型客车',
|
||||
4: '四型客车',
|
||||
11: '一型货车',
|
||||
12: '二型货车',
|
||||
13: '三型货车',
|
||||
14: '四型货车',
|
||||
15: '五型货车',
|
||||
16: '六型货车',
|
||||
21: '一型专项作业车',
|
||||
22: '二型专项作业车',
|
||||
23: '三型专项作业车',
|
||||
24: '四型专项作业车',
|
||||
25: '五型专项作业车',
|
||||
26: '六型专项作业车'
|
||||
};
|
||||
|
||||
// 计算属性
|
||||
const vehicleList = computed(() => props.data?.list || []);
|
||||
const vehicleCount = computed(() => props.data?.vehicleCount || 0);
|
||||
|
||||
// 获取车牌颜色文本
|
||||
const getPlateColorText = (plateColor) => {
|
||||
return plateColorMap[plateColor] || '未知颜色 - 未知类型';
|
||||
};
|
||||
|
||||
// 获取车牌颜色样式类
|
||||
const getPlateColorClass = (plateColor) => {
|
||||
const colorClassMap = {
|
||||
0: 'bg-blue-500',
|
||||
1: 'bg-yellow-500',
|
||||
2: 'bg-gray-800',
|
||||
3: 'bg-gray-200 text-gray-800',
|
||||
4: 'bg-green-500',
|
||||
5: 'bg-gradient-to-r from-yellow-500 to-green-500',
|
||||
6: 'bg-gradient-to-r from-blue-500 to-white text-blue-800',
|
||||
7: 'bg-red-500',
|
||||
11: 'bg-green-500',
|
||||
12: 'bg-red-500'
|
||||
};
|
||||
return colorClassMap[plateColor] || 'bg-gray-500';
|
||||
};
|
||||
|
||||
// 获取车辆类型文本
|
||||
const getVehicleTypeText = (vehicleType) => {
|
||||
return vehicleTypeMap[vehicleType] || '未知类型';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log('车辆数据:', props.data);
|
||||
});
|
||||
import VehiclePlateList from '@/ui/vehicle/VehiclePlateList.vue'
|
||||
|
||||
defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 自定义样式 - 仅保留必要的样式 */
|
||||
</style>
|
||||
<template>
|
||||
<VehiclePlateList v-bind="$props" title="名下车辆车牌查询 A" />
|
||||
</template>
|
||||
|
||||
43
src/ui/CQCXGFallback.vue
Normal file
43
src/ui/CQCXGFallback.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: { default: null },
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { payload } = useVehiclePayload(props)
|
||||
|
||||
const bodyText = computed(() => {
|
||||
const p = payload.value
|
||||
if (p === '000' || p === 0)
|
||||
return '(本模块暂无示例明细)'
|
||||
if (p == null || p === '')
|
||||
return '暂无数据'
|
||||
if (typeof p === 'string')
|
||||
return p
|
||||
try {
|
||||
return JSON.stringify(p, null, 2)
|
||||
}
|
||||
catch {
|
||||
return String(p)
|
||||
}
|
||||
})
|
||||
|
||||
const riskScore = computed(() => 100)
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card">
|
||||
<p class="text-xs text-gray-500 mb-2">
|
||||
以下为结构化数据预览;专用版式可联系产品补充独立模块。
|
||||
</p>
|
||||
<pre class="text-xs text-gray-800 bg-gray-50 rounded-lg p-3 overflow-auto max-h-80 whitespace-pre-wrap break-all">{{ bodyText }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
58
src/ui/CQCXGGB2Q.vue
Normal file
58
src/ui/CQCXGGB2Q.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { maskName } from '@/utils/vehicleReportBlockMaps'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj, params } = useVehiclePayload(props)
|
||||
|
||||
/** verify_code:1 一致,0 不一致(上游接口约定) */
|
||||
const isMatch = computed(() => {
|
||||
const n = Number(obj.value.verify_code)
|
||||
if (n === 1) return true
|
||||
if (n === 0) return false
|
||||
return null
|
||||
})
|
||||
|
||||
const resultText = computed(() => {
|
||||
if (isMatch.value === true) return '一致'
|
||||
if (isMatch.value === false) return '不一致'
|
||||
return '暂无结果'
|
||||
})
|
||||
|
||||
const sectionClass = computed(() => {
|
||||
if (isMatch.value === true) return 'bg-green-50 border-green-200'
|
||||
if (isMatch.value === false) return 'bg-red-50 border-red-200'
|
||||
return 'bg-gray-50 border-gray-200'
|
||||
})
|
||||
|
||||
const riskScore = computed(() => (isMatch.value === false ? 40 : 100))
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-4">
|
||||
<div class="bg-gradient-to-br from-blue-600 to-blue-700 text-white rounded-lg p-4">
|
||||
<h3 class="text-lg font-bold">车辆二要素核验</h3>
|
||||
<p class="text-sm opacity-90 mt-1">校验人员姓名与车辆号牌是否匹配</p>
|
||||
</div>
|
||||
<div :class="['rounded-lg p-6 text-center border', sectionClass]">
|
||||
<div class="text-4xl font-bold mb-2">{{ isMatch === true ? '✓' : isMatch === false ? '✕' : '?' }}</div>
|
||||
<div class="text-sm text-gray-500">核验结果</div>
|
||||
<div class="text-2xl font-bold mt-1">{{ resultText }}</div>
|
||||
</div>
|
||||
<div class="divide-y text-sm">
|
||||
<div class="flex py-2"><span class="w-24 text-gray-500">姓名</span><span>{{ maskName(params.name) }}</span></div>
|
||||
<div class="flex py-2"><span class="w-24 text-gray-500">车牌号</span><span class="font-mono">{{ params.plate_no || params.car_license || '-' }}</span></div>
|
||||
<div class="flex py-2"><span class="w-24 text-gray-500">号牌类型</span><span>{{ params.carplate_type || params.car_type || '-' }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
99
src/ui/CQCXGP00W.vue
Normal file
99
src/ui/CQCXGP00W.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj } = useVehiclePayload(props)
|
||||
|
||||
const retdata = computed(() => {
|
||||
const p = obj.value
|
||||
if (p.retdata && typeof p.retdata === 'object') return p.retdata
|
||||
return p
|
||||
})
|
||||
|
||||
const hasData = computed(() => Object.keys(retdata.value).length > 0)
|
||||
const clxx = computed(() => retdata.value.clxx || {})
|
||||
const tjxx = computed(() => retdata.value.tjxx || {})
|
||||
const pzRecords = computed(() => {
|
||||
const mx = retdata.value.pzlsmx || {}
|
||||
return Array.isArray(mx.records) ? mx.records : []
|
||||
})
|
||||
|
||||
function formatFen(val) {
|
||||
if (val !== 0 && !val) return '-'
|
||||
const n = Number(val)
|
||||
if (Number.isNaN(n)) return `${val} 元`
|
||||
return `${(n / 100).toLocaleString()} 元`
|
||||
}
|
||||
|
||||
function dangerTypeText(t) {
|
||||
if (t === '1') return '更换'
|
||||
if (t === '2') return '维修'
|
||||
if (t === '3') return '材料'
|
||||
return '其他'
|
||||
}
|
||||
|
||||
const riskScore = computed(() => {
|
||||
const count = Number(tjxx.value.claimCount)
|
||||
if (count > 3) return 40
|
||||
if (count > 0) return 60
|
||||
return 90
|
||||
})
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-red-50 to-orange-50 rounded-lg p-4 border border-red-100">
|
||||
<h3 class="text-lg font-bold">车辆出险详版查询</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">碰撞记录与统计(精简展示)</p>
|
||||
</div>
|
||||
|
||||
<template v-if="hasData">
|
||||
<div class="bg-gray-50 rounded-lg p-4 border text-sm">
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div>
|
||||
<div class="text-gray-500 text-xs">品牌</div>
|
||||
<div class="font-medium">{{ clxx.brandName || '未知' }}</div>
|
||||
<div v-if="clxx.vehicleStyle" class="text-xs text-gray-500">{{ clxx.vehicleStyle }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-gray-500 text-xs">VIN</div>
|
||||
<div class="font-mono text-xs">{{ pzRecords[0]?.vin || clxx.vin || '-' }}</div>
|
||||
<div v-if="clxx.licensePlate" class="text-xs">车牌 {{ clxx.licensePlate }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-3 mt-3 text-xs text-gray-600">
|
||||
<span>事故 {{ tjxx.claimCount ?? '-' }} 次</span>
|
||||
<span>总维修 {{ tjxx.totalAmount || '-' }}</span>
|
||||
<span>最大单次 {{ tjxx.largestAmount || '-' }}</span>
|
||||
<span>结案 {{ tjxx.claimCacCount ?? 0 }} / 未结案 {{ tjxx.claimUnCacCount ?? 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="pzRecords.length" class="space-y-2">
|
||||
<h4 class="font-semibold text-sm">碰撞出险记录</h4>
|
||||
<div v-for="(rec, idx) in pzRecords" :key="idx" class="bg-white rounded-lg p-3 border text-sm">
|
||||
<div class="flex justify-between">
|
||||
<span>{{ rec.date || '-' }}</span>
|
||||
<span class="font-semibold text-red-600">{{ formatFen(rec.serviceMoney) }}</span>
|
||||
</div>
|
||||
<p v-if="rec.dangerSingleList?.length" class="text-xs text-gray-500 mt-1">
|
||||
<span v-for="(d, di) in rec.dangerSingleList" :key="di" class="mr-2">
|
||||
{{ dangerTypeText(d.dangerSingleType) }}:{{ d.dangerSingleName || d.dangerSingleDesc }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无出险详版数据</div>
|
||||
</div>
|
||||
</template>
|
||||
51
src/ui/CQCXGY7F2.vue
Normal file
51
src/ui/CQCXGY7F2.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj } = useVehiclePayload(props)
|
||||
const hasData = computed(() => Object.keys(obj.value).length > 0)
|
||||
const riskScore = computed(() => 100)
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-3">
|
||||
<div class="bg-gradient-to-br from-violet-50 to-purple-50 rounded-lg p-4 border flex justify-between items-start">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">二手车 VIN 估值</h3>
|
||||
<p class="text-sm text-gray-600 mt-1">基于车型、排量、排放等给出参考估值</p>
|
||||
</div>
|
||||
<div class="text-right bg-white rounded-lg px-3 py-2 border">
|
||||
<div class="text-xs text-gray-500">估值</div>
|
||||
<div class="text-lg font-bold text-violet-700">{{ obj.estimatedValue || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-if="hasData">
|
||||
<div class="text-center py-4 bg-violet-50 rounded-lg border border-violet-100">
|
||||
<div class="text-3xl font-bold text-violet-800">{{ obj.estimatedValue || '-' }}</div>
|
||||
<p class="text-xs text-gray-500 mt-1">参考估值(仅供参考)</p>
|
||||
<p class="text-sm mt-2">{{ obj.seriesName || '未知车系' }} · {{ obj.manufacturerName || '未知厂商' }}</p>
|
||||
<p v-if="obj.productionDate" class="text-sm text-gray-600">{{ obj.productionDate }} 年出厂</p>
|
||||
</div>
|
||||
<div class="divide-y text-sm">
|
||||
<div class="flex py-2 justify-between"><span class="text-gray-500">厂商</span><span>{{ obj.manufacturerName || '-' }}</span></div>
|
||||
<div class="flex py-2 justify-between"><span class="text-gray-500">车系</span><span>{{ obj.seriesName || '-' }}</span></div>
|
||||
<div class="flex py-2 justify-between"><span class="text-gray-500">车型年款</span><span>{{ obj.modelYear || obj.productionDate || '-' }}</span></div>
|
||||
<div class="flex py-2 justify-between"><span class="text-gray-500">座位数</span><span>{{ obj.seatingCapacity ?? '-' }}</span></div>
|
||||
<div class="flex py-2 justify-between"><span class="text-gray-500">车型名称</span><span>{{ obj.modelName || '-' }}</span></div>
|
||||
<div class="flex py-2 justify-between"><span class="text-gray-500">指导价</span><span>{{ obj.msrp || '-' }}</span></div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="text-center py-10 text-gray-500">暂无估值结果</div>
|
||||
</div>
|
||||
</template>
|
||||
76
src/ui/CQCXGYTS2.vue
Normal file
76
src/ui/CQCXGYTS2.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useRiskNotifier } from '@/composables/useRiskNotifier'
|
||||
import { maskName } from '@/utils/vehicleReportBlockMaps'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
})
|
||||
|
||||
const { obj, params } = useVehiclePayload(props)
|
||||
|
||||
const status = computed(() => {
|
||||
const raw = obj.value
|
||||
const s = Number(raw?.status ?? raw?.result?.status)
|
||||
if ([0, -1, -2, -4].includes(s)) return s
|
||||
return null
|
||||
})
|
||||
|
||||
const resultText = computed(() => {
|
||||
const s = status.value
|
||||
if (s === 0) return '一致'
|
||||
if (s === -1) return '不一致'
|
||||
if (s === -2) return '非法姓名'
|
||||
if (s === -4) return '无记录'
|
||||
return '暂无结果'
|
||||
})
|
||||
|
||||
const resultDesc = computed(() => {
|
||||
const s = status.value
|
||||
if (s === -2) return '姓名长度或格式不正确,请核对后重试'
|
||||
if (s === -4) return '未查询到相关核验记录'
|
||||
return ''
|
||||
})
|
||||
|
||||
const sectionClass = computed(() => {
|
||||
const s = status.value
|
||||
if (s === 0) return 'bg-green-50 border-green-200'
|
||||
if (s === -1) return 'bg-red-50 border-red-200'
|
||||
if (s === -2) return 'bg-orange-50 border-orange-200'
|
||||
if (s === -4) return 'bg-gray-100 border-gray-300'
|
||||
return 'bg-gray-50 border-gray-200'
|
||||
})
|
||||
|
||||
const riskScore = computed(() => {
|
||||
const s = status.value
|
||||
if (s === -1) return 35
|
||||
if (s === -2 || s === -4) return 55
|
||||
return 100
|
||||
})
|
||||
useRiskNotifier(props, riskScore)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card space-y-4">
|
||||
<div class="bg-gradient-to-br from-indigo-600 to-indigo-800 text-white rounded-lg p-4">
|
||||
<h3 class="text-lg font-bold">人车核验(详版)</h3>
|
||||
<p class="text-sm opacity-90 mt-1">人员与车辆的详细匹配结果</p>
|
||||
</div>
|
||||
<div :class="['rounded-lg p-6 text-center border', sectionClass]">
|
||||
<div class="text-4xl font-bold mb-2">{{ status === 0 ? '✓' : status === -1 ? '✕' : status === -2 ? '!' : '—' }}</div>
|
||||
<div class="text-sm text-gray-500">认证结果</div>
|
||||
<div class="text-2xl font-bold mt-1">{{ resultText }}</div>
|
||||
<p v-if="resultDesc" class="text-sm text-gray-600 mt-2">{{ resultDesc }}</p>
|
||||
</div>
|
||||
<div class="divide-y text-sm">
|
||||
<div class="flex py-2"><span class="w-24 text-gray-500">姓名</span><span>{{ maskName(params.name) }}</span></div>
|
||||
<div class="flex py-2"><span class="w-24 text-gray-500">车牌号</span><span class="font-mono">{{ params.plate_no || params.car_license || '-' }}</span></div>
|
||||
<div class="flex py-2"><span class="w-24 text-gray-500">号牌类型</span><span>{{ params.carplate_type || params.car_type || '-' }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
63
src/ui/vehicle/VehiclePlateList.vue
Normal file
63
src/ui/vehicle/VehiclePlateList.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { plateColorLabel, vehicleTypeLabel } from '@/utils/vehicleReportBlockMaps'
|
||||
import { useVehiclePayload } from '@/composables/useVehiclePayload'
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
params: Object,
|
||||
apiId: { type: String, default: '' },
|
||||
index: { type: Number, default: 0 },
|
||||
notifyRiskStatus: { type: Function, default: () => {} },
|
||||
title: { type: String, default: '个人名下车辆' },
|
||||
})
|
||||
|
||||
const { obj } = useVehiclePayload(props)
|
||||
|
||||
const vehicleCount = computed(() => {
|
||||
const n = obj.value.vehicleCount ?? obj.value.carNum
|
||||
if (n === '' || n == null)
|
||||
return null
|
||||
const num = Number(n)
|
||||
return Number.isFinite(num) ? num : null
|
||||
})
|
||||
|
||||
const vehicleList = computed(() => {
|
||||
const list = obj.value.list
|
||||
return Array.isArray(list) ? list : []
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card bg-white rounded-lg shadow-sm border border-gray-200 p-4">
|
||||
<div class="flex justify-between items-center mb-4 pb-4 border-b border-gray-100">
|
||||
<div class="text-lg font-semibold text-gray-900">{{ title }}</div>
|
||||
<div class="bg-blue-50 text-blue-700 px-4 py-2 rounded-full text-sm font-medium">
|
||||
共 {{ vehicleCount != null ? vehicleCount : '—' }} 辆
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="vehicleList.length" class="space-y-3">
|
||||
<div
|
||||
v-for="(vehicle, index) in vehicleList"
|
||||
:key="index"
|
||||
class="bg-gray-50 rounded-lg p-4 border border-gray-200"
|
||||
>
|
||||
<div class="text-xl font-bold text-gray-900 font-mono tracking-wider">
|
||||
{{ vehicle.plateNum ?? '—' }}
|
||||
</div>
|
||||
<div class="flex items-center gap-3 mt-2 flex-wrap">
|
||||
<span class="inline-flex items-center px-3 py-1 rounded text-sm font-medium text-white bg-blue-500">
|
||||
{{ plateColorLabel(vehicle.plateColor) }}
|
||||
</span>
|
||||
<span class="text-sm text-gray-600">
|
||||
车辆类型:<span class="font-medium text-gray-900">{{ vehicleTypeLabel(vehicle.vehicleType) }}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="text-center py-12 text-gray-500">
|
||||
<div class="text-lg font-medium mb-1">暂无车辆信息</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
73
src/utils/vehiclePayload.js
Normal file
73
src/utils/vehiclePayload.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 解包车辆报告业务体:兼容 BaseReport 传入的 data 仍带一层 { apiID, data } 或 JSON 字符串
|
||||
*/
|
||||
export function unwrapVehiclePayload(raw, depth = 0) {
|
||||
if (raw == null || depth > 5)
|
||||
return raw
|
||||
|
||||
if (typeof raw === 'string') {
|
||||
const s = raw.trim()
|
||||
if (!s)
|
||||
return null
|
||||
try {
|
||||
return unwrapVehiclePayload(JSON.parse(s), depth + 1)
|
||||
}
|
||||
catch {
|
||||
return raw
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(raw))
|
||||
return raw
|
||||
|
||||
if (typeof raw !== 'object')
|
||||
return raw
|
||||
|
||||
const hasApiWrapper = ('apiID' in raw || 'apiId' in raw) && 'data' in raw
|
||||
if (hasApiWrapper)
|
||||
return unwrapVehiclePayload(raw.data ?? raw, depth + 1)
|
||||
|
||||
if (typeof raw.data === 'string' || (raw.data && typeof raw.data === 'object' && !Array.isArray(raw.data))) {
|
||||
const inner = raw.data
|
||||
const onlyDataKey = Object.keys(raw).length <= 2 && 'data' in raw
|
||||
if (onlyDataKey || (typeof inner === 'object' && inner && !Array.isArray(inner) && Object.keys(inner).length > 0 && !('list' in raw) && !('vehicleCount' in raw) && !('carNum' in raw)))
|
||||
return unwrapVehiclePayload(inner, depth + 1)
|
||||
}
|
||||
|
||||
return raw
|
||||
}
|
||||
|
||||
/** 解析为数组:直连数组、list、record、retdata 等 */
|
||||
export function payloadAsArray(raw) {
|
||||
const p = unwrapVehiclePayload(raw)
|
||||
if (!p)
|
||||
return []
|
||||
if (Array.isArray(p))
|
||||
return p
|
||||
if (typeof p !== 'object')
|
||||
return []
|
||||
if (Array.isArray(p.list))
|
||||
return p.list
|
||||
if (Array.isArray(p.record))
|
||||
return p.record
|
||||
if (Array.isArray(p.retdata))
|
||||
return p.retdata
|
||||
if (typeof p.data === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(p.data)
|
||||
return Array.isArray(parsed) ? parsed : []
|
||||
}
|
||||
catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
/** 解析为对象 */
|
||||
export function payloadAsObject(raw) {
|
||||
const p = unwrapVehiclePayload(raw)
|
||||
if (!p || typeof p !== 'object' || Array.isArray(p))
|
||||
return {}
|
||||
return p
|
||||
}
|
||||
73
src/utils/vehicleReportBlockMaps.js
Normal file
73
src/utils/vehicleReportBlockMaps.js
Normal file
@@ -0,0 +1,73 @@
|
||||
/** 车辆报告共用车牌颜色、车辆类型、日期里程文案 */
|
||||
|
||||
export const PLATE_COLOR_LABELS = {
|
||||
0: '蓝色',
|
||||
1: '黄色',
|
||||
2: '黑色',
|
||||
3: '白色',
|
||||
4: '渐变绿',
|
||||
5: '黄绿双拼',
|
||||
6: '蓝白渐变',
|
||||
7: '临牌',
|
||||
11: '绿色',
|
||||
12: '红色',
|
||||
}
|
||||
|
||||
export function plateColorLabel(c) {
|
||||
const n = Number(c)
|
||||
if (Number.isNaN(n))
|
||||
return '—'
|
||||
return PLATE_COLOR_LABELS[n] ?? '其他'
|
||||
}
|
||||
|
||||
const VEHICLE_TYPE_LABELS = {
|
||||
1: '一型客车',
|
||||
2: '二型客车',
|
||||
3: '三型客车',
|
||||
4: '四型客车',
|
||||
11: '一型货车',
|
||||
12: '二型货车',
|
||||
13: '三型货车',
|
||||
14: '四型货车',
|
||||
15: '五型货车',
|
||||
16: '六型货车',
|
||||
21: '一型专项作业车',
|
||||
22: '二型专项作业车',
|
||||
23: '三型专项作业车',
|
||||
24: '四型专项作业车',
|
||||
25: '五型专项作业车',
|
||||
26: '六型专项作业车',
|
||||
}
|
||||
|
||||
export function vehicleTypeLabel(t) {
|
||||
if (t == null || t === '')
|
||||
return '—'
|
||||
const n = Number(t)
|
||||
if (!Number.isNaN(n) && VEHICLE_TYPE_LABELS[n])
|
||||
return VEHICLE_TYPE_LABELS[n]
|
||||
return String(t)
|
||||
}
|
||||
|
||||
export function formatDateZh(val) {
|
||||
if (!val)
|
||||
return '-'
|
||||
const m = String(val).match(/^(\d{4})-(\d{2})-(\d{2})/)
|
||||
if (m)
|
||||
return `${m[1]}年${m[2]}月${m[3]}日`
|
||||
return String(val)
|
||||
}
|
||||
|
||||
export function formatMileageKm(val) {
|
||||
if (val !== 0 && !val)
|
||||
return '-'
|
||||
const num = Number(val)
|
||||
if (Number.isNaN(num))
|
||||
return `${val} km`
|
||||
return `${num.toLocaleString()} km`
|
||||
}
|
||||
|
||||
export function maskName(name) {
|
||||
if (!name)
|
||||
return '-'
|
||||
return name.length > 1 ? `${name[0]}${'*'.repeat(name.length - 1)}` : '*'
|
||||
}
|
||||
44
src/utils/vehicleReportNormalize.js
Normal file
44
src/utils/vehicleReportNormalize.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import { unwrapVehiclePayload } from './vehiclePayload'
|
||||
|
||||
/**
|
||||
* 规范 query_data 单项,供 BaseReport 使用:{ data: { apiID, data: 业务体 } }
|
||||
*/
|
||||
export function normalizeVehicleReportItem(item) {
|
||||
if (!item || typeof item !== 'object')
|
||||
return item
|
||||
|
||||
const outer = item.data
|
||||
if (outer == null)
|
||||
return item
|
||||
|
||||
let apiID = ''
|
||||
let payload = outer
|
||||
|
||||
if (typeof outer === 'object' && !Array.isArray(outer)) {
|
||||
apiID = String(outer.apiID ?? outer.apiId ?? '')
|
||||
if ('data' in outer)
|
||||
payload = outer.data
|
||||
}
|
||||
|
||||
payload = unwrapVehiclePayload(payload)
|
||||
|
||||
if (!apiID && payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
||||
apiID = String(payload.apiID ?? payload.apiId ?? '')
|
||||
if (apiID && 'data' in payload)
|
||||
payload = unwrapVehiclePayload(payload.data)
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
data: {
|
||||
apiID: apiID || '',
|
||||
data: payload,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeVehicleReportData(items) {
|
||||
if (!Array.isArray(items))
|
||||
return []
|
||||
return items.map(normalizeVehicleReportItem)
|
||||
}
|
||||
Reference in New Issue
Block a user