refactor: 使用 Naive UI 重构后台布局与黑白双主题

This commit is contained in:
GouJ
2026-09-01 16:31:21 +08:00
parent 55516537bf
commit db8c176b55
51 changed files with 3277 additions and 2442 deletions
+67 -58
View File
@@ -1,76 +1,85 @@
<script setup lang="ts">
import { computed, provide } from 'vue'
import { useRoute } from 'vue-router'
import {
ArrowLeft,
RefreshCw,
FileText,
Layers,
Camera,
Images,
Palette,
Fingerprint,
LoaderCircle,
Clapperboard
} from '@lucide/vue'
import { useRoute, useRouter } from 'vue-router'
import { NAlert, NButton, NPageHeader, NSpin, NTab, NTabs, NEllipsis } from 'naive-ui'
import { RefreshCw } from '@lucide/vue'
import { StatusBadge } from '../../components/ui'
import { projectContextKey, useProjectData } from './context'
import { getOperation } from '../workflows/operations'
/** 项目级数据与操作状态跨 graph 页面共享。 */
/** 项目共享查询保持不变,标题与导航固定,子工作区负责内容滚动。 */
const route = useRoute()
const router = useRouter()
const id = computed(() => String(route.params.projectId))
const context = useProjectData(id)
provide(projectContextKey, context)
const operation = computed(() => getOperation(id.value))
const tabs = [
['create-drama', '剧本创作'],
['breakdown', '剧本拆解'],
['visual-style', '视觉风格'],
['subject-identity', '主体身份'],
['subject-images', '形态图片'],
['storyboard', '分镜设计'],
['production', '镜头生产']
] as const
/** 路由控制当前工作流,不额外缓存一份可能失步的标签状态。 */
function switchWorkflow(path: string) {
void router.push('/projects/' + id.value + '/' + path)
}
</script>
<template>
<section class="page-container">
<RouterLink to="/projects" class="back-link"><ArrowLeft :size="14" />全部剧本</RouterLink>
<div class="page-heading mt-5">
<div class="min-w-0">
<p class="eyebrow">项目工作台</p>
<h1 class="break-words">
{{ context.project.value?.title || context.project.value?.topic || '读取项目' }}
</h1>
<p class="page-description">{{ context.project.value?.style || '剧本与拆解结果' }}</p>
</div>
<div class="flex shrink-0 items-center gap-3">
<StatusBadge v-if="context.project.value" :status="context.project.value.status" /><button
class="button button-secondary"
:disabled="context.loading.value"
@click="context.refresh"
<section class="project-frame">
<header class="project-header">
<NPageHeader @back="router.push('/projects')">
<template #title
><NEllipsis class="project-title">{{
context.project.value?.title || context.project.value?.topic || '读取项目'
}}</NEllipsis></template
>
<RefreshCw :size="14" :class="{ 'animate-spin': context.loading.value }" /><span
class="hidden sm:inline"
>刷新</span
>
</button>
</div>
<template #extra
><div class="flex items-center gap-3">
<StatusBadge v-if="context.project.value" :status="context.project.value.status" /><NButton
size="small"
:loading="context.loading.value"
@click="context.refresh"
><template #icon><RefreshCw :size="14" /></template>刷新</NButton
>
</div></template
>
</NPageHeader>
<NTabs
:value="String(route.path.split('/').at(-1))"
type="line"
size="small"
class="project-tabs"
aria-label="项目工作流"
@update:value="switchWorkflow"
>
<NTab v-for="[path, label] in tabs" :key="path" :name="path">{{ label }}</NTab>
</NTabs>
</header>
<div
v-if="context.error.value || operation.pending || operation.error || operation.notice"
class="project-notices"
>
<NAlert v-if="context.error.value" type="error" :show-icon="false"
>{{ context.error.value
}}<span v-if="context.project.value"> 当前保留上次成功读取的数据</span></NAlert
>
<NAlert v-if="operation.pending" :show-icon="false" role="status"
>{{ operation.label }}可切换页面查看结果请勿重复提交关闭页面不会取消后端任务</NAlert
>
<NAlert v-if="operation.error" type="error" :show-icon="false">{{ operation.error }}</NAlert>
<NAlert v-if="operation.notice" :show-icon="false" role="status">{{ operation.notice }}</NAlert>
</div>
<nav class="workflow-nav" aria-label="项目工作流">
<RouterLink :to="`/projects/${id}/create-drama`"><FileText :size="17" />剧本创作</RouterLink>
<RouterLink :to="`/projects/${id}/breakdown`"><Layers :size="17" />剧本拆解</RouterLink>
<RouterLink :to="`/projects/${id}/visual-style`"><Palette :size="17" />视觉风格</RouterLink>
<RouterLink :to="`/projects/${id}/subject-identity`"><Fingerprint :size="17" />主体身份</RouterLink>
<RouterLink :to="`/projects/${id}/subject-images`"><Images :size="17" />形态图片</RouterLink>
<RouterLink :to="`/projects/${id}/storyboard`"><Camera :size="17" />分镜设计</RouterLink>
<RouterLink :to="`/projects/${id}/production`"><Clapperboard :size="17" />镜头生产</RouterLink>
</nav>
<p v-if="context.error.value" class="alert alert-error mt-4" role="alert">
{{ context.error.value }}<span v-if="context.project.value"> 当前保留上次成功读取的数据</span>
</p>
<p v-if="operation.pending" class="alert mt-4 flex items-center gap-2" role="status">
<LoaderCircle :size="16" class="shrink-0 animate-spin" />{{
operation.label
}}可切换页面查看结果请勿重复提交或关闭浏览器关闭页面不会取消后端任务
</p>
<p v-if="operation.error" class="alert alert-error mt-4" role="alert">{{ operation.error }}</p>
<p v-if="operation.notice" class="alert mt-4" role="status">{{ operation.notice }}</p>
<RouterView v-if="context.project.value" :key="id" />
<div v-else-if="context.loading.value" class="py-12 text-sm text-muted" role="status">
正在读取项目和工作流记录
<div class="project-view">
<RouterView v-if="context.project.value" :key="id" />
<NSpin
v-else-if="context.loading.value"
class="workspace-loading"
description="正在读取项目和工作流记录…"
/>
</div>
</section>
</template>