+
查看分片
+
+ 弹幕回放
+
@@ -544,6 +597,24 @@ onMounted(loadDetail);
+
+
+
+ 正在准备预览资源…
+ {{ danmakuPreviewMessage }}
+
+ 无法加载该分片的预览。
+
@@ -720,6 +791,17 @@ onMounted(loadDetail);
color: var(--text-secondary);
}
+.preview-empty {
+ display: grid;
+ place-items: center;
+ min-height: 260px;
+ padding: 24px;
+ border-radius: 12px;
+ border: 1px dashed var(--border-base);
+ color: var(--text-muted);
+ background: var(--surface-muted);
+}
+
@media (max-width: 768px) {
.header-actions {
width: 100%;
@@ -730,5 +812,10 @@ onMounted(loadDetail);
flex: 1 1 0;
margin: 0;
}
+
+ .preview-empty {
+ min-height: 180px;
+ padding: 18px;
+ }
}
diff --git a/frontend/src/views/RecordTaskDetailView.vue b/frontend/src/views/RecordTaskDetailView.vue
index fc35cdf..292b5c4 100644
--- a/frontend/src/views/RecordTaskDetailView.vue
+++ b/frontend/src/views/RecordTaskDetailView.vue
@@ -4,6 +4,8 @@ import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import apiClient, { getApiErrorMessage } from "@/api/client";
import { useViewport } from "@/composables/useViewport";
+import { useDanmakuPlayer } from "@/composables/useDanmakuPlayer";
+import DanmakuPlayer from "@/components/player/DanmakuPlayer.vue";
import type {
ManualSegmentCompletedTriggerResult,
RecordArtifactUploadItemResult,
@@ -35,6 +37,29 @@ const previewUrl = ref("");
const previewExpiresAt = ref("");
const previewMessage = ref("");
const { isMobile } = useViewport();
+
+// Danmaku replay state
+const { danmakuEvents, loading: danmakuLoading, error: danmakuError, loadTaskDanmaku, clear: clearDanmaku } = useDanmakuPlayer();
+const showDanmaku = ref(false);
+const danmakuLoaded = ref(false);
+
+async function toggleDanmaku() {
+ if (showDanmaku.value) {
+ showDanmaku.value = false;
+ return;
+ }
+
+ if (!danmakuLoaded.value) {
+ const hasEvents = await loadTaskDanmaku(props.id);
+ if (!hasEvents && danmakuError.value) {
+ ElMessage.warning(danmakuError.value);
+ return;
+ }
+ danmakuLoaded.value = true;
+ }
+
+ showDanmaku.value = true;
+}
const rowGutter = computed(() => (isMobile.value ? 14 : 18));
const logTableHeight = computed(() => (isMobile.value ? undefined : 420));
const activeTaskStatuses = new Set([1, 2, 3, 7]);
@@ -234,7 +259,16 @@ function formatProgress(value?: number) {
return typeof value === "number" && Number.isFinite(value) ? `${value.toFixed(0)}%` : "-";
}
-watch(() => props.id, loadDetailAndPreview);
+function resetDanmakuState() {
+ showDanmaku.value = false;
+ danmakuLoaded.value = false;
+ clearDanmaku();
+}
+
+watch(() => props.id, () => {
+ resetDanmakuState();
+ loadDetailAndPreview();
+});
onMounted(loadDetailAndPreview);
@@ -388,6 +422,14 @@ onMounted(loadDetailAndPreview);
票据有效至 {{ formatDate(previewExpiresAt) }}
+
+ {{ showDanmaku ? "关闭弹幕" : "弹幕回放" }}
+
- 正在准备预览资源…
+ 正在准备预览资源…
+