2026-04-16-18-51-06 - 修复路由切换后视频分析图片丢失问题
This commit is contained in:
@@ -67,9 +67,7 @@ export default function ReportEditor() {
|
||||
const videoInputRef = useRef<HTMLInputElement>(null);
|
||||
const contentLoadedRef = useRef(false);
|
||||
const contentRef = useRef('');
|
||||
const stateRef = useRef({ reportData, videos, capturedFrames, activeTab });
|
||||
|
||||
stateRef.current = { reportData, videos, capturedFrames, activeTab };
|
||||
const stateRef = useRef({ reportData, videos, capturedFrames, activeTab, loadedTemplateId });
|
||||
|
||||
const draftKey = currentUser ? `reportEditorDraft_${currentUser.username}` : '';
|
||||
|
||||
@@ -88,12 +86,11 @@ export default function ReportEditor() {
|
||||
if (key) {
|
||||
storage.set(key, {
|
||||
content: contentRef.current,
|
||||
loadedTemplateId,
|
||||
draftReportId: reportId || null,
|
||||
...stateRef.current
|
||||
});
|
||||
}
|
||||
}, [reportId, loadedTemplateId]);
|
||||
}, [reportId]);
|
||||
|
||||
useEffect(() => {
|
||||
const user = storage.get<User | null>('currentUser', null);
|
||||
@@ -128,6 +125,13 @@ export default function ReportEditor() {
|
||||
contentRef.current = draft.content;
|
||||
contentLoadedRef.current = true;
|
||||
setLoadedTemplateId(draft.loadedTemplateId || '');
|
||||
stateRef.current = {
|
||||
...stateRef.current,
|
||||
reportData: draft.reportData,
|
||||
videos: draft.videos,
|
||||
capturedFrames: draft.capturedFrames,
|
||||
loadedTemplateId: draft.loadedTemplateId || ''
|
||||
};
|
||||
setTimeout(() => updatePageHeight(), 0);
|
||||
}
|
||||
} else {
|
||||
@@ -146,6 +150,12 @@ export default function ReportEditor() {
|
||||
contentRef.current = found.content;
|
||||
}
|
||||
contentLoadedRef.current = true;
|
||||
stateRef.current = {
|
||||
...stateRef.current,
|
||||
reportData: found,
|
||||
videos: found.videos || [],
|
||||
capturedFrames: found.capturedFrames || []
|
||||
};
|
||||
setTimeout(() => updatePageHeight(), 0);
|
||||
}
|
||||
if (found.capturedFrames) {
|
||||
@@ -176,6 +186,13 @@ export default function ReportEditor() {
|
||||
contentRef.current = draft.content;
|
||||
contentLoadedRef.current = true;
|
||||
setLoadedTemplateId(draft.loadedTemplateId || '');
|
||||
stateRef.current = {
|
||||
...stateRef.current,
|
||||
reportData: draft.reportData,
|
||||
videos: draft.videos,
|
||||
capturedFrames: draft.capturedFrames,
|
||||
loadedTemplateId: draft.loadedTemplateId || ''
|
||||
};
|
||||
setTimeout(() => updatePageHeight(), 0);
|
||||
}
|
||||
}
|
||||
@@ -185,6 +202,7 @@ export default function ReportEditor() {
|
||||
const tpl = filteredTemplates.find(t => t.id === settings.defaultTemplate);
|
||||
if (tpl) {
|
||||
setLoadedTemplateId(tpl.id);
|
||||
stateRef.current = { ...stateRef.current, loadedTemplateId: tpl.id };
|
||||
editorRef.current.innerHTML = tpl.content;
|
||||
contentRef.current = tpl.content;
|
||||
} else {
|
||||
@@ -398,6 +416,7 @@ export default function ReportEditor() {
|
||||
}));
|
||||
const combined = [...videos, ...newVideos];
|
||||
setVideos(combined);
|
||||
stateRef.current = { ...stateRef.current, videos: combined };
|
||||
setCurrentVideoIndex(videos.length); // select first newly uploaded video
|
||||
if (videoInputRef.current) videoInputRef.current.value = '';
|
||||
saveDraftToStorage();
|
||||
@@ -407,15 +426,18 @@ export default function ReportEditor() {
|
||||
const idx = videos.findIndex(v => v.id === id);
|
||||
const updated = videos.filter(v => v.id !== id);
|
||||
setVideos(updated);
|
||||
stateRef.current = { ...stateRef.current, videos: updated };
|
||||
if (currentVideoIndex >= updated.length) {
|
||||
setCurrentVideoIndex(updated.length > 0 ? 0 : -1);
|
||||
} else if (currentVideoIndex === idx && updated.length > 0) {
|
||||
setCurrentVideoIndex(0);
|
||||
}
|
||||
setCapturedFrames(prev => prev.filter(f => f.videoIndex !== idx).map(f => {
|
||||
const nextFrames = capturedFrames.filter(f => f.videoIndex !== idx).map(f => {
|
||||
if (f.videoIndex > idx) return { ...f, videoIndex: f.videoIndex - 1 };
|
||||
return f;
|
||||
}).sort((a, b) => a.time - b.time));
|
||||
}).sort((a, b) => a.time - b.time);
|
||||
setCapturedFrames(nextFrames);
|
||||
stateRef.current = { ...stateRef.current, capturedFrames: nextFrames };
|
||||
saveDraftToStorage();
|
||||
};
|
||||
|
||||
@@ -449,7 +471,9 @@ export default function ReportEditor() {
|
||||
dataUrl: canvas.toDataURL('image/jpeg', 0.9),
|
||||
isManual: true
|
||||
};
|
||||
setCapturedFrames(prev => [...prev, newFrame].sort((a, b) => a.time - b.time));
|
||||
const nextFrames = [...capturedFrames, newFrame].sort((a, b) => a.time - b.time);
|
||||
setCapturedFrames(nextFrames);
|
||||
stateRef.current = { ...stateRef.current, capturedFrames: nextFrames };
|
||||
saveDraftToStorage();
|
||||
};
|
||||
|
||||
@@ -468,8 +492,9 @@ export default function ReportEditor() {
|
||||
const wasPlaying = !video.paused;
|
||||
if (wasPlaying) video.pause();
|
||||
|
||||
const newFrames: CapturedFrame[] = [];
|
||||
for (const pos of positions) {
|
||||
let accumulatedFrames = [...capturedFrames];
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
const pos = positions[i];
|
||||
const time = (pos / 100) * dur;
|
||||
video.currentTime = time;
|
||||
await new Promise<void>(resolve => {
|
||||
@@ -482,7 +507,7 @@ export default function ReportEditor() {
|
||||
canvas.width = video.videoWidth;
|
||||
canvas.height = video.videoHeight;
|
||||
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||
newFrames.push({
|
||||
const newFrame: CapturedFrame = {
|
||||
id: Date.now() + Math.random(),
|
||||
videoIndex: currentVideoIndex,
|
||||
videoName: videos[currentVideoIndex].name,
|
||||
@@ -490,9 +515,27 @@ export default function ReportEditor() {
|
||||
timeFormatted: formatTime(time),
|
||||
dataUrl: canvas.toDataURL('image/jpeg', 0.9),
|
||||
isManual: false
|
||||
});
|
||||
};
|
||||
accumulatedFrames = [...accumulatedFrames, newFrame].sort((a, b) => a.time - b.time);
|
||||
setCapturedFrames(accumulatedFrames);
|
||||
stateRef.current = { ...stateRef.current, capturedFrames: accumulatedFrames };
|
||||
if (settings.autoInsertFrames && settings.autoInsertFrameIndices?.includes(i) && editorRef.current) {
|
||||
if ((settings.autoInsertDelay || 0) > 0) {
|
||||
await new Promise<void>(r => setTimeout(r, (settings.autoInsertDelay || 0) * 1000));
|
||||
}
|
||||
const emptyPlaceholder = editorRef.current.querySelector('.image-placeholder:not(.has-image)') as HTMLElement | null;
|
||||
if (emptyPlaceholder) {
|
||||
emptyPlaceholder.innerHTML = `
|
||||
<span class="delete-btn" contenteditable="false">×</span>
|
||||
<img src="${newFrame.dataUrl}" style="max-width: 100%; height: auto; display: block; margin: 0 auto;" draggable="false">
|
||||
`;
|
||||
emptyPlaceholder.classList.add('has-image');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (settings.autoInsertFrames && editorRef.current) {
|
||||
contentRef.current = editorRef.current.innerHTML;
|
||||
}
|
||||
setCapturedFrames(prev => [...prev, ...newFrames].sort((a, b) => a.time - b.time));
|
||||
if (wasPlaying) video.play();
|
||||
saveDraftToStorage();
|
||||
};
|
||||
@@ -596,8 +639,7 @@ export default function ReportEditor() {
|
||||
if (tpl) {
|
||||
editorRef.current.innerHTML = tpl.content;
|
||||
contentRef.current = tpl.content;
|
||||
setLoadedTemplateId(tpl.id);
|
||||
setReportData({
|
||||
const nextReportData = {
|
||||
title: tpl.name || '腹腔镜胆囊切除术报告',
|
||||
patientName: '',
|
||||
hospitalId: '',
|
||||
@@ -615,11 +657,21 @@ export default function ReportEditor() {
|
||||
anesthesiologist: [],
|
||||
anesthesiaType: '',
|
||||
status: 'draft'
|
||||
});
|
||||
};
|
||||
setLoadedTemplateId(tpl.id);
|
||||
setReportData(nextReportData);
|
||||
setVideos([]);
|
||||
setCapturedFrames([]);
|
||||
setCurrentVideoIndex(-1);
|
||||
prevVideoCountRef.current = 0;
|
||||
stateRef.current = {
|
||||
...stateRef.current,
|
||||
loadedTemplateId: tpl.id,
|
||||
reportData: nextReportData,
|
||||
videos: [],
|
||||
capturedFrames: [],
|
||||
activeTab: stateRef.current.activeTab
|
||||
};
|
||||
updatePageHeight();
|
||||
saveDraftToStorage();
|
||||
}
|
||||
@@ -641,6 +693,13 @@ export default function ReportEditor() {
|
||||
contentRef.current = draft.content;
|
||||
contentLoadedRef.current = true;
|
||||
setLoadedTemplateId(draft.loadedTemplateId || '');
|
||||
stateRef.current = {
|
||||
...stateRef.current,
|
||||
reportData: draft.reportData,
|
||||
videos: draft.videos,
|
||||
capturedFrames: draft.capturedFrames,
|
||||
loadedTemplateId: draft.loadedTemplateId || ''
|
||||
};
|
||||
setTimeout(() => updatePageHeight(), 0);
|
||||
return;
|
||||
}
|
||||
@@ -655,6 +714,12 @@ export default function ReportEditor() {
|
||||
editorRef.current.innerHTML = found.content;
|
||||
}
|
||||
contentLoadedRef.current = true;
|
||||
stateRef.current = {
|
||||
...stateRef.current,
|
||||
reportData: found,
|
||||
videos: found.videos || [],
|
||||
capturedFrames: found.capturedFrames || []
|
||||
};
|
||||
setTimeout(() => updatePageHeight(), 0);
|
||||
return;
|
||||
}
|
||||
@@ -664,6 +729,13 @@ export default function ReportEditor() {
|
||||
contentRef.current = draft.content;
|
||||
contentLoadedRef.current = true;
|
||||
setLoadedTemplateId(draft.loadedTemplateId || '');
|
||||
stateRef.current = {
|
||||
...stateRef.current,
|
||||
reportData: draft.reportData,
|
||||
videos: draft.videos,
|
||||
capturedFrames: draft.capturedFrames,
|
||||
loadedTemplateId: draft.loadedTemplateId || ''
|
||||
};
|
||||
setTimeout(() => updatePageHeight(), 0);
|
||||
return;
|
||||
}
|
||||
@@ -678,6 +750,7 @@ export default function ReportEditor() {
|
||||
const tpl = filteredTemplates.find(t => t.id === settings.defaultTemplate);
|
||||
if (tpl) {
|
||||
setLoadedTemplateId(tpl.id);
|
||||
stateRef.current = { ...stateRef.current, loadedTemplateId: tpl.id };
|
||||
editorRef.current.innerHTML = tpl.content;
|
||||
} else {
|
||||
editorRef.current.innerHTML = defaultReportContent;
|
||||
|
||||
Reference in New Issue
Block a user