feat: 功能优化

This commit is contained in:
2026-02-04 01:46:55 +08:00
parent 0e1b6fe643
commit b1bd711cea
6 changed files with 300 additions and 495 deletions

View File

@@ -1,19 +1,21 @@
/**
* @fileoverview useDigitalHumanGeneration Hook - 数字人生成逻辑
*
* 重构后:不管理识别状态,只提供数据和操作方法
* 状态由 Pipeline 统一管理
*/
import { ref, computed } from 'vue'
import { message } from 'ant-design-vue'
import type {
UseDigitalHumanGeneration,
VideoState,
IdentifyState,
IdentifyResult,
Video,
} from '../types/identify-face'
import { identifyUploadedVideo } from '@/api/kling'
import { useUpload } from '@/composables/useUpload'
export function useDigitalHumanGeneration(): UseDigitalHumanGeneration {
export function useDigitalHumanGeneration() {
// ========== 状态 ==========
const videoState = ref<VideoState>({
uploadedVideo: '',
@@ -25,9 +27,8 @@ export function useDigitalHumanGeneration(): UseDigitalHumanGeneration {
selectorVisible: false,
})
const identifyState = ref<IdentifyState>({
identifying: false,
identified: false,
// 识别结果数据(不含状态标志)
const identifyResult = ref<IdentifyResult>({
sessionId: '',
faceId: '',
faceStartTime: 0,
@@ -39,7 +40,15 @@ export function useDigitalHumanGeneration(): UseDigitalHumanGeneration {
// ========== 计算属性 ==========
const faceDuration = computed(function() {
return identifyState.value.faceEndTime - identifyState.value.faceStartTime
return identifyResult.value.faceEndTime - identifyResult.value.faceStartTime
})
const hasVideo = computed(function() {
return !!videoState.value.uploadedVideo || !!videoState.value.selectedVideo
})
const isIdentified = computed(function() {
return !!identifyResult.value.sessionId
})
// ========== 方法 ==========
@@ -55,7 +64,7 @@ export function useDigitalHumanGeneration(): UseDigitalHumanGeneration {
videoState.value.selectedVideo = null
videoState.value.previewVideoUrl = ''
videoState.value.videoSource = 'upload'
resetIdentifyState()
resetIdentifyResult()
}
async function handleVideoSelect(video: Video): Promise<void> {
@@ -64,67 +73,65 @@ export function useDigitalHumanGeneration(): UseDigitalHumanGeneration {
videoState.value.videoFile = null
videoState.value.videoSource = 'select'
videoState.value.selectorVisible = false
resetIdentifyState()
identifyState.value.videoFileId = video.fileId
resetIdentifyResult()
identifyResult.value.videoFileId = video.fileId
}
async function performFaceRecognition(): Promise<void> {
/**
* 执行人脸识别
* 返回识别结果供 Pipeline 使用
*/
async function performFaceRecognition(): Promise<IdentifyResult> {
const hasUploadFile = videoState.value.videoFile
const hasSelectedVideo = videoState.value.selectedVideo
if (!hasUploadFile && !hasSelectedVideo) return
if (!hasUploadFile && !hasSelectedVideo) {
throw new Error('请先选择视频')
}
identifyState.value.identifying = true
try {
if (hasSelectedVideo) {
const res = await identifyUploadedVideo(hasSelectedVideo) as { success: boolean; data: { sessionId: string; faceId: string | null; startTime: number; endTime: number } }
identifyState.value.videoFileId = hasSelectedVideo.fileId
identifyState.value.sessionId = res.data.sessionId
identifyState.value.faceId = res.data.faceId || ''
identifyState.value.faceStartTime = res.data.startTime || 0
identifyState.value.faceEndTime = res.data.endTime || 0
} else {
const file = hasUploadFile!
let coverBase64 = null
try {
const { extractVideoCover } = await import('@/utils/video-cover')
const cover = await extractVideoCover(file, { maxWidth: 800, quality: 0.8 })
coverBase64 = cover.base64
} catch {
// 封面提取失败不影响主流程
}
const fileId = await upload(file, {
fileCategory: 'video',
groupId: null,
coverBase64,
onStart: function() {},
onProgress: function() {},
onSuccess: function() {},
onError: function(err: Error) {
message.error(err.message || '上传失败')
}
})
identifyState.value.videoFileId = fileId
identifyState.value.sessionId = ''
identifyState.value.faceId = ''
identifyState.value.faceStartTime = 0
identifyState.value.faceEndTime = 0
if (hasSelectedVideo) {
const res = await identifyUploadedVideo(hasSelectedVideo) as {
success: boolean;
data: { sessionId: string; faceId: string | null; startTime: number; endTime: number }
}
identifyResult.value.videoFileId = hasSelectedVideo.fileId
identifyResult.value.sessionId = res.data.sessionId
identifyResult.value.faceId = res.data.faceId || ''
identifyResult.value.faceStartTime = res.data.startTime || 0
identifyResult.value.faceEndTime = res.data.endTime || 0
} else {
const file = hasUploadFile!
let coverBase64 = null
try {
const { extractVideoCover } = await import('@/utils/video-cover')
const cover = await extractVideoCover(file, { maxWidth: 800, quality: 0.8 })
coverBase64 = cover.base64
} catch {
// 封面提取失败不影响主流程
}
identifyState.value.identified = true
const fileId = await upload(file, {
fileCategory: 'video',
groupId: null,
coverBase64,
onStart: function() {},
onProgress: function() {},
onSuccess: function() {},
onError: function(err: Error) {
message.error(err.message || '上传失败')
}
})
// 识别完成,不显示提示信息
} catch (error: unknown) {
const err = error as Error
message.error(err.message || '识别失败')
throw error
} finally {
identifyState.value.identifying = false
identifyResult.value.videoFileId = fileId
// 上传后需要再调用识别接口获取人脸信息
// 暂时清空,等待后续识别
identifyResult.value.sessionId = ''
identifyResult.value.faceId = ''
identifyResult.value.faceStartTime = 0
identifyResult.value.faceEndTime = 0
}
return { ...identifyResult.value }
}
function resetVideoState(): void {
@@ -135,7 +142,7 @@ export function useDigitalHumanGeneration(): UseDigitalHumanGeneration {
videoState.value.videoSource = null
videoState.value.previewVideoUrl = ''
videoState.value.selectorVisible = false
resetIdentifyState()
resetIdentifyResult()
}
function getVideoPreviewUrl(video: Video): string {
@@ -149,22 +156,23 @@ export function useDigitalHumanGeneration(): UseDigitalHumanGeneration {
return 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjExMCIgdmlld0JveD0iMCAwIDIwMCAxMTAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTEwIiBmaWxsPSIjMzc0MTUxIi8+CjxwYXRoIGQ9Ik04NSA0NUwxMTUgNjVMMTA1IDg1TDc1IDc1TDg1IDQ1WiIgZmlsbD0iIzU3MjY1MSIvPgo8L3N2Zz4K'
}
function resetIdentifyState(): void {
identifyState.value.identified = false
identifyState.value.sessionId = ''
identifyState.value.faceId = ''
identifyState.value.videoFileId = null
function resetIdentifyResult(): void {
identifyResult.value.sessionId = ''
identifyResult.value.faceId = ''
identifyResult.value.videoFileId = null
}
return {
videoState,
identifyState,
identifyResult,
hasVideo,
isIdentified,
faceDuration,
handleFileUpload,
handleVideoSelect,
performFaceRecognition,
resetVideoState,
resetIdentifyState,
resetIdentifyResult,
getVideoPreviewUrl,
}
}