feat: 同步角色选角与首帧身份约束

This commit is contained in:
GouJ
2026-08-31 19:14:58 +08:00
parent aeb3ce2986
commit 53917268d3
20 changed files with 940 additions and 112 deletions
@@ -75,6 +75,12 @@ const videoRunning = computed(
(query.data.value?.videoStatus.queued ?? 0) +
(query.data.value?.videoStatus.running ?? 0)
)
const identityBlocked = computed(
() =>
(query.data.value?.keyframes.missingIdentity ?? 0) +
(query.data.value?.keyframes.missingIdentityAnchor ?? 0) +
(query.data.value?.keyframes.identityUnlocked ?? 0)
)
/** 父页查询包含整个项目的就绪统计,单镜头变更后立即刷新。 */
function refreshProject() {
@@ -149,6 +155,11 @@ function refreshProject() {
<p v-if="videoRunning" class="alert mt-4" role="status">
当前有 {{ videoRunning }} 个视频任务等待或生成中后端正在轮询 Provider同一镜头不会重复提交活动任务
</p>
<p v-if="identityBlocked" class="alert mt-4">
当前有 {{ identityBlocked }} 项角色身份前置问题Character 必须生成
Identity确认演员母版并锁定后首帧才能就绪
<RouterLink :to="`/projects/${id}/subject-identity`" class="text-button ml-2">前往角色选角 </RouterLink>
</p>
<ProductionReceipt v-if="session.receipt" :receipt="session.receipt" />
<div v-if="query.data.value" class="production-pipeline mt-5">
@@ -191,6 +202,11 @@ function refreshProject() {
<dd>{{ stage.data?.blocked ?? 0 }}</dd>
</div>
</dl>
<p v-if="stage.key === 'keyframes' && identityBlocked" class="mt-3 text-[11px] leading-5 text-danger">
身份缺失 {{ query.data.value?.keyframes.missingIdentity ?? 0 }} · 母版缺失
{{ query.data.value?.keyframes.missingIdentityAnchor ?? 0 }} · 未锁定
{{ query.data.value?.keyframes.identityUnlocked ?? 0 }}
</p>
<ConfirmAction
class="mt-4"
:label="stage.action"
@@ -387,6 +387,16 @@ async function inspectSpecs() {
<div v-if="keyframeSpec">
<h5 class="font-medium">首帧 Prompt · {{ keyframeSpec.references.length }} 张参考图</h5>
<p class="mt-3 whitespace-pre-wrap leading-6 text-muted">{{ keyframeSpec.prompt }}</p>
<ul v-if="keyframeSpec.references.length" class="mt-3 space-y-1 text-[11px] text-muted">
<li
v-for="reference in keyframeSpec.references"
:key="`${reference.role}:${reference.imageId}`"
>
{{ reference.subjectRef }} ·
{{ reference.role === 'identity-anchor' ? '演员身份母版' : '形态主图' }} ·
<code>{{ reference.imageId }}</code>
</li>
</ul>
</div>
<div v-if="videoSpec">
<h5 class="font-medium">视频 Prompt · {{ videoSpec.durationSeconds }}s</h5>
+3
View File
@@ -43,6 +43,9 @@ export function issueLabel(code: ProductionIssueCode): string {
invalid_generation_spec: '生成规格不完整',
missing_visual_style: '缺少视觉风格',
missing_reference: '缺少主体参考图',
missing_identity: '缺少角色身份',
missing_identity_anchor: '缺少演员母版',
identity_unlocked: '演员身份未锁定',
missing_prompt: '缺少视频提示词',
missing_keyframe: '缺少主首帧'
}
@@ -51,6 +51,8 @@ describe('镜头生产数据契约', () => {
it('就绪问题和异步任务状态提供中文标签', () => {
expect(issueLabel('missing_keyframe')).toBe('缺少主首帧')
expect(issueLabel('invalid_generation_spec')).toBe('生成规格不完整')
expect(issueLabel('missing_identity_anchor')).toBe('缺少演员母版')
expect(issueLabel('identity_unlocked')).toBe('演员身份未锁定')
expect(productionStatusLabel('in_progress')).toBe('任务进行中')
expect(productionStatusLabel('unknown')).toBe('unknown')
})
+18 -2
View File
@@ -6,6 +6,9 @@ export type ProductionIssueCode =
| 'invalid_generation_spec'
| 'missing_visual_style'
| 'missing_reference'
| 'missing_identity'
| 'missing_identity_anchor'
| 'identity_unlocked'
| 'missing_prompt'
| 'missing_keyframe'
@@ -37,12 +40,17 @@ export interface PromptReadiness {
/** 镜头首帧就绪检查的逐镜结果。 */
export interface KeyframeReadinessItem extends PromptReadinessItem {
episodeNo: number
beatNo: number
primaryKeyframeId?: string | null
}
/** 镜头首帧项目级就绪汇总。 */
export interface KeyframeReadiness extends PromptReadiness {
missingVisualStyle: number
missingIdentity: number
missingIdentityAnchor: number
identityUnlocked: number
items: KeyframeReadinessItem[]
}
@@ -111,7 +119,8 @@ export interface KeyframeReference {
subjectId: string
subjectRef: string
module: string
subjectFormId: string
role: 'identity-anchor' | 'form-primary'
subjectFormId?: string
imageId: string
imageUrl: string
providerImageUrl?: string
@@ -129,9 +138,16 @@ export interface KeyframeSpec {
}
/** 视频生成规格中的主体参考图。 */
export interface VideoReference extends KeyframeReference {
export interface VideoReference {
subjectId: string
subjectRef: string
subjectName: string
module: string
subjectFormId: string
subjectFormName: string
imageId: string
imageUrl: string
providerImageUrl?: string
}
/** 后端实际提交 Seedance 前编译的完整视频生成规格。 */