Files
ai-novel-studio/app/pages/projects/[id]/setting.vue
T
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

156 lines
7.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { ProjectRequirements, ProjectSettings } from '#shared/types'
const store = useProjectStore()
const api = useApi()
const p = store.project
const draft = reactive({
title: p.value!.title,
description: p.value!.description,
worldSetting: p.value!.worldSetting,
storyOutline: p.value!.storyOutline,
userName: p.value!.userName,
requirements: { ...p.value!.requirements } as ProjectRequirements,
settings: { ...p.value!.settings } as ProjectSettings
})
const snapshot = ref(JSON.stringify(draft))
const dirty = computed(() => JSON.stringify(draft) !== snapshot.value)
const saving = ref(false)
async function save() {
saving.value = true
try {
await store.saveProject(JSON.parse(JSON.stringify(draft)))
snapshot.value = JSON.stringify(draft)
api.toast.add({ title: '设定已保存', color: 'success', icon: 'i-lucide-save' })
}
finally {
saving.value = false
}
}
// Ctrl+S 保存
defineShortcuts({ meta_s: { usingInput: true, handler: () => { if (dirty.value) save() } } })
onBeforeRouteLeave(() => {
if (dirty.value && !window.confirm('有未保存的修改,确定离开吗?')) return false
})
const POV_OPTIONS = ['第一人称', '第三人称有限视角', '第三人称全知视角', '第二人称', '多视角轮换']
</script>
<template>
<UDashboardPanel id="project-setting">
<template #header>
<UDashboardNavbar title="故事设定">
<template #right>
<span v-if="dirty" class="text-xs text-warning">有未保存的修改</span>
<UButton icon="i-lucide-save" label="保存" :loading="saving" :disabled="!dirty" @click="save" />
</template>
</UDashboardNavbar>
</template>
<template #body>
<div class="max-w-4xl mx-auto w-full space-y-8 pb-16">
<section class="space-y-4">
<h2 class="text-base font-semibold flex items-center gap-2">
<UIcon name="i-lucide-book" class="text-primary" />基本信息
</h2>
<div class="grid sm:grid-cols-2 gap-4">
<UFormField label="作品名" required>
<UInput v-model="draft.title" class="w-full" />
</UFormField>
<UFormField label="主角名 / {{user}}" hint="导入角色卡时用于替换 {{user}}">
<UInput v-model="draft.userName" placeholder="例如:陈默" class="w-full" />
</UFormField>
</div>
<UFormField label="简介">
<UTextarea v-model="draft.description" autoresize :rows="2" class="w-full" />
</UFormField>
</section>
<section class="space-y-4">
<h2 class="text-base font-semibold flex items-center gap-2">
<UIcon name="i-lucide-globe" class="text-primary" />故事背景 / 世界观
</h2>
<UTextarea
v-model="draft.worldSetting"
autoresize
:rows="8"
:maxrows="30"
class="w-full"
placeholder="时代、地点、社会环境、力量体系、重要组织、基调……越具体,AI 写得越贴合。导入角色卡时,卡片的场景描述会追加到这里。"
/>
</section>
<section class="space-y-4">
<h2 class="text-base font-semibold flex items-center gap-2">
<UIcon name="i-lucide-route" class="text-primary" />故事大纲
</h2>
<UTextarea
v-model="draft.storyOutline"
autoresize
:rows="10"
:maxrows="40"
class="w-full"
placeholder="整个故事的主线:起因、发展、高潮、结局,以及主要的转折点和人物弧光。主编会据此拆分章节。"
/>
</section>
<section class="space-y-4">
<h2 class="text-base font-semibold flex items-center gap-2">
<UIcon name="i-lucide-pen-tool" class="text-primary" />写作要求 <span class="text-xs font-normal text-muted">(可选)</span>
</h2>
<div class="grid sm:grid-cols-3 gap-4">
<UFormField label="语言">
<UInput v-model="draft.requirements.language" class="w-full" />
</UFormField>
<UFormField label="叙事视角">
<UInputMenu v-model="draft.requirements.pov" :items="POV_OPTIONS" create-item class="w-full" @create="(v: string) => (draft.requirements.pov = v)" />
</UFormField>
<UFormField label="每章字数">
<UInputNumber v-model="draft.requirements.wordsPerChapter" :min="500" :max="50000" :step="500" class="w-full" />
</UFormField>
</div>
<UFormField label="文风">
<UInput v-model="draft.requirements.style" placeholder="例如:细腻克制的现实主义,带一点潮湿的南方气息" class="w-full" />
</UFormField>
<UFormField label="叙述方式">
<UTextarea v-model="draft.requirements.narration" autoresize :rows="2" placeholder="例如:过去时;对白占比约三成;多用环境烘托情绪;少用心理独白" class="w-full" />
</UFormField>
<UFormField label="其他要求">
<UTextarea v-model="draft.requirements.extra" autoresize :rows="2" placeholder="例如:避免现代网络用语;不要出现“仿佛”“不禁”等滥用词" class="w-full" />
</UFormField>
</section>
<section class="space-y-4">
<h2 class="text-base font-semibold flex items-center gap-2">
<UIcon name="i-lucide-settings-2" class="text-primary" />写作流程
</h2>
<div class="grid sm:grid-cols-2 gap-4">
<USwitch v-model="draft.settings.autoMode" label="自动模式" description="章节写完后自动确认并继续下一章;关闭时每章写完都会停下来等你确认" />
<USwitch v-model="draft.settings.pauseAfterBrief" label="简报后暂停" description="主编写完简报后先暂停,你确认或修改简报后再开始写作(仅逐章模式)" />
<USwitch v-model="draft.settings.allowEditorCreateWriters" label="允许主编创建作家" description="主编可以用已配置的模型创建新的作家来写特定风格的章节" />
</div>
<div class="grid sm:grid-cols-4 gap-4">
<UFormField label="上一章衔接字数" hint="注入作家">
<UInputNumber v-model="draft.settings.prevTailChars" :min="0" :max="20000" :step="500" class="w-full" />
</UFormField>
<UFormField label="详细前情章数">
<UInputNumber v-model="draft.settings.recentSummaryCount" :min="0" :max="50" class="w-full" />
</UFormField>
<UFormField label="截断自动续写次数">
<UInputNumber v-model="draft.settings.maxContinuations" :min="0" :max="10" class="w-full" />
</UFormField>
<UFormField label="自动模式章节上限">
<UInputNumber v-model="draft.settings.autoChapterLimit" :min="1" :max="200" class="w-full" />
</UFormField>
</div>
</section>
</div>
</template>
</UDashboardPanel>
</template>