refactor: 使用 Naive UI 重构后台布局与黑白双主题
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user