152 lines
2.9 KiB
Vue
152 lines
2.9 KiB
Vue
<script setup lang="ts">
|
|
import type { ChapterInfo } from '@engine/types'
|
|
|
|
defineProps<{
|
|
chapters: ChapterInfo[]
|
|
unlockedIds: Set<string>
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
select: [chapterId: string]
|
|
back: []
|
|
}>()
|
|
</script>
|
|
|
|
<template>
|
|
<div class="chapter-overlay">
|
|
<div class="chapter-panel">
|
|
<h2 class="chapter-title">章节选择</h2>
|
|
|
|
<div class="chapter-grid">
|
|
<div
|
|
v-for="ch in chapters"
|
|
:key="ch.id"
|
|
class="chapter-card"
|
|
:class="{ locked: !unlockedIds.has(ch.id) }"
|
|
@click="unlockedIds.has(ch.id) && emit('select', ch.id)"
|
|
>
|
|
<div class="chapter-thumb">
|
|
<img v-if="ch.thumbnail" :src="ch.thumbnail" class="thumb-img" />
|
|
<div v-else class="thumb-placeholder">?</div>
|
|
</div>
|
|
<div class="chapter-label">{{ ch.label }}</div>
|
|
<div v-if="!unlockedIds.has(ch.id)" class="lock-icon">🔒</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="back-btn" @click="emit('back')">返回</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.chapter-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.88);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 200;
|
|
}
|
|
|
|
.chapter-panel {
|
|
background: #1a1a2e;
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 10px;
|
|
padding: 36px 40px;
|
|
min-width: 520px;
|
|
max-width: 700px;
|
|
}
|
|
|
|
.chapter-title {
|
|
text-align: center;
|
|
font-size: 22px;
|
|
font-weight: 400;
|
|
color: #ddd;
|
|
letter-spacing: 3px;
|
|
margin-bottom: 28px;
|
|
}
|
|
|
|
.chapter-grid {
|
|
display: flex;
|
|
gap: 16px;
|
|
justify-content: center;
|
|
}
|
|
|
|
.chapter-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 16px;
|
|
background: rgba(255, 255, 255, 0.04);
|
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: background 0.2s, border-color 0.2s, transform 0.15s;
|
|
width: 150px;
|
|
}
|
|
|
|
.chapter-card:hover:not(.locked) {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-color: rgba(255, 255, 255, 0.25);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.chapter-card.locked {
|
|
opacity: 0.4;
|
|
cursor: default;
|
|
}
|
|
|
|
.chapter-thumb {
|
|
width: 100px;
|
|
height: 56px;
|
|
background: rgba(0, 0, 0, 0.4);
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.thumb-img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.thumb-placeholder {
|
|
font-size: 24px;
|
|
color: #555;
|
|
}
|
|
|
|
.chapter-label {
|
|
font-size: 13px;
|
|
color: #ccc;
|
|
text-align: center;
|
|
}
|
|
|
|
.lock-icon {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.back-btn {
|
|
display: block;
|
|
margin: 24px auto 0;
|
|
padding: 10px 36px;
|
|
font-size: 14px;
|
|
color: #888;
|
|
background: rgba(255, 255, 255, 0.05);
|
|
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: background 0.15s, color 0.15s;
|
|
}
|
|
|
|
.back-btn:hover {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
color: #ccc;
|
|
}
|
|
</style>
|