2026-02-05 01:33:28 +08:00
|
|
|
|
<!--
|
2026-02-14 12:14:07 +08:00
|
|
|
|
SliceRequestDemo.vue - HTTP请求优化对比
|
|
|
|
|
|
用"搬家"的比喻来解释雪碧图 vs 切片请求
|
2026-02-05 01:33:28 +08:00
|
|
|
|
-->
|
|
|
|
|
|
<template>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<div class="slice-request-demo">
|
|
|
|
|
|
<!-- 标题区 -->
|
|
|
|
|
|
<div class="demo-header">
|
|
|
|
|
|
<span class="icon">📦</span>
|
|
|
|
|
|
<span class="title">HTTP请求优化</span>
|
|
|
|
|
|
<span class="subtitle">雪碧图 vs 独立请求</span>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<!-- 主内容区 -->
|
|
|
|
|
|
<div class="demo-content">
|
|
|
|
|
|
<!-- 故事引入 -->
|
|
|
|
|
|
<div class="story-box">
|
|
|
|
|
|
<p class="story-text">
|
|
|
|
|
|
<strong>通俗说法:</strong>就像搬家——<br>
|
|
|
|
|
|
<strong>切图模式</strong>:一箱一箱搬,需要6趟(6次HTTP请求)<br>
|
|
|
|
|
|
<strong>雪碧图模式</strong>:打包一次性运走,只需1趟(1次HTTP请求)
|
|
|
|
|
|
</p>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<!-- 模式选择 -->
|
|
|
|
|
|
<div class="mode-selector">
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="mode-card"
|
|
|
|
|
|
:class="{ active: mode === 'separate' }"
|
|
|
|
|
|
@click="mode = 'separate'"
|
|
|
|
|
|
>
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="mode-icon">
|
|
|
|
|
|
🛵
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mode-name">
|
|
|
|
|
|
切图模式
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mode-desc">
|
|
|
|
|
|
通俗说法: 一箱一趟
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mode-detail">
|
|
|
|
|
|
需要 6 趟运输
|
|
|
|
|
|
</div>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="vs-divider">
|
|
|
|
|
|
VS
|
|
|
|
|
|
</div>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
|
|
|
|
|
|
<div
|
2026-02-14 12:14:07 +08:00
|
|
|
|
class="mode-card"
|
|
|
|
|
|
:class="{ active: mode === 'packed' }"
|
|
|
|
|
|
@click="mode = 'packed'"
|
2026-02-06 03:34:50 +08:00
|
|
|
|
>
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="mode-icon">
|
|
|
|
|
|
🚚
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mode-name">
|
|
|
|
|
|
雪碧图模式
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mode-desc">
|
|
|
|
|
|
通俗说法: 打包一车拉
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="mode-detail">
|
|
|
|
|
|
只需 1 趟运输
|
|
|
|
|
|
</div>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 动画演示区 -->
|
|
|
|
|
|
<div class="animation-area">
|
|
|
|
|
|
<!-- 起点 -->
|
|
|
|
|
|
<div class="location start">
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="location-icon">
|
|
|
|
|
|
🏠
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="location-label">
|
|
|
|
|
|
旧家
|
|
|
|
|
|
</div>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<div class="boxes-remaining">
|
|
|
|
|
|
剩余箱子: <span class="count">{{ remainingBoxes }}</span>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 道路 -->
|
|
|
|
|
|
<div class="road">
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="road-line" />
|
2026-02-14 12:14:07 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 运输车辆 -->
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="vehicle in vehicles"
|
|
|
|
|
|
:key="vehicle.id"
|
|
|
|
|
|
class="vehicle"
|
|
|
|
|
|
:class="{ 'moving': vehicle.isMoving }"
|
|
|
|
|
|
:style="{ left: vehicle.position + '%' }"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="vehicle-body">
|
|
|
|
|
|
{{ mode === 'separate' ? '🛵' : '🚚' }}
|
|
|
|
|
|
</div>
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-if="vehicle.cargo > 0"
|
|
|
|
|
|
class="vehicle-cargo"
|
|
|
|
|
|
>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
{{ mode === 'separate' ? '📦' : '📦×' + vehicle.cargo }}
|
|
|
|
|
|
</div>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<!-- 终点 -->
|
|
|
|
|
|
<div class="location end">
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="location-icon">
|
|
|
|
|
|
🏡
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="location-label">
|
|
|
|
|
|
新家
|
|
|
|
|
|
</div>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<div class="boxes-delivered">
|
|
|
|
|
|
已送达: <span class="count">{{ deliveredBoxes }}</span>/6
|
|
|
|
|
|
</div>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
2026-02-06 03:34:50 +08:00
|
|
|
|
</div>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<!-- 统计面板 -->
|
|
|
|
|
|
<div class="stats-panel">
|
|
|
|
|
|
<div class="stat-item">
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="stat-label">
|
|
|
|
|
|
运输趟数
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="stat-value"
|
|
|
|
|
|
:class="{ 'good': trips <= 2, 'bad': trips > 2 }"
|
|
|
|
|
|
>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
{{ trips }} 趟
|
|
|
|
|
|
</div>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<div class="stat-item">
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="stat-label">
|
|
|
|
|
|
总耗时
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-value">
|
|
|
|
|
|
{{ totalTime.toFixed(1) }} 秒
|
|
|
|
|
|
</div>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stat-item">
|
2026-02-18 17:38:10 +08:00
|
|
|
|
<div class="stat-label">
|
|
|
|
|
|
效率评分
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div
|
|
|
|
|
|
class="stat-value"
|
|
|
|
|
|
:class="efficiencyClass"
|
|
|
|
|
|
>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
{{ efficiency }}
|
|
|
|
|
|
</div>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<!-- 控制按钮 -->
|
|
|
|
|
|
<div class="controls">
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="btn btn-primary"
|
|
|
|
|
|
:disabled="isRunning"
|
2026-02-18 17:38:10 +08:00
|
|
|
|
@click="startSimulation"
|
2026-02-14 12:14:07 +08:00
|
|
|
|
>
|
|
|
|
|
|
{{ isRunning ? '运输中...' : '开始搬家' }}
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="btn btn-secondary"
|
|
|
|
|
|
@click="resetSimulation"
|
|
|
|
|
|
>
|
|
|
|
|
|
重置
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
<!-- 信息框 -->
|
|
|
|
|
|
<div class="info-box">
|
|
|
|
|
|
<span class="icon">💡</span>
|
|
|
|
|
|
<strong>核心思想:</strong>
|
|
|
|
|
|
<span v-if="mode === 'separate'">切图模式每次只拉一件货,需要6次HTTP请求,效率低。</span>
|
|
|
|
|
|
<span v-else>雪碧图模式打包一次性运走,只需1次HTTP请求,大幅减少连接开销。</span>
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 模式选择
|
|
|
|
|
|
const mode = ref('separate')
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 运行状态
|
|
|
|
|
|
const isRunning = ref(false)
|
|
|
|
|
|
const trips = ref(0)
|
|
|
|
|
|
const totalTime = ref(0)
|
|
|
|
|
|
const remainingBoxes = ref(6)
|
|
|
|
|
|
const deliveredBoxes = ref(0)
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 车辆动画
|
|
|
|
|
|
const vehicles = ref([])
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 计算效率评分
|
|
|
|
|
|
const efficiency = computed(() => {
|
|
|
|
|
|
if (mode.value === 'packed') {
|
|
|
|
|
|
return trips.value <= 1 ? '优秀' : '良好'
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return trips.value <= 3 ? '一般' : '低效'
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
const efficiencyClass = computed(() => {
|
|
|
|
|
|
const score = efficiency.value
|
|
|
|
|
|
if (score === '优秀') return 'excellent'
|
|
|
|
|
|
if (score === '良好') return 'good'
|
|
|
|
|
|
if (score === '一般') return 'average'
|
|
|
|
|
|
return 'poor'
|
|
|
|
|
|
})
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 开始模拟
|
|
|
|
|
|
const startSimulation = async () => {
|
|
|
|
|
|
if (isRunning.value) return
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
isRunning.value = true
|
|
|
|
|
|
resetStats()
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
if (mode.value === 'separate') {
|
|
|
|
|
|
// 分开运输:一箱一趟
|
|
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
|
|
|
|
await runTrip(1)
|
|
|
|
|
|
trips.value++
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 打包运输:6箱一趟
|
|
|
|
|
|
await runTrip(6)
|
|
|
|
|
|
trips.value = 1
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
isRunning.value = false
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 单次运输动画
|
|
|
|
|
|
const runTrip = (cargoCount) => {
|
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
|
// 创建车辆
|
|
|
|
|
|
const vehicle = {
|
|
|
|
|
|
id: Date.now(),
|
|
|
|
|
|
position: 0,
|
|
|
|
|
|
cargo: cargoCount,
|
|
|
|
|
|
isMoving: true
|
|
|
|
|
|
}
|
|
|
|
|
|
vehicles.value = [vehicle]
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 更新剩余箱子
|
|
|
|
|
|
remainingBoxes.value = Math.max(0, remainingBoxes.value - cargoCount)
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 动画:去程
|
|
|
|
|
|
const goTrip = setInterval(() => {
|
|
|
|
|
|
vehicle.position += 2
|
|
|
|
|
|
if (vehicle.position >= 100) {
|
|
|
|
|
|
clearInterval(goTrip)
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 送达
|
|
|
|
|
|
deliveredBoxes.value += cargoCount
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 动画:返程
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
const returnTrip = setInterval(() => {
|
|
|
|
|
|
vehicle.position -= 2
|
|
|
|
|
|
if (vehicle.position <= 0) {
|
|
|
|
|
|
clearInterval(returnTrip)
|
|
|
|
|
|
vehicles.value = []
|
|
|
|
|
|
resolve()
|
|
|
|
|
|
}
|
|
|
|
|
|
}, 20)
|
|
|
|
|
|
}, 300)
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}, 20)
|
2026-02-05 01:33:28 +08:00
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
// 累计时间
|
|
|
|
|
|
totalTime.value += 2.5
|
2026-02-05 01:33:28 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
2026-02-06 03:34:50 +08:00
|
|
|
|
|
|
|
|
|
|
// 重置模拟
|
|
|
|
|
|
const resetSimulation = () => {
|
|
|
|
|
|
isRunning.value = false
|
|
|
|
|
|
vehicles.value = []
|
|
|
|
|
|
resetStats()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const resetStats = () => {
|
|
|
|
|
|
trips.value = 0
|
|
|
|
|
|
totalTime.value = 0
|
|
|
|
|
|
remainingBoxes.value = 6
|
|
|
|
|
|
deliveredBoxes.value = 0
|
|
|
|
|
|
}
|
2026-02-05 01:33:28 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2026-02-14 12:14:07 +08:00
|
|
|
|
.slice-request-demo {
|
|
|
|
|
|
border: 1px solid var(--vp-c-divider);
|
2026-02-14 20:23:34 +08:00
|
|
|
|
border-radius: 6px;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
background: var(--vp-c-bg-soft);
|
2026-02-14 20:23:34 +08:00
|
|
|
|
padding: 0.75rem;
|
|
|
|
|
|
margin: 0.5rem 0;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
/* 标题区 */
|
|
|
|
|
|
.demo-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
|
margin-bottom: 0.75rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
.demo-header .icon {
|
|
|
|
|
|
font-size: 1.25rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
.demo-header .title {
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 1rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.demo-header .subtitle {
|
|
|
|
|
|
color: var(--vp-c-text-2);
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
margin-left: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 主内容区 */
|
|
|
|
|
|
.demo-content {
|
|
|
|
|
|
margin-bottom: 0.75rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 故事框 */
|
|
|
|
|
|
.story-box {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
|
padding: 0.75rem;
|
|
|
|
|
|
background: var(--vp-c-bg-alt);
|
|
|
|
|
|
border-radius: 6px;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.story-text {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
color: var(--vp-c-text-2);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
margin: 0;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
line-height: 1.6;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
/* 模式选择 */
|
|
|
|
|
|
.mode-selector {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
display: flex;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
|
margin-bottom: 1rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
flex-wrap: wrap;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-card {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
background: var(--vp-c-bg);
|
|
|
|
|
|
border: 2px solid var(--vp-c-divider);
|
2026-02-14 20:23:34 +08:00
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
padding: 0.75rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
text-align: center;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
cursor: pointer;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
|
min-width: 160px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
flex: 1;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
max-width: 220px;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-card:hover {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
transform: translateY(-2px);
|
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-card.active {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
border-color: var(--vp-c-brand);
|
|
|
|
|
|
background: var(--vp-c-brand-soft);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-icon {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 2rem;
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-name {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.9rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-text-1);
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-desc {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
color: var(--vp-c-text-2);
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-detail {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.85rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-brand);
|
|
|
|
|
|
padding: 0.25rem 0.75rem;
|
|
|
|
|
|
background: var(--vp-c-bg-alt);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
border-radius: 12px;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
display: inline-block;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.vs-divider {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 1.25rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-text-3);
|
|
|
|
|
|
padding: 0 0.5rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
/* 动画演示区 */
|
|
|
|
|
|
.animation-area {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
justify-content: space-between;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
margin-bottom: 1rem;
|
2026-02-14 20:23:34 +08:00
|
|
|
|
padding: 0.75rem;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
background: var(--vp-c-bg);
|
2026-02-14 20:23:34 +08:00
|
|
|
|
border-radius: 6px;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
border: 2px solid var(--vp-c-divider);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.location {
|
|
|
|
|
|
text-align: center;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
min-width: 80px;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.location-icon {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 2rem;
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.location-label {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.85rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-text-1);
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.boxes-remaining,
|
|
|
|
|
|
.boxes-delivered {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
color: var(--vp-c-text-2);
|
|
|
|
|
|
padding: 0.25rem 0.5rem;
|
|
|
|
|
|
background: var(--vp-c-bg-alt);
|
|
|
|
|
|
border-radius: 6px;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.count {
|
|
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-brand);
|
|
|
|
|
|
font-size: 0.9rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.road {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
flex: 1;
|
|
|
|
|
|
position: relative;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
height: 60px;
|
|
|
|
|
|
background: var(--vp-c-bg-alt);
|
|
|
|
|
|
border-radius: 6px;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.road-line {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 50%;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
left: 10%;
|
|
|
|
|
|
right: 10%;
|
|
|
|
|
|
height: 4px;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
background: repeating-linear-gradient(
|
|
|
|
|
|
90deg,
|
2026-02-14 12:14:07 +08:00
|
|
|
|
var(--vp-c-brand) 0px,
|
|
|
|
|
|
var(--vp-c-brand) 20px,
|
2026-02-05 01:33:28 +08:00
|
|
|
|
transparent 20px,
|
|
|
|
|
|
transparent 40px
|
|
|
|
|
|
);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
transform: translateY(-50%);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.vehicle {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 50%;
|
|
|
|
|
|
transform: translate(-50%, -50%);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
align-items: center;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
transition: none;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.vehicle-body {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
|
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.vehicle-cargo {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
background: var(--vp-c-bg);
|
|
|
|
|
|
padding: 0.125rem 0.375rem;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
margin-top: 0.125rem;
|
|
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-brand);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
/* 统计面板 */
|
|
|
|
|
|
.stats-panel {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: repeat(3, 1fr);
|
2026-02-14 12:14:07 +08:00
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
margin-bottom: 1rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.stat-item {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
background: var(--vp-c-bg);
|
2026-02-14 20:23:34 +08:00
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
padding: 0.75rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
text-align: center;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
border: 2px solid var(--vp-c-divider);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.stat-label {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
color: var(--vp-c-text-2);
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.stat-value {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
font-size: 1.25rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-text-1);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.stat-value.good {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-success);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.stat-value.bad {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-danger);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.stat-value.excellent {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-brand);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.stat-value.poor {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
color: var(--vp-c-warning);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
/* 控制按钮 */
|
|
|
|
|
|
.controls {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
gap: 0.75rem;
|
|
|
|
|
|
margin-bottom: 1rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.btn {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
padding: 0.5rem 1rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
border: none;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
font-size: 0.9rem;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
font-weight: bold;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
cursor: pointer;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
transition: all 0.2s;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.btn:hover:not(:disabled) {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
transform: translateY(-2px);
|
2026-02-14 12:14:07 +08:00
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.btn:disabled {
|
|
|
|
|
|
opacity: 0.6;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.btn-primary {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
background: var(--vp-c-brand);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
color: white;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.btn-secondary {
|
2026-02-14 12:14:07 +08:00
|
|
|
|
background: var(--vp-c-bg-alt);
|
|
|
|
|
|
color: var(--vp-c-text-1);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
/* 信息框 */
|
|
|
|
|
|
.info-box {
|
|
|
|
|
|
background: var(--vp-c-bg-alt);
|
|
|
|
|
|
padding: 0.75rem;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
color: var(--vp-c-text-2);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 0.25rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
.info-box .icon {
|
|
|
|
|
|
flex-shrink: 0;
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-14 12:14:07 +08:00
|
|
|
|
.info-box strong {
|
|
|
|
|
|
color: var(--vp-c-text-1);
|
2026-02-06 03:34:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 响应式 */
|
2026-02-05 01:33:28 +08:00
|
|
|
|
@media (max-width: 768px) {
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.mode-selector {
|
|
|
|
|
|
flex-direction: column;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.vs-divider {
|
|
|
|
|
|
transform: rotate(90deg);
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.animation-area {
|
2026-02-05 01:33:28 +08:00
|
|
|
|
flex-direction: column;
|
2026-02-14 12:14:07 +08:00
|
|
|
|
gap: 0.75rem;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 03:34:50 +08:00
|
|
|
|
.road {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 60px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.stats-panel {
|
|
|
|
|
|
grid-template-columns: 1fr;
|
2026-02-05 01:33:28 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|