refactor: 将资产库移至全局导航

This commit is contained in:
GouJ
2026-09-22 09:28:38 +08:00
parent b834d1d259
commit eec957a2e4
5 changed files with 136 additions and 6 deletions
@@ -0,0 +1,121 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { RouterLink } from 'vue-router'
import { NAlert, NButton, NInput, NTag } from 'naive-ui'
import { ArrowRight, RefreshCw, Search } from '@lucide/vue'
import { EmptyState } from '../../components/ui'
import WorkspacePage from '../../components/ui/WorkspacePage.vue'
import { useQuery } from '../../composables/useQuery'
import { projectsApi } from '../projects/api'
/** 全局资产库只负责选择项目;素材读写仍严格使用后端的项目级接口。 */
const query = useQuery(ref('asset-projects'), (_, signal) => projectsApi.list(signal))
const search = ref('')
const projects = computed(() => {
const keyword = search.value.trim().toLowerCase()
return (query.data.value ?? []).filter(project =>
[project.title, project.topic, project.style].some(value => value?.toLowerCase().includes(keyword))
)
})
function projectName(title: string | null, topic: string) {
return title?.trim() || topic
}
</script>
<template>
<WorkspacePage compact class="asset-library-page">
<template #header>
<div class="page-heading asset-library-heading">
<div>
<h1>资产库</h1>
<p class="page-description">按剧本管理人物场景道具和参考素材</p>
</div>
<NButton
quaternary
class="icon-button"
:loading="query.loading.value"
aria-label="刷新资产项目"
title="刷新资产项目"
@click="query.refresh"
>
<template #icon><RefreshCw :size="16" /></template>
</NButton>
</div>
</template>
<NAlert v-if="query.error.value" type="error" :show-icon="false" class="mb-4">
{{ query.error.value }}<NButton text class="ml-3" @click="query.refresh">重新连接</NButton>
</NAlert>
<NInput
v-model:value="search"
clearable
class="asset-project-search"
placeholder="搜索剧本名称、主题或风格"
:input-props="{ 'aria-label': '搜索资产所属剧本' }"
>
<template #prefix><Search :size="15" /></template>
</NInput>
<div v-if="projects.length" class="asset-project-list">
<article v-for="project in projects" :key="project.id" class="asset-project-item">
<div class="min-w-0">
<div class="flex min-w-0 items-center gap-2">
<h2 class="truncate text-sm font-semibold" :title="projectName(project.title, project.topic)">
{{ projectName(project.title, project.topic) }}
</h2>
<NTag v-if="project.status !== 'completed'" size="small" :bordered="false">剧本未完成</NTag>
</div>
<p class="mt-1 truncate text-xs text-muted">{{ project.topic }}</p>
</div>
<RouterLink
v-if="project.status === 'completed'"
v-slot="{ href, navigate }"
:to="`/projects/${encodeURIComponent(project.id)}/assets`"
custom
>
<NButton tag="a" :href="href" text type="primary" @click="navigate">
管理素材<template #icon><ArrowRight :size="15" /></template>
</NButton>
</RouterLink>
<NButton v-else text disabled
>管理素材<template #icon><ArrowRight :size="15" /></template
></NButton>
</article>
</div>
<EmptyState
v-else-if="query.data.value"
:title="search ? '没有匹配的剧本' : '还没有可管理的剧本'"
:description="search ? '请调整搜索关键词。' : '创建剧本后,可在这里进入对应的项目资产库。'"
>
<NButton v-if="search" @click="search = ''">清除搜索</NButton>
<RouterLink v-else v-slot="{ href, navigate }" to="/projects" custom>
<NButton tag="a" :href="href" type="primary" @click="navigate">前往我的剧本</NButton>
</RouterLink>
</EmptyState>
<p v-else class="py-10 text-center text-sm text-muted" role="status">正在读取剧本列表</p>
</WorkspacePage>
</template>
<style scoped>
@reference "../../styles/styles.css";
.asset-library-heading {
@apply flex items-start justify-between gap-4;
}
.asset-project-search {
@apply max-w-[420px] mb-4;
}
.asset-project-list {
@apply grid gap-2;
}
.asset-project-item {
@apply flex min-w-0 items-center justify-between gap-5 px-4 py-3 bg-(--app-surface);
}
@media (max-width: 640px) {
.asset-project-item {
@apply items-start gap-3;
}
}
</style>