Files
SmartElderlyCare/src/views/robot/Monitor.vue
祀梦 1b87097447 refactor(功能模块): 将"时间银行"重命名为"互助中心"并完善相关功能
重构项目中的"时间银行"模块,统一更名为"互助中心",涉及前端路由、组件、文档及多处文本替换。新增互助中心页面功能,包括:
1. 通证兑换服务弹窗
2. 互助任务列表展示
3. 区块链存证交互流程

同时优化护理记录页面,增加筛选、导出功能,完善监管端仪表盘交互
2026-01-13 10:30:21 +08:00

597 lines
28 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="h-full grid grid-cols-12 gap-0">
<!-- Left: Vision Stream & AI Detection -->
<div class="col-span-8 bg-black relative border-r border-gray-800">
<!-- Live Stream Overlay -->
<div class="absolute top-4 left-4 z-10 flex space-x-2">
<div class="px-2 py-1 bg-red-600/80 text-white text-xs font-bold rounded animate-pulse">录制中</div>
<div class="px-2 py-1 bg-gray-800/80 text-green-400 text-xs font-mono border border-green-500/30">
帧率: {{ fps }} | 延迟: {{ latency }}ms
</div>
<div class="px-2 py-1 bg-blue-600/80 text-white text-xs font-bold rounded">
当前场景: {{ scenes[currentScene].name }}
</div>
</div>
<!-- Main Visual Area (Simulated Pose Estimation) -->
<div class="w-full h-full flex items-center justify-center relative overflow-hidden group transition-colors duration-1000"
:class="scenes[currentScene].color">
<!-- Scene Selector -->
<div class="absolute top-16 left-4 z-10 flex flex-col space-y-2">
<button v-for="(scene, key) in scenes" :key="key"
@click="currentScene = key"
class="px-3 py-1.5 text-xs border rounded-sm backdrop-blur-md transition-all duration-300 flex items-center space-x-2"
:class="currentScene === key ? 'bg-green-500 text-black border-green-500 shadow-[0_0_10px_rgba(34,197,94,0.5)]' : 'bg-black/50 text-green-500/50 border-green-500/20 hover:border-green-500/50'">
<span>{{ scene.icon }}</span>
<span>{{ scene.name }}</span>
</button>
</div>
<!-- Manual Control Toggle -->
<div class="absolute top-16 right-4 z-10">
<button @click="toggleManualMode"
class="px-4 py-2 text-xs font-bold border rounded-lg backdrop-blur-md transition-all flex items-center space-x-2"
:class="isManualMode ? 'bg-orange-500 text-white border-orange-500 animate-pulse' : 'bg-black/50 text-white/50 border-white/20 hover:border-white/50'">
<span>🎮</span>
<span>{{ isManualMode ? '人工接管中' : '自动巡航模式' }}</span>
</button>
</div>
<!-- Manual Control Pad -->
<div v-if="isManualMode" class="absolute bottom-8 left-1/2 -translate-x-1/2 z-20 flex flex-col items-center space-y-2 animate-fade-in-up">
<button class="w-12 h-12 bg-white/10 hover:bg-white/30 backdrop-blur border border-white/20 rounded-lg flex items-center justify-center text-white text-xl active:bg-orange-500 active:scale-95 transition-all">
</button>
<div class="flex space-x-4">
<button class="w-12 h-12 bg-white/10 hover:bg-white/30 backdrop-blur border border-white/20 rounded-lg flex items-center justify-center text-white text-xl active:bg-orange-500 active:scale-95 transition-all">
</button>
<button class="w-12 h-12 bg-red-500/80 hover:bg-red-500 backdrop-blur border border-red-400 rounded-full flex items-center justify-center text-white text-xl active:scale-95 transition-all shadow-[0_0_15px_rgba(239,68,68,0.5)]">
🛑
</button>
<button class="w-12 h-12 bg-white/10 hover:bg-white/30 backdrop-blur border border-white/20 rounded-lg flex items-center justify-center text-white text-xl active:bg-orange-500 active:scale-95 transition-all">
</button>
</div>
<button class="w-12 h-12 bg-white/10 hover:bg-white/30 backdrop-blur border border-white/20 rounded-lg flex items-center justify-center text-white text-xl active:bg-orange-500 active:scale-95 transition-all">
</button>
</div>
<!-- SVG Filters for Edge Detection Effect -->
<svg class="absolute w-0 h-0">
<filter id="edge-detection">
<feConvolveMatrix kernelMatrix="-1 -1 -1 -1 8 -1 -1 -1 -1" />
</filter>
</svg>
<!-- Grid Background -->
<div class="absolute inset-0 opacity-10"
style="background-image: linear-gradient(rgba(0, 255, 0, 0.1) 1px, transparent 1px), linear-gradient(90deg, rgba(0, 255, 0, 0.1) 1px, transparent 1px); background-size: 80px 80px;">
</div>
<!-- Scene Elements (Simplified Props) -->
<div v-for="(el, i) in scenes[currentScene].elements" :key="i"
class="absolute border border-white/10 bg-white/5 rounded-lg backdrop-blur-sm group/el transition-all duration-700 hover:bg-white/10"
:style="{ left: el.x + '%', top: el.y + '%', width: (120 * el.scale) + 'px', height: (80 * el.scale) + 'px', transform: 'perspective(500px) rotateX(15deg)' }">
<div class="absolute inset-0 flex flex-col items-center justify-center font-mono italic">
<span class="text-2xl mb-1 opacity-50 group-hover/el:opacity-100 transition-opacity">{{ el.icon }}</span>
<span class="text-[10px] text-white/40 group-hover/el:text-white/80">{{ el.type }}</span>
</div>
</div>
<!-- Target Tracking Simulation (Multiple Boxes) -->
<div v-for="target in targets" :key="target.id"
class="absolute border border-green-500/30 transition-all duration-1000"
:style="{ left: target.x + '%', top: target.y + '%', width: '120px', height: '180px', transform: 'translate(-50%, -50%)', opacity: target.active ? 0.3 : 0.1 }">
<div class="absolute -top-4 left-0 text-[8px] font-mono text-green-500/50">{{ target.id }}</div>
<div class="absolute top-0 left-0 w-2 h-2 border-t border-l border-green-500"></div>
<div class="absolute top-0 right-0 w-2 h-2 border-t border-r border-green-500"></div>
<div class="absolute bottom-0 left-0 w-2 h-2 border-b border-l border-green-500"></div>
<div class="absolute bottom-0 right-0 w-2 h-2 border-b border-r border-green-500"></div>
</div>
<!-- Radar Scanning UI -->
<div class="absolute top-4 right-4 w-32 h-32 border border-green-500/20 rounded-full overflow-hidden hidden md:block">
<div class="absolute inset-0 border border-green-500/10 rounded-full scale-75"></div>
<div class="absolute inset-0 border border-green-500/10 rounded-full scale-50"></div>
<div class="absolute top-1/2 left-0 w-full h-px bg-green-500/20"></div>
<div class="absolute left-1/2 top-0 w-px h-full bg-green-500/20"></div>
<div class="absolute inset-0 origin-center animate-radar-spin bg-gradient-to-tr from-green-500/20 to-transparent"></div>
<div class="absolute top-1/4 left-1/3 w-1.5 h-1.5 bg-green-400 rounded-full shadow-[0_0_8px_rgba(74,222,128,1)] animate-pulse"></div>
</div>
<!-- System Status Floating Panel -->
<div class="absolute top-4 right-40 hidden lg:flex flex-col space-y-2 bg-black/40 backdrop-blur-md p-3 border border-white/10 rounded font-mono text-[10px]">
<div class="text-white/60 mb-1 border-b border-white/10 pb-1 flex justify-between">
<span>系统负载</span>
<span class="text-green-500">正常</span>
</div>
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
<div class="flex items-center justify-between space-x-2">
<span class="text-white/40">CPU:</span>
<span class="text-white/80">{{ systemStats.cpu }}%</span>
</div>
<div class="flex items-center justify-between space-x-2">
<span class="text-white/40">内存:</span>
<span class="text-white/80">{{ systemStats.mem }}%</span>
</div>
<div class="flex items-center justify-between space-x-2">
<span class="text-white/40">温度:</span>
<span class="text-white/80">{{ systemStats.temp }}°C</span>
</div>
<div class="flex items-center justify-between space-x-2">
<span class="text-white/40">电量:</span>
<span class="text-white/80">{{ systemStats.battery }}%</span>
</div>
</div>
</div>
<!-- Environmental Sensors (Floating) -->
<div class="absolute bottom-4 right-4 space-y-3 font-mono text-[10px] bg-black/40 backdrop-blur-md p-4 border border-white/10 rounded-lg">
<div class="text-white/60 mb-2 border-b border-white/10 pb-1 text-center">多维传感器数据</div>
<div class="space-y-2 min-w-[140px]">
<div class="flex items-center justify-between space-x-3">
<span class="text-green-400/70">环境温度:</span>
<div class="flex items-center space-x-2">
<span class="text-white">24.5°C</span>
<div class="w-12 h-1 bg-gray-800 rounded-full overflow-hidden">
<div class="w-3/5 h-full bg-green-500"></div>
</div>
</div>
</div>
<div class="flex items-center justify-between space-x-3">
<span class="text-blue-400/70">空气湿度:</span>
<div class="flex items-center space-x-2">
<span class="text-white">45%</span>
<div class="w-12 h-1 bg-gray-800 rounded-full overflow-hidden">
<div class="w-2/5 h-full bg-blue-500"></div>
</div>
</div>
</div>
<div class="flex items-center justify-between space-x-3">
<span class="text-yellow-400/70">噪音水平:</span>
<div class="flex items-center space-x-2">
<span class="text-white">-42dB</span>
<div class="w-12 h-1 bg-gray-800 rounded-full overflow-hidden">
<div class="h-full bg-yellow-500" :style="{width: currentLevel === 'L3' ? '90%' : '30%'}"></div>
</div>
</div>
</div>
<div class="flex items-center justify-between space-x-3">
<span class="text-orange-400/70">CO2浓度:</span>
<div class="flex items-center space-x-2">
<span class="text-white">{{ environmentDetails.co2 }}ppm</span>
<div class="w-12 h-1 bg-gray-800 rounded-full overflow-hidden">
<div class="w-4/5 h-full bg-orange-500"></div>
</div>
</div>
</div>
</div>
</div>
<!-- Skeleton / Person Placeholder (SVG Based for better animation) -->
<div class="relative transition-all duration-1000 ease-in-out"
:class="{
'transform rotate-[85deg] translate-y-24 translate-x-4': currentLevel === 'L4',
'translate-y-12': currentLevel === 'L2'
}">
<!-- Virtual Chair for L2 -->
<svg v-if="currentLevel === 'L2'" width="120" height="120" viewBox="0 0 120 120" class="absolute top-24 left-0 opacity-40">
<path d="M30,80 L90,80 M40,80 L40,110 M80,80 L80,110 M30,80 L30,40" stroke="currentColor" stroke-width="2" fill="none" class="text-gray-500" />
</svg>
<svg width="120" height="240" viewBox="0 0 120 240" class="overflow-visible">
<!-- Glow Effect -->
<defs>
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="blur" />
<feComposite in="SourceGraphic" in2="blur" operator="over" />
</filter>
</defs>
<g :class="statusTextClass" filter="url(#glow)">
<!-- Head with Breathing and L2 Tilt -->
<g class="transition-all duration-1000"
:style="{
transform: currentLevel === 'L2' ? 'translate(60px, 40px) rotate(45deg) translate(-60px, -40px)' : '',
animation: currentLevel === 'L1' ? 'breathing 3s ease-in-out infinite' : ''
}">
<circle cx="60" cy="40" r="18" fill="none" stroke="currentColor" stroke-width="2" class="animate-pulse" />
<text x="60" y="42" text-anchor="middle" font-size="8" fill="currentColor" opacity="0.6" font-family="monospace">
{{ confidence }}%
</text>
</g>
<!-- Neck -->
<line x1="60" y1="58" x2="60" y2="65" stroke="currentColor" stroke-width="2" />
<!-- Torso / Spine (Breathing) -->
<path :d="currentLevel === 'L2' ? 'M60,65 Q70,90 60,110' : 'M60,65 Q65,100 60,130'"
fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"
class="transition-all duration-1000"
:style="{ animation: currentLevel === 'L1' ? 'breathing-torso 3s ease-in-out infinite' : '' }" />
<!-- Shoulders & Arms -->
<g class="transition-transform duration-1000"
:style="currentLevel === 'L3' ? 'transform: translateY(-5px)' : (currentLevel === 'L2' ? 'transform: translateY(5px)' : '')">
<line x1="30" y1="75" x2="90" y2="75" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
<!-- Left Arm -->
<path :d="currentLevel === 'L3' ? 'M30,75 L15,45' : (currentLevel === 'L2' ? 'M30,75 L25,105 L45,100' : 'M30,75 L20,115')"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="transition-all duration-1000" />
<!-- Right Arm -->
<path :d="currentLevel === 'L3' ? 'M90,75 L105,45' : (currentLevel === 'L2' ? 'M90,75 L95,105 L75,100' : 'M90,75 L100,115')"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="transition-all duration-1000" />
</g>
<!-- Hips & Legs (Sitting for L2) -->
<g class="transition-all duration-1000">
<line :x1="currentLevel === 'L2' ? 45 : 45" y1="130" :x2="currentLevel === 'L2' ? 75 : 75" y2="130" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
<!-- Left Leg -->
<path :d="currentLevel === 'L4' ? 'M45,130 L30,170 L50,210' : (currentLevel === 'L2' ? 'M45,110 L25,115 L20,160' : 'M45,130 L40,180 L45,230')"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="transition-all duration-1000" />
<!-- Right Leg -->
<path :d="currentLevel === 'L4' ? 'M75,130 L95,175 L85,215' : (currentLevel === 'L2' ? 'M75,110 L95,115 L100,160' : 'M75,130 L80,180 L75,230')"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="transition-all duration-1000" />
</g>
</g>
</svg>
<!-- Bounding Box -->
<div class="absolute -inset-10 border border-dashed opacity-40 animate-pulse transition-all duration-500"
:class="statusBorderClass">
<div class="absolute -top-6 left-0 text-[10px] font-mono whitespace-nowrap" :class="statusTextClass">
ID: PERSON_01 [{{ currentLevel }}]
</div>
</div>
</div>
<!-- Privacy Mask Effect (Scanline) -->
<div class="absolute inset-0 pointer-events-none bg-gradient-to-b from-transparent via-green-500/10 to-transparent h-8 w-full animate-scan"></div>
</div>
<!-- Bottom Console Log -->
<div class="absolute bottom-0 left-0 w-full h-48 bg-black/90 border-t border-gray-800 p-4 font-mono text-xs overflow-y-auto custom-scrollbar">
<div class="flex items-center space-x-2 text-white/40 mb-2 border-b border-white/5 pb-1">
<span class="animate-pulse"></span>
<span>系统实时审计日志 (SYSTEM AUDIT LOG)</span>
</div>
<div v-for="(log, i) in logs" :key="i" class="mb-1 flex items-start space-x-2">
<span class="text-gray-500 shrink-0">[{{ log.time }}]</span>
<span :class="log.color" class="break-all">{{ log.message }}</span>
</div>
</div>
</div>
<!-- Right: AI Logic & Control -->
<div class="col-span-4 bg-gray-900 flex flex-col">
<!-- Header -->
<div class="p-4 border-b border-gray-800 bg-gray-800/50">
<h2 class="text-white font-bold flex items-center">
<span class="mr-2 text-xl">🧠</span> 决策引擎 (Brain)
</h2>
</div>
<!-- Status Panel -->
<div class="p-6 flex-1 space-y-8">
<!-- Current Level Indicator -->
<div class="text-center p-6 rounded-2xl border-2 transition-all duration-300"
:class="levelBorderClass">
<div class="text-sm text-gray-400 uppercase tracking-widest mb-2">当前风险等级</div>
<div class="text-6xl font-black transition-all duration-300" :class="statusTextClass">
{{ currentLevel }}
</div>
<div class="mt-4 text-lg font-medium text-white">{{ currentStatusText }}</div>
</div>
<!-- AI Logic Tree Visualization -->
<div class="space-y-4">
<h3 class="text-xs text-gray-500 uppercase font-bold tracking-wider">推理逻辑链 (Inference Chain)</h3>
<div class="flex flex-col space-y-2">
<div class="flex items-center space-x-3 p-3 rounded bg-gray-800/50 border border-gray-700">
<div class="w-2 h-2 rounded-full" :class="inferenceStats.yolo.color"></div>
<span class="text-sm text-gray-300">目标检测 (YOLOv8)</span>
<span class="ml-auto text-xs font-mono text-gray-500">{{ inferenceStats.yolo.time }}ms</span>
<span class="ml-2 text-xs text-green-400">Done</span>
</div>
<div class="flex items-center space-x-3 p-3 rounded bg-gray-800/50 border border-gray-700">
<div class="w-2 h-2 rounded-full" :class="inferenceStats.pose.color"></div>
<span class="text-sm text-gray-300">姿态分析 (PosePipe)</span>
<span class="ml-auto text-xs font-mono text-gray-500">{{ inferenceStats.pose.time }}ms</span>
<span class="ml-2 text-xs text-green-400">Done</span>
</div>
<div class="flex items-center space-x-3 p-3 rounded bg-gray-800/50 border border-gray-700">
<div class="w-2 h-2 rounded-full" :class="inferenceStats.risk.color"></div>
<span class="text-sm text-gray-300">风险评估 (RiskEval)</span>
<span class="ml-auto text-xs font-mono text-gray-500">{{ inferenceStats.risk.time }}ms</span>
<span class="ml-2 text-xs text-green-400">Done</span>
</div>
</div>
</div>
<!-- Action Panel (Demo Controls) -->
<div class="pt-6 border-t border-gray-800">
<h3 class="text-xs text-gray-500 uppercase font-bold tracking-wider mb-4">模拟触发 (Simulate Trigger)</h3>
<div class="grid grid-cols-2 gap-3">
<button @click="setLevel('L1')" class="px-4 py-3 rounded bg-gray-800 hover:bg-gray-700 text-green-400 text-xs font-bold border border-gray-700 transition-colors">
L1 正常巡护
</button>
<button @click="setLevel('L2')" class="px-4 py-3 rounded bg-gray-800 hover:bg-gray-700 text-yellow-400 text-xs font-bold border border-gray-700 transition-colors">
L2 异常静止
</button>
<button @click="setLevel('L3')" class="px-4 py-3 rounded bg-gray-800 hover:bg-gray-700 text-orange-400 text-xs font-bold border border-gray-700 transition-colors">
L3 呼救检测
</button>
<button @click="setLevel('L4')" class="px-4 py-3 rounded bg-red-900/50 hover:bg-red-800 text-red-400 text-xs font-bold border border-red-800 transition-colors animate-pulse">
L4 跌倒告警
</button>
</div>
</div>
</div>
<!-- Blockchain Status Footer -->
<div class="p-4 bg-gray-800 border-t border-gray-700 flex justify-between items-center">
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-400">TrustLink 状态:</span>
<span class="flex h-2 w-2 relative">
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-blue-400 opacity-75"></span>
<span class="relative inline-flex rounded-full h-2 w-2 bg-blue-500"></span>
</span>
<span class="text-xs text-blue-400 font-mono font-bold uppercase">已连接</span>
</div>
<div class="text-xs font-mono text-gray-500">
上链哈希: {{ lastHash }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useDateFormat, useNow } from '@vueuse/core'
import { useGlobalStore } from '../../stores/global'
import { useBlockchainStore } from '../../stores/blockchain'
const store = useGlobalStore()
const blockchainStore = useBlockchainStore()
// State
const currentLevel = computed(() => store.robotStatus.level)
const isManualMode = ref(false)
const toggleManualMode = () => {
isManualMode.value = !isManualMode.value
if (isManualMode.value) {
addLog('操作:已切换至人工遥控模式,自动巡航暂停。', 'info')
} else {
addLog('操作:恢复自动巡航模式。', 'info')
}
}
const fps = ref(30)
const latency = ref(12)
const logs = ref([])
const confidence = ref(98)
// Inference Stats
const inferenceStats = ref({
yolo: { time: 4.2, status: 'Done', color: 'bg-green-500' },
pose: { time: 8.5, status: 'Done', color: 'bg-green-500' },
risk: { time: 2.1, status: 'Done', color: 'bg-green-500' }
})
// Targets for tracking simulation
const targets = ref([
{ id: 'PERSON_01', x: 50, y: 50, active: true },
{ id: 'OBJECT_04', x: 20, y: 30, active: false },
{ id: 'OBJECT_09', x: 80, y: 70, active: false }
])
const currentScene = ref('hospital') // 'hospital', 'office', 'warehouse'
const scenes = {
hospital: {
name: '医院走廊',
color: 'from-blue-900/30 to-slate-900/50',
elements: [
{ type: '智能病床', x: 15, y: 65, scale: 1.2, icon: '🏥' },
{ type: '输液架', x: 82, y: 45, scale: 0.8, icon: '🧪' },
{ type: '消毒机', x: 5, y: 40, scale: 0.7, icon: '🛡️' },
{ type: '导视牌', x: 90, y: 20, scale: 0.6, icon: '🪧' }
]
},
office: {
name: '现代办公室',
color: 'from-emerald-900/20 to-stone-900/40',
elements: [
{ type: '升降桌', x: 75, y: 60, scale: 1.1, icon: '🖥️' },
{ type: '人体工学椅', x: 68, y: 65, scale: 0.9, icon: '💺' },
{ type: '绿植', x: 10, y: 70, scale: 1.0, icon: '🌿' },
{ type: '饮水机', x: 92, y: 50, scale: 0.8, icon: '💧' }
]
},
warehouse: {
name: '自动化仓库',
color: 'from-orange-900/20 to-zinc-900/50',
elements: [
{ type: '重型货架', x: 85, y: 35, scale: 1.4, icon: '📦' },
{ type: '自动分拣机', x: 10, y: 75, scale: 1.3, icon: '🤖' },
{ type: '搬运托盘', x: 40, y: 85, scale: 0.9, icon: '🛒' },
{ type: '监控立柱', x: 5, y: 25, scale: 1.1, icon: '📹' }
]
}
}
// 机器人系统状态
const systemStats = ref({
cpu: 42,
mem: 65,
temp: 38,
battery: 85,
wifi: -45
})
// 环境详细传感器
const environmentDetails = ref({
co2: 420,
oxygen: 20.9,
voc: 0.12,
pressure: 101.3
})
// Mock Data Generators
const lastHash = ref(store.robotStatus.hash || 'Waiting...')
// Computed Styles & Status
const statusColorClass = computed(() => {
const map = { L1: 'border-green-500', L2: 'border-yellow-500', L3: 'border-orange-500', L4: 'border-red-500' }
return map[currentLevel.value] || 'border-green-500'
})
const statusBgClass = computed(() => {
const map = { L1: 'bg-green-500', L2: 'bg-yellow-500', L3: 'bg-orange-500', L4: 'bg-red-500' }
return map[currentLevel.value] || 'bg-green-500'
})
const statusBorderClass = computed(() => {
const map = { L1: 'border-green-500/50', L2: 'border-yellow-500/50', L3: 'border-orange-500/50', L4: 'border-red-500/50' }
return map[currentLevel.value] || 'border-green-500/50'
})
const statusTextClass = computed(() => {
const map = { L1: 'text-green-500', L2: 'text-yellow-500', L3: 'text-orange-500', L4: 'text-red-500' }
return map[currentLevel.value] || 'text-green-500'
})
const levelBorderClass = computed(() => {
const map = { L1: 'border-green-500/20 bg-green-500/5', L2: 'border-yellow-500/20 bg-yellow-500/5', L3: 'border-orange-500/20 bg-orange-500/5', L4: 'border-red-500/20 bg-red-500/5' }
return map[currentLevel.value] || 'border-green-500/20 bg-green-500/5'
})
const currentStatusText = computed(() => {
const map = { L1: '巡视中 - 环境安全', L2: '观察中 - 行为异常', L3: '警告 - 检测到求救', L4: '紧急 - 跌倒告警' }
return map[currentLevel.value] || '系统就绪'
})
// Methods
const addLog = (msg, level = 'info') => {
const time = useDateFormat(useNow(), 'HH:mm:ss.SSS').value
const colors = { info: 'text-gray-400', warning: 'text-yellow-400', error: 'text-red-400', success: 'text-green-400' }
logs.value.unshift({ time, message: msg, color: colors[level] })
if (logs.value.length > 50) logs.value.pop()
}
const setLevel = (level) => {
// Update Global Store
if (level === 'L1') {
store.triggerAlert('L1', '系统恢复正常')
addLog('系统状态已恢复正常,继续执行预定巡护任务。', 'success')
} else if (level === 'L2') {
store.triggerAlert('L2', '检测到长时间静止')
addLog('异常警告检测到目标在当前区域静止超过180秒。', 'warning')
} else if (level === 'L3') {
store.triggerAlert('L3', '检测到呼救声')
addLog('语音事件:实时捕捉到疑似“救命”或“帮帮我”的音频特征。', 'warning')
} else if (level === 'L4') {
store.triggerAlert('L4', '检测到跌倒事件')
addLog('紧急:视觉算法确认目标发生跌倒行为,立即启动应急响应。', 'error')
// Blockchain Linkage
const txHash = '0x' + Math.random().toString(16).slice(2, 10) + '...' + Math.random().toString(16).slice(2, 6)
blockchainStore.transactions.unshift({
id: txHash,
type: '告警',
from: 'Robot_01',
to: '应急响应中心',
amount: '0.00',
status: '已确认',
time: '刚刚'
})
addLog('正在生成隐私保护哈希存证...', 'info')
setTimeout(() => {
lastHash.value = store.robotStatus.hash
addLog(`存证已上链:${lastHash.value}`, 'success')
addLog('已通过 API 通知紧急联系人及医疗中心。', 'error')
}, 800)
}
}
// Simulation Loops
let interval
let confidenceInterval
onMounted(() => {
addLog('系统初始化完成。视觉模型YOLOv8-Nano 已加载。', 'success')
addLog('隐私保护引擎:运行中(本地处理模式)。', 'success')
interval = setInterval(() => {
fps.value = Math.floor(Math.random() * 5) + 28
latency.value = Math.floor(Math.random() * 5) + 10
// Randomize system stats
systemStats.value.cpu = 35 + Math.floor(Math.random() * 15)
systemStats.value.mem = 60 + Math.floor(Math.random() * 10)
systemStats.value.temp = 36 + Math.floor(Math.random() * 5)
// Randomize inference times
inferenceStats.value.yolo.time = (Math.random() * 2 + 3).toFixed(1)
inferenceStats.value.pose.time = (Math.random() * 5 + 6).toFixed(1)
inferenceStats.value.risk.time = (Math.random() * 1 + 1.5).toFixed(1)
// Move targets slightly
targets.value.forEach(t => {
t.x += (Math.random() - 0.5) * 2
t.y += (Math.random() - 0.5) * 2
// Keep within bounds
t.x = Math.max(10, Math.min(90, t.x))
t.y = Math.max(10, Math.min(90, t.y))
})
if (currentLevel.value === 'L1' && Math.random() > 0.8) {
addLog('常规扫描完成。未发现异常目标。', 'info')
}
}, 2000)
confidenceInterval = setInterval(() => {
const base = currentLevel.value === 'L1' ? 98 : (currentLevel.value === 'L4' ? 99 : 85)
confidence.value = base + Math.floor(Math.random() * 3) - 1
}, 500)
})
onUnmounted(() => {
clearInterval(interval)
clearInterval(confidenceInterval)
})
</script>
<style scoped>
@keyframes breathing {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-3px); }
}
@keyframes breathing-torso {
0%, 100% { transform: scaleY(1); }
50% { transform: scaleY(1.02); }
}
.custom-scrollbar::-webkit-scrollbar {
width: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background: #111;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background: #333;
}
@keyframes scan {
0% { top: 0%; opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { top: 100%; opacity: 0; }
}
.animate-scan {
animation: scan 3s linear infinite;
}
</style>