引入二级大纲功能,新增父子节点关系及子节点排序,同时调整API与UI以支持嵌套大纲管理

This commit is contained in:
2026-03-22 16:53:33 +08:00
parent 20c759150a
commit 098ff4e16d
33 changed files with 2516 additions and 381 deletions

View File

@@ -0,0 +1,91 @@
<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>