feat(digital-human): 对口型生成弹窗增加计时器
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m44s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m51s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m56s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 2m26s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m31s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m36s
AI Code Review / AI Code Review (pull_request) Successful in 6m43s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 8m32s
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 2s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 9m41s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m44s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m51s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m56s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 2m26s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 3m31s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 3m36s
AI Code Review / AI Code Review (pull_request) Successful in 6m43s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 8m32s
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 2s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 9m41s
- 对口型生成弹窗loading状态显示已用时(MM:SS格式,每秒更新) - 使用useState+useEffect+setInterval实现计时,组件卸载时clearInterval防内存泄漏 - 取消生成或生成完成/失败时停止并重置计时器 - 样式与现有灰色提示文字风格一致
This commit is contained in:
@@ -61,6 +61,9 @@ const AiAvatarPage: React.FC = () => {
|
||||
"generating",
|
||||
)
|
||||
const [lipsyncErrorMessage, setLipsyncErrorMessage] = useState("")
|
||||
const [lipsyncElapsed, setLipsyncElapsed] = useState(0)
|
||||
/* ── 对口型计时器 ── */
|
||||
const lipsyncElapsedTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
/* ── 渲染进度弹窗 ── */
|
||||
const [showRenderModal, setShowRenderModal] = useState(false)
|
||||
const [renderStatus, setRenderStatus] = useState<"generating" | "completed" | "failed">(
|
||||
@@ -80,6 +83,30 @@ const AiAvatarPage: React.FC = () => {
|
||||
/* ── 渲染进度轮询 ── */
|
||||
const renderTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
/* ── 对口型计时器工具 ── */
|
||||
const _formatElapsed = useCallback((seconds: number) => {
|
||||
const m = Math.floor(seconds / 60)
|
||||
const s = seconds % 60
|
||||
return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`
|
||||
}, [])
|
||||
|
||||
const _startLipsyncElapsedTimer = useCallback(() => {
|
||||
if (lipsyncElapsedTimerRef.current) {
|
||||
clearInterval(lipsyncElapsedTimerRef.current)
|
||||
}
|
||||
setLipsyncElapsed(0)
|
||||
lipsyncElapsedTimerRef.current = setInterval(() => {
|
||||
setLipsyncElapsed((prev) => prev + 1)
|
||||
}, 1000)
|
||||
}, [])
|
||||
|
||||
const _stopLipsyncElapsedTimer = useCallback(() => {
|
||||
if (lipsyncElapsedTimerRef.current) {
|
||||
clearInterval(lipsyncElapsedTimerRef.current)
|
||||
lipsyncElapsedTimerRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
const togglePanel = useCallback((key: PanelKey) => {
|
||||
setCollapsed((prev) => ({ ...prev, [key]: !prev[key] }))
|
||||
}, [])
|
||||
@@ -222,6 +249,7 @@ const AiAvatarPage: React.FC = () => {
|
||||
setShowLipsyncModal(true)
|
||||
setLipsyncStatus("generating")
|
||||
setLipsyncErrorMessage("")
|
||||
_startLipsyncElapsedTimer()
|
||||
|
||||
const asset = await getAssetById(video.id)
|
||||
const videoUrl = asset?.file_url
|
||||
@@ -265,6 +293,7 @@ const AiAvatarPage: React.FC = () => {
|
||||
state.setLipsyncJob(updated)
|
||||
if (updated.status === "completed") {
|
||||
if (lipsyncTimerRef.current) clearInterval(lipsyncTimerRef.current)
|
||||
_stopLipsyncElapsedTimer()
|
||||
setLipsyncStatus("completed")
|
||||
setTimeout(() => {
|
||||
setShowLipsyncModal(false)
|
||||
@@ -272,6 +301,7 @@ const AiAvatarPage: React.FC = () => {
|
||||
}, 1000)
|
||||
} else if (updated.status === "failed") {
|
||||
if (lipsyncTimerRef.current) clearInterval(lipsyncTimerRef.current)
|
||||
_stopLipsyncElapsedTimer()
|
||||
setLipsyncStatus("failed")
|
||||
setLipsyncErrorMessage(updated.error_message || "对口型生成失败")
|
||||
}
|
||||
@@ -285,6 +315,8 @@ const AiAvatarPage: React.FC = () => {
|
||||
data: (err as { response?: { data?: unknown } })?.response?.data,
|
||||
message: err instanceof Error ? err.message : String(err),
|
||||
})
|
||||
_stopLipsyncElapsedTimer()
|
||||
setLipsyncElapsed(0)
|
||||
setShowLipsyncModal(false)
|
||||
message.error(err instanceof Error ? err.message : "对口型任务提交失败,请重试")
|
||||
}
|
||||
@@ -297,6 +329,8 @@ const AiAvatarPage: React.FC = () => {
|
||||
|
||||
state.style,
|
||||
state.ttsPreview,
|
||||
_startLipsyncElapsedTimer,
|
||||
_stopLipsyncElapsedTimer,
|
||||
])
|
||||
|
||||
// 取消对口型生成
|
||||
@@ -305,16 +339,19 @@ const AiAvatarPage: React.FC = () => {
|
||||
clearInterval(lipsyncTimerRef.current)
|
||||
lipsyncTimerRef.current = null
|
||||
}
|
||||
_stopLipsyncElapsedTimer()
|
||||
setLipsyncElapsed(0)
|
||||
setShowLipsyncModal(false)
|
||||
setLipsyncStatus("generating")
|
||||
setLipsyncErrorMessage("")
|
||||
}, [])
|
||||
}, [_stopLipsyncElapsedTimer])
|
||||
|
||||
// 清理轮询
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (lipsyncTimerRef.current) clearInterval(lipsyncTimerRef.current)
|
||||
if (renderTimerRef.current) clearInterval(renderTimerRef.current)
|
||||
if (lipsyncElapsedTimerRef.current) clearInterval(lipsyncElapsedTimerRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -964,6 +1001,9 @@ const AiAvatarPage: React.FC = () => {
|
||||
对口型视频生成中…
|
||||
</div>
|
||||
<div style={{ marginTop: 8, fontSize: 13, color: "#8c8ca1" }}>
|
||||
已用时:{_formatElapsed(lipsyncElapsed)}
|
||||
</div>
|
||||
<div style={{ marginTop: 4, fontSize: 13, color: "#8c8ca1" }}>
|
||||
请勿关闭页面,完成后将自动提示
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user