2026-05-18-19-19-36 修复样例加载提示和favicon
This commit is contained in:
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -46,9 +46,11 @@ function setBusy(isBusy, text = "运行分割") {
|
|||||||
|
|
||||||
function setFile(file) {
|
function setFile(file) {
|
||||||
selectedFile = file;
|
selectedFile = file;
|
||||||
|
if (typeof DataTransfer !== "undefined") {
|
||||||
const transfer = new DataTransfer();
|
const transfer = new DataTransfer();
|
||||||
transfer.items.add(file);
|
transfer.items.add(file);
|
||||||
fileInput.files = transfer.files;
|
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 = "加载样例";
|
||||||
|
|||||||
22
工程分析/实现方案-2026-05-18-19-19-36.md
Normal file
22
工程分析/实现方案-2026-05-18-19-19-36.md
Normal 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` 一般不是本项目代码产生,多见于浏览器扩展。若无痕模式或禁用扩展后消失,即可确认与系统无关。
|
||||||
24
工程分析/测试方案-2026-05-18-19-19-36.md
Normal file
24
工程分析/测试方案-2026-05-18-19-19-36.md
Normal 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 首页截图:通过,页面可正常渲染。
|
||||||
12
工程分析/经验记录.md
12
工程分析/经验记录.md
@@ -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` 时优先用无痕模式或禁用扩展排查来源。
|
||||||
|
|||||||
23
工程分析/需求分析-2026-05-18-19-19-36.md
Normal file
23
工程分析/需求分析-2026-05-18-19-19-36.md
Normal 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。
|
||||||
|
- 为“加载样例”增加成功和失败提示。
|
||||||
|
- 增强样例加载异常捕获。
|
||||||
|
- 验证服务和样例接口可用。
|
||||||
Reference in New Issue
Block a user