2026-02-24 18:22:58 +08:00
|
|
|
<template>
|
|
|
|
|
<div class="desktop-demo">
|
2026-02-25 01:38:27 +08:00
|
|
|
<div class="demo-title">从开机到桌面</div>
|
|
|
|
|
<div class="screen-wrapper">
|
|
|
|
|
<div class="screen">
|
|
|
|
|
<div v-if="phase === 0" class="phase-bios">
|
|
|
|
|
<div class="bios-text">POST 自检中...</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else-if="phase === 1" class="phase-boot">
|
|
|
|
|
<div class="boot-spinner"></div>
|
|
|
|
|
<div class="boot-text">正在加载内核...</div>
|
2026-02-24 18:22:58 +08:00
|
|
|
</div>
|
2026-02-25 01:38:27 +08:00
|
|
|
<div v-else-if="phase === 2" class="phase-loading">
|
|
|
|
|
<div class="loading-bar-track">
|
|
|
|
|
<div class="loading-bar-fill"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="loading-text">启动系统服务...</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else class="phase-desktop">
|
2026-02-24 18:22:58 +08:00
|
|
|
<div class="desktop-icons">
|
2026-02-25 01:38:27 +08:00
|
|
|
<div class="icon-item" v-for="icon in icons" :key="icon.label">
|
|
|
|
|
<div class="icon-box">{{ icon.emoji }}</div>
|
|
|
|
|
<div class="icon-label">{{ icon.label }}</div>
|
|
|
|
|
</div>
|
2026-02-24 18:22:58 +08:00
|
|
|
</div>
|
|
|
|
|
<div class="taskbar">
|
2026-02-25 01:38:27 +08:00
|
|
|
<span class="taskbar-start">☰</span>
|
|
|
|
|
<span class="taskbar-spacer"></span>
|
|
|
|
|
<span class="taskbar-clock">{{ clock }}</span>
|
2026-02-24 18:22:58 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-02-25 01:38:27 +08:00
|
|
|
<div class="phase-labels">
|
|
|
|
|
<span v-for="(label, i) in labels" :key="label" class="phase-label" :class="{ active: phase >= i }">
|
|
|
|
|
{{ label }}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
2026-02-24 18:22:58 +08:00
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { ref, onMounted, onUnmounted } from 'vue'
|
|
|
|
|
|
2026-02-25 01:38:27 +08:00
|
|
|
const phase = ref(0)
|
|
|
|
|
const clock = ref('')
|
2026-02-24 18:22:58 +08:00
|
|
|
let timer = null
|
2026-02-25 01:38:27 +08:00
|
|
|
let phaseTimer = null
|
|
|
|
|
|
|
|
|
|
const icons = [
|
|
|
|
|
{ emoji: '📁', label: '文件' },
|
|
|
|
|
{ emoji: '🌐', label: '浏览器' },
|
|
|
|
|
{ emoji: '⚙️', label: '设置' },
|
|
|
|
|
{ emoji: '🗑️', label: '回收站' }
|
|
|
|
|
]
|
|
|
|
|
const labels = ['BIOS 自检', '内核加载', '服务启动', '桌面就绪']
|
2026-02-24 18:22:58 +08:00
|
|
|
|
2026-02-25 01:38:27 +08:00
|
|
|
const updateClock = () => {
|
2026-02-24 18:22:58 +08:00
|
|
|
const now = new Date()
|
2026-02-25 01:38:27 +08:00
|
|
|
clock.value = now.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const runSequence = () => {
|
2026-02-26 04:35:28 +08:00
|
|
|
if (phaseTimer) clearTimeout(phaseTimer)
|
2026-02-25 01:38:27 +08:00
|
|
|
phase.value = 0
|
|
|
|
|
const delays = [1500, 1500, 1800]
|
|
|
|
|
let i = 0
|
|
|
|
|
const next = () => {
|
|
|
|
|
if (i < delays.length) {
|
2026-02-26 04:35:28 +08:00
|
|
|
phaseTimer = setTimeout(() => {
|
|
|
|
|
phase.value = i + 1
|
|
|
|
|
i++
|
|
|
|
|
next()
|
|
|
|
|
}, delays[i])
|
2026-02-25 01:38:27 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
next()
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
2026-02-25 01:38:27 +08:00
|
|
|
updateClock()
|
|
|
|
|
timer = setInterval(updateClock, 30000)
|
|
|
|
|
runSequence()
|
2026-02-24 18:22:58 +08:00
|
|
|
})
|
2026-02-26 04:35:28 +08:00
|
|
|
|
2026-02-24 18:22:58 +08:00
|
|
|
onUnmounted(() => {
|
|
|
|
|
if (timer) clearInterval(timer)
|
2026-02-25 01:38:27 +08:00
|
|
|
if (phaseTimer) clearTimeout(phaseTimer)
|
2026-02-24 18:22:58 +08:00
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.desktop-demo {
|
|
|
|
|
border: 1px solid var(--vp-c-divider);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
background: var(--vp-c-bg-soft);
|
|
|
|
|
padding: 1rem 1.2rem;
|
|
|
|
|
margin: 1rem 0;
|
|
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.demo-title {
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
font-weight: 600;
|
2026-02-24 18:22:58 +08:00
|
|
|
color: var(--vp-c-text-2);
|
2026-02-25 01:38:27 +08:00
|
|
|
margin-bottom: 0.8rem;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.screen-wrapper { display: flex; justify-content: center; }
|
2026-02-24 18:22:58 +08:00
|
|
|
.screen {
|
2026-02-25 01:38:27 +08:00
|
|
|
width: 16rem;
|
|
|
|
|
height: 10rem;
|
|
|
|
|
background: #111;
|
2026-02-24 18:22:58 +08:00
|
|
|
border-radius: 6px;
|
|
|
|
|
overflow: hidden;
|
2026-02-25 01:38:27 +08:00
|
|
|
position: relative;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
/* BIOS phase */
|
|
|
|
|
.phase-bios {
|
|
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.bios-text {
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: #0f0;
|
|
|
|
|
font-family: monospace;
|
|
|
|
|
animation: blink 1s steps(1) infinite;
|
|
|
|
|
}
|
|
|
|
|
@keyframes blink {
|
|
|
|
|
50% { opacity: 0; }
|
|
|
|
|
}
|
|
|
|
|
/* Boot phase */
|
|
|
|
|
.phase-boot {
|
2026-02-24 18:22:58 +08:00
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-02-25 01:38:27 +08:00
|
|
|
gap: 0.6rem;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.boot-spinner {
|
|
|
|
|
width: 1.5rem;
|
|
|
|
|
height: 1.5rem;
|
|
|
|
|
border: 2px solid #333;
|
|
|
|
|
border-top-color: #fff;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
animation: spin 0.8s linear infinite;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
|
|
|
|
@keyframes spin {
|
|
|
|
|
to { transform: rotate(360deg); }
|
|
|
|
|
}
|
|
|
|
|
.boot-text {
|
2026-02-25 01:38:27 +08:00
|
|
|
font-size: 0.65rem;
|
|
|
|
|
color: #888;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
/* Loading phase */
|
|
|
|
|
.phase-loading {
|
2026-02-24 18:22:58 +08:00
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
2026-02-25 01:38:27 +08:00
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
}
|
|
|
|
|
.loading-bar-track {
|
|
|
|
|
width: 8rem;
|
|
|
|
|
height: 4px;
|
|
|
|
|
background: #333;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
.loading-bar-fill {
|
|
|
|
|
height: 100%;
|
|
|
|
|
background: #4a9eff;
|
|
|
|
|
border-radius: 2px;
|
|
|
|
|
animation: fill 1.6s ease-out forwards;
|
|
|
|
|
}
|
|
|
|
|
@keyframes fill {
|
|
|
|
|
from { width: 0; }
|
|
|
|
|
to { width: 100%; }
|
|
|
|
|
}
|
|
|
|
|
.loading-text {
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
color: #888;
|
|
|
|
|
}
|
|
|
|
|
/* Desktop phase */
|
|
|
|
|
.phase-desktop {
|
|
|
|
|
height: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
background: linear-gradient(135deg, #1e3a8a 0%, #2563eb 100%);
|
|
|
|
|
animation: fadeIn 0.5s ease;
|
|
|
|
|
}
|
|
|
|
|
@keyframes fadeIn {
|
|
|
|
|
from { opacity: 0; }
|
|
|
|
|
to { opacity: 1; }
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
|
|
|
|
.desktop-icons {
|
2026-02-25 01:38:27 +08:00
|
|
|
flex: 1;
|
2026-02-24 18:22:58 +08:00
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 0.5rem;
|
2026-02-25 01:38:27 +08:00
|
|
|
padding: 0.5rem;
|
|
|
|
|
align-content: flex-start;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.icon-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
width: 2.5rem;
|
|
|
|
|
}
|
|
|
|
|
.icon-box {
|
|
|
|
|
width: 2rem;
|
|
|
|
|
height: 2rem;
|
2026-02-24 18:22:58 +08:00
|
|
|
background: rgba(255,255,255,0.15);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2026-02-25 01:38:27 +08:00
|
|
|
font-size: 1rem;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.icon-label {
|
|
|
|
|
font-size: 0.5rem;
|
|
|
|
|
color: rgba(255,255,255,0.8);
|
|
|
|
|
margin-top: 0.15rem;
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.taskbar {
|
2026-02-24 18:22:58 +08:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-02-25 01:38:27 +08:00
|
|
|
padding: 0.25rem 0.5rem;
|
|
|
|
|
background: rgba(0,0,0,0.4);
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.taskbar-start {
|
2026-02-24 18:22:58 +08:00
|
|
|
font-size: 0.7rem;
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
2026-02-25 01:38:27 +08:00
|
|
|
.taskbar-spacer { flex: 1; }
|
|
|
|
|
.taskbar-clock {
|
|
|
|
|
font-size: 0.6rem;
|
|
|
|
|
color: rgba(255,255,255,0.8);
|
|
|
|
|
}
|
|
|
|
|
/* Phase labels */
|
|
|
|
|
.phase-labels {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
gap: 0.8rem;
|
|
|
|
|
margin-top: 0.7rem;
|
|
|
|
|
}
|
|
|
|
|
.phase-label {
|
|
|
|
|
font-size: 0.62rem;
|
2026-02-24 18:22:58 +08:00
|
|
|
color: var(--vp-c-text-3);
|
2026-02-25 01:38:27 +08:00
|
|
|
opacity: 0.4;
|
|
|
|
|
transition: opacity 0.3s;
|
|
|
|
|
}
|
|
|
|
|
.phase-label.active {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
color: var(--vp-c-brand);
|
2026-02-24 18:22:58 +08:00
|
|
|
}
|
|
|
|
|
</style>
|