2026-05-18-19-19-36 修复样例加载提示和favicon

This commit is contained in:
2026-05-18 19:21:10 +08:00
parent 77b8ecdfbe
commit e5f0b1dca9
6 changed files with 103 additions and 4 deletions

View File

@@ -8,7 +8,7 @@ from typing import Any
import cv2 import cv2
from fastapi import FastAPI, File, Form, HTTPException, UploadFile from fastapi import FastAPI, File, Form, HTTPException, UploadFile
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse from fastapi.responses import FileResponse, Response
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from backend.segmentation import METHOD_DESCRIPTIONS, compare_frame, segment_frame from backend.segmentation import METHOD_DESCRIPTIONS, compare_frame, segment_frame
@@ -70,6 +70,15 @@ def samples() -> dict[str, Any]:
return {"samples": items} return {"samples": items}
@app.get("/favicon.ico", include_in_schema=False)
def favicon() -> Response:
svg = """<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="12" fill="#10211d"/>
<path d="M18 40V18h6v22h-6Zm14 0c-4.9 0-8.2-2.4-8.5-6.4h5.8c.3 1.4 1.4 2.2 3.1 2.2 1.6 0 2.6-.6 2.6-1.8 0-1.3-1.3-1.6-4.3-2.3-3.4-.8-6.6-2-6.6-6.5 0-4.3 3.3-7.2 8.1-7.2 4.7 0 7.8 2.4 8.2 6.4h-5.7c-.2-1.2-1.1-1.9-2.6-1.9-1.4 0-2.3.6-2.3 1.7 0 1.1 1.2 1.5 3.8 2.1 3.8.9 7.2 2.1 7.2 6.9 0 4.2-3.4 6.8-8.8 6.8Z" fill="#38d8b8"/>
</svg>"""
return Response(content=svg, media_type="image/svg+xml")
def _public(path: Path) -> str: def _public(path: Path) -> str:
return "/" + path.relative_to(ROOT).as_posix() return "/" + path.relative_to(ROOT).as_posix()

View File

