Files
jocay 3c9b65d982 AI 小说工坊:主编 + 作家多模型协作写作应用
- Nuxt 4 + Nuxt UI 4 + AI SDK 7 + Drizzle/SQLite
- 主编规划大纲与章节简报、选派作家;作家流式写入章节文件并实时推送前端
- 逐章确认模式(默认)与自动模式;摘要与故事记忆
- 酒馆角色卡(PNG/JSON,V1-V3)与世界书导入
- 自定义最高优先级系统提示词、审稿/修改/多版本、任务日志与提示词检视
- 模拟 LLM 服务与端到端测试脚本

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 02:04:40 +08:00

76 lines
2.8 KiB
Vue

<script setup lang="ts">
import type { NavigationMenuItem } from '@nuxt/ui'
const route = useRoute()
const projectId = computed(() => (route.params.id as string | undefined) ?? null)
const { data: projectTitle } = useAsyncData(
'layout-project-title',
async () => (projectId.value ? (await $fetch<ProjectDetail>(`/api/projects/${projectId.value}`)).title : ''),
{ watch: [projectId], default: () => '' }
)
const mainItems = computed<NavigationMenuItem[]>(() => [
{ label: '我的作品', icon: 'i-lucide-library', to: '/', exact: true },
{ label: '模型与 API', icon: 'i-lucide-plug-zap', to: '/settings' }
])
const projectItems = computed<NavigationMenuItem[]>(() => {
if (!projectId.value) return []
const base = `/projects/${projectId.value}`
return [
{ label: '写作台', icon: 'i-lucide-pen-line', to: base, exact: true },
{ label: '故事设定', icon: 'i-lucide-book-open', to: `${base}/setting` },
{ label: '角色与世界书', icon: 'i-lucide-users', to: `${base}/characters` },
{ label: 'AI 团队', icon: 'i-lucide-bot', to: `${base}/team` },
{ label: '故事记忆', icon: 'i-lucide-brain', to: `${base}/memory` },
{ label: '任务日志', icon: 'i-lucide-list-checks', to: `${base}/jobs` }
]
})
const colorMode = useColorMode()
const isDark = computed({
get: () => colorMode.value === 'dark',
set: v => (colorMode.preference = v ? 'dark' : 'light')
})
</script>
<template>
<UDashboardGroup unit="rem">
<UDashboardSidebar collapsible resizable :default-size="15" :ui="{ footer: 'border-t border-default' }">
<template #header="{ collapsed }">
<NuxtLink to="/" class="flex items-center gap-2 font-semibold">
<UIcon name="i-lucide-feather" class="size-5 text-primary shrink-0" />
<span v-if="!collapsed" class="truncate">AI 小说工坊</span>
</NuxtLink>
</template>
<template #default="{ collapsed }">
<UNavigationMenu :collapsed="collapsed" :items="mainItems" orientation="vertical" />
<template v-if="projectId">
<USeparator class="my-1" />
<div v-if="!collapsed" class="px-2.5 text-xs text-muted truncate" :title="projectTitle">
{{ projectTitle || '加载中…' }}
</div>
<UNavigationMenu :collapsed="collapsed" :items="projectItems" orientation="vertical" />
</template>
</template>
<template #footer="{ collapsed }">
<UButton
:icon="isDark ? 'i-lucide-moon' : 'i-lucide-sun'"
:label="collapsed ? undefined : (isDark ? '深色模式' : '浅色模式')"
color="neutral"
variant="ghost"
class="w-full"
:block="collapsed"
@click="isDark = !isDark"
/>
</template>
</UDashboardSidebar>
<slot />
</UDashboardGroup>
</template>