Files
xfc_userfront/src/ui/YYSY8B1C/index.vue
2026-01-15 18:03:13 +08:00

404 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<template>
<div class="mobile-online-duration card">
<div class="verification-section mb-4">
<div class="bg-white rounded-xl border border-gray-200 p-4 relative">
<div class="flex items-center mb-3">
<div class="w-8 h-8 flex items-center justify-center mr-3">
<img src="@/assets/images/report/sjh.png" alt="手机在网时长" class="w-8 h-8 object-contain" />
</div>
<span class="font-bold text-gray-800">手机在网时长</span>
</div>
<!-- 查询结果 -->
<div class="verification-details">
<div v-if="hasData" class="space-y-4">
<!-- 运营商 -->
<div class="flex justify-between items-center p-3 bg-gray-50 rounded-lg">
<span class="text-sm text-gray-600">运营商</span>
<span class="text-sm font-bold text-gray-800">{{ operators || '-' }}</span>
</div>
<!-- 在网时长信息 -->
<div class="">
<!-- 在网时长区间 -->
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">在网时长</span>
<span class="text-sm font-bold text-gray-800">{{ friendlyDurationText }}</span>
</div>
<!-- 进度条 -->
<div v-if="showProgressBar" class="mt-16">
<div class="relative">
<!-- 当前值标签上方 -->
<div class="absolute -top-12 left-1/2 transform -translate-x-1/2 whitespace-nowrap transition-all duration-700 ease-out z-30"
:class="progressBarTextColor" :style="{ left: progressBarWidth + '%' }">
<div class="px-3 py-1.5 rounded-lg shadow-lg backdrop-blur-sm bg-white/90 border-2 font-semibold text-sm"
:class="progressBarBorderColor">
{{ friendlyDurationText }}
</div>
<!-- 小三角指向 -->
<div class="absolute top-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-l-[6px] border-r-[6px] border-t-[6px] border-transparent transition-colors duration-700"
:style="{ borderTopColor: progressBarArrowBorderColor }">
</div>
</div>
<!-- 进度条容器 -->
<div
class="relative h-6 bg-gradient-to-r from-gray-50 to-gray-100 rounded-full overflow-hidden shadow-inner border-2 border-gray-200/50">
<!-- 背景分段渐变效果 -->
<div class="absolute inset-0 flex h-full">
<div
class="w-[15%] bg-gradient-to-br from-red-50 to-red-100/50 border-r border-red-200/60">
</div>
<div
class="w-[15%] bg-gradient-to-br from-orange-50 to-orange-100/50 border-r border-orange-200/60">
</div>
<div
class="w-[20%] bg-gradient-to-br from-yellow-50 to-yellow-100/50 border-r border-yellow-200/60">
</div>
<div
class="w-[25%] bg-gradient-to-br from-green-50 to-green-100/50 border-r border-green-200/60">
</div>
<div class="flex-1 bg-gradient-to-br from-blue-50 to-indigo-100/50"></div>
</div>
<!-- 进度条填充渐变 -->
<div class="absolute inset-0 flex items-center">
<div class="h-full transition-all duration-700 ease-out rounded-full flex items-center justify-end pr-1.5 shadow-lg"
:class="progressBarGradient" :style="{ width: progressBarWidth + '%' }">
<!-- 标记点带光晕效果 -->
<div class="relative">
<!-- 外圈光晕 -->
<div class="absolute inset-0 rounded-full animate-ping opacity-30"
:class="progressBarColor">
</div>
<!-- 标记点主体 -->
<div class="relative w-3 h-3 rounded-full bg-white shadow-xl"
:class="progressBarBorderColor" style="border-width: 2.5px;">
<!-- 内圈高光 -->
<div class="absolute inset-0.5 rounded-full bg-white/50"></div>
</div>
</div>
</div>
</div>
<!-- 区间分割线更精致 -->
<div class="absolute inset-0 flex h-full pointer-events-none">
<div class="w-[15%] border-r-2 border-dashed border-gray-300/70"></div>
<div class="w-[15%] border-r-2 border-dashed border-gray-300/70"></div>
<div class="w-[20%] border-r-2 border-dashed border-gray-300/70"></div>
<div class="w-[25%] border-r-2 border-dashed border-gray-300/70"></div>
</div>
</div>
<!-- 区间标签底部 -->
<div class="relative mt-2 flex justify-between text-sm text-gray-600">
<span class="font-bold">0</span>
<span class="font-bold">3</span>
<span class="font-bold">6</span>
<span class="font-bold">12</span>
<span class="font-bold">24</span>
<span class="font-bold"></span>
</div>
</div>
</div>
<!-- 特殊状态说明 -->
<div v-if="isSpecialStatus" class="mt-3 p-3 rounded-lg" :class="specialStatusClass">
<div class="text-sm font-medium" :class="specialStatusTextClass">
{{ specialStatusText }}
</div>
<div class="text-sm mt-1" :class="specialStatusDescClass">
{{ specialStatusDesc }}
</div>
</div>
</div>
</div>
<div v-else class="p-8 text-center text-gray-500">
<div class="flex flex-col items-center justify-center">
<van-empty description="暂无查询结果" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useRiskNotifier } from '@/composables/useRiskNotifier'
const props = defineProps({
data: {
type: Object,
required: true,
default: () => ({})
},
apiId: {
type: String,
default: '',
},
index: {
type: Number,
default: 0,
},
notifyRiskStatus: {
type: Function,
default: () => { },
},
})
// 获取数据
const reportData = computed(() => {
return props.data?.data?.data || props.data?.data || props.data || {}
})
// 运营商
const operators = computed(() => {
return reportData.value.operators || ''
})
// 在网时长
const inTime = computed(() => {
const value = reportData.value.inTime
if (value === undefined || value === null || value === '') {
return null
}
return String(value)
})
// 是否有数据
const hasData = computed(() => {
return inTime.value !== null || operators.value
})
// 是否显示进度条
const showProgressBar = computed(() => {
const value = inTime.value
return value !== null && value !== '99' && value !== '-1'
})
// 是否特殊状态
const isSpecialStatus = computed(() => {
const value = inTime.value
return value === '99' || value === '-1'
})
// 在网时长文本(原始格式)
const durationText = computed(() => {
const value = inTime.value
if (value === null) return '-'
const durationMap = {
'0': '[0, 3) 个月',
'3': '[3, 6) 个月',
'6': '[6, 12) 个月',
'12': '[12, 24) 个月',
'24': '[24, +∞) 个月',
'99': '手机号已离网/新入网/手机状态异常',
'-1': '查无记录'
}
return durationMap[value] || '-'
})
// 友好的在网时长文本
const friendlyDurationText = computed(() => {
const value = inTime.value
if (value === null) return '-'
const friendlyMap = {
'0': '0-3个月新入网',
'3': '3-6个月短期在网',
'6': '6-12个月中期在网',
'12': '12-24个月长期在网',
'24': '24个月以上稳定在网',
'99': '状态异常',
'-1': '查无记录'
}
return friendlyMap[value] || '-'
})
// 进度条宽度(百分比)- 精准对应区间中点
const progressBarWidth = computed(() => {
const value = inTime.value
if (!value || value === '99' || value === '-1') return 0
// 区间分布0-3(15%), 3-6(15%), 6-12(20%), 12-24(25%), 24+(25%)
// 计算每个区间的中点位置
const widthMap = {
'0': 7.5, // [0,3) 的中点 = 15% / 2 = 7.5%
'3': 22.5, // [3,6) 的中点 = 15% + 15% / 2 = 22.5%
'6': 40, // [6,12) 的中点 = 30% + 20% / 2 = 40%
'12': 57.5, // [12,24) 的中点 = 50% + 25% / 2 = 57.5%
'24': 87.5 // [24,+) 的中点 = 75% + 25% / 2 = 87.5%
}
return widthMap[value] || 0
})
// 进度条颜色
const progressBarColor = computed(() => {
const value = inTime.value
if (!value || value === '99' || value === '-1') return ''
const colorMap = {
'0': 'bg-red-500',
'3': 'bg-orange-500',
'6': 'bg-yellow-500',
'12': 'bg-green-500',
'24': 'bg-blue-500'
}
return colorMap[value] || 'bg-gray-500'
})
// 进度条渐变效果
const progressBarGradient = computed(() => {
const value = inTime.value
if (!value || value === '99' || value === '-1') return ''
const gradientMap = {
'0': 'bg-gradient-to-r from-red-400 via-red-500 to-red-600',
'3': 'bg-gradient-to-r from-orange-400 via-orange-500 to-orange-600',
'6': 'bg-gradient-to-r from-yellow-400 via-yellow-500 to-yellow-600',
'12': 'bg-gradient-to-r from-green-400 via-green-500 to-green-600',
'24': 'bg-gradient-to-r from-blue-400 via-blue-500 to-blue-600'
}
return gradientMap[value] || 'bg-gradient-to-r from-gray-400 to-gray-600'
})
// 进度条边框颜色
const progressBarBorderColor = computed(() => {
const value = inTime.value
if (!value || value === '99' || value === '-1') return 'border-gray-300'
const colorMap = {
'0': 'border-red-400',
'3': 'border-orange-400',
'6': 'border-yellow-400',
'12': 'border-green-400',
'24': 'border-blue-400'
}
return colorMap[value] || 'border-gray-300'
})
// 进度条文本颜色
const progressBarTextColor = computed(() => {
const value = inTime.value
if (!value || value === '99' || value === '-1') return 'text-gray-700'
const colorMap = {
'0': 'text-red-600',
'3': 'text-orange-600',
'6': 'text-yellow-600',
'12': 'text-green-600',
'24': 'text-blue-600'
}
return colorMap[value] || 'text-gray-700'
})
// 箭头边框颜色(用于内联样式)
const progressBarArrowBorderColor = computed(() => {
const value = inTime.value
if (!value || value === '99' || value === '-1') return '#d1d5db'
const colorMap = {
'0': '#ef4444', // red-500
'3': '#f97316', // orange-500
'6': '#eab308', // yellow-500
'12': '#22c55e', // green-500
'24': '#3b82f6' // blue-500
}
return colorMap[value] || '#6b7280'
})
// 特殊状态样式
const specialStatusClass = computed(() => {
const value = inTime.value
if (value === '99') return 'bg-orange-50 border border-orange-200'
if (value === '-1') return 'bg-gray-50 border border-gray-200'
return ''
})
const specialStatusTextClass = computed(() => {
const value = inTime.value
if (value === '99') return 'text-orange-700'
if (value === '-1') return 'text-gray-700'
return ''
})
const specialStatusDescClass = computed(() => {
const value = inTime.value
if (value === '99') return 'text-orange-600'
if (value === '-1') return 'text-gray-600'
return ''
})
// 特殊状态文本
const specialStatusText = computed(() => {
const value = inTime.value
if (value === '99') return '状态异常'
if (value === '-1') return '查无记录'
return ''
})
// 特殊状态描述
const specialStatusDesc = computed(() => {
const value = inTime.value
if (value === '99') return '手机号可能已离网、新入网或状态异常,无法准确查询在网时长'
if (value === '-1') return '系统中未查询到该手机号的在网时长记录'
return ''
})
// 计算风险评分(在网时长越长,风险越低)
const riskScore = computed(() => {
const value = inTime.value
if (!value || value === '-1') {
return 50 // 查无记录,中等风险
}
if (value === '99') {
return 30 // 状态异常,较高风险
}
// 在网时长越长,分数越高(越安全)
const scoreMap = {
'0': 20, // [0,3) 个月 - 高风险
'3': 40, // [3,6) 个月 - 中高风险
'6': 60, // [6,12) 个月 - 中等风险
'12': 80, // [12,24) 个月 - 低风险
'24': 100 // [24,+) 个月 - 最低风险
}
return scoreMap[value] || 50
})
// 使用 composable 通知父组件风险评分
useRiskNotifier(props, riskScore)
// 暴露给父组件
defineExpose({
riskScore
})
</script>
<style lang="scss" scoped>
.mobile-online-duration {
@apply space-y-4;
}
.verification-section {
.verification-details {
@apply mt-3;
}
}
</style>