feat: 同步质量校验修复并精简工作台操作

This commit is contained in:
GouJ
2026-09-03 19:38:57 +08:00
parent 771b66cafc
commit 13bb6dc83b
47 changed files with 2244 additions and 260 deletions
@@ -298,7 +298,7 @@ watch(
!casting.missingAnchor
"
acknowledgement
description="最多按本批上限为缺少 Anchor 的 Character 调用 Seedream,各自独立生成一张停用的 primary 候选,不复用旧身份母版。已有候选、已有母版或已完成选角的角色不会重复处理。"
description="最多按本批上限为缺少 Anchor 的 Character 调用 图片模型,各自独立生成一张停用的 primary 候选,不复用旧身份母版。已有候选、已有母版或已完成选角的角色不会重复处理。"
@confirm="generateCastingCandidates"
/>
</div>
@@ -559,7 +559,7 @@ watch(
? '演员身份已确认并锁定,可用于形态图与镜头首帧。'
: '已有演员母版但 Identity 尚未锁定,镜头首帧仍会被阻塞。'
: subject.module === 'prop'
? '道具形态生图暂不自动引用此母版。'
? '锁定后,后续道具形态图会继承当前母版。'
: '后续场景形态图会继承此图的空间结构。'
}}
</p>
@@ -28,7 +28,6 @@ watch([() => props.subjectId, open], reset)
function submit() {
if (props.disabled || !valid.value || !acknowledged.value) return
emit('generate', {
provider: 'seedream',
...(prompt.value.trim() ? { prompt: prompt.value.trim() } : {}),
...(width.value !== '' && height.value !== '' ? { width: width.value, height: height.value } : {})
})
@@ -82,7 +81,7 @@ function submit() {
id="casting-candidate-cost"
v-model:checked="acknowledged"
class="flex items-start gap-2 text-xs leading-6"
>我已确认调用 Seedream 可能产生费用且当前没有同项目生成任务
>我已确认调用图片模型可能产生费用且当前没有同项目生成任务
</NCheckbox>
<div class="dialog-footer">
<NButton @click="open = false">取消</NButton>
@@ -1,4 +1,5 @@
<script setup lang="ts">
import DetailDisclosure from '../../../components/ui/DetailDisclosure.vue'
import { NButton, NCheckbox, NInput } from 'naive-ui'
import { computed, reactive, ref, watch } from 'vue'
import type { SaveIdentityInput, SubjectIdentity } from '../types'
@@ -57,26 +58,27 @@ function save() {
:autosize="{ minRows: 3, maxRows: 12 }"
></NInput>
</label>
<p class="text-xs leading-6 text-muted">
{{
module === 'prop'
? '保留道具固定文字、编号、部件位置、结构与材质组合;不要泛化原有辨识细节,也不要写临时损伤或摆放状态。'
: module === 'scene'
? '保留建筑骨架、入口、窗户、楼梯与固定布局,不写临时天气、人群或灯光。'
: '保留有依据的五官、骨相、发际线与体型,不固定某套服装、临时表情或伤势。'
}}
这里保存身份事实背景视角和参考图约束由后端另行编译不是最终整段生图 Prompt
</p>
<DetailDisclosure title="身份填写与锁定规则"
><p class="text-xs leading-6 text-muted">
{{
module === 'prop'
? '保留道具固定文字、编号、部件位置、结构与材质组合;不要泛化原有辨识细节,也不要写临时损伤或摆放状态。'
: module === 'scene'
? '保留建筑骨架、入口、窗户、楼梯与固定布局,不写临时天气、人群或灯光。'
: '保留有依据的五官、骨相、发际线与体型,不固定某套服装、临时表情或伤势。'
}}
这里保存身份事实背景视角和参考图约束由后端另行编译不是最终整段生图 Prompt
</p>
<p class="mt-2 text-xs text-muted">
人物需要同时确认母版并锁定身份首帧才能就绪锁定后仍可人工编辑生成图片及切换母版
</p></DetailDisclosure
>
<NCheckbox
:disabled="disabled"
id="identity-lock"
v-model:checked="form.isLocked"
class="flex items-start gap-2 text-xs leading-6"
>{{
module === 'character'
? '确认并锁定演员身份。Character 必须同时具有身份母版并锁定 Identity,首帧才能进入就绪状态。'
: '确认并锁定身份文本。锁定后仍可人工保存、生成图片及切换母版。'
}}</NCheckbox
>{{ module === 'character' ? '确认并锁定演员身份' : '确认并锁定身份文本' }}</NCheckbox
>
<div class="flex flex-wrap items-center justify-between gap-3">
<span class="text-xs text-muted">{{
@@ -45,11 +45,10 @@ watch(
{ immediate: true }
)
/** 提交显式的 Seedream 参数;省略空参考 ID 以保留后端自动选择逻辑。 */
/** 提交生图参数;省略空参考 ID 以保留后端自动选择逻辑。 */
function submit() {
if (props.disabled || !valid.value || !acknowledged.value) return
emit('generate', {
provider: 'seedream',
viewType: viewType.value,
...(referenceImageId.value ? { referenceImageId: referenceImageId.value } : {}),
...(prompt.value.trim() ? { prompt: prompt.value.trim() } : {}),
@@ -63,7 +62,7 @@ function submit() {
<AppDialog v-model:open="open" title="生成身份参考图" :description="subjectName">
<form class="mt-5 space-y-4" @submit.prevent="submit">
<NAlert type="info" :show-icon="false" class="text-xs">
调用 Seedream每次新增一张图片可能产生费用第一张成功的 primary 图自动成为母版已有母版时
调用 图片模型每次新增一张图片可能产生费用第一张成功的 primary 图自动成为母版已有母版时
primary 图仅作为停用候选保留
</NAlert>
<label class="block"
@@ -292,7 +292,6 @@ describe('身份图与母版契约', () => {
expect(wrapper.emitted('generate')).toEqual([
[
{
provider: 'seedream',
viewType: 'three-quarter',
referenceImageId: 'identity-image-1',
width: 2048,
@@ -379,7 +378,7 @@ describe('身份图与母版契约', () => {
expect(wrapper.emitted('anchor')).toEqual([['identity-image-1']])
})
it('选角候选固定发送 Seedream,不携带普通身份图视角字段', async () => {
it('选角候选不再发送 Provider,不携带普通身份图视角字段', async () => {
wrapper = mount(CastingCandidateDialog, {
attachTo: document.body,
props: { open: true, subjectId: 'subject-1', subjectName: '林夏', disabled: false }
@@ -389,7 +388,7 @@ describe('身份图与母版契约', () => {
document.querySelector<HTMLInputElement>('#casting-candidate-cost')!.click()
await flushPromises()
button('确认生成候选').click()
expect(wrapper.emitted('generate')).toEqual([[{ provider: 'seedream' }]])
expect(wrapper.emitted('generate')).toEqual([[{}]])
})
it('角色选角接口区分批量身份、候选生图与确认事务', async () => {
@@ -411,7 +410,7 @@ describe('身份图与母版契约', () => {
)
vi.stubGlobal('fetch', fetcher)
await subjectIdentityApi.generateCharacters('project/1', { force: false, concurrency: 2 })
await subjectIdentityApi.generateCastingCandidate('subject/1', { provider: 'seedream' })
await subjectIdentityApi.generateCastingCandidate('subject/1', {})
await subjectIdentityApi.confirmCasting('subject/1', 'image/1')
expect(fetcher.mock.calls.map(([url]) => url)).toEqual([
'/api/projects/project%2F1/character-identities/generate',
-3
View File
@@ -46,7 +46,6 @@ export type SaveIdentityInput = Partial<Pick<SubjectIdentity, 'description' | 'g
/** 单张身份生图;空 referenceImageId 应省略,由后端自动引用当前母版。 */
export interface GenerateIdentityImageInput {
provider: 'seedream'
viewType: IdentityViewType
referenceImageId?: string
prompt?: string
@@ -56,7 +55,6 @@ export interface GenerateIdentityImageInput {
/** Character 选角候选固定独立生成 primary,不复用旧 Anchor,也不会自动成为新 Anchor。 */
export interface GenerateCastingCandidateInput {
provider: 'seedream'
prompt?: string
width?: number
height?: number
@@ -110,7 +108,6 @@ export interface CharacterCastingReadiness {
/** 小批量生成选角候选的配置。 */
export interface GenerateCastingCandidatesInput {
provider: 'seedream'
limit: number
concurrency: number
width?: number
@@ -183,7 +183,6 @@ export function useSubjectIdentity() {
kind: 'casting',
title,
result: await subjectIdentityApi.generateCastingCandidates(id, {
provider: 'seedream',
limit: candidateLimit.value,
concurrency: concurrency.value
})