feat: 补齐前端主流程并收紧工作台布局

统一工作台紧凑头部与形态图库间距,修复主体身份布局,并接入正式严格视频质量流水线。
This commit is contained in:
GouJ
2026-09-08 09:25:14 +08:00
committed by GitHub
parent 0f8b2074e4
commit 4edb0ff6cc
25 changed files with 355 additions and 233 deletions
@@ -27,7 +27,7 @@ import ActionMenu from '../../../components/ui/ActionMenu.vue'
import { AppDialog } from '../../../components/ui'
import ModelCapabilities from './ModelCapabilities.vue'
import type { QualityTarget } from '../quality.types'
import { savedVideoValidation, videoRepairInfo } from '../quality'
import { savedVideoValidation, videoQualityInfo, videoRepairInfo } from '../quality'
/** 单镜头生产面板只负责正式资产,不修改上游导演设计与即时状态。 */
const props = defineProps<{
@@ -162,14 +162,17 @@ async function setPrimaryKeyframe() {
await refreshAll()
}
/** 创建单镜头视频模型视频任务;任务完成由后台轮询确认。 */
/** 创建单镜头严格质量候选;后端完成生成后自动校验、有限修复并按结果晋升。 */
async function generateVideo() {
if (!canGenerateVideo.value) return
const shotId = props.shot.shotId
await runOperation(props.projectId, `提交镜头 ${props.shot.shotNo} 视频任务`, async () => {
const result = await productionApi.generateVideo(shotId)
if (!result || result.shotId !== shotId || !isActiveVideo(result))
throw new Error('接口未返回有效的视频生成任务。')
await runOperation(props.projectId, `提交镜头 ${props.shot.shotNo} 视频质量任务`, async () => {
const result = await productionApi.generateQualityVideo(shotId, {
maxRepairAttempts: 2,
allowedTexts: []
})
if (!result || result.shotId !== shotId || !result.candidate || !isActiveVideo(result.candidate))
throw new Error('接口未返回有效的视频质量候选任务。')
})
await refreshAll()
}
@@ -190,7 +193,12 @@ async function setPrimaryVideo() {
const videoId = confirmingVideo.value
if (assetBlocked.value || !videoId) return
const video = videos.value.find(item => item.id === videoId)
if (!video || (videoRepairInfo(video.rawJson) && savedVideoValidation(video.rawJson)?.passed !== true)) return
if (
!video ||
((videoRepairInfo(video.rawJson) || videoQualityInfo(video.rawJson)) &&
savedVideoValidation(video.rawJson)?.passed !== true)
)
return
const shotId = props.shot.shotId
confirmingVideo.value = ''
await runOperation(props.projectId, `设置镜头 ${props.shot.shotNo} 主视频`, async () => {
@@ -396,6 +404,19 @@ async function inspectSpecs() {
<NTag v-if="videoRepairInfo(item.rawJson) && !item.isPrimary" size="small" :bordered="false"
>修复候选 · 待复检</NTag
>
<NTag
v-else-if="videoQualityInfo(item.rawJson) && !item.isPrimary"
size="small"
:bordered="false"
>质量候选 ·
{{
savedVideoValidation(item.rawJson)
? savedVideoValidation(item.rawJson)?.passed
? '已通过'
: '未通过'
: '自动校验中'
}}</NTag
>
<NTag v-if="item.isPrimary && videoStale" size="small" :bordered="false">已过期</NTag>
<span class="text-[10px] text-muted"
>{{ item.durationSeconds || shot.durationSeconds || '—' }}s</span
@@ -430,7 +451,7 @@ async function inspectSpecs() {
item.status === 'completed' &&
item.videoUrl &&
!item.isPrimary &&
(!videoRepairInfo(item.rawJson) ||
(!(videoRepairInfo(item.rawJson) || videoQualityInfo(item.rawJson)) ||
savedVideoValidation(item.rawJson)?.passed === true)
"
:disabled="assetBlocked"
@@ -454,15 +475,17 @@ async function inspectSpecs() {
<p v-else class="mt-4 text-xs text-muted">尚无视频任务记录。</p>
<div class="mt-4 flex flex-wrap gap-3">
<ConfirmAction
:label="videoStale ? '重新生成主视频' : currentVideo ? '再生成一个视频候选' : '提交本镜视频任务'"
:label="
videoStale ? '重新生成质量视频' : currentVideo ? '再生成一个质量候选' : '提交本镜视频质量任务'
"
:disabled="!canGenerateVideo"
acknowledgement
:description="
videoStale
? '使用当前视频提示词、主首帧和主体参考图重新创建视频模型任务;完成后会自动接替当前过期主视频。'
? '使用当前视频提示词、主首帧和主体参考图启动严格质量流水线;最多自动修复 2 次,通过后才接替当前过期主视频。'
: currentVideo
? '使用当前视频提示词、主首帧和主体参考图新增一个视频模型视频候选,不自动替换主视频。'
: '使用当前视频提示词、主首帧和主体参考图创建视频模型任务;任务在后台继续运行。'
? '新增一个严格质量候选;后端会自动校验并最多修复 2 次,通过后才替换主视频。'
: '启动严格视频质量流水线;任务在后台生成、校验并有限修复,通过后才晋升主视频。'
"
primary
@confirm="generateVideo"