feat(short-drama-agent-front): 优化项目查询与工作区界面
This commit is contained in:
@@ -290,8 +290,9 @@ const videoRules = { videoLimit: integerRule('本批镜头上限'), videoRepairA
|
||||
:show-icon="false"
|
||||
class="mt-4"
|
||||
>
|
||||
当前有 {{ videoRunning }} 个视频任务等待或生成中。后端正在轮询
|
||||
Provider;同一镜头不会重复提交活动任务。
|
||||
当前有
|
||||
{{ videoRunning }}
|
||||
个视频任务等待或生成中。请手动刷新查看最新状态;同一镜头不会重复提交活动任务。
|
||||
</NAlert>
|
||||
<NAlert v-if="identityBlocked" type="info" :show-icon="false" class="mt-4">
|
||||
当前有 {{ identityBlocked }} 项角色身份前置问题。Character 必须生成
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Download } from '@lucide/vue'
|
||||
import { downloadText } from '../../../lib/format'
|
||||
import type { ProductionReceipt } from '../types'
|
||||
|
||||
/** 批量回执只描述本次请求,不能替代持续轮询的数据库状态。 */
|
||||
/** 批量回执只描述本次请求,不能替代显式刷新后的数据库状态。 */
|
||||
const props = defineProps<{ receipt: ProductionReceipt }>()
|
||||
|
||||
/** 导出完整回执,保留逐镜失败 ID 供后端排障。 */
|
||||
|
||||
@@ -4,7 +4,7 @@ import { NAlert, NButton, NCollapse, NCollapseItem, NTag } from 'naive-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ExternalLink, ImagePlus, RefreshCw } from '@lucide/vue'
|
||||
import { AssetImage, StatusBadge } from '../../../components/ui'
|
||||
import { usePolling } from '../../../composables/usePolling'
|
||||
import { useQuery } from '../../../composables/useQuery'
|
||||
import { mediaAssetUrl } from '../../../lib/assets'
|
||||
import { errorMessage } from '../../../lib/http'
|
||||
import { formatDate } from '../../../lib/format'
|
||||
@@ -41,20 +41,16 @@ const props = defineProps<{
|
||||
}>()
|
||||
const emit = defineEmits<{ changed: [] }>()
|
||||
const key = computed(() => props.shot.shotId)
|
||||
/** 单镜头资产已有刷新入口,不再定时轮询。 */
|
||||
const query = usePolling(
|
||||
key,
|
||||
async (shotId, signal) => {
|
||||
const [keyframes, videos] = await Promise.all([
|
||||
productionApi.listKeyframes(shotId, signal),
|
||||
productionApi.listVideos(shotId, signal)
|
||||
])
|
||||
if (keyframes.some(item => item.shotId !== shotId) || videos.some(item => item.shotId !== shotId))
|
||||
throw new Error('资产记录与当前镜头不匹配,请刷新后重试。')
|
||||
return { keyframes, videos }
|
||||
},
|
||||
false
|
||||
)
|
||||
/** 单镜头资产仅在进入、切换镜头或显式刷新时读取。 */
|
||||
const query = useQuery(key, async (shotId, signal) => {
|
||||
const [keyframes, videos] = await Promise.all([
|
||||
productionApi.listKeyframes(shotId, signal),
|
||||
productionApi.listVideos(shotId, signal)
|
||||
])
|
||||
if (keyframes.some(item => item.shotId !== shotId) || videos.some(item => item.shotId !== shotId))
|
||||
throw new Error('资产记录与当前镜头不匹配,请刷新后重试。')
|
||||
return { keyframes, videos }
|
||||
})
|
||||
const keyframes = computed(() => query.data.value?.keyframes ?? [])
|
||||
const videos = computed(() => query.data.value?.videos ?? [])
|
||||
const currentKeyframe = computed(() => primaryKeyframe(keyframes.value))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { computed, ref } from 'vue'
|
||||
import { usePolling } from '../../composables/usePolling'
|
||||
import { useQuery } from '../../composables/useQuery'
|
||||
import { useProjectContext } from '../projects/context'
|
||||
import { mergeDesignedShots } from '../storyboard/model'
|
||||
import { storyboardApi } from '../storyboard/api'
|
||||
@@ -45,24 +45,20 @@ export function useProduction() {
|
||||
)
|
||||
const queryKey = computed(() => JSON.stringify([id.value, episodeNo.value, force.value]))
|
||||
/** 生产页已有刷新按钮,就绪状态改为手动更新。 */
|
||||
const query = usePolling(
|
||||
queryKey,
|
||||
async (key, signal) => {
|
||||
const [projectId, number, overwrite] = JSON.parse(key) as [string, number, boolean]
|
||||
if (!projectId || !number) return null
|
||||
const [directions, prompts, keyframes, videos, videoStatus] = await Promise.all([
|
||||
storyboardApi.directions(projectId, number, signal),
|
||||
productionApi.promptReadiness(projectId, overwrite, signal),
|
||||
productionApi.keyframeReadiness(projectId, overwrite, signal),
|
||||
productionApi.videoReadiness(projectId, overwrite, signal),
|
||||
productionApi.projectVideoStatus(projectId, signal)
|
||||
])
|
||||
if (directions.projectId !== projectId || directions.episodeNo !== number)
|
||||
throw new Error('镜头生产查询返回了不匹配的项目或剧集,请刷新后重试。')
|
||||
return { directions, prompts, keyframes, videos, videoStatus }
|
||||
},
|
||||
false
|
||||
)
|
||||
const query = useQuery(queryKey, async (key, signal) => {
|
||||
const [projectId, number, overwrite] = JSON.parse(key) as [string, number, boolean]
|
||||
if (!projectId || !number) return null
|
||||
const [directions, prompts, keyframes, videos, videoStatus] = await Promise.all([
|
||||
storyboardApi.directions(projectId, number, signal),
|
||||
productionApi.promptReadiness(projectId, overwrite, signal),
|
||||
productionApi.keyframeReadiness(projectId, overwrite, signal),
|
||||
productionApi.videoReadiness(projectId, overwrite, signal),
|
||||
productionApi.projectVideoStatus(projectId, signal)
|
||||
])
|
||||
if (directions.projectId !== projectId || directions.episodeNo !== number)
|
||||
throw new Error('镜头生产查询返回了不匹配的项目或剧集,请刷新后重试。')
|
||||
return { directions, prompts, keyframes, videos, videoStatus }
|
||||
})
|
||||
const source = computed(() => sourceEpisodes.value.find(item => item.episodeNo === episodeNo.value))
|
||||
const shots = computed(() => mergeDesignedShots(query.data.value?.directions ?? null, null, source.value))
|
||||
const shot = computed(() => shots.value.find(item => item.shotId === selectedShot.value) ?? shots.value[0])
|
||||
@@ -99,7 +95,7 @@ export function useProduction() {
|
||||
context.project.value?.status !== 'completed'
|
||||
)
|
||||
|
||||
/** 批量操作只向就绪镜头提交;最终资产状态由轮询查询确认。 */
|
||||
/** 批量操作只向就绪镜头提交;最终资产状态由显式刷新确认。 */
|
||||
async function run(command: ProductionCommand) {
|
||||
if (
|
||||
blocked.value ||
|
||||
|
||||
@@ -1,29 +1,16 @@
|
||||
import { computed, type MaybeRefOrGetter, type Ref } from 'vue'
|
||||
import { usePolling } from '../../composables/usePolling'
|
||||
import { computed, type Ref } from 'vue'
|
||||
import { useQuery } from '../../composables/useQuery'
|
||||
import { storyboardApi } from '../storyboard/api'
|
||||
|
||||
/** 仅查询已由当前项目列表确认的镜头,切换目标自动取消旧响应。 */
|
||||
export function useShotReferences(
|
||||
projectId: Ref<string>,
|
||||
shotId: Ref<string>,
|
||||
interval: MaybeRefOrGetter<number | false> = 6000
|
||||
) {
|
||||
export function useShotReferences(projectId: Ref<string>, shotId: Ref<string>) {
|
||||
const key = computed(() => JSON.stringify([projectId.value, shotId.value]))
|
||||
return usePolling(
|
||||
key,
|
||||
async (value, signal) => {
|
||||
const [project, shot] = JSON.parse(value) as [string, string]
|
||||
if (!project || !shot) return null
|
||||
const result = await storyboardApi.references(shot, signal)
|
||||
if (
|
||||
!result ||
|
||||
result.shotId !== shot ||
|
||||
!Array.isArray(result.references) ||
|
||||
!Array.isArray(result.missing)
|
||||
)
|
||||
throw new Error('镜头参考素材返回不匹配,请刷新后重试。')
|
||||
return result
|
||||
},
|
||||
interval
|
||||
)
|
||||
return useQuery(key, async (value, signal) => {
|
||||
const [project, shot] = JSON.parse(value) as [string, string]
|
||||
if (!project || !shot) return null
|
||||
const result = await storyboardApi.references(shot, signal)
|
||||
if (!result || result.shotId !== shot || !Array.isArray(result.references) || !Array.isArray(result.missing))
|
||||
throw new Error('镜头参考素材返回不匹配,请刷新后重试。')
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user