fix: v0.10.2 — admin重新编辑隐藏/frozen防负数/进度条刷新保持/navigate修正
- admin资产页视频详情隐藏「重新编辑」按钮(hideReEdit prop)
- 团管重新编辑跳转修正:navigate('/') → navigate('/app')
- _release_freeze 防止 frozen_amount 变负数
- 生成进度条用 sessionStorage 持久化,刷新页面后从之前位置继续
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ef2212e345
commit
699a390f45
@ -320,7 +320,10 @@ def _release_freeze(record):
|
|||||||
frozen = record.frozen_amount
|
frozen = record.frozen_amount
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
locked_team = Team.objects.select_for_update().get(pk=team.pk)
|
locked_team = Team.objects.select_for_update().get(pk=team.pk)
|
||||||
locked_team.frozen_amount = F('frozen_amount') - frozen
|
# 防止 frozen_amount 变负
|
||||||
|
actual_release = min(frozen, locked_team.frozen_amount)
|
||||||
|
if actual_release > 0:
|
||||||
|
locked_team.frozen_amount = F('frozen_amount') - actual_release
|
||||||
locked_team.total_seconds_used = F('total_seconds_used') - record.seconds_consumed
|
locked_team.total_seconds_used = F('total_seconds_used') - record.seconds_consumed
|
||||||
locked_team.save(update_fields=['frozen_amount', 'total_seconds_used'])
|
locked_team.save(update_fields=['frozen_amount', 'total_seconds_used'])
|
||||||
record.frozen_amount = 0
|
record.frozen_amount = 0
|
||||||
|
|||||||
@ -13,13 +13,14 @@ interface Props {
|
|||||||
onReEdit?: (id: string) => void;
|
onReEdit?: (id: string) => void;
|
||||||
onRegenerate?: (id: string) => void;
|
onRegenerate?: (id: string) => void;
|
||||||
onDelete?: (id: string) => void;
|
onDelete?: (id: string) => void;
|
||||||
|
hideReEdit?: boolean;
|
||||||
onPrev?: () => void;
|
onPrev?: () => void;
|
||||||
onNext?: () => void;
|
onNext?: () => void;
|
||||||
hasPrev?: boolean;
|
hasPrev?: boolean;
|
||||||
hasNext?: boolean;
|
hasNext?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDelete, onPrev, onNext, hasPrev, hasNext }: Props) {
|
export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDelete, onPrev, onNext, hasPrev, hasNext, hideReEdit }: Props) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
const videoContainerRef = useRef<HTMLDivElement>(null);
|
const videoContainerRef = useRef<HTMLDivElement>(null);
|
||||||
@ -230,7 +231,7 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onClose();
|
onClose();
|
||||||
navigate('/');
|
navigate('/app');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -497,7 +498,7 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Re-edit button above info bar */}
|
{/* Re-edit button above info bar */}
|
||||||
<div style={{ padding: '0 20px 12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
|
{!hideReEdit && <div style={{ padding: '0 20px 12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>
|
||||||
<button className={styles.cardBtn} onClick={handleReEdit} style={{ width: '100%', justifyContent: 'center' }}>
|
<button className={styles.cardBtn} onClick={handleReEdit} style={{ width: '100%', justifyContent: 'center' }}>
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
||||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
|
||||||
@ -505,7 +506,7 @@ export function VideoDetailModal({ task, onClose, onReEdit, onRegenerate, onDele
|
|||||||
</svg>
|
</svg>
|
||||||
重新编辑
|
重新编辑
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>}
|
||||||
|
|
||||||
{/* Fixed bottom: info bar + actions card */}
|
{/* Fixed bottom: info bar + actions card */}
|
||||||
<div className={styles.infoPanelBottom}>
|
<div className={styles.infoPanelBottom}>
|
||||||
|
|||||||
@ -229,6 +229,7 @@ export function AdminAssetsPage() {
|
|||||||
<VideoDetailModal
|
<VideoDetailModal
|
||||||
task={detailTask}
|
task={detailTask}
|
||||||
onClose={() => setDetailTask(null)}
|
onClose={() => setDetailTask(null)}
|
||||||
|
hideReEdit
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -75,7 +75,7 @@ function backendToFrontend(bt: BackendTask): GenerationTask {
|
|||||||
duration: bt.duration as GenerationTask['duration'],
|
duration: bt.duration as GenerationTask['duration'],
|
||||||
references,
|
references,
|
||||||
status: mapStatus(bt.status),
|
status: mapStatus(bt.status),
|
||||||
progress: mapProgress(bt.status),
|
progress: bt.status === 'processing' ? Number(sessionStorage.getItem(`progress_${bt.task_id}`) || mapProgress(bt.status)) : mapProgress(bt.status),
|
||||||
resultUrl: bt.result_url || undefined,
|
resultUrl: bt.result_url || undefined,
|
||||||
errorMessage: mapErrorMessage(bt.error_message),
|
errorMessage: mapErrorMessage(bt.error_message),
|
||||||
createdAt: new Date(bt.created_at).getTime(),
|
createdAt: new Date(bt.created_at).getTime(),
|
||||||
@ -105,7 +105,9 @@ function ensureSmoothProgress() {
|
|||||||
if (t.status !== 'generating') return t;
|
if (t.status !== 'generating') return t;
|
||||||
// Decelerate: fast at start, slow near end
|
// Decelerate: fast at start, slow near end
|
||||||
const increment = t.progress < 30 ? 2 : t.progress < 60 ? 1 : 0.5;
|
const increment = t.progress < 30 ? 2 : t.progress < 60 ? 1 : 0.5;
|
||||||
return { ...t, progress: Math.min(t.progress + increment, 95) };
|
const newProgress = Math.min(t.progress + increment, 95);
|
||||||
|
if (t.taskId) sessionStorage.setItem(`progress_${t.taskId}`, String(newProgress));
|
||||||
|
return { ...t, progress: newProgress };
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
}, 2000);
|
}, 2000);
|
||||||
@ -146,6 +148,7 @@ function startPolling(taskId: string, frontendId: string) {
|
|||||||
|
|
||||||
if (newStatus === 'completed' || newStatus === 'failed') {
|
if (newStatus === 'completed' || newStatus === 'failed') {
|
||||||
pollTimers.delete(frontendId);
|
pollTimers.delete(frontendId);
|
||||||
|
sessionStorage.removeItem(`progress_${taskId}`);
|
||||||
if (newStatus === 'completed') {
|
if (newStatus === 'completed') {
|
||||||
useAuthStore.getState().fetchUserInfo();
|
useAuthStore.getState().fetchUserInfo();
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user