refactor: 精简导航并扩大工作区内容显示面积
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import WorkspacePage from '../../components/ui/WorkspacePage.vue'
|
||||
import WorkspaceTools from '../../components/ui/WorkspaceTools.vue'
|
||||
import { NAlert, NButton, NCheckbox, NCollapse, NCollapseItem, NInput, NInputNumber, NSelect, NTag } from 'naive-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
@@ -31,6 +32,14 @@ const {
|
||||
generateProject,
|
||||
generateStale
|
||||
} = useSubjectImages()
|
||||
const toolsOpen = ref(false)
|
||||
/** 新批量回执到达时显示诊断,进入页面不自动遮挡图库。 */
|
||||
watch(
|
||||
() => session.value.receipt,
|
||||
receipt => {
|
||||
if (receipt) toolsOpen.value = true
|
||||
}
|
||||
)
|
||||
const search = ref('')
|
||||
const module = ref('all')
|
||||
const onlyMissing = ref(false)
|
||||
@@ -89,154 +98,181 @@ function resetFilters() {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WorkspacePage
|
||||
><template #header
|
||||
><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>
|
||||
<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" @click="query.refresh" text size="small"
|
||||
><RefreshCw :size="13" :class="{ 'animate-spin': query.loading.value }" />刷新图库 </NButton
|
||||
><WorkspaceTools
|
||||
v-model:open="toolsOpen"
|
||||
title="形态生图配置与回执"
|
||||
:has-receipt="!!session.receipt"
|
||||
><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>
|
||||
<NAlert type="info" :show-icon="false" class="mt-5 text-xs">
|
||||
Character 形态图会记录生成时使用的 Identity
|
||||
Anchor。更换演员母版后,旧主图会标记为“身份母版已变更”,且后端会阻止它继续进入分镜首帧;请基于当前母版重新生成候选并确认新的主参考图。
|
||||
<RouterLink :to="`/projects/${id}/subject-identity`" class="text-button ml-2"
|
||||
>管理主体身份 →</RouterLink
|
||||
></NAlert
|
||||
>
|
||||
<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">Seedream · 后端配置</span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="mt-2 text-[11px] leading-6 text-muted">
|
||||
每 6
|
||||
秒更新图片记录;主图供分镜引用,候选图和历史失败记录均保留。过期主图不会自动删除,也不会被批量任务静默替换。
|
||||
</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 }} 个 Character Form
|
||||
仍在使用旧 Identity Anchor。
|
||||
</span>
|
||||
<ConfirmAction
|
||||
label="重新生成过期形态"
|
||||
:disabled="blocked || !batchValid || !staleForms.length"
|
||||
acknowledgement
|
||||
description="只为身份母版已经变化的 Character Form 调用 Seedream,各新增一张基于当前 Anchor 的候选图;不会自动替换旧主图,生成后请人工确认并设置新的主参考图。"
|
||||
@confirm="generateStale"
|
||||
/></div
|
||||
></NAlert>
|
||||
<NCollapse class="mt-4 border-t border-line 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>
|
||||
<NCheckbox
|
||||
v-model:checked="force"
|
||||
:disabled="operation.pending"
|
||||
class="flex items-center gap-2 pb-2 text-xs"
|
||||
>已有主图也新增候选图
|
||||
</NCheckbox>
|
||||
<ConfirmAction
|
||||
:label="force ? '为全项目新增候选图' : '补齐项目主参考图'"
|
||||
:disabled="blocked || !batchValid || !forms.length"
|
||||
acknowledgement
|
||||
:description="
|
||||
force
|
||||
? '对项目全部形态调用 Seedream 新增一张图片。已有主图不会被替换,生成后可在图片记录中手动选择主图。'
|
||||
: '仅对没有主图的形态调用 Seedream 生图,已有主图的形态跳过。操作面向整个项目,不受列表筛选影响。'
|
||||
"
|
||||
@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 }}
|
||||
</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>
|
||||
<RouterLink :to="`/projects/${id}/storyboard`" class="text-button"
|
||||
>去检查分镜参考图<ArrowRight :size="14"
|
||||
/></RouterLink></div></template
|
||||
><NAlert type="info" :show-icon="false" class="mt-5 text-xs">
|
||||
Character 形态图会记录生成时使用的 Identity
|
||||
Anchor。更换演员母版后,旧主图会标记为“身份母版已变更”,且后端会阻止它继续进入分镜首帧;请基于当前母版重新生成候选并确认新的主参考图。
|
||||
<RouterLink :to="`/projects/${id}/subject-identity`" class="text-button ml-2"
|
||||
>管理主体身份 →</RouterLink
|
||||
></NAlert
|
||||
>
|
||||
<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">Seedream · 后端配置</span>
|
||||
</p>
|
||||
<NButton :disabled="query.loading.value" @click="query.refresh" text size="small"
|
||||
><RefreshCw :size="13" :class="{ 'animate-spin': query.loading.value }" />刷新图库
|
||||
</NButton>
|
||||
</div>
|
||||
<p class="mt-2 text-[11px] leading-6 text-muted">
|
||||
每 6
|
||||
秒更新图片记录;主图供分镜引用,候选图和历史失败记录均保留。过期主图不会自动删除,也不会被批量任务静默替换。
|
||||
</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 }} 个 Character Form 仍在使用旧 Identity
|
||||
Anchor。
|
||||
</span>
|
||||
<ConfirmAction
|
||||
label="重新生成过期形态"
|
||||
:disabled="blocked || !batchValid || !staleForms.length"
|
||||
acknowledgement
|
||||
description="只为身份母版已经变化的 Character Form 调用 Seedream,各新增一张基于当前 Anchor 的候选图;不会自动替换旧主图,生成后请人工确认并设置新的主参考图。"
|
||||
@confirm="generateStale"
|
||||
/></div
|
||||
></NAlert>
|
||||
<NCollapse class="mt-4 border-t border-line 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>
|
||||
<NCheckbox
|
||||
v-model:checked="force"
|
||||
:disabled="operation.pending"
|
||||
class="flex items-center gap-2 pb-2 text-xs"
|
||||
>已有主图也新增候选图
|
||||
</NCheckbox>
|
||||
<ConfirmAction
|
||||
:label="force ? '为全项目新增候选图' : '补齐项目主参考图'"
|
||||
:disabled="blocked || !batchValid || !forms.length"
|
||||
acknowledgement
|
||||
:description="
|
||||
force
|
||||
? '对项目全部形态调用 Seedream 新增一张图片。已有主图不会被替换,生成后可在图片记录中手动选择主图。'
|
||||
: '仅对没有主图的形态调用 Seedream 生图,已有主图的形态跳过。操作面向整个项目,不受列表筛选影响。'
|
||||
"
|
||||
@confirm="generateProject"
|
||||
/>
|
||||
<div class="form-image-filter-region">
|
||||
<div class="form-image-filters" role="group" aria-label="形态图片筛选">
|
||||
<NInput
|
||||
placeholder="搜索主体、引用或形态"
|
||||
:input-props="{ 'aria-label': '搜索形态图片' }"
|
||||
v-model:value="search"
|
||||
clearable
|
||||
><template #prefix><Search :size="14" /></template
|
||||
></NInput>
|
||||
<NSelect
|
||||
aria-label="筛选主体类型"
|
||||
v-model:value="module"
|
||||
:options="[
|
||||
{ label: String('全部类型'), value: 'all' },
|
||||
{ label: String('人物'), value: 'character' },
|
||||
{ label: String('场景'), value: 'scene' },
|
||||
{ label: String('道具'), value: 'prop' }
|
||||
]"
|
||||
></NSelect>
|
||||
<div class="filter-toggles">
|
||||
<NCheckbox v-model:checked="onlyMissing">仅看缺少主图</NCheckbox>
|
||||
<NCheckbox v-model:checked="onlyStale">仅看身份过期</NCheckbox>
|
||||
</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>
|
||||
<NAlert v-if="query.error.value" role="alert" type="error" :show-icon="false" class="mt-4"
|
||||
>{{ query.error.value }}<span v-if="forms.length"> 当前展示上次读取的数据。</span></NAlert
|
||||
><NAlert v-if="running" role="status" type="info" :show-icon="false" class="mt-4">
|
||||
数据库中仍有排队或生成中的图片,已暂停新的生图操作。长时间无变化时,请先核对后台任务;刷新或关闭页面不会取消任务。
|
||||
</NAlert>
|
||||
<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 }}
|
||||
</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>
|
||||
<div class="form-image-filter-region">
|
||||
<div class="form-image-filters" role="group" aria-label="形态图片筛选">
|
||||
<NInput
|
||||
placeholder="搜索主体、引用或形态"
|
||||
:input-props="{ 'aria-label': '搜索形态图片' }"
|
||||
v-model:value="search"
|
||||
clearable
|
||||
><template #prefix><Search :size="14" /></template
|
||||
></NInput>
|
||||
<NSelect
|
||||
aria-label="筛选主体类型"
|
||||
v-model:value="module"
|
||||
:options="[
|
||||
{ label: String('全部类型'), value: 'all' },
|
||||
{ label: String('人物'), value: 'character' },
|
||||
{ label: String('场景'), value: 'scene' },
|
||||
{ label: String('道具'), value: 'prop' }
|
||||
]"
|
||||
></NSelect>
|
||||
<div class="filter-toggles">
|
||||
<NCheckbox v-model:checked="onlyMissing">仅看缺少主图</NCheckbox>
|
||||
<NCheckbox v-model:checked="onlyStale">仅看身份过期</NCheckbox>
|
||||
<span class="filter-result-count">{{ filtered.length }} 个形态</span>
|
||||
</div>
|
||||
<span class="filter-result-count">{{ filtered.length }} 个形态</span>
|
||||
</div>
|
||||
</div>
|
||||
<NAlert v-if="query.error.value" role="alert" type="error" :show-icon="false" class="mt-4"
|
||||
>{{ query.error.value }}<span v-if="forms.length"> 当前展示上次读取的数据。</span></NAlert
|
||||
><NAlert v-if="running" role="status" type="info" :show-icon="false" class="mt-4">
|
||||
数据库中仍有排队或生成中的图片,已暂停新的生图操作。长时间无变化时,请先核对后台任务;刷新或关闭页面不会取消任务。
|
||||
</NAlert>
|
||||
</template>
|
||||
<p v-if="!query.data.value && query.loading.value" class="py-10 text-sm text-muted" role="status">
|
||||
正在读取形态和已有图片…
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user