diff --git a/web/server.cjs b/web/server.cjs
index 838b5dc..4a814e2 100644
--- a/web/server.cjs
+++ b/web/server.cjs
@@ -24,7 +24,7 @@ const SECURITY_HEADERS = {
'X-Frame-Options': 'DENY',
'Referrer-Policy': 'no-referrer',
'Content-Security-Policy':
- "default-src 'self'; img-src 'self' data: blob:; media-src 'self' blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'",
+ "default-src 'self'; img-src 'self' data: blob: http://100.83.103.1:7480; media-src 'self' blob:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss:; font-src 'self' data:; object-src 'none'; frame-ancestors 'none'",
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
};
diff --git a/web/src/pages/Knowledge.tsx b/web/src/pages/Knowledge.tsx
index b49b03a..16543da 100644
--- a/web/src/pages/Knowledge.tsx
+++ b/web/src/pages/Knowledge.tsx
@@ -183,6 +183,10 @@ function DiseaseTab() {
)}
+ {/* imageUrl 通过隐藏字段注册到表单,保证 validateFields 提交时携带(修复:未注册字段会被丢弃) */}
+
+
+
diff --git a/变更记录.md b/变更记录.md
index 9802e06..98896c1 100644
--- a/变更记录.md
+++ b/变更记录.md
@@ -1,5 +1,29 @@
# 变更记录
+## 2026-08-12 Web 知识库图谱预览与保存修复(故障排查 #三十二)
+
+### 变更背景
+
+用户反馈 Web 知识库上传新症状图谱后编辑页预览图打不开、提交后图谱未保存。排查确认上传与存储正常(Ceph 对象存在、匿名 GET 200),问题在前端:CSP 拦截跨源预览 + 表单未注册 `imageUrl` 导致提交体缺失该字段。
+
+### 变更内容
+
+| 文件 | 变更 |
+|------|------|
+| `web/src/pages/Knowledge.tsx` | 新增隐藏 `Form.Item name="imageUrl"`,使 `validateFields()` 提交时携带图片 URL |
+| `web/server.cjs` | CSP `img-src` 增加 `http://100.83.103.1:7480`(开发 S3 源) |
+
+### 部署与验证
+
+- 本地:`npm run lint`(通过)、`npm run build`(通过,新 bundle `index-CSVEb7gG.js`)。
+- 部署:备份 `/home/pan/backups/web-20260812-1605/`(旧 dist、server.cjs、web.log),上传新产物并重启 node。
+- 验证:`http://localhost:5174/` 200;CSP 头含 S3 源;新 bundle 200;`/api/v1/health` 200;DB 白僵病 `image_url` 已补绑(`UPDATE 1`),图片匿名 GET 200。
+
+### 回滚点
+
+- 前端:恢复 `/home/pan/backups/web-20260812-1605/` 旧产物后重启 node。
+- 数据:`/home/pan/backups/diseases-20260812-1605.dump`(pg_restore),或执行反向 UPDATE 将 `image_url` 置空。
+
## 2026-08-12 知识库初始版开发与部署
### 变更背景
diff --git a/开发交接记录.md b/开发交接记录.md
index 36ca7d0..1f008e9 100644
--- a/开发交接记录.md
+++ b/开发交接记录.md
@@ -12,6 +12,33 @@
---
+## 2026-08-12 补充:Web 知识库图谱预览与保存修复(故障排查 #三十二)
+
+### 做了什么
+
+- 修复 Web 知识库「蚕病百科」两个问题:编辑页图谱预览打不开(CSP 拦截跨源 S3 图片)、提交后 `imageUrl` 未保存(表单未注册该字段)。
+- `web/src/pages/Knowledge.tsx`:「症状图谱图片」后新增隐藏 `Form.Item name="imageUrl"`,保证 `form.validateFields()` 提交时携带图片 URL。
+- `web/server.cjs`:CSP `img-src` 放行 `http://100.83.103.1:7480`(开发 S3 源)。
+- 已部署并重启前端(新 bundle `index-CSVEb7gG.js`),并按用户确认直接改库把白僵病已上传的图谱 URL 补绑到 `diseases.image_url`。
+
+### 思路与决策
+
+- 根因定位:日志显示上传 200、PATCH 200,但 `image_url` 为空 → 定位为前端表单未注册 `imageUrl`(antd `validateFields()` 只返回已注册字段),以及 CSP 跨源拦截预览。
+- 修复方式:用隐藏 `Form.Item` 注册字段(antd 标准做法),避免手工拼请求体;CSP 采用放行开发 S3 源,生产切云 OSS/COS 时再替换为 OSS/CDN 域名或改同源代理。
+- 数据补绑:用户明确选择「直接改库」,回滚点为备份的 `diseases` 表 dump 与逐行 UPDATE 反向语句。
+
+### 验证
+
+- `web`:`npm run lint` 通过(仅既有无关警告)、`npm run build` 通过。
+- 机制红绿验证:用 rc-field-form 验证 `validateFields()` 会丢弃未注册的 `imageUrl`,注册/合并后可携带。
+- 部署验证:`http://localhost:5174/` 200;CSP 头含 `http://100.83.103.1:7480`;新 bundle 200;`/api/v1/health` 200;bundle 中可见 `name:"imageUrl", hidden:!0`。
+- 数据验证:`UPDATE 1`;查询确认白僵病 `image_url` 已绑定;图片匿名 GET 200(image/jpeg)。
+
+### 回滚点
+
+- 前端:`/home/pan/backups/web-20260812-1605/`(旧 dist、server.cjs、web.log)恢复后重启 node。
+- 数据:`diseases` 表 dump `/home/pan/backups/diseases-20260812-1605.dump`;或执行 `UPDATE diseases SET image_url=NULL, updated_at=now() WHERE id='17b1cae4-21cc-4996-9db3-96f785aef800';`
+
## 2026-08-12 模块:知识库初始版(计划 #10)
### 做了什么
diff --git a/故障排查处理记录.md b/故障排查处理记录.md
index 717a26d..2497291 100644
--- a/故障排查处理记录.md
+++ b/故障排查处理记录.md
@@ -1023,3 +1023,58 @@ APP 使用 `react-native-vector-icons` 的 `MaterialCommunityIcons` 字体渲染
- `ScrollView` 的 `style` 设置容器样式(如 flex、背景色),`contentContainerStyle` 设置内容样式(如 padding)
- 仅修改 JS/TS 代码,Metro reload 即可生效,无需重新构建 APK
+---
+
+## 三十二、Web 知识库症状图谱预览打不开且保存不生效
+
+**现象**:Web「知识库 → 蚕病百科」编辑病种时上传新的症状图谱:编辑弹窗里的预览图打不开;点击提交提示"保存成功",但重新打开/刷新后图谱仍未绑定(`image_url` 为空)。
+
+**原因(两个独立问题,均为前端问题,后端与存储正常)**:
+
+1. **预览图打不开:CSP 拦截跨源图片**
+ `web/server.cjs` 的 `Content-Security-Policy` 为 `img-src 'self' data: blob:`,只允许同源图片。上传成功后返回的图片 URL 是 `http://100.83.103.1:7480/silk-images/...`(Ceph RGW,端口 7480 与页面 5174 不同源),浏览器直接拦截,图片本身可匿名访问(200 image/jpeg)。
+
+2. **保存不生效:`imageUrl` 未注册到表单,提交时被丢弃**
+ `web/src/pages/Knowledge.tsx` 上传成功后执行 `form.setFieldValue('imageUrl', res.url)`,但「症状图谱图片」的 `Form.Item` 只有 label、没有 `name="imageUrl"`。antd 的 `form.validateFields()` 只返回已注册(有 `Form.Item name`)的字段,`imageUrl` 不在其中,因此 `PATCH /knowledge/diseases/:id` 的请求体里根本没有 `imageUrl`,后端收到 200 但 `image_url` 列不更新(`updated_at` 会变)。
+
+**排查证据(开发服务器 100.83.103.1)**:
+- `web/web.log`:`POST /api/v1/knowledge/images` → `PATCH /api/v1/knowledge/diseases/17b1cae4-21cc-4996-9db3-96f785aef800` → `GET /api/v1/knowledge/diseases`
+- `server-go/server-go.log`(实际生效日志,非 `/home/pan/silk/server-go.log`):15:57:54 `POST images` 200;15:58:03 `PATCH` 200
+- Ceph `silk-images` 桶:存在 `knowledge/20260812/36a2bc56e466fb41.jpeg`(286608 字节,image/jpeg),匿名 GET 200
+- PostgreSQL:白僵病 `image_url` 为 NULL;全表无任何病种有 `image_url`
+- 部署产物校验:服务器 `server.cjs` md5 与仓库一致(CSP 生效);新 bundle 中已含修复代码
+
+**处理**:
+
+1. 前端修复(`web/src/pages/Knowledge.tsx`、`web/server.cjs`):
+ - 「症状图谱图片」后新增隐藏字段,使 `imageUrl` 注册进表单并被 `validateFields()` 返回:
+ ```tsx
+
+
+
+ ```
+ - CSP `img-src` 放行开发环境 S3 源:`img-src 'self' data: blob: http://100.83.103.1:7480`
+2. 构建与部署(本地 `npm run lint`、`npm run build` 均通过):
+ - 备份旧产物:`/home/pan/backups/web-20260812-1605/`(dist、server.cjs、web.log)
+ - 上传新 `dist` 与 `server.cjs`,重启 node(旧 pid 3764542 → 新 pid 3803472)
+ - 验证:`http://localhost:5174/` 200;CSP 头含 `http://100.83.103.1:7480`;新 bundle `index-CSVEb7gG.js` 200;`/api/v1/health` 200
+3. 数据补绑(用户确认直接改库):
+ ```sql
+ UPDATE diseases
+ SET image_url = 'http://100.83.103.1:7480/silk-images/knowledge/20260812/36a2bc56e466fb41.jpeg',
+ updated_at = now()
+ WHERE id = '17b1cae4-21cc-4996-9db3-96f785aef800';
+ ```
+ 结果:`UPDATE 1`,查询确认 `image_url` 已写入;图片匿名 GET 200。
+
+**验证结果**:DB 行已绑定图谱 URL;CSP 已放行;部署产物包含隐藏字段注册;lint/build 通过(详见上文)。
+
+**回滚点**:
+- 前端回滚:恢复 `/home/pan/backups/web-20260812-1605/` 下的旧 `dist`/`server.cjs` 并重启 node
+- 数据回滚:`UPDATE diseases SET image_url = NULL, updated_at = now() WHERE id = '17b1cae4-21cc-4996-9db3-96f785aef800';` 或 `pg_restore` 恢复 `/home/pan/backups/diseases-20260812-1605.dump`
+
+**注意事项**:
+- `/home/pan/silk` 不是 git 仓库,无法打 tag 回滚,只能依赖文件备份(本次已备份)
+- 部署指南中的 `admin/admin123` 已无法登录(401 用户名或密码错误),本次 API 冒烟改用 DB 直接验证;如需 API 级冒烟请先确认当前 admin 密码
+- 已知边界:编辑页若点「移除」图片再保存,`imageUrl` 会以 `undefined` 提交(键被省略),不会清空库里已有图片;如需「移除即清空」后续应显式传 `null`
+- CSP 当前放行的是开发服务器 IP;生产切云 OSS/COS 时需把 OSS/CDN 域名加入 `img-src`,或改为同源代理