# v3.9 UI 重构方案(保持功能不变)
## 背景与问题
当前 `src/mvp/py/argus/service/ui.py` 单文件约 1400+ 行,包含:
- 全局 CSS/JS(长字符串)
- 布局渲染(nav/page 拼接)
- 11 个页面的 HTML + 大段内嵌 JS(包含 TaskSpec 模板与表单逻辑)
导致:变更难定位、合并冲突多、缺少模块边界、复用困难、测试覆盖薄弱。
## 目标(功能不变)
- **路由与页面行为完全不变**:URL、返回内容、按钮/表单行为、localStorage key(`mvp_token`/`mvp_sftp_password`)、API 调用路径保持不变。
- **不引入前端构建链/新依赖**(仍然用纯字符串/轻量模板函数)。
- 将 UI 拆分为可维护的多个文件(放到 `src/mvp/py/argus/ui/`)。
- 增加最小的单测(确保路由可访问、关键 DOM 标识存在)。
## 非目标
- 不重做 UI 样式/交互;不引入 React/Vue;不改后端 API。
- 不新增鉴权逻辑(仍然是浏览器 localStorage + Bearer token)。
## 拆分后的目录结构(建议)
新增包:`src/mvp/py/argus/ui/`
```
argus/ui/
__init__.py # register_ui_routes(app) 统一入口
assets/
base_css.py # BASE_CSS 常量
base_js.py # BASE_JS 常量(apiFetch/apiJson 等通用函数)
layout/
nav.py # nav(active) + 链接配置
page.py # page(title, active, body, script, extra_head=...)
pages/
login.py # /ui/login
tasks.py # /ui/tasks
task_new.py # /ui/tasks/new(模板常量 + 表单 JS)
task_detail.py # /ui/tasks/{task_id}
task_logs.py # /ui/tasks/{task_id}/logs
serving.py # /ui/serving, /ui/serving/new, /ui/serving/{model_key}
data.py # /ui/data
admin.py # /ui/admin
routes.py # 将各 pages.register(app) 聚合注册
```
兼容层(可选但推荐):保留 `src/mvp/py/argus/service/ui.py` 仅做转发:
```py
from argus.ui import register_ui_routes
```
这样可以避免一次性改动 `service/app.py` 的 import 路径,减少风险。
## 页面拆分原则
每个 page 模块提供两个函数:
- `render(...) -> HTMLResponse`:只负责拼接 body/script(不直接碰 FastAPI app)。
- `register(app: FastAPI) -> None`:只负责挂载路由(`@app.get(...)`)。
通用能力下沉:
- `_BASE_CSS`/`_BASE_JS` 移到 `assets/`。
- `_nav()`、`_page()` 移到 `layout/`。
- 大块常量(TaskSpec 模板、UI 文案)放在页面模块同文件顶部,避免散落在函数内部。
## 资源交付方式(两种可选)
### 方案 A(最稳):继续内联 CSS/JS,但拆到不同 Python 文件
- `page()` 内继续 ``、``。
- 只改变代码组织,不改变浏览器加载方式,风险最低。
### 方案 B(推荐中期):新增静态端点分发资源
新增:
- `GET /ui/assets/base.css`
- `GET /ui/assets/base.js`
页面改为 `` + ``。
优点:减少 HTML 体积、浏览器缓存更好;缺点:需要确认反向代理/中间件不拦截这些路由。
建议 v3.9 先落地方案 A,稳定后再做方案 B。
## 迁移步骤(建议分 3 次 PR)
1) **抽公共层**:引入 `argus/ui/assets/*`、`argus/ui/layout/*`,保持 UI 输出完全一致;`service/ui.py` 仍在但内部改为调用新 layout(或先不动)。
2) **按页面迁移**:逐个把 routes 迁移到 `argus/ui/pages/*`,每迁一个页面就加一个最小测试用例(200 + 关键文本存在)。
3) **清理与稳定**:`service/ui.py` 变为兼容转发;可选引入 `/ui/assets/*` 静态端点(方案 B)。
## 测试策略(最小但有效)
新增 `src/mvp/py/tests/test_ui_pages.py`:
- 创建 FastAPI app(复用现有测试的 app 初始化方式)
- 请求下列页面,断言 `status_code == 200`:
- `/ui/login`, `/ui/tasks`, `/ui/tasks/new`, `/ui/serving`, `/ui/data`, `/ui/admin`
- 断言响应包含稳定锚点文本(例如 `Argus MVP`, `New Task`, `Tasks`),避免脆弱的全量快照。
## 验收标准(Definition of Done)
- 11 个 `/ui/*` 路由行为与输出不变(人工 smoke + 自动化最小测试)。
- `src/mvp/py/argus/service/ui.py` 不再包含大段 HTML/JS(仅兼容转发或极薄封装)。
- 新增/修改 UI 页面不需要触碰 1000+ 行单文件;每页的改动范围限定在对应模块。