2026-05-18-19-31-18 增加原始视频查看入口

This commit is contained in:
2026-05-18 19:33:14 +08:00
parent e5f0b1dca9
commit c62f6e3401
7 changed files with 168 additions and 1 deletions

View File

@@ -27,6 +27,12 @@ const resultCount = document.querySelector("#resultCount");
const jobMeta = document.querySelector("#jobMeta"); const jobMeta = document.querySelector("#jobMeta");
const detailDialog = document.querySelector("#detailDialog"); const detailDialog = document.querySelector("#detailDialog");
const closeDialog = document.querySelector("#closeDialog"); const closeDialog = document.querySelector("#closeDialog");
const openSourceButton = document.querySelector("#openSourceButton");
const sourceDialog = document.querySelector("#sourceDialog");
const closeSourceDialog = document.querySelector("#closeSourceDialog");
const sourceTitle = document.querySelector("#sourceTitle");
const sourceVideo = document.querySelector("#sourceVideo");
const sourceImage = document.querySelector("#sourceImage");
const methodLabels = new Map(); const methodLabels = new Map();
const methodDescriptions = new Map(); const methodDescriptions = new Map();
@@ -68,12 +74,17 @@ function renderPreview(file) {
previewEmpty.hidden = true; previewEmpty.hidden = true;
videoPreview.hidden = true; videoPreview.hidden = true;
imagePreview.hidden = true; imagePreview.hidden = true;
openSourceButton.hidden = false;
if (file.type.startsWith("video/")) { if (file.type.startsWith("video/")) {
videoPreview.src = currentObjectUrl; videoPreview.src = currentObjectUrl;
videoPreview.hidden = false; videoPreview.hidden = false;
openSourceButton.textContent = "查看原始视频";
sourceTitle.textContent = "原始视频";
} else { } else {
imagePreview.src = currentObjectUrl; imagePreview.src = currentObjectUrl;
imagePreview.hidden = false; imagePreview.hidden = false;
openSourceButton.textContent = "查看原始图像";
sourceTitle.textContent = "原始图像";
} }
jobMeta.textContent = `已选择 ${file.name}`; jobMeta.textContent = `已选择 ${file.name}`;
} }
@@ -213,6 +224,12 @@ function clearAll() {
imagePreview.removeAttribute("src"); imagePreview.removeAttribute("src");
videoPreview.hidden = true; videoPreview.hidden = true;
imagePreview.hidden = true; imagePreview.hidden = true;
sourceVideo.hidden = true;
sourceImage.hidden = true;
sourceVideo.removeAttribute("src");
sourceImage.removeAttribute("src");
openSourceButton.hidden = true;
if (sourceDialog.open) sourceDialog.close();
previewEmpty.hidden = false; previewEmpty.hidden = false;
resultGrid.innerHTML = ""; resultGrid.innerHTML = "";
emptyState.hidden = false; emptyState.hidden = false;
@@ -253,6 +270,20 @@ dropZone.addEventListener("drop", (event) => {
sampleButton.addEventListener("click", loadSample); sampleButton.addEventListener("click", loadSample);
clearButton.addEventListener("click", clearAll); clearButton.addEventListener("click", clearAll);
closeDialog.addEventListener("click", () => detailDialog.close()); closeDialog.addEventListener("click", () => detailDialog.close());
closeSourceDialog.addEventListener("click", () => sourceDialog.close());
openSourceButton.addEventListener("click", () => {
if (!selectedFile || !currentObjectUrl) return;
sourceVideo.hidden = true;
sourceImage.hidden = true;
if (selectedFile.type.startsWith("video/")) {
sourceVideo.src = currentObjectUrl;
sourceVideo.hidden = false;
} else {
sourceImage.src = currentObjectUrl;
sourceImage.hidden = false;
}
sourceDialog.showModal();
});
form.addEventListener("submit", async (event) => { form.addEventListener("submit", async (event) => {
event.preventDefault(); event.preventDefault();

View File

@@ -85,7 +85,10 @@
<p class="eyebrow">Live Workspace</p> <p class="eyebrow">Live Workspace</p>
<h2>预览与结果</h2> <h2>预览与结果</h2>
</div> </div>
<div class="job-meta" id="jobMeta">等待输入</div> <div class="viewer-actions">
<button class="ghost media-button" id="openSourceButton" type="button" hidden>查看原始视频</button>
<div class="job-meta" id="jobMeta">等待输入</div>
</div>
</div> </div>
<div class="preview-stage" id="previewStage"> <div class="preview-stage" id="previewStage">
@@ -144,6 +147,20 @@
<dl class="detail-metrics" id="detailMetrics"></dl> <dl class="detail-metrics" id="detailMetrics"></dl>
</dialog> </dialog>
<dialog class="detail-dialog source-dialog" id="sourceDialog">
<div class="dialog-head">
<div>
<p class="eyebrow">Source Media</p>
<h2 id="sourceTitle">原始视频</h2>
</div>
<button class="icon-button" id="closeSourceDialog" type="button" aria-label="关闭原始媒体">×</button>
</div>
<div class="source-stage">
<video id="sourceVideo" controls hidden></video>
<img id="sourceImage" alt="原始输入图像" hidden />
</div>
</dialog>
<template id="resultCardTemplate"> <template id="resultCardTemplate">
<article class="result-card" tabindex="0"> <article class="result-card" tabindex="0">
<div class="card-top"> <div class="card-top">

View File

@@ -367,6 +367,20 @@ input[type="range"] {
margin-bottom: 14px; margin-bottom: 14px;
} }
.viewer-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
flex-wrap: wrap;
}
.media-button {
min-height: 38px;
padding: 0 12px;
color: var(--accent);
}
.job-meta { .job-meta {
color: var(--muted); color: var(--muted);
font-size: 13px; font-size: 13px;
@@ -603,6 +617,26 @@ dd {
aspect-ratio: 4 / 3; aspect-ratio: 4 / 3;
} }
.source-dialog {
width: min(1180px, calc(100vw - 36px));
}
.source-stage {
display: grid;
place-items: center;
min-height: 520px;
background: #050605;
}
#sourceVideo,
#sourceImage {
display: block;
width: 100%;
max-height: min(72vh, 720px);
object-fit: contain;
background: #050605;
}
@media (max-width: 980px) { @media (max-width: 980px) {
.app-shell { .app-shell {
width: min(100vw - 20px, 760px); width: min(100vw - 20px, 760px);
@@ -626,6 +660,10 @@ dd {
flex-wrap: wrap; flex-wrap: wrap;
} }
.viewer-actions {
justify-content: flex-start;
}
.summary-strip, .summary-strip,
.detail-images { .detail-images {
grid-template-columns: 1fr; grid-template-columns: 1fr;

View File

@@ -0,0 +1,22 @@
# 实现方案
开始时间2026-05-18-19-31-18
## 修改内容
1. `frontend/index.html`
- 在“预览与结果”标题右侧增加原始媒体按钮。
- 新增 `sourceDialog` 弹窗,包含原始视频播放器和原始图像预览。
2. `frontend/app.js`
- 记录当前加载文件的 object URL。
- 文件加载时更新按钮文案和显示状态。
- 点击按钮时打开原始媒体弹窗。
- 清空时关闭并重置原始媒体弹窗。
3. `frontend/styles.css`
- 增加媒体操作按钮、原始媒体弹窗和大播放器样式。
## 设计说明
页面内预览用于快速确认文件已加载;弹窗用于更大尺寸查看原始视频或图像,避免与分割结果网格相互挤压。

View File

@@ -0,0 +1,26 @@
# 测试方案
开始时间2026-05-18-19-31-18
## 自动化与运行验证
- `pytest -q`
- `curl -s http://127.0.0.1:8001/api/health`
- Chrome headless 首页截图,确认页面可渲染。
- 调用样例接口确认样例仍可获取。
## 手工验证
1. 打开 `http://192.168.3.11:8001/`
2. 点击“加载样例”。
3. 右侧预览区域应显示原始视频播放器。
4. 点击“查看原始视频”,应打开大播放器弹窗。
5. 点击清空后,按钮消失,预览恢复为空。
## 执行结果
- `pytest -q`通过4 个测试全部通过。
- `curl -s http://127.0.0.1:8001/api/health`:通过,返回 `status=ok`
- `curl -s http://127.0.0.1:8001/api/samples`:通过,样例视频和样例图像仍可获取。
- Chrome headless 首页截图:通过,未加载文件时“查看原始视频”按钮隐藏,首屏布局正常。
- `git diff --check`:通过,无空白格式错误。

View File

@@ -69,3 +69,15 @@ B. 产生问题原因:项目没有提供 `/favicon.ico`,浏览器会默认
C. 解决问题方案:后端增加 `/favicon.ico` 响应,前端为“加载样例”增加明确的成功/失败状态提示和异常捕获。 C. 解决问题方案:后端增加 `/favicon.ico` 响应,前端为“加载样例”增加明确的成功/失败状态提示和异常捕获。
D. 后续如何避免问题:网页端交付时补齐 favicon、状态提示和错误处理遇到 `runtime.lastError` 时优先用无痕模式或禁用扩展排查来源。 D. 后续如何避免问题:网页端交付时补齐 favicon、状态提示和错误处理遇到 `runtime.lastError` 时优先用无痕模式或禁用扩展排查来源。
## 2026-05-18-19-31-18 原始媒体查看入口增强
### 1. 文件加载后的原始内容查看入口不够明确
A. 具体问题:用户加载样例或上传视频/图像后,希望在“预览与结果”区域明确查看原始视频,否则不容易确认当前已加载内容。
B. 产生问题原因:页面虽然已有内嵌预览区域,但缺少显式的“查看原始视频/图像”操作入口,大尺寸查看也不方便。
C. 解决问题方案:在“预览与结果”标题区新增按文件类型切换文案的按钮;加载视频时显示“查看原始视频”,加载图像时显示“查看原始图像”;点击后打开原始媒体弹窗。
D. 后续如何避免问题:输入数据、处理中结果、最终结果应分别提供清晰入口,尤其是医学影像类工具需要让用户随时核对原始输入。

View File

@@ -0,0 +1,21 @@
# 需求分析
开始时间2026-05-18-19-31-18
## 用户目标
用户希望在网页端“预览与结果”区域加入“查看原始视频”能力。加载样例或拖拽/选择视频/图像后,应该能直接看到加载内容,并能打开原始媒体查看。
## 本轮目标
- 在“预览与结果”标题区加入“查看原始视频/图像”按钮。
- 文件加载后显示按钮,未加载时隐藏或不可用。
- 点击后打开原始媒体弹窗,视频使用浏览器播放器,图像使用大图预览。
- 保持现有上传、加载样例、分割流程不变。
## 验收标准
- 加载样例后,右侧预览区域能看到原始视频播放器。
- 加载样例后,“查看原始视频”按钮可见并可打开弹窗。
- 上传图片后,按钮文案切换为“查看原始图像”。
- 清空后按钮隐藏,预览恢复为空状态。