748 lines
26 KiB
Vue
748 lines
26 KiB
Vue
<script setup>
|
||
const props = defineProps({
|
||
data: {
|
||
type: Object,
|
||
default: () => ({})
|
||
},
|
||
apiId: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
index: {
|
||
type: Number,
|
||
default: 0,
|
||
},
|
||
notifyRiskStatus: {
|
||
type: Function,
|
||
default: () => { },
|
||
},
|
||
})
|
||
|
||
// 风险等级转换为文字描述
|
||
const riskLevelText = (level, type) => {
|
||
if (type === 'black_gray_level') {
|
||
const levels = {
|
||
'': '无风险',
|
||
1: '低风险',
|
||
2: '中等风险',
|
||
3: '高风险',
|
||
4: '极高风险',
|
||
}
|
||
return levels[level] || '未知风险'
|
||
} else if (type === 'telefraud_level') {
|
||
const levels = {
|
||
0: '无风险',
|
||
1: '极低风险',
|
||
2: '低风险',
|
||
3: '中低风险',
|
||
4: '中等风险',
|
||
5: '高风险',
|
||
6: '极高风险',
|
||
}
|
||
return levels[level] || '未知风险'
|
||
} else if (type === 'frg_list_level') {
|
||
if (level >= '3' && level <= '5') return '低风险团伙'
|
||
if (level >= '6' && level <= '7') return '中风险团伙'
|
||
if (level >= '8' && level <= '10') return '高风险团伙'
|
||
return '无风险'
|
||
} else if (type === 'risk_level') {
|
||
const levels = {
|
||
A: '无风险',
|
||
F: '低风险',
|
||
C: '中风险',
|
||
D: '中风险',
|
||
B: '高风险',
|
||
E: '高风险',
|
||
}
|
||
return levels[level] || '未知风险'
|
||
} else if (type === 'gaming') {
|
||
const levelNum = parseInt(level)
|
||
if (levelNum === 0) return '无风险'
|
||
if (levelNum > 0 && levelNum <= 20) return '极低风险'
|
||
if (levelNum > 20 && levelNum <= 40) return '低风险'
|
||
if (levelNum > 40 && levelNum <= 60) return '中等风险'
|
||
if (levelNum > 60 && levelNum <= 80) return '高风险'
|
||
if (levelNum > 80) return '极高风险'
|
||
return '未知风险'
|
||
}
|
||
return '未知风险'
|
||
}
|
||
|
||
// 风险等级转换为颜色
|
||
const riskLevelColor = (level, type) => {
|
||
if (type === 'black_gray_level') {
|
||
if (level === '' || level === '1') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
|
||
if (level === '2') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
|
||
if (level === '3') return 'bg-gradient-to-r from-orange-400 to-amber-600'
|
||
if (level === '4') return 'bg-gradient-to-r from-rose-400 to-red-500'
|
||
return 'bg-gradient-to-r from-gray-400 to-gray-500'
|
||
} else if (type === 'telefraud_level') {
|
||
if (level === '0') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
|
||
if (level === '1' || level === '2') return 'bg-gradient-to-r from-teal-300 to-green-400'
|
||
if (level === '3' || level === '4') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
|
||
if (level === '5') return 'bg-gradient-to-r from-orange-400 to-amber-600'
|
||
if (level === '6') return 'bg-gradient-to-r from-rose-400 to-red-500'
|
||
return 'bg-gradient-to-r from-gray-400 to-gray-500'
|
||
} else if (type === 'frg_list_level') {
|
||
if (level >= '3' && level <= '5') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
|
||
if (level >= '6' && level <= '7') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
|
||
if (level >= '8' && level <= '10') return 'bg-gradient-to-r from-rose-400 to-red-500'
|
||
return 'bg-gradient-to-r from-gray-400 to-gray-500'
|
||
} else if (type === 'risk_level') {
|
||
if (level === 'A') return 'bg-gradient-to-r from-emerald-400 to-teal-500'
|
||
if (level === 'F') return 'bg-gradient-to-r from-amber-400 to-yellow-500'
|
||
if (level === 'C' || level === 'D') return 'bg-gradient-to-r from-orange-400 to-amber-600'
|
||
if (level === 'B' || level === 'E') return 'bg-gradient-to-r from-rose-400 to-red-500'
|
||
return 'bg-gradient-to-r from-gray-400 to-gray-500'
|
||
} else if (type === 'gaming') {
|
||
const levelNum = parseInt(level)
|
||
if (levelNum === 0) return 'bg-gradient-to-r from-emerald-400 to-teal-500'
|
||
if (levelNum > 0 && levelNum <= 20) return 'bg-gradient-to-r from-teal-300 to-green-400'
|
||
if (levelNum > 20 && levelNum <= 40) return 'bg-gradient-to-r from-green-400 to-green-500'
|
||
if (levelNum > 40 && levelNum <= 60) return 'bg-gradient-to-r from-amber-400 to-yellow-500'
|
||
if (levelNum > 60 && levelNum <= 80) return 'bg-gradient-to-r from-orange-400 to-amber-600'
|
||
if (levelNum > 80) return 'bg-gradient-to-r from-rose-400 to-red-500'
|
||
return 'bg-gradient-to-r from-gray-400 to-gray-500'
|
||
}
|
||
return 'bg-gradient-to-r from-gray-400 to-gray-500'
|
||
}
|
||
|
||
// 根据风险类型获取名称
|
||
const getRiskTypeName = type => {
|
||
const types = {
|
||
110: '疑似欺诈',
|
||
130: '疑似赌博庄家',
|
||
150: '疑似赌博玩家',
|
||
170: '疑似涉赌跑分',
|
||
}
|
||
return types[type] || '未知类型'
|
||
}
|
||
|
||
// 获取团伙规模描述
|
||
const getGroupSizeDesc = code => {
|
||
const sizes = {
|
||
a: '小规模(少于50人)',
|
||
b: '中等规模(50-100人)',
|
||
c: '大规模(100-500人)',
|
||
d: '超大规模(500人以上)',
|
||
}
|
||
return sizes[code] || '未知规模'
|
||
}
|
||
|
||
// 获取风险图标
|
||
const getRiskIcon = type => {
|
||
switch (type) {
|
||
case '110':
|
||
return 'fa-exclamation-triangle'
|
||
case '130':
|
||
return 'fa-dice'
|
||
case '150':
|
||
return 'fa-gamepad'
|
||
case '170':
|
||
return 'fa-money-bill-wave'
|
||
default:
|
||
return 'fa-question-circle'
|
||
}
|
||
}
|
||
|
||
// 获取不良记录详情
|
||
const getRiskLevelDetail = level => {
|
||
switch (level) {
|
||
case 'A':
|
||
return '无任何不良记录'
|
||
case 'F':
|
||
return '涉稳、寻衅滋事'
|
||
case 'C':
|
||
case 'D':
|
||
return '吸毒、涉毒、犯罪前科'
|
||
case 'B':
|
||
case 'E':
|
||
return '涉案人员、在逃、犯罪嫌疑人'
|
||
default:
|
||
return '未知记录'
|
||
}
|
||
}
|
||
|
||
// 风险评估总结
|
||
const getRiskSummary = () => {
|
||
if (!props.data) return { text: '无法评估风险', level: 'low', color: 'text-gray-500' }
|
||
|
||
let highRiskCount = 0
|
||
let mediumRiskCount = 0
|
||
|
||
// 检查黑灰产等级
|
||
if (props.data.black_gray_level && parseInt(props.data.black_gray_level) > 2) {
|
||
highRiskCount++
|
||
} else if (props.data.black_gray_level && parseInt(props.data.black_gray_level) === 2) {
|
||
mediumRiskCount++
|
||
}
|
||
|
||
// 检查电诈风险
|
||
if (props.data.telefraud_level && parseInt(props.data.telefraud_level) > 4) {
|
||
highRiskCount++
|
||
} else if (props.data.telefraud_level && parseInt(props.data.telefraud_level) > 2) {
|
||
mediumRiskCount++
|
||
}
|
||
|
||
// 检查团伙欺诈
|
||
if (
|
||
props.data.fraud_group &&
|
||
props.data.fraud_group.frg_list_level &&
|
||
parseInt(props.data.fraud_group.frg_list_level) > 7
|
||
) {
|
||
highRiskCount++
|
||
} else if (
|
||
props.data.fraud_group &&
|
||
props.data.fraud_group.frg_list_level &&
|
||
parseInt(props.data.fraud_group.frg_list_level) > 5
|
||
) {
|
||
mediumRiskCount++
|
||
}
|
||
|
||
// 检查风险等级
|
||
if (props.data.risk_level && props.data.risk_level.risk_level) {
|
||
if (['B', 'E'].includes(props.data.risk_level.risk_level)) {
|
||
highRiskCount++
|
||
} else if (['C', 'D'].includes(props.data.risk_level.risk_level)) {
|
||
mediumRiskCount++
|
||
} else if (props.data.risk_level.risk_level === 'F') {
|
||
// 低风险,不增加计数
|
||
}
|
||
}
|
||
|
||
// 检查反诈反赌核验
|
||
if (props.data.anti_fraud_gaming) {
|
||
props.data.anti_fraud_gaming.forEach(item => {
|
||
const levelNum = parseInt(item.riskLevel)
|
||
if (levelNum > 60) {
|
||
highRiskCount++
|
||
} else if (levelNum > 40) {
|
||
mediumRiskCount++
|
||
}
|
||
})
|
||
}
|
||
|
||
if (highRiskCount > 0) {
|
||
return {
|
||
text: '该用户存在较高风险行为,建议进行进一步核实和监控',
|
||
level: 'high',
|
||
color: 'text-red-500',
|
||
}
|
||
} else if (mediumRiskCount > 0) {
|
||
return {
|
||
text: '该用户存在一定风险行为,建议提高警惕',
|
||
level: 'medium',
|
||
color: 'text-yellow-500',
|
||
}
|
||
} else {
|
||
return {
|
||
text: '该用户行为正常,风险较低',
|
||
level: 'low',
|
||
color: 'text-green-500',
|
||
}
|
||
}
|
||
}
|
||
|
||
const summary = getRiskSummary()
|
||
|
||
// 计算风险评分(0-100分,分数越高越安全)
|
||
const riskScore = computed(() => {
|
||
// 计算总风险项数量
|
||
const totalRiskCount = Object.values(summary).reduce((sum, item) => sum + item.count, 0);
|
||
|
||
// 根据风险项数量计算评分
|
||
// 0项:100分(最安全)
|
||
// 1-2项:80分(较安全)
|
||
// 3-5项:60分(中等风险)
|
||
// 6-10项:40分(较高风险)
|
||
// 10项以上:20分(高风险)
|
||
if (totalRiskCount === 0) return 100;
|
||
if (totalRiskCount <= 2) return 80;
|
||
if (totalRiskCount <= 5) return 60;
|
||
if (totalRiskCount <= 10) return 40;
|
||
return 20;
|
||
});
|
||
|
||
// 使用 composable 通知父组件风险评分
|
||
useRiskNotifier(props, riskScore);
|
||
|
||
// 暴露给父组件
|
||
defineExpose({
|
||
riskScore
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<div class="card main-card">
|
||
<div v-if="!data || Object.keys(data).length === 0" class="py-4 text-center text-gray-500">
|
||
暂无风险行为扫描数据
|
||
</div>
|
||
<div v-else class="risk-content">
|
||
<!-- 风险总结 -->
|
||
<div class="summary-card" :class="{
|
||
'border-red-500 glow-red': summary.level === 'high',
|
||
'border-yellow-500 glow-yellow': summary.level === 'medium',
|
||
'border-green-500 glow-green': summary.level === 'low',
|
||
}">
|
||
<div class="flex items-center">
|
||
<div class="summary-icon" :class="summary.color">
|
||
<i class="fas" :class="summary.level === 'high'
|
||
? 'fa-exclamation-triangle'
|
||
: summary.level === 'medium'
|
||
? 'fa-exclamation-circle'
|
||
: 'fa-check-circle'
|
||
"></i>
|
||
</div>
|
||
<div class="font-bold text-lg" :class="summary.color">风险评估总结</div>
|
||
</div>
|
||
<div class="mt-1 text-gray-700">{{ summary.text }}</div>
|
||
</div>
|
||
|
||
<div class="grid-container">
|
||
<!-- 左侧列 -->
|
||
<div class="grid-left">
|
||
<!-- 黑灰产等级 -->
|
||
<!-- <div class="risk-section hover-lift">
|
||
<div class="section-title flex items-center">
|
||
<div class="title-icon bg-indigo-100 text-indigo-600">
|
||
<i class="fas fa-user-secret"></i>
|
||
</div>
|
||
<span>黑灰产等级</span>
|
||
</div>
|
||
<div class="section-content">
|
||
<div class="risk-level-indicator">
|
||
<div class="indicator-label">风险等级</div>
|
||
<div class="indicator-bar">
|
||
<div class="indicator-value" :class="riskLevelColor(data.black_gray_level || '', 'black_gray_level')"
|
||
:style="{
|
||
width: data.black_gray_level ? `${Math.min(parseInt(data.black_gray_level) * 25, 100)}%` : '0%',
|
||
}"></div>
|
||
</div>
|
||
<div class="indicator-text" :class="{
|
||
'text-green-500': (data.black_gray_level || '') === '' || (data.black_gray_level || '') === '1',
|
||
'text-yellow-500': (data.black_gray_level || '') === '2',
|
||
'text-orange-500': (data.black_gray_level || '') === '3',
|
||
'text-red-500': (data.black_gray_level || '') === '4',
|
||
}">
|
||
{{ riskLevelText(data.black_gray_level || '', 'black_gray_level') }}
|
||
</div>
|
||
</div>
|
||
<div class="description">黑灰产等级评估用户是否参与非法活动,等级越高风险越大</div>
|
||
</div>
|
||
</div> -->
|
||
|
||
<!-- 电诈风险预警 -->
|
||
<!-- <div class="risk-section hover-lift">
|
||
<div class="section-title flex items-center">
|
||
<div class="title-icon bg-red-100 text-red-600">
|
||
<i class="fas fa-phone-slash"></i>
|
||
</div>
|
||
<span>电诈风险预警</span>
|
||
</div>
|
||
<div class="section-content">
|
||
<div class="risk-level-indicator">
|
||
<div class="indicator-label">风险等级</div>
|
||
<div class="indicator-bar">
|
||
<div class="indicator-value" :class="riskLevelColor(data.telefraud_level || '0', 'telefraud_level')"
|
||
:style="{ width: `${Math.min(parseInt(data.telefraud_level || '0') * 16.6, 100)}%` }"></div>
|
||
</div>
|
||
<div class="indicator-text" :class="{
|
||
'text-green-500':
|
||
(data.telefraud_level || '0') === '0' ||
|
||
(data.telefraud_level || '0') === '1' ||
|
||
(data.telefraud_level || '0') === '2',
|
||
'text-yellow-500': (data.telefraud_level || '0') === '3' || (data.telefraud_level || '0') === '4',
|
||
'text-orange-500': (data.telefraud_level || '0') === '5',
|
||
'text-red-500': (data.telefraud_level || '0') === '6',
|
||
}">
|
||
{{ riskLevelText(data.telefraud_level || '0', 'telefraud_level') }}
|
||
</div>
|
||
</div>
|
||
<div class="description">电诈风险预警评估用户是否涉及电信诈骗活动,值越大风险越高</div>
|
||
</div>
|
||
</div> -->
|
||
|
||
<!-- 综合风险等级 -->
|
||
<!-- <div class="risk-section hover-lift">
|
||
<div class="section-title flex items-center">
|
||
<div class="title-icon bg-emerald-100 text-emerald-600">
|
||
<i class="fas fa-shield-alt"></i>
|
||
</div>
|
||
<span>不良个人核查</span>
|
||
</div>
|
||
<div class="section-content">
|
||
<div v-if="data.risk_level" class="flex items-center justify-center py-3">
|
||
<div
|
||
class="risk-level-badge"
|
||
:class="{
|
||
'bg-green-100 text-green-700 badge-pulse-green': data.risk_level.risk_level === 'A',
|
||
'bg-yellow-100 text-yellow-700 badge-pulse-yellow': data.risk_level.risk_level === 'F',
|
||
'bg-orange-100 text-orange-700 badge-pulse-orange': ['C', 'D'].includes(data.risk_level.risk_level),
|
||
'bg-red-100 text-red-700 badge-pulse-red': ['B', 'E'].includes(data.risk_level.risk_level),
|
||
}"
|
||
>
|
||
<span class="text-xl font-bold">{{
|
||
riskLevelText(data.risk_level.risk_level || 'A', 'risk_level')
|
||
}}</span>
|
||
</div>
|
||
<div class="ml-4 text-sm">
|
||
<div class="font-medium">详情:</div>
|
||
<div
|
||
class="mt-1"
|
||
:class="{
|
||
'text-green-600': data.risk_level.risk_level === 'A',
|
||
'text-yellow-600': data.risk_level.risk_level === 'F',
|
||
'text-orange-600': ['C', 'D'].includes(data.risk_level.risk_level),
|
||
'text-red-600': ['B', 'E'].includes(data.risk_level.risk_level),
|
||
}"
|
||
>
|
||
{{ getRiskLevelDetail(data.risk_level.risk_level || 'A') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="text-center py-2 text-gray-500">暂无不良个人核查数据</div>
|
||
<div class="description">不良个人核查评估用户的风险状况,从无风险到高风险分级</div>
|
||
</div>
|
||
</div> -->
|
||
</div>
|
||
|
||
<div class="grid-right">
|
||
<!-- <div class="risk-section hover-lift">
|
||
<div class="section-title flex items-center">
|
||
<div class="title-icon bg-amber-100 text-amber-600">
|
||
<i class="fas fa-users-slash"></i>
|
||
</div>
|
||
<span>团伙欺诈排查</span>
|
||
</div>
|
||
<div class="section-content">
|
||
<div v-if="data.fraud_group" class="flex flex-col md:flex-row gap-3">
|
||
<div class="risk-level-indicator flex-1">
|
||
<div class="indicator-label">团伙风险等级</div>
|
||
<div class="indicator-bar">
|
||
<div class="indicator-value"
|
||
:class="riskLevelColor(data.fraud_group.frg_list_level || '3', 'frg_list_level')" :style="{
|
||
width: `${Math.min((parseInt(data.fraud_group.frg_list_level || '3') - 2) * 12.5, 100)}%`,
|
||
}"></div>
|
||
</div>
|
||
<div class="indicator-text" :class="{
|
||
'text-green-500': parseInt(data.fraud_group.frg_list_level || '3') <= 5,
|
||
'text-yellow-500':
|
||
parseInt(data.fraud_group.frg_list_level || '3') >= 6 &&
|
||
parseInt(data.fraud_group.frg_list_level || '3') <= 7,
|
||
'text-red-500': parseInt(data.fraud_group.frg_list_level || '3') >= 8,
|
||
}">
|
||
{{ riskLevelText(data.fraud_group.frg_list_level || '3', 'frg_list_level') }}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="group-size flex-1">
|
||
<div class="font-medium text-gray-700">团伙规模</div>
|
||
<div class="mt-2 flex items-center">
|
||
<i class="fas fa-users text-blue-500 mr-2 text-xl"></i>
|
||
<span>{{ getGroupSizeDesc(data.fraud_group.frg_group_num || 'a') }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="text-center py-2 text-gray-500">暂无团伙欺诈数据</div>
|
||
<div class="description mt-1">团伙欺诈排查评估用户是否属于欺诈团伙及团伙规模大小</div>
|
||
</div>
|
||
</div> -->
|
||
|
||
<div class="risk-section hover-lift">
|
||
<div class="section-title flex items-center">
|
||
<div class="title-icon bg-purple-100 text-purple-600">
|
||
<i class="fas fa-dice-slash"></i>
|
||
</div>
|
||
<span>反诈反赌核验</span>
|
||
</div>
|
||
<div class="section-content">
|
||
<div v-if="data.anti_fraud_gaming && data.anti_fraud_gaming.length > 0" class="grid grid-cols-1 gap-3">
|
||
<div v-for="(item, index) in data.anti_fraud_gaming" :key="index" class="gaming-item" :class="parseInt(item.riskLevel) === 0
|
||
? 'border-green-500'
|
||
: parseInt(item.riskLevel) < 4
|
||
? 'border-green-400'
|
||
: parseInt(item.riskLevel) < 7
|
||
? 'border-yellow-500'
|
||
: 'border-red-500'
|
||
">
|
||
<div class="gaming-icon" :class="parseInt(item.riskLevel) === 0
|
||
? 'bg-green-100 text-green-500'
|
||
: parseInt(item.riskLevel) < 4
|
||
? 'bg-green-100 text-green-500'
|
||
: parseInt(item.riskLevel) < 7
|
||
? 'bg-yellow-100 text-yellow-600'
|
||
: 'bg-red-100 text-red-500'
|
||
">
|
||
<i class="fas" :class="getRiskIcon(item.riskType)"></i>
|
||
</div>
|
||
<div class="flex-1">
|
||
<div class="font-medium text-sm">{{ getRiskTypeName(item.riskType) }}</div>
|
||
<div class="flex items-center mt-2">
|
||
<div class="progress-container">
|
||
<div class="progress-bar" :class="riskLevelColor(item.riskLevel, 'gaming')"
|
||
:style="{ width: `${Math.min(parseInt(item.riskLevel), 100)}%` }"></div>
|
||
</div>
|
||
<span class="risk-level-text" :class="{
|
||
'text-green-500': parseInt(item.riskLevel) <= 20,
|
||
'text-green-600': parseInt(item.riskLevel) > 20 && parseInt(item.riskLevel) <= 40,
|
||
'text-yellow-500': parseInt(item.riskLevel) > 40 && parseInt(item.riskLevel) <= 60,
|
||
'text-orange-500': parseInt(item.riskLevel) > 60 && parseInt(item.riskLevel) <= 80,
|
||
'text-red-500': parseInt(item.riskLevel) > 80,
|
||
}">
|
||
{{ riskLevelText(item.riskLevel, 'gaming') }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-else class="text-center py-2 text-gray-500">暂无反诈反赌核验数据</div>
|
||
<div class="description mt-1">反诈反赌核验评估用户是否有涉及诈骗或赌博活动的风险</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="security-tips hover-lift">
|
||
<div class="flex items-center">
|
||
<div class="title-icon bg-blue-100 text-blue-600 mr-2">
|
||
<i class="fas fa-lightbulb"></i>
|
||
</div>
|
||
<div class="font-bold text-blue-700">安全建议</div>
|
||
</div>
|
||
<div class="tip-list">
|
||
<div class="tip-item">
|
||
<i class="fas fa-check-circle text-green-500 mr-1"></i>
|
||
<span>定期更新密码,使用复杂且不易猜测的密码</span>
|
||
</div>
|
||
<div class="tip-item">
|
||
<i class="fas fa-check-circle text-green-500 mr-1"></i>
|
||
<span>开启双因素认证,提高账户安全性</span>
|
||
</div>
|
||
<div class="tip-item">
|
||
<i class="fas fa-check-circle text-green-500 mr-1"></i>
|
||
<span>不点击来源不明的链接或下载不明文件</span>
|
||
</div>
|
||
<div class="tip-item">
|
||
<i class="fas fa-check-circle text-green-500 mr-1"></i>
|
||
<span>不向陌生人透露个人敏感信息</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.main-card {
|
||
@apply bg-white shadow-md rounded-xl p-4 mb-3 border border-gray-100;
|
||
}
|
||
|
||
.risk-content {
|
||
@apply space-y-4;
|
||
}
|
||
|
||
.grid-container {
|
||
@apply grid grid-cols-1 md:grid-cols-2 gap-4;
|
||
}
|
||
|
||
.grid-left,
|
||
.grid-right {
|
||
@apply flex flex-col gap-4;
|
||
}
|
||
|
||
.summary-card {
|
||
@apply p-4 rounded-xl shadow-sm bg-gradient-to-br from-sky-50 to-indigo-100 border-l-4 transition-all duration-300;
|
||
}
|
||
|
||
.glow-red {
|
||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.15);
|
||
border-color: rgba(239, 68, 68, 0.6);
|
||
}
|
||
|
||
.glow-yellow {
|
||
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.15);
|
||
border-color: rgba(245, 158, 11, 0.6);
|
||
}
|
||
|
||
.glow-green {
|
||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15);
|
||
border-color: rgba(16, 185, 129, 0.6);
|
||
}
|
||
|
||
.summary-icon {
|
||
@apply mr-2 text-xl;
|
||
}
|
||
|
||
.risk-section {
|
||
@apply bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden transition-all duration-300;
|
||
}
|
||
|
||
.hover-lift:hover {
|
||
transform: translateY(-3px);
|
||
box-shadow:
|
||
0 8px 16px -2px rgba(0, 0, 0, 0.1),
|
||
0 4px 8px -2px rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.section-title {
|
||
@apply bg-gradient-to-r from-gray-50 to-gray-100 px-4 py-3 font-bold text-gray-700 border-b border-gray-200 flex items-center;
|
||
}
|
||
|
||
.title-icon {
|
||
@apply w-7 h-7 rounded-full flex items-center justify-center mr-3 shadow-sm;
|
||
}
|
||
|
||
.section-content {
|
||
@apply p-4;
|
||
}
|
||
|
||
.risk-level-indicator {
|
||
@apply mb-2;
|
||
}
|
||
|
||
.indicator-label {
|
||
@apply text-gray-700 font-medium mb-1 text-sm;
|
||
}
|
||
|
||
.indicator-bar {
|
||
@apply w-full bg-gray-200 rounded-full h-3 overflow-hidden shadow-inner;
|
||
}
|
||
|
||
.indicator-value {
|
||
@apply h-3 rounded-full transition-all duration-500;
|
||
}
|
||
|
||
.indicator-text {
|
||
@apply mt-1 font-medium text-sm;
|
||
}
|
||
|
||
.description {
|
||
@apply text-xs text-gray-500 mt-2 italic;
|
||
}
|
||
|
||
.risk-level-badge {
|
||
@apply flex flex-col items-center justify-center w-20 h-20 rounded-full shadow-md border transition-transform duration-300 backdrop-blur-sm;
|
||
}
|
||
|
||
.badge-pulse-green {
|
||
background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.3));
|
||
border-color: rgba(5, 150, 105, 0.4);
|
||
animation: pulse-green 3s infinite;
|
||
}
|
||
|
||
.badge-pulse-yellow {
|
||
background: linear-gradient(135deg, rgba(245, 158, 11, 0.15), rgba(217, 119, 6, 0.3));
|
||
border-color: rgba(217, 119, 6, 0.4);
|
||
animation: pulse-yellow 3s infinite;
|
||
}
|
||
|
||
.badge-pulse-orange {
|
||
background: linear-gradient(135deg, rgba(249, 115, 22, 0.15), rgba(234, 88, 12, 0.3));
|
||
border-color: rgba(234, 88, 12, 0.4);
|
||
animation: pulse-orange 3s infinite;
|
||
}
|
||
|
||
.badge-pulse-red {
|
||
background: linear-gradient(135deg, rgba(239, 68, 68, 0.15), rgba(220, 38, 38, 0.3));
|
||
border-color: rgba(220, 38, 38, 0.4);
|
||
animation: pulse-red 3s infinite;
|
||
}
|
||
|
||
@keyframes pulse-green {
|
||
0% {
|
||
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.3);
|
||
}
|
||
|
||
70% {
|
||
box-shadow: 0 0 0 8px rgba(16, 185, 129, 0);
|
||
}
|
||
|
||
100% {
|
||
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0);
|
||
}
|
||
}
|
||
|
||
@keyframes pulse-yellow {
|
||
0% {
|
||
box-shadow: 0 0 0 0 rgba(245, 158, 11, 0.3);
|
||
}
|
||
|
||
70% {
|
||
box-shadow: 0 0 0 8px rgba(245, 158, 11, 0);
|
||
}
|
||
|
||
100% {
|
||
box-shadow: 0 0 0 0 rgba(245, 158, 11, 0);
|
||
}
|
||
}
|
||
|
||
@keyframes pulse-orange {
|
||
0% {
|
||
box-shadow: 0 0 0 0 rgba(249, 115, 22, 0.3);
|
||
}
|
||
|
||
70% {
|
||
box-shadow: 0 0 0 8px rgba(249, 115, 22, 0);
|
||
}
|
||
|
||
100% {
|
||
box-shadow: 0 0 0 0 rgba(249, 115, 22, 0);
|
||
}
|
||
}
|
||
|
||
@keyframes pulse-red {
|
||
0% {
|
||
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.3);
|
||
}
|
||
|
||
70% {
|
||
box-shadow: 0 0 0 8px rgba(239, 68, 68, 0);
|
||
}
|
||
|
||
100% {
|
||
box-shadow: 0 0 0 0 rgba(239, 68, 68, 0);
|
||
}
|
||
}
|
||
|
||
.group-size {
|
||
@apply bg-gradient-to-br from-gray-50 to-gray-100 p-3 rounded-lg shadow-sm;
|
||
}
|
||
|
||
.gaming-item {
|
||
@apply flex items-center bg-white shadow-sm rounded-lg p-3 border-l-2 transition-all duration-300;
|
||
}
|
||
|
||
.gaming-item:hover {
|
||
@apply shadow-md;
|
||
transform: scale(1.01);
|
||
}
|
||
|
||
.gaming-icon {
|
||
@apply w-9 h-9 flex items-center justify-center rounded-full mr-3 shadow-sm;
|
||
}
|
||
|
||
.progress-container {
|
||
@apply w-full bg-gray-200 rounded-full h-3 mr-3 flex-1 shadow-inner;
|
||
}
|
||
|
||
.progress-bar {
|
||
@apply h-3 rounded-full transition-all duration-500;
|
||
}
|
||
|
||
.risk-level-text {
|
||
@apply text-xs whitespace-nowrap min-w-[3.5rem] text-right font-semibold;
|
||
}
|
||
|
||
.security-tips {
|
||
@apply bg-gradient-to-br from-sky-50 to-indigo-100 rounded-xl p-4 shadow-sm border border-blue-200;
|
||
}
|
||
|
||
.tip-list {
|
||
@apply mt-3 space-y-2;
|
||
}
|
||
|
||
.tip-item {
|
||
@apply flex items-start text-sm text-gray-700 bg-white p-2 rounded-lg shadow-sm border border-gray-100;
|
||
}
|
||
</style>
|