@@ -46,9 +46,11 @@ function setBusy(isBusy, text = "运行分割") {
function setFile(file) { function setFile(file) {
selectedFile = file; selectedFile = file;
const transfer = new DataTransfer(); if (typeof DataTransfer !== "undefined") {
transfer.items.add(file); const transfer = new DataTransfer();
fileInput.files = transfer.files; transfer.items.add(file);
fileInput.files = transfer.files;
}
fileName.textContent = `${file.name} · ${(file.size / 1024 / 1024).toFixed(2)} MB`; fileName.textContent = `${file.name} · ${(file.size / 1024 / 1024).toFixed(2)} MB`;
renderPreview(file); renderPreview(file);
} }
@@ -180,15 +182,22 @@ function renderResults(data) {
async function loadSample() { async function loadSample() {
sampleButton.disabled = true; sampleButton.disabled = true;
sampleButton.textContent = "加载中"; sampleButton.textContent = "加载中";
emptyState.hidden = false;
emptyState.textContent = "正在加载内置样例视频。";
try { try {
const response = await fetch("/api/samples"); const response = await fetch("/api/samples");
if (!response.ok) throw new Error("样例清单读取失败");
const data = await response.json(); const data = await response.json();
const sample = data.samples.find((item) => item.kind === "video") || data.samples[0]; const sample = data.samples.find((item) => item.kind === "video") || data.samples[0];
if (!sample) throw new Error("未找到样例文件"); if (!sample) throw new Error("未找到样例文件");
const blobResponse = await fetch(sample.url); const blobResponse = await fetch(sample.url);
if (!blobResponse.ok) throw new Error("样例文件下载失败");
const blob = await blobResponse.blob(); const blob = await blobResponse.blob();
const file = new File([blob], sample.name, { type: sample.kind === "video" ? "video/mp4" : "image/png" }); const file = new File([blob], sample.name, { type: sample.kind === "video" ? "video/mp4" : "image/png" });
setFile(file); setFile(file);
emptyState.textContent = "样例已加载。点击左侧“运行分割”即可生成导丝掩膜和叠加结果。";
} catch (error) {
emptyState.textContent = error.message || "样例加载失败";
} finally { } finally {
sampleButton.disabled = false; sampleButton.disabled = false;
sampleButton.textContent = "加载样例"; sampleButton.textContent = "加载样例";

View File

@@ -0,0 +1,22 @@
# 实现方案
开始时间2026-05-18-19-19-36
## 修改内容
1. 后端新增 `/favicon.ico`
- 返回一个轻量 SVG 图标响应。
- 避免浏览器默认请求 favicon 时产生 404 控制台噪声。
2. 前端增强 `loadSample`
- 加载开始时显示“正在加载内置样例视频”。
- 成功后显示“样例已加载,点击运行分割”。
- 接口或文件下载失败时在页面中显示错误。
3. 前端增强 `setFile`
- 仅在浏览器支持 `DataTransfer` 时同步设置 `fileInput.files`
- 保留 `selectedFile` 作为真实提交来源,兼容性更稳。
## 说明
`Unchecked runtime.lastError` 一般不是本项目代码产生,多见于浏览器扩展。若无痕模式或禁用扩展后消失,即可确认与系统无关。

View File

@@ -0,0 +1,24 @@
# 测试方案
开始时间2026-05-18-19-19-36
## 测试项
- `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8001/favicon.ico`
- `curl -s http://127.0.0.1:8001/api/samples`
- `pytest -q`
- Chrome headless 首页截图确认页面仍可渲染。
## 验收标准
- `/favicon.ico` 不再返回 404。
- `/api/samples` 返回样例视频和样例图像。
- 自动化测试通过。
- 网页端加载样例有明确页面提示。
## 执行结果
- `/favicon.ico`:通过,返回 `200 image/svg+xml`
- `/api/samples`:通过,返回 `synthetic_guidewire.mp4``synthetic_guidewire.png`
- `pytest -q`通过4 个测试全部通过。
- Chrome headless 首页截图:通过,页面可正常渲染。

View File

@@ -57,3 +57,15 @@ B. 产生问题原因CSS 中对 `#videoPreview`、`#imagePreview` 设置了 `
C. 解决问题方案:在全局 CSS 中增加 `[hidden] { display: none !important; }`,确保所有使用 `hidden` 的状态元素都能可靠隐藏。 C. 解决问题方案:在全局 CSS 中增加 `[hidden] { display: none !important; }`,确保所有使用 `hidden` 的状态元素都能可靠隐藏。
D. 后续如何避免问题:前端页面涉及显隐状态时,必须用 headless 截图检查初始状态;如果自定义了 display 样式,需要显式保护 `[hidden]` D. 后续如何避免问题:前端页面涉及显隐状态时,必须用 headless 截图检查初始状态;如果自定义了 display 样式,需要显式保护 `[hidden]`
## 2026-05-18-19-19-36 加载样例控制台提示处理
### 1. favicon.ico 404 干扰问题判断
A. 具体问题:点击“加载样例”后,浏览器控制台出现 `/favicon.ico` 404同时还有 `Unchecked runtime.lastError`
B. 产生问题原因:项目没有提供 `/favicon.ico`,浏览器会默认请求该资源并记录 404`runtime.lastError` 更常见于浏览器扩展消息通道关闭,通常不是普通网页代码产生。
C. 解决问题方案:后端增加 `/favicon.ico` 响应,前端为“加载样例”增加明确的成功/失败状态提示和异常捕获。
D. 后续如何避免问题:网页端交付时补齐 favicon、状态提示和错误处理遇到 `runtime.lastError` 时优先用无痕模式或禁用扩展排查来源。

View File

@@ -0,0 +1,23 @@
# 需求分析
开始时间2026-05-18-19-19-36
## 用户反馈
点击网页端“加载样例”后,浏览器控制台出现:
- `Unchecked runtime.lastError: The message port closed before a response was received.`
- `:8001/favicon.ico:1 Failed to load resource: the server responded with a status of 404 (Not Found)`
## 判断
- `runtime.lastError` 通常来自浏览器扩展消息通道,页面自身代码未直接使用扩展消息接口。
- `favicon.ico 404` 是项目没有提供站点图标,确实会在控制台产生干扰。
- “加载样例”需要更明确的页面状态提示,避免用户只看到控制台信息却不知道样例是否加载成功。
## 本轮目标
- 修复 `/favicon.ico` 404。
- 为“加载样例”增加成功和失败提示。
- 增强样例加载异常捕获。
- 验证服务和样例接口可用。