fix: 合并图库显示与操作入口

移除顶部主图统计,将刷新图库和批量生图改为带悬浮说明的图标按钮,并移动到布局切换按钮之后。
This commit is contained in:
GouJ
2026-09-08 13:40:10 +08:00
committed by GitHub
parent 7230f2238c
commit e912eada04
5 changed files with 243 additions and 241 deletions
+194 -186
View File
@@ -11,6 +11,7 @@ import {
NCollapseItem,
NInput,
NInputNumber,
NPopover,
NScrollbar,
NSelect,
NTag
@@ -26,6 +27,7 @@ import {
ArrowRight,
LayoutGrid,
Columns3,
Settings2,
AlertTriangle
} from '@lucide/vue'
import { AssetImage, EmptyState, StatusBadge } from '../../components/ui'
@@ -308,179 +310,163 @@ async function showStaleForms() {
<template>
<WorkspacePage compact class="gallery-workspace-page"
><template #header>
<!-- 图库统计和固定操作使用与其他工作台一致的紧凑头部 -->
<div class="workspace-toolbar">
<span class="toolbar-summary">主图 {{ completeCount }} / {{ forms.length }}</span>
<NButton v-if="staleForms.length" text type="error" size="small" @click="toolsOpen = true"
>{{ staleForms.length }} 个形态主图过期</NButton
>
<div class="toolbar-actions">
<NButton
:disabled="query.loading.value || impact.query.loading.value"
@click="refreshAssets"
text
size="small"
><RefreshCw :size="13" :class="{ 'animate-spin': query.loading.value }" />刷新图库 </NButton
><WorkspaceTools
v-model:open="toolsOpen"
title="形态生图配置与回执"
label="批量生图"
:has-receipt="!!session.receipt || !!session.promptReceipt"
><div class="flex flex-wrap items-end justify-between gap-4">
<div>
<h2 class="text-lg font-semibold">形态图片</h2>
<p class="mt-2 text-sm text-muted">
为人物场景和道具准备造型参考图已有图片直接从数据库读取
</p>
</div>
<RouterLink :to="`/projects/${id}/storyboard`" class="text-button"
>去检查分镜参考图<ArrowRight :size="14"
/></RouterLink>
</div>
<DetailDisclosure title="母版继承与过期处理" class="mt-4">
人物场景道具形态图均可继承已锁定 Identity
的母版并记录本次引用来源母版变化后可筛选过期图重新生成候选并人工确认新的主参考图不要直接沿用旧资产进入下游
<RouterLink :to="`/projects/${id}/subject-identity`" class="text-button ml-2"
>管理主体身份 </RouterLink
></DetailDisclosure
>
<FormPromptBatch
:forms="forms"
:disabled="blocked"
:pending="operation.pending"
:receipt="session.promptReceipt"
v-model:concurrency="promptConcurrency"
v-model:force="promptForce"
@generate="generatePrompts"
/>
<div class="panel mt-5 p-5">
<div class="flex flex-wrap items-center justify-between gap-4">
<p class="text-sm">
<strong>{{ completeCount }}</strong> / {{ forms.length }} 个形态已有主图
<span v-if="staleForms.length" class="ml-3 text-xs text-danger">
{{ staleForms.length }} 个形态主图已过期
</span>
<span class="ml-3 text-xs text-muted">图片模型 · 后端配置</span>
</p>
</div>
<p class="mt-2 text-[11px] leading-6 text-muted">
图库不自动刷新可点击刷新图库读取最新记录主图供分镜引用候选图和历史失败记录均保留过期主图不会自动删除或替换
</p>
<NAlert v-if="staleForms.length" type="error" :show-icon="false" class="mt-4"
><div class="flex flex-wrap items-center justify-between gap-3">
<span class="flex items-center gap-2 text-xs">
<AlertTriangle :size="14" />检测到 {{ staleForms.length }} 个形态 仍在使用旧
Identity Anchor
</span>
<ConfirmAction
label="重新生成过期形态"
:disabled="blocked || !concurrencyValid || !staleForms.length"
acknowledgement
description="只为身份母版已经变化的形态调用 图片模型,各新增一张基于当前已锁定母版的候选图;不会自动替换旧主图,生成后请人工确认并设置新的主参考图。"
@confirm="generateStale"
/></div
></NAlert>
<NCollapse class="mt-4 pt-4"
><NCollapseItem name="details"
><template #header>项目批量生图</template>
<div class="mt-4 flex flex-wrap items-end gap-4">
<label class="w-28">
<span class="field-label">批量并发</span>
<NInputNumber
:disabled="operation.pending"
:input-props="{ id: 'image-concurrency' }"
:value="typeof concurrency === 'number' ? concurrency : null"
@update:value="concurrency = $event ?? 0"
:min="1"
:step="1"
></NInputNumber>
</label>
<label class="w-36"
><span class="field-label">本批上限可选</span
><NInputNumber
:disabled="operation.pending"
:value="typeof limit === 'number' ? limit : null"
@update:value="limit = $event ?? ''"
:min="1"
:step="1"
placeholder="不限制"
:input-props="{ 'aria-label': '生图本批上限' }"
/></label>
<NCheckbox
v-model:checked="force"
:disabled="operation.pending"
class="control-row-checkbox text-xs"
>已有主图也新增候选图
</NCheckbox>
<ConfirmAction
:label="force ? '为全项目新增候选图' : '补齐项目主参考图'"
:disabled="blocked || !batchValid || !forms.length"
acknowledgement
:description="
force
? '对项目全部形态调用图片模型新增一张图片。已有主图不会被替换,生成后可在图片记录中手动选择主图。'
: '仅对没有主图的形态调用图片模型生图,已有主图的形态跳过。操作面向整个项目,不受列表筛选影响。'
"
@confirm="generateProject"
/>
</div>
<p v-if="!batchValid" class="mt-2 text-xs text-danger">
并发和填写的数量上限必须是正整数
</p>
<p class="mt-3 text-xs leading-6 text-muted">
作用于整个项目不受下方筛选影响上限只限制本次真正生图数量其余显示为待后续处理不计入跳过默认使用后端尺寸与正式提示词不会启动视频生成
</p></NCollapseItem
></NCollapse
>
</div>
<section v-if="session.receipt" class="panel mt-4 p-5" aria-label="生图批量回执">
<div class="flex items-center justify-between gap-3">
<h3 class="text-sm font-medium">{{ session.receipt.title }} · 本次回执</h3>
<NButton @click="exportReceipt" text size="small"
><Download :size="13" />导出诊断</NButton
>
</div>
<p class="mt-3 text-xs text-muted">
{{ session.receipt.result.total }} 个形态 · 本次目标
{{ session.receipt.result.targetCount }} · 生成 {{ session.receipt.result.generated }} ·
跳过 {{ session.receipt.result.skipped }} · 失败
{{ session.receipt.result.failed }}
<span v-if="session.receipt.result.remaining !== undefined">
· 待后续处理 {{ session.receipt.result.remaining }}</span
>
</p>
<NAlert
v-if="session.receipt.result.failed"
role="alert"
type="error"
:show-icon="false"
class="mt-3"
>
部分形态生图失败已生成的图片保留先查看失败原因再按形态重新生图
</NAlert>
<NAlert
v-else-if="session.receipt.title === '刷新过期形态图'"
type="info"
:show-icon="false"
class="mt-3 text-xs"
>
新图当前仍是候选图请进入对应形态的查看图片与记录确认人物身份和造型后再设为主参考图旧主图在此之前仍保持过期状态
</NAlert>
<ul class="mt-3 space-y-2 text-xs text-danger">
<li v-for="failure in session.receipt.result.failures" :key="failure.subjectFormId">
<code>{{ failure.subjectFormId }}</code
>{{ failure.error }}
</li>
</ul>
<p class="mt-3 text-[11px] text-muted">
回执仅保留于当前浏览器会话图片以刷新后的数据库记录为准
</p>
</section>
</WorkspaceTools>
</div>
</div></template
><template #default>
<!-- 抽屉只保留内容入口跟随图库显示控制移动到筛选区 -->
<WorkspaceTools
triggerless
v-model:open="toolsOpen"
title="形态生图配置与回执"
label="批量生图"
:has-receipt="!!session.receipt || !!session.promptReceipt"
><div class="flex flex-wrap items-end justify-between gap-4">
<div>
<h2 class="text-lg font-semibold">形态图片</h2>
<p class="mt-2 text-sm text-muted">
为人物场景和道具准备造型参考图已有图片直接从数据库读取
</p>
</div>
<RouterLink :to="`/projects/${id}/storyboard`" class="text-button"
>去检查分镜参考图<ArrowRight :size="14"
/></RouterLink>
</div>
<DetailDisclosure title="母版继承与过期处理" class="mt-4">
人物场景道具形态图均可继承已锁定 Identity
的母版并记录本次引用来源母版变化后可筛选过期图重新生成候选并人工确认新的主参考图不要直接沿用旧资产进入下游
<RouterLink :to="`/projects/${id}/subject-identity`" class="text-button ml-2"
>管理主体身份 </RouterLink
></DetailDisclosure
>
<FormPromptBatch
:forms="forms"
:disabled="blocked"
:pending="operation.pending"
:receipt="session.promptReceipt"
v-model:concurrency="promptConcurrency"
v-model:force="promptForce"
@generate="generatePrompts"
/>
<div class="panel mt-5 p-5">
<div class="flex flex-wrap items-center justify-between gap-4">
<p class="text-sm">
<strong>{{ completeCount }}</strong> / {{ forms.length }} 个形态已有主图
<span v-if="staleForms.length" class="ml-3 text-xs text-danger">
{{ staleForms.length }} 个形态主图已过期
</span>
<span class="ml-3 text-xs text-muted">图片模型 · 后端配置</span>
</p>
</div>
<p class="mt-2 text-[11px] leading-6 text-muted">
图库不自动刷新可点击刷新图库读取最新记录主图供分镜引用候选图和历史失败记录均保留过期主图不会自动删除或替换
</p>
<NAlert v-if="staleForms.length" type="error" :show-icon="false" class="mt-4"
><div class="flex flex-wrap items-center justify-between gap-3">
<span class="flex items-center gap-2 text-xs">
<AlertTriangle :size="14" />检测到 {{ staleForms.length }} 个形态 仍在使用旧 Identity
Anchor
</span>
<ConfirmAction
label="重新生成过期形态"
:disabled="blocked || !concurrencyValid || !staleForms.length"
acknowledgement
description="只为身份母版已经变化的形态调用 图片模型,各新增一张基于当前已锁定母版的候选图;不会自动替换旧主图,生成后请人工确认并设置新的主参考图。"
@confirm="generateStale"
/></div
></NAlert>
<NCollapse class="mt-4 pt-4"
><NCollapseItem name="details"
><template #header>项目批量生图</template>
<div class="mt-4 flex flex-wrap items-end gap-4">
<label class="w-28">
<span class="field-label">批量并发</span>
<NInputNumber
:disabled="operation.pending"
:input-props="{ id: 'image-concurrency' }"
:value="typeof concurrency === 'number' ? concurrency : null"
@update:value="concurrency = $event ?? 0"
:min="1"
:step="1"
></NInputNumber>
</label>
<label class="w-36"
><span class="field-label">本批上限可选</span
><NInputNumber
:disabled="operation.pending"
:value="typeof limit === 'number' ? limit : null"
@update:value="limit = $event ?? ''"
:min="1"
:step="1"
placeholder="不限制"
:input-props="{ 'aria-label': '生图本批上限' }"
/></label>
<NCheckbox
v-model:checked="force"
:disabled="operation.pending"
class="control-row-checkbox text-xs"
>已有主图也新增候选图
</NCheckbox>
<ConfirmAction
:label="force ? '为全项目新增候选图' : '补齐项目主参考图'"
:disabled="blocked || !batchValid || !forms.length"
acknowledgement
:description="
force
? '对项目全部形态调用图片模型新增一张图片。已有主图不会被替换,生成后可在图片记录中手动选择主图。'
: '仅对没有主图的形态调用图片模型生图,已有主图的形态跳过。操作面向整个项目,不受列表筛选影响。'
"
@confirm="generateProject"
/>
</div>
<p v-if="!batchValid" class="mt-2 text-xs text-danger">
并发和填写的数量上限必须是正整数
</p>
<p class="mt-3 text-xs leading-6 text-muted">
作用于整个项目不受下方筛选影响上限只限制本次真正生图数量其余显示为待后续处理不计入跳过默认使用后端尺寸与正式提示词不会启动视频生成
</p></NCollapseItem
></NCollapse
>
</div>
<section v-if="session.receipt" class="panel mt-4 p-5" aria-label="生图批量回执">
<div class="flex items-center justify-between gap-3">
<h3 class="text-sm font-medium">{{ session.receipt.title }} · 本次回执</h3>
<NButton @click="exportReceipt" text size="small"><Download :size="13" />导出诊断</NButton>
</div>
<p class="mt-3 text-xs text-muted">
{{ session.receipt.result.total }} 个形态 · 本次目标
{{ session.receipt.result.targetCount }} · 生成 {{ session.receipt.result.generated }} · 跳过
{{ session.receipt.result.skipped }} · 失败
{{ session.receipt.result.failed }}
<span v-if="session.receipt.result.remaining !== undefined">
· 待后续处理 {{ session.receipt.result.remaining }}</span
>
</p>
<NAlert
v-if="session.receipt.result.failed"
role="alert"
type="error"
:show-icon="false"
class="mt-3"
>
部分形态生图失败已生成的图片保留先查看失败原因再按形态重新生图
</NAlert>
<NAlert
v-else-if="session.receipt.title === '刷新过期形态图'"
type="info"
:show-icon="false"
class="mt-3 text-xs"
>
新图当前仍是候选图请进入对应形态的查看图片与记录确认人物身份和造型后再设为主参考图旧主图在此之前仍保持过期状态
</NAlert>
<ul class="mt-3 space-y-2 text-xs text-danger">
<li v-for="failure in session.receipt.result.failures" :key="failure.subjectFormId">
<code>{{ failure.subjectFormId }}</code
>{{ failure.error }}
</li>
</ul>
<p class="mt-3 text-[11px] text-muted">
回执仅保留于当前浏览器会话图片以刷新后的数据库记录为准
</p>
</section>
</WorkspaceTools>
<!-- 关联说明筛选和图片共用正文滚动区避免镜头切换时两块滚动区域高度不同步 -->
<NAlert v-if="staleForms.length" type="error" :show-icon="false" class="mt-3" title="形态主图需要更新">
{{ staleForms.length }}
@@ -608,6 +594,42 @@ async function showStaleForms() {
<template #icon><Columns3 :size="16" /></template>
</NButton>
</div>
<!-- 高频图库操作与显示方式连续排列纯图标按钮通过 Popover 解释用途 -->
<NPopover trigger="hover" placement="top">
<template #trigger>
<NButton
class="icon-button"
size="small"
:disabled="query.loading.value || impact.query.loading.value"
aria-label="刷新图库"
@click="refreshAssets"
>
<template #icon>
<RefreshCw
:size="16"
:class="{ 'animate-spin': query.loading.value }"
/>
</template>
</NButton>
</template>
刷新图库
</NPopover>
<NPopover trigger="hover" placement="top">
<template #trigger>
<NButton
class="icon-button"
size="small"
data-workspace-tools
aria-label="批量生图"
aria-haspopup="dialog"
:aria-expanded="toolsOpen"
@click="toolsOpen = true"
>
<template #icon><Settings2 :size="16" /></template>
</NButton>
</template>
{{ session.receipt || session.promptReceipt ? '批量生图(有回执)' : '批量生图' }}
</NPopover>
</div>
</div>
</div>
@@ -830,20 +852,6 @@ async function showStaleForms() {
.gallery-workspace-page .workspace-scroll-content {
@apply pt-2;
}
/* PC 端工具栏保持紧凑并从左向右连续排列;统一最小高度让统计和按钮上下居中。 */
.gallery-workspace-page .workspace-heading {
@apply py-[5px];
}
.gallery-workspace-page .workspace-toolbar,
.gallery-workspace-page .toolbar-summary,
.gallery-workspace-page .toolbar-actions {
@apply min-h-[34px] items-center;
}
@media (min-width: 761px) {
.gallery-workspace-page .toolbar-actions {
@apply ml-0;
}
}
.gallery-workspace-page .gallery-sticky-controls {
@apply sticky top-0 z-10 bg-(--app-body) mt-2.5 mb-0;
}