2026-05-18-19-37-10 调整原始视频与结果同级分栏
This commit is contained in:
@@ -28,6 +28,7 @@ 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 openSourceButton = document.querySelector("#openSourceButton");
|
||||||
|
const sourcePaneTitle = document.querySelector("#sourcePaneTitle");
|
||||||
const sourceDialog = document.querySelector("#sourceDialog");
|
const sourceDialog = document.querySelector("#sourceDialog");
|
||||||
const closeSourceDialog = document.querySelector("#closeSourceDialog");
|
const closeSourceDialog = document.querySelector("#closeSourceDialog");
|
||||||
const sourceTitle = document.querySelector("#sourceTitle");
|
const sourceTitle = document.querySelector("#sourceTitle");
|
||||||
@@ -78,12 +79,14 @@ function renderPreview(file) {
|
|||||||
if (file.type.startsWith("video/")) {
|
if (file.type.startsWith("video/")) {
|
||||||
videoPreview.src = currentObjectUrl;
|
videoPreview.src = currentObjectUrl;
|
||||||
videoPreview.hidden = false;
|
videoPreview.hidden = false;
|
||||||
openSourceButton.textContent = "查看原始视频";
|
openSourceButton.textContent = "放大查看";
|
||||||
|
sourcePaneTitle.textContent = "查看原始视频";
|
||||||
sourceTitle.textContent = "原始视频";
|
sourceTitle.textContent = "原始视频";
|
||||||
} else {
|
} else {
|
||||||
imagePreview.src = currentObjectUrl;
|
imagePreview.src = currentObjectUrl;
|
||||||
imagePreview.hidden = false;
|
imagePreview.hidden = false;
|
||||||
openSourceButton.textContent = "查看原始图像";
|
openSourceButton.textContent = "放大查看";
|
||||||
|
sourcePaneTitle.textContent = "查看原始图像";
|
||||||
sourceTitle.textContent = "原始图像";
|
sourceTitle.textContent = "原始图像";
|
||||||
}
|
}
|
||||||
jobMeta.textContent = `已选择 ${file.name}`;
|
jobMeta.textContent = `已选择 ${file.name}`;
|
||||||
@@ -229,6 +232,7 @@ function clearAll() {
|
|||||||
sourceVideo.removeAttribute("src");
|
sourceVideo.removeAttribute("src");
|
||||||
sourceImage.removeAttribute("src");
|
sourceImage.removeAttribute("src");
|
||||||
openSourceButton.hidden = true;
|
openSourceButton.hidden = true;
|
||||||
|
sourcePaneTitle.textContent = "查看原始视频";
|
||||||
if (sourceDialog.open) sourceDialog.close();
|
if (sourceDialog.open) sourceDialog.close();
|
||||||
previewEmpty.hidden = false;
|
previewEmpty.hidden = false;
|
||||||
resultGrid.innerHTML = "";
|
resultGrid.innerHTML = "";
|
||||||
|
|||||||
@@ -80,44 +80,51 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
<section class="viewer">
|
<section class="viewer">
|
||||||
<div class="viewer-head">
|
<div class="viewer-split">
|
||||||
<div>
|
<section class="workspace-pane source-pane">
|
||||||
<p class="eyebrow">Live Workspace</p>
|
<div class="pane-head">
|
||||||
<h2>预览与结果</h2>
|
<div>
|
||||||
</div>
|
<p class="eyebrow">Source Media</p>
|
||||||
<div class="viewer-actions">
|
<h2 id="sourcePaneTitle">查看原始视频</h2>
|
||||||
<button class="ghost media-button" id="openSourceButton" type="button" hidden>查看原始视频</button>
|
</div>
|
||||||
<div class="job-meta" id="jobMeta">等待输入</div>
|
<div class="viewer-actions">
|
||||||
</div>
|
<button class="ghost media-button" id="openSourceButton" type="button" hidden>放大查看</button>
|
||||||
</div>
|
<div class="job-meta" id="jobMeta">等待输入</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="preview-stage" id="previewStage">
|
<div class="preview-stage" id="previewStage">
|
||||||
<div class="preview-empty" id="previewEmpty">选择文件或加载样例后开始</div>
|
<div class="preview-empty" id="previewEmpty">选择文件或加载样例后开始</div>
|
||||||
<video id="videoPreview" controls muted hidden></video>
|
<video id="videoPreview" controls muted hidden></video>
|
||||||
<img id="imagePreview" alt="输入图像预览" hidden />
|
<img id="imagePreview" alt="输入图像预览" hidden />
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="progress-wrap" id="progressWrap" hidden>
|
<section class="workspace-pane result-pane">
|
||||||
<div class="progress-line"><span id="progressBar"></span></div>
|
<div class="pane-head">
|
||||||
<p id="progressText">准备任务</p>
|
<div>
|
||||||
</div>
|
<p class="eyebrow">Live Workspace</p>
|
||||||
|
<h2>预览与结果</h2>
|
||||||
|
</div>
|
||||||
|
<span id="resultCount">0 个结果</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="summary-strip" id="summaryStrip" hidden>
|
<div class="progress-wrap" id="progressWrap" hidden>
|
||||||
<div><span>任务</span><strong id="summaryJob">-</strong></div>
|
<div class="progress-line"><span id="progressBar"></span></div>
|
||||||
<div><span>帧数</span><strong id="summaryFrames">-</strong></div>
|
<p id="progressText">准备任务</p>
|
||||||
<div><span>平均覆盖率</span><strong id="summaryCoverage">-</strong></div>
|
</div>
|
||||||
<div><span>平均骨架</span><strong id="summarySkeleton">-</strong></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="results-toolbar">
|
<div class="summary-strip" id="summaryStrip" hidden>
|
||||||
<div>
|
<div><span>任务</span><strong id="summaryJob">-</strong></div>
|
||||||
<p class="eyebrow">Result Frames</p>
|
<div><span>帧数</span><strong id="summaryFrames">-</strong></div>
|
||||||
<h3>分割帧</h3>
|
<div><span>平均覆盖率</span><strong id="summaryCoverage">-</strong></div>
|
||||||
</div>
|
<div><span>平均骨架</span><strong id="summarySkeleton">-</strong></div>
|
||||||
<span id="resultCount">0 个结果</span>
|
</div>
|
||||||
|
|
||||||
|
<div class="empty" id="emptyState">运行分割后,这里会显示原帧、叠加图、掩膜和指标。</div>
|
||||||
|
<div class="result-grid" id="resultGrid"></div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div class="empty" id="emptyState">运行分割后,这里会显示原帧、叠加图、掩膜和指标。</div>
|
|
||||||
<div class="result-grid" id="resultGrid"></div>
|
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ button {
|
|||||||
.top-actions,
|
.top-actions,
|
||||||
.section-title,
|
.section-title,
|
||||||
.field-head,
|
.field-head,
|
||||||
.viewer-head,
|
|
||||||
.results-toolbar,
|
.results-toolbar,
|
||||||
.card-top,
|
.card-top,
|
||||||
.dialog-head {
|
.dialog-head,
|
||||||
|
.pane-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@@ -363,7 +363,23 @@ input[type="range"] {
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewer-head {
|
.viewer-split {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||||
|
gap: 14px;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-pane {
|
||||||
|
min-height: 690px;
|
||||||
|
padding: 14px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(16, 20, 17, 0.58);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pane-head {
|
||||||
|
min-height: 48px;
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +408,7 @@ input[type="range"] {
|
|||||||
.preview-stage {
|
.preview-stage {
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
min-height: 320px;
|
min-height: 590px;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background:
|
background:
|
||||||
@@ -414,7 +430,7 @@ input[type="range"] {
|
|||||||
#imagePreview {
|
#imagePreview {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 520px;
|
max-height: 590px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
background: #050605;
|
background: #050605;
|
||||||
}
|
}
|
||||||
@@ -459,7 +475,7 @@ input[type="range"] {
|
|||||||
|
|
||||||
.summary-strip {
|
.summary-strip {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: 1px;
|
gap: 1px;
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -486,12 +502,8 @@ input[type="range"] {
|
|||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.results-toolbar {
|
|
||||||
margin: 18px 0 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
min-height: 180px;
|
min-height: 590px;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
@@ -501,8 +513,10 @@ input[type="range"] {
|
|||||||
|
|
||||||
.result-grid {
|
.result-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
max-height: 590px;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.result-card {
|
.result-card {
|
||||||
@@ -668,4 +682,17 @@ dd {
|
|||||||
.detail-images {
|
.detail-images {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.viewer-split {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-pane {
|
||||||
|
min-height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-stage,
|
||||||
|
.empty {
|
||||||
|
min-height: 320px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
工程分析/实现方案-2026-05-18-19-37-10.md
Normal file
26
工程分析/实现方案-2026-05-18-19-37-10.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# 实现方案
|
||||||
|
|
||||||
|
开始时间:2026-05-18-19-37-10
|
||||||
|
|
||||||
|
## 修改内容
|
||||||
|
|
||||||
|
1. `frontend/index.html`
|
||||||
|
- 将 `viewer` 内部改为 `viewer-split`。
|
||||||
|
- 左侧新增 `workspace-pane source-pane`,标题为“查看原始视频”。
|
||||||
|
- 右侧新增 `workspace-pane result-pane`,标题为“预览与结果”。
|
||||||
|
- 原始媒体预览控件移动到左侧面板。
|
||||||
|
- 进度、摘要、结果帧移动到右侧面板。
|
||||||
|
|
||||||
|
2. `frontend/app.js`
|
||||||
|
- 增加 `sourcePaneTitle` 引用。
|
||||||
|
- 根据加载文件类型切换左侧标题为“查看原始视频”或“查看原始图像”。
|
||||||
|
- 原始媒体弹窗按钮改为“放大查看”。
|
||||||
|
|
||||||
|
3. `frontend/styles.css`
|
||||||
|
- 增加左右分栏布局。
|
||||||
|
- 给左右面板独立边框、标题栏、内容高度。
|
||||||
|
- 移动端改为单列上下堆叠。
|
||||||
|
|
||||||
|
## 设计说明
|
||||||
|
|
||||||
|
这个结构把“输入核对”和“算法输出”分开,符合医学影像工作台的使用习惯:先确认原始输入,再查看分割结果。
|
||||||
27
工程分析/测试方案-2026-05-18-19-37-10.md
Normal file
27
工程分析/测试方案-2026-05-18-19-37-10.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# 测试方案
|
||||||
|
|
||||||
|
开始时间:2026-05-18-19-37-10
|
||||||
|
|
||||||
|
## 自动化验证
|
||||||
|
|
||||||
|
- `pytest -q`
|
||||||
|
- `curl -s http://127.0.0.1:8001/api/health`
|
||||||
|
- `curl -s http://127.0.0.1:8001/api/samples`
|
||||||
|
- `git diff --check`
|
||||||
|
- 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`:通过,样例视频和样例图像仍可获取。
|
||||||
|
- `git diff --check`:通过,无空白格式错误。
|
||||||
|
- Chrome headless 首页截图:通过,右侧工作区已拆成同级左右两栏,左侧为“查看原始视频”,右侧为“预览与结果”。
|
||||||
12
工程分析/经验记录.md
12
工程分析/经验记录.md
@@ -81,3 +81,15 @@ B. 产生问题原因:页面虽然已有内嵌预览区域,但缺少显式
|
|||||||
C. 解决问题方案:在“预览与结果”标题区新增按文件类型切换文案的按钮;加载视频时显示“查看原始视频”,加载图像时显示“查看原始图像”;点击后打开原始媒体弹窗。
|
C. 解决问题方案:在“预览与结果”标题区新增按文件类型切换文案的按钮;加载视频时显示“查看原始视频”,加载图像时显示“查看原始图像”;点击后打开原始媒体弹窗。
|
||||||
|
|
||||||
D. 后续如何避免问题:输入数据、处理中结果、最终结果应分别提供清晰入口,尤其是医学影像类工具需要让用户随时核对原始输入。
|
D. 后续如何避免问题:输入数据、处理中结果、最终结果应分别提供清晰入口,尤其是医学影像类工具需要让用户随时核对原始输入。
|
||||||
|
|
||||||
|
## 2026-05-18-19-37-10 原始视频与结果同级分栏
|
||||||
|
|
||||||
|
### 1. 原始媒体入口层级不符合用户预期
|
||||||
|
|
||||||
|
A. 具体问题:用户希望“查看原始视频”和“预览与结果”是同一级,而不是把原始视频查看作为“预览与结果”标题区里的按钮。
|
||||||
|
|
||||||
|
B. 产生问题原因:上一版将原始媒体查看入口作为辅助操作放在结果区标题右侧,信息架构上仍然偏向“结果主导”,不符合用户希望的左右对照工作流。
|
||||||
|
|
||||||
|
C. 解决问题方案:将右侧工作区拆成左右两个 `workspace-pane`:左侧固定展示原始视频/图像,右侧展示分割进度、摘要和结果帧;保留“放大查看”作为左侧面板内的辅助操作。
|
||||||
|
|
||||||
|
D. 后续如何避免问题:医学影像交互页面应优先考虑“原始输入”和“算法输出”并列对照,而不是把原始输入藏在结果区域的附属操作中。
|
||||||
|
|||||||
22
工程分析/需求分析-2026-05-18-19-37-10.md
Normal file
22
工程分析/需求分析-2026-05-18-19-37-10.md
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# 需求分析
|
||||||
|
|
||||||
|
开始时间:2026-05-18-19-37-10
|
||||||
|
|
||||||
|
## 用户目标
|
||||||
|
|
||||||
|
用户希望“查看原始视频”和“预览与结果”成为同级区域,而不是“预览与结果”中的一个按钮。右侧工作区需要横向分为两半:左侧查看原始视频/图像,右侧展示预览与分割结果。
|
||||||
|
|
||||||
|
## 本轮目标
|
||||||
|
|
||||||
|
- 将右侧工作区拆成左右两个同级面板。
|
||||||
|
- 左侧面板标题为“查看原始视频/图像”,负责原始输入预览。
|
||||||
|
- 右侧面板标题为“预览与结果”,负责分割进度、统计摘要和结果帧。
|
||||||
|
- 保留原始媒体大弹窗能力,入口放在左侧面板标题区。
|
||||||
|
- 保持移动端自动上下堆叠。
|
||||||
|
|
||||||
|
## 验收标准
|
||||||
|
|
||||||
|
- 初始页面右侧区域左右分栏明确。
|
||||||
|
- 加载样例后左侧显示原始视频播放器。
|
||||||
|
- 分割后右侧显示结果摘要和结果帧。
|
||||||
|
- 清空后左侧恢复未加载状态,右侧恢复未运行状态。
|
||||||
Reference in New Issue
Block a user