refactor: 使用 Naive UI 重构后台布局与黑白双主题
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import WorkspacePage from '../../components/ui/WorkspacePage.vue'
|
||||
import { NButton, NCollapse, NCollapseItem, NProgress, NTab, NTabs, NTag } from 'naive-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { TabsRoot, TabsList, TabsTrigger, TabsContent, ProgressRoot, ProgressIndicator } from 'reka-ui'
|
||||
import { ArrowRight, Download, Check, Circle } from '@lucide/vue'
|
||||
import { EmptyState } from '../../components/ui'
|
||||
import { useProjectContext } from '../projects/context'
|
||||
@@ -67,13 +68,19 @@ function exportScript() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="mt-6">
|
||||
<div class="stage-strip">
|
||||
<div v-for="(stage, index) in stages" :key="stage.label" class="stage-item" :class="{ done: stage.done }">
|
||||
<Check v-if="stage.done" :size="15" /><Circle v-else :size="13" /><span>{{ stage.label }}</span
|
||||
><ArrowRight v-if="index < stages.length - 1" :size="13" class="stage-arrow" />
|
||||
</div>
|
||||
</div>
|
||||
<WorkspacePage split class="script-workspace-page"
|
||||
><template #header
|
||||
><div class="stage-strip">
|
||||
<div
|
||||
v-for="(stage, index) in stages"
|
||||
:key="stage.label"
|
||||
class="stage-item"
|
||||
:class="{ done: stage.done }"
|
||||
>
|
||||
<Check v-if="stage.done" :size="15" /><Circle v-else :size="13" /><span>{{ stage.label }}</span
|
||||
><ArrowRight v-if="index < stages.length - 1" :size="13" class="stage-arrow" />
|
||||
</div></div
|
||||
></template>
|
||||
<div v-if="!complete" class="mt-5 flex flex-wrap items-center justify-between gap-4">
|
||||
<p class="text-sm text-muted">
|
||||
已写入 {{ episodes.length }}<span v-if="total"> / {{ total }}</span> 集<span v-if="!total">
|
||||
@@ -98,28 +105,26 @@ function exportScript() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ProgressRoot
|
||||
<NProgress
|
||||
v-if="total && !complete"
|
||||
class="progress-track mt-4"
|
||||
:model-value="Math.min(episodes.length, total)"
|
||||
:max="total"
|
||||
aria-label="已写入剧集数量"
|
||||
><ProgressIndicator
|
||||
class="progress-fill"
|
||||
:style="{ width: Math.min(100, (episodes.length / total) * 100) + '%' }"
|
||||
/></ProgressRoot>
|
||||
<TabsRoot v-model="tab" class="mt-7">
|
||||
type="line"
|
||||
:show-indicator="false"
|
||||
:height="4"
|
||||
:percentage="Math.min(100, Math.max(0, (Math.min(episodes.length, total) / Math.max(1, total)) * 100))"
|
||||
class="mt-4"
|
||||
></NProgress>
|
||||
<div class="script-section">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<TabsList class="tabs-list" aria-label="剧本内容"
|
||||
><TabsTrigger class="tab-trigger" value="episodes"
|
||||
>剧集正文 <span>{{ episodes.length }}</span></TabsTrigger
|
||||
><TabsTrigger class="tab-trigger" value="characters">角色设定</TabsTrigger
|
||||
><TabsTrigger class="tab-trigger" value="world">世界观</TabsTrigger
|
||||
><TabsTrigger class="tab-trigger" value="review">审核记录</TabsTrigger></TabsList
|
||||
<NTabs v-model:value="tab" type="line" size="small" aria-label="剧本内容"
|
||||
><NTab name="episodes"
|
||||
>剧集正文 <span>{{ episodes.length }}</span></NTab
|
||||
><NTab name="characters">角色设定</NTab><NTab name="world">世界观</NTab
|
||||
><NTab name="review">审核记录</NTab></NTabs
|
||||
>
|
||||
<div class="flex items-center gap-3">
|
||||
<button class="text-button" :disabled="!episodes.length" @click="exportScript">
|
||||
<Download :size="14" />导出剧本</button
|
||||
<NButton :disabled="!episodes.length" @click="exportScript" text size="small"
|
||||
><Download :size="14" />导出剧本</NButton
|
||||
><RouterLink
|
||||
v-if="complete"
|
||||
class="text-button text-accent"
|
||||
@@ -130,21 +135,20 @@ function exportScript() {
|
||||
</div>
|
||||
<div class="content-with-history panel mt-4">
|
||||
<main class="min-w-0">
|
||||
<TabsContent value="episodes" class="h-full">
|
||||
<div v-if="tab === 'episodes'" class="h-full">
|
||||
<div v-if="episode" class="script-workspace">
|
||||
<aside class="episode-list">
|
||||
<p class="px-4 pb-3 pt-5 text-[11px] font-medium tracking-wider text-muted">剧集目录</p>
|
||||
<button
|
||||
<NButton
|
||||
v-for="item in episodes"
|
||||
:key="item.episode"
|
||||
class="episode-link"
|
||||
:class="{ active: episode.episode === item.episode }"
|
||||
:aria-pressed="episode.episode === item.episode"
|
||||
@click="selected = item.episode"
|
||||
class="episode-link"
|
||||
:class="{ active: episode.episode === item.episode }"
|
||||
><span class="episode-number">{{ String(item.episode).padStart(2, '0') }}</span
|
||||
><span class="truncate">{{ item.title }}</span></NButton
|
||||
>
|
||||
<span class="episode-number">{{ String(item.episode).padStart(2, '0') }}</span
|
||||
><span class="truncate">{{ item.title }}</span>
|
||||
</button>
|
||||
</aside>
|
||||
<article class="script-page">
|
||||
<p class="eyebrow">第 {{ String(episode.episode).padStart(2, '0') }} 集</p>
|
||||
@@ -167,9 +171,9 @@ function exportScript() {
|
||||
title="剧本正在酝酿"
|
||||
description="剧集写入数据库后会自动出现在这里。角色与世界观生成期间,可以在右侧查看已保存的执行记录。"
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="characters" class="p-6 lg:p-8"
|
||||
><div v-if="project?.characters.length" class="divide-y divide-line">
|
||||
</div>
|
||||
<div v-if="tab === 'characters'" class="p-6 lg:p-8">
|
||||
<div v-if="project?.characters.length" class="divide-y divide-line">
|
||||
<article
|
||||
v-for="character in project.characters"
|
||||
:key="character.id"
|
||||
@@ -177,7 +181,7 @@ function exportScript() {
|
||||
>
|
||||
<div class="mb-3 flex items-center gap-3">
|
||||
<h3 class="text-lg font-semibold">{{ character.name }}</h3>
|
||||
<span class="tag">{{ character.role || '角色' }}</span
|
||||
<NTag size="small" :bordered="false">{{ character.role || '角色' }}</NTag
|
||||
><span class="text-xs text-muted">{{ character.occupation }}</span>
|
||||
</div>
|
||||
<dl class="detail-grid">
|
||||
@@ -194,10 +198,10 @@ function exportScript() {
|
||||
</dl>
|
||||
</article>
|
||||
</div>
|
||||
<EmptyState v-else title="尚无角色设定" description="角色生成结束后会在这里显示。"
|
||||
/></TabsContent>
|
||||
<TabsContent value="world" class="p-6 lg:p-8"
|
||||
><div v-if="project?.world">
|
||||
<EmptyState v-else title="尚无角色设定" description="角色生成结束后会在这里显示。" />
|
||||
</div>
|
||||
<div v-if="tab === 'world'" class="p-6 lg:p-8">
|
||||
<div v-if="project?.world">
|
||||
<p class="eyebrow">故事的发生之地</p>
|
||||
<h2 class="mb-7 mt-3 text-xl font-semibold">
|
||||
{{ project.world.era }} · {{ project.world.location }}
|
||||
@@ -214,15 +218,17 @@ function exportScript() {
|
||||
<dd>{{ project.world[field.key] || '未提供' }}</dd></template
|
||||
>
|
||||
</dl>
|
||||
<details v-if="project.world.rules" class="mt-6 text-sm">
|
||||
<summary class="cursor-pointer text-muted">世界规则</summary>
|
||||
<pre class="json-view mt-3">{{ JSON.stringify(project.world.rules, null, 2) }}</pre>
|
||||
</details>
|
||||
<NCollapse v-if="project.world.rules" class="mt-6 text-sm"
|
||||
><NCollapseItem name="details"
|
||||
><template #header>世界规则</template>
|
||||
<pre class="json-view mt-3">{{ JSON.stringify(project.world.rules, null, 2) }}</pre>
|
||||
</NCollapseItem></NCollapse
|
||||
>
|
||||
</div>
|
||||
<EmptyState v-else title="尚无世界观" description="世界观会在角色设定之后生成。"
|
||||
/></TabsContent>
|
||||
<TabsContent value="review" class="p-6 lg:p-8"
|
||||
><div v-if="project?.reviews.length" class="space-y-5">
|
||||
<EmptyState v-else title="尚无世界观" description="世界观会在角色设定之后生成。" />
|
||||
</div>
|
||||
<div v-if="tab === 'review'" class="p-6 lg:p-8">
|
||||
<div v-if="project?.reviews.length" class="space-y-5">
|
||||
<article
|
||||
v-for="review in project.reviews"
|
||||
:key="review.id"
|
||||
@@ -244,15 +250,15 @@ function exportScript() {
|
||||
v-else
|
||||
title="还没有审核记录"
|
||||
description="剧集生成完成后,工作流会进行内容审核与必要的改写。"
|
||||
/>
|
||||
<details v-if="state?.rewriteSuggestion" class="mt-5 text-sm">
|
||||
<summary class="cursor-pointer text-muted">查看改写建议原文</summary>
|
||||
<pre class="json-view mt-3">{{ JSON.stringify(state.rewriteSuggestion, null, 2) }}</pre>
|
||||
</details></TabsContent
|
||||
>
|
||||
/><NCollapse v-if="state?.rewriteSuggestion" class="mt-5 text-sm"
|
||||
><NCollapseItem name="details"
|
||||
><template #header>查看改写建议原文</template>
|
||||
<pre class="json-view mt-3">{{ JSON.stringify(state.rewriteSuggestion, null, 2) }}</pre>
|
||||
</NCollapseItem></NCollapse
|
||||
>
|
||||
</div>
|
||||
</main>
|
||||
<HistoryPanel :checkpoints="checkpoints" workflow="create-drama" />
|
||||
</div>
|
||||
</TabsRoot>
|
||||
</div>
|
||||
</div></div
|
||||
></WorkspacePage>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user