feat: 对齐后端能力并补齐形态提示词与生产诊断

This commit is contained in:
GouJ
2026-09-02 18:21:20 +08:00
parent 7d8074f90e
commit f71d2bf0a4
30 changed files with 1707 additions and 66 deletions
@@ -13,6 +13,8 @@ import { coverImage, hasRunningImages, isPrimaryIdentityStale, primaryImage } fr
import type { GenerateFormImageInput, SubjectFormAsset } from './types'
import GenerateImageDialog from './components/GenerateImageDialog.vue'
import ImageGalleryDialog from './components/ImageGalleryDialog.vue'
import FormPromptDialog from './components/FormPromptDialog.vue'
import FormPromptBatch from './components/FormPromptBatch.vue'
/** 形态图库以正式数据库为准,自动显示已在后端生成的图片。 */
const route = useRoute()
@@ -25,6 +27,12 @@ const {
session,
concurrency,
force,
limit,
promptConcurrency,
promptForce,
concurrencyValid,
generatePrompt,
generatePrompts,
blocked,
running,
batchValid,
@@ -40,6 +48,12 @@ watch(
if (receipt) toolsOpen.value = true
}
)
watch(
() => session.value.promptReceipt,
receipt => {
if (receipt) toolsOpen.value = true
}
)
const search = ref('')
const module = ref('all')
const onlyMissing = ref(false)
@@ -48,6 +62,9 @@ const generateOpen = ref(false)
const galleryOpen = ref(false)
const generateId = ref('')
const galleryId = ref('')
const promptOpen = ref(false)
const promptId = ref('')
const promptForm = computed(() => forms.value.find(form => form.id === promptId.value) ?? null)
const generateForm = computed(() => forms.value.find(form => form.id === generateId.value) ?? null)
const galleryForm = computed(() => forms.value.find(form => form.id === galleryId.value) ?? null)
const completeCount = computed(() => forms.value.filter(form => primaryImage(form.images)).length)
@@ -70,6 +87,12 @@ watch(
{ immediate: true }
)
/** 打开指定正式形态的提示词管理。 */
function openPrompt(form: SubjectFormAsset) {
promptId.value = form.id
promptOpen.value = true
}
/** 打开指定正式形态的生图表单。 */
function openGenerate(form: SubjectFormAsset) {
generateId.value = form.id
@@ -111,7 +134,7 @@ function resetFilters() {
><WorkspaceTools
v-model:open="toolsOpen"
title="形态生图配置与回执"
:has-receipt="!!session.receipt"
: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>
@@ -124,18 +147,27 @@ function resetFilters() {
/></RouterLink>
</div>
<NAlert type="info" :show-icon="false" class="mt-5 text-xs">
Character 形态图会记录生成时使用的 Identity
Anchor更换演员母版旧主图会标记为身份母版已变更且后端会阻止它继续进入分镜首帧请基于当前母版重新生成候选并确认新的主参考图
人物场景道具形态图均可继承已锁定 Identity
母版并记录本次引用来源母版变化后可筛选过期图重新生成候选并人工确认新的主参考图不要直接沿用旧资产进入下游
<RouterLink :to="`/projects/${id}/subject-identity`" class="text-button ml-2"
>管理主体身份 </RouterLink
></NAlert
>
<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 }} 人物形态主图已过期
{{ staleForms.length }} 个形态主图已过期
</span>
<span class="ml-3 text-xs text-muted">Seedream · 后端配置</span>
</p>
@@ -147,14 +179,14 @@ function resetFilters() {
<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
<AlertTriangle :size="14" />检测到 {{ staleForms.length }} 形态 仍在使用旧
Identity Anchor
</span>
<ConfirmAction
label="重新生成过期形态"
:disabled="blocked || !batchValid || !staleForms.length"
:disabled="blocked || !concurrencyValid || !staleForms.length"
acknowledgement
description="只为身份母版已经变化的 Character Form 调用 Seedream,各新增一张基于当前 Anchor 的候选图;不会自动替换旧主图,生成后请人工确认并设置新的主参考图。"
description="只为身份母版已经变化的形态调用 Seedream,各新增一张基于当前已锁定母版的候选图;不会自动替换旧主图,生成后请人工确认并设置新的主参考图。"
@confirm="generateStale"
/></div
></NAlert>
@@ -173,6 +205,17 @@ function resetFilters() {
: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"
@@ -191,9 +234,11 @@ function resetFilters() {
@confirm="generateProject"
/>
</div>
<p v-if="!batchValid" class="mt-2 text-xs text-danger">并发必须是正整数</p>
<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
>
@@ -210,6 +255,9 @@ function resetFilters() {
{{ 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"
@@ -221,7 +269,7 @@ function resetFilters() {
部分形态生图失败已生成的图片保留先查看失败原因再按形态重新生图
</NAlert>
<NAlert
v-else-if="session.receipt.title === '刷新过期人物形态图'"
v-else-if="session.receipt.title === '刷新过期形态图'"
type="info"
:show-icon="false"
class="mt-3 text-xs"
@@ -322,13 +370,14 @@ function resetFilters() {
:show-icon="false"
class="mt-3 text-xs leading-6"
>
当前主图由旧演员母版生成后续分镜已禁止继续引用请基于当前 Identity Anchor
当前主图未能匹配最新已锁定母版请基于当前 Identity Anchor
重新生成候选图并在确认后设置为新的主参考图
</NAlert>
<p v-if="form.images[0]?.status === 'failed'" class="mt-2 line-clamp-2 text-xs text-danger">
{{ form.images[0].error }}
</p>
<div class="mt-4 flex flex-wrap items-center justify-between gap-2">
<NButton text size="small" @click="openPrompt(form)">提示词与生成</NButton>
<NButton @click="openGallery(form)" text size="small">查看图片与记录</NButton>
<NButton :disabled="blocked" @click="openGenerate(form)"
><ImagePlus :size="14" />
@@ -357,7 +406,11 @@ function resetFilters() {
<RouterLink v-else :to="`/projects/${id}/breakdown`" class="button button-secondary"
>前往剧本拆解</RouterLink
> </EmptyState
><GenerateImageDialog
><FormPromptDialog
v-model:open="promptOpen"
:form="promptForm"
:disabled="blocked || !promptForm"
@generate="generatePrompt" /><GenerateImageDialog
v-model:open="generateOpen"
:form="generateForm"
:disabled="blocked || !generateForm"