92 lines
2.2 KiB
Vue
92 lines
2.2 KiB
Vue
<script setup lang="ts">
|
||
import { ref, onMounted, provide } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { fetchNovel } from '@/api/novels'
|
||
import { useToast } from '@/composables/useToast'
|
||
import type { Novel } from '@/types/novel'
|
||
import NovelSubNav from '@/components/novel/NovelSubNav.vue'
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const toast = useToast()
|
||
|
||
const novelId = Number(route.params.id)
|
||
const novel = ref<Novel | null>(null)
|
||
const loading = ref(true)
|
||
|
||
/** 子页面可设置的副标题(如 "共 5 个节点") */
|
||
const subtitle = ref('')
|
||
|
||
async function load() {
|
||
loading.value = true
|
||
try {
|
||
novel.value = await fetchNovel(novelId)
|
||
} catch {
|
||
toast.error('作品不存在')
|
||
router.push({ name: 'home' })
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 提供给子路由
|
||
provide('workspace-novel', novel)
|
||
provide('workspace-novel-id', novelId)
|
||
provide('workspace-subtitle', subtitle)
|
||
|
||
onMounted(load)
|
||
</script>
|
||
|
||
<template>
|
||
<div v-if="!loading && novel" class="workspace">
|
||
<NovelSubNav
|
||
:novel-id="novelId"
|
||
:novel-title="novel.title"
|
||
:subtitle="subtitle"
|
||
>
|
||
<template #actions>
|
||
<!-- 子页面通过 Teleport 注入操作按钮 -->
|
||
<div id="workspace-actions" />
|
||
</template>
|
||
</NovelSubNav>
|
||
|
||
<div class="workspace-content">
|
||
<RouterView />
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else-if="loading" class="workspace-loading">
|
||
<div class="loading-pulse" style="height: 1.5rem; width: 30%; margin-bottom: var(--space-md)" />
|
||
<div class="loading-pulse" style="height: 1rem; width: 60%; margin-bottom: var(--space-2xl)" />
|
||
<div class="loading-pulse" style="height: 20rem; width: 100%" />
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.workspace {
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.workspace-content {
|
||
animation: fadeInUp var(--duration-normal) var(--ease-out-smooth) both;
|
||
}
|
||
|
||
.workspace-loading {
|
||
max-width: 900px;
|
||
margin: var(--space-3xl) auto;
|
||
}
|
||
|
||
.loading-pulse {
|
||
background: linear-gradient(
|
||
90deg,
|
||
var(--paper-100) 25%,
|
||
var(--paper-200) 50%,
|
||
var(--paper-100) 75%
|
||
);
|
||
background-size: 200% 100%;
|
||
border-radius: var(--radius-sm);
|
||
animation: shimmer 1.5s ease infinite;
|
||
}
|
||
</style>
|