diff --git a/.gitignore b/.gitignore
index d7fbf8d..bac501e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -187,9 +187,11 @@ data/sub/waterindex*.xlsx
data/sub/png/watermask.png
# 图标文件(仅需保留 vector/svg,删除像素图标压缩包副本)
-data/icons-1/*.ico
-data/icons/*.png
-data/icons/word/*.png
+data/icons-1/
+data/icons/
# 旧版脚手架(遗留实验代码)
new/
+
+# 前端脚手架(未集成的独立 Vue 项目)
+frontend/
diff --git a/data/icons-1/1.ico b/data/icons-1/1.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/1.ico and /dev/null differ
diff --git a/data/icons-1/10.ico b/data/icons-1/10.ico
deleted file mode 100644
index 7d1af4d..0000000
Binary files a/data/icons-1/10.ico and /dev/null differ
diff --git a/data/icons-1/11.ico b/data/icons-1/11.ico
deleted file mode 100644
index e1f6d1e..0000000
Binary files a/data/icons-1/11.ico and /dev/null differ
diff --git a/data/icons-1/2.ico b/data/icons-1/2.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/2.ico and /dev/null differ
diff --git a/data/icons-1/3.ico b/data/icons-1/3.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/3.ico and /dev/null differ
diff --git a/data/icons-1/4.ico b/data/icons-1/4.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/4.ico and /dev/null differ
diff --git a/data/icons-1/5.ico b/data/icons-1/5.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/5.ico and /dev/null differ
diff --git a/data/icons-1/6.ico b/data/icons-1/6.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/6.ico and /dev/null differ
diff --git a/data/icons-1/7.ico b/data/icons-1/7.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/7.ico and /dev/null differ
diff --git a/data/icons-1/8.ico b/data/icons-1/8.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/8.ico and /dev/null differ
diff --git a/data/icons-1/9.ico b/data/icons-1/9.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/9.ico and /dev/null differ
diff --git a/data/icons-1/IMG_20250904_123453.ico b/data/icons-1/IMG_20250904_123453.ico
deleted file mode 100644
index 24e2721..0000000
Binary files a/data/icons-1/IMG_20250904_123453.ico and /dev/null differ
diff --git a/data/icons-1/IMG_20250904_134825.ico b/data/icons-1/IMG_20250904_134825.ico
deleted file mode 100644
index 26da39f..0000000
Binary files a/data/icons-1/IMG_20250904_134825.ico and /dev/null differ
diff --git a/data/icons-1/IRIS.ico b/data/icons-1/IRIS.ico
deleted file mode 100644
index 4a5f875..0000000
Binary files a/data/icons-1/IRIS.ico and /dev/null differ
diff --git a/data/icons-1/Mega Water 1.0.ico b/data/icons-1/Mega Water 1.0.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/Mega Water 1.0.ico and /dev/null differ
diff --git a/data/icons-1/fenmian.ico b/data/icons-1/fenmian.ico
deleted file mode 100644
index 9a3f85c..0000000
Binary files a/data/icons-1/fenmian.ico and /dev/null differ
diff --git a/data/icons-1/lica.ico b/data/icons-1/lica.ico
deleted file mode 100644
index 38a666e..0000000
Binary files a/data/icons-1/lica.ico and /dev/null differ
diff --git a/data/icons-1/liucheng.ico b/data/icons-1/liucheng.ico
deleted file mode 100644
index 71a5177..0000000
Binary files a/data/icons-1/liucheng.ico and /dev/null differ
diff --git a/data/icons-1/logo.ico b/data/icons-1/logo.ico
deleted file mode 100644
index 9158369..0000000
Binary files a/data/icons-1/logo.ico and /dev/null differ
diff --git a/data/icons-1/table.ico b/data/icons-1/table.ico
deleted file mode 100644
index 1a46946..0000000
Binary files a/data/icons-1/table.ico and /dev/null differ
diff --git a/data/icons-1/uitubiao.ico b/data/icons-1/uitubiao.ico
deleted file mode 100644
index c095b28..0000000
Binary files a/data/icons-1/uitubiao.ico and /dev/null differ
diff --git a/data/icons-1/图片矢量化与编辑.ico b/data/icons-1/图片矢量化与编辑.ico
deleted file mode 100644
index 1b277a9..0000000
Binary files a/data/icons-1/图片矢量化与编辑.ico and /dev/null differ
diff --git a/data/icons-1/屏幕截图 2026-03-27 172136.ico b/data/icons-1/屏幕截图 2026-03-27 172136.ico
deleted file mode 100644
index 4012786..0000000
Binary files a/data/icons-1/屏幕截图 2026-03-27 172136.ico and /dev/null differ
diff --git a/data/icons-1/屏幕截图 2026-03-31 144131.ico b/data/icons-1/屏幕截图 2026-03-31 144131.ico
deleted file mode 100644
index 11b5bad..0000000
Binary files a/data/icons-1/屏幕截图 2026-03-31 144131.ico and /dev/null differ
diff --git a/data/icons-1/演示文稿1.ico b/data/icons-1/演示文稿1.ico
deleted file mode 100644
index 414c7a0..0000000
Binary files a/data/icons-1/演示文稿1.ico and /dev/null differ
diff --git a/data/icons-1/生成软件GUI矢量图标 (2).ico b/data/icons-1/生成软件GUI矢量图标 (2).ico
deleted file mode 100644
index 8a58994..0000000
Binary files a/data/icons-1/生成软件GUI矢量图标 (2).ico and /dev/null differ
diff --git a/data/icons-1/生成软件GUI矢量图标 (3).ico b/data/icons-1/生成软件GUI矢量图标 (3).ico
deleted file mode 100644
index c2ea214..0000000
Binary files a/data/icons-1/生成软件GUI矢量图标 (3).ico and /dev/null differ
diff --git a/data/icons-1/生成软件GUI矢量图标 (4).ico b/data/icons-1/生成软件GUI矢量图标 (4).ico
deleted file mode 100644
index 512b2c8..0000000
Binary files a/data/icons-1/生成软件GUI矢量图标 (4).ico and /dev/null differ
diff --git a/data/icons/1.png b/data/icons/1.png
deleted file mode 100644
index 8314047..0000000
Binary files a/data/icons/1.png and /dev/null differ
diff --git a/data/icons/10.png b/data/icons/10.png
deleted file mode 100644
index 91f5c67..0000000
Binary files a/data/icons/10.png and /dev/null differ
diff --git a/data/icons/11.png b/data/icons/11.png
deleted file mode 100644
index 7c766e8..0000000
Binary files a/data/icons/11.png and /dev/null differ
diff --git a/data/icons/2.png b/data/icons/2.png
deleted file mode 100644
index aa39638..0000000
Binary files a/data/icons/2.png and /dev/null differ
diff --git a/data/icons/3.png b/data/icons/3.png
deleted file mode 100644
index a8ee2cd..0000000
Binary files a/data/icons/3.png and /dev/null differ
diff --git a/data/icons/4.png b/data/icons/4.png
deleted file mode 100644
index 3c4bef5..0000000
Binary files a/data/icons/4.png and /dev/null differ
diff --git a/data/icons/5.png b/data/icons/5.png
deleted file mode 100644
index 0b09d37..0000000
Binary files a/data/icons/5.png and /dev/null differ
diff --git a/data/icons/6.png b/data/icons/6.png
deleted file mode 100644
index 87db169..0000000
Binary files a/data/icons/6.png and /dev/null differ
diff --git a/data/icons/7.png b/data/icons/7.png
deleted file mode 100644
index bde5180..0000000
Binary files a/data/icons/7.png and /dev/null differ
diff --git a/data/icons/8.png b/data/icons/8.png
deleted file mode 100644
index f1d6e5d..0000000
Binary files a/data/icons/8.png and /dev/null differ
diff --git a/data/icons/9.png b/data/icons/9.png
deleted file mode 100644
index 687d418..0000000
Binary files a/data/icons/9.png and /dev/null differ
diff --git a/data/icons/IRIS.png b/data/icons/IRIS.png
deleted file mode 100644
index 39465c9..0000000
Binary files a/data/icons/IRIS.png and /dev/null differ
diff --git a/data/icons/Mega Water 1.0.jpg b/data/icons/Mega Water 1.0.jpg
deleted file mode 100644
index 7f4a9bd..0000000
Binary files a/data/icons/Mega Water 1.0.jpg and /dev/null differ
diff --git a/data/icons/logo.png b/data/icons/logo.png
deleted file mode 100644
index a790f36..0000000
Binary files a/data/icons/logo.png and /dev/null differ
diff --git a/data/icons/table.png b/data/icons/table.png
deleted file mode 100644
index 5c61379..0000000
Binary files a/data/icons/table.png and /dev/null differ
diff --git a/data/icons/uitubiao.jpg b/data/icons/uitubiao.jpg
deleted file mode 100644
index d0f8a5a..0000000
Binary files a/data/icons/uitubiao.jpg and /dev/null differ
diff --git a/data/icons/word/IMG_20250904_123453.jpg b/data/icons/word/IMG_20250904_123453.jpg
deleted file mode 100644
index 4f564fe..0000000
Binary files a/data/icons/word/IMG_20250904_123453.jpg and /dev/null differ
diff --git a/data/icons/word/IMG_20250904_134825.jpg b/data/icons/word/IMG_20250904_134825.jpg
deleted file mode 100644
index b47bc1d..0000000
Binary files a/data/icons/word/IMG_20250904_134825.jpg and /dev/null differ
diff --git a/data/icons/word/fenmian.png b/data/icons/word/fenmian.png
deleted file mode 100644
index 645b156..0000000
Binary files a/data/icons/word/fenmian.png and /dev/null differ
diff --git a/data/icons/word/lica.png b/data/icons/word/lica.png
deleted file mode 100644
index 3ecf891..0000000
Binary files a/data/icons/word/lica.png and /dev/null differ
diff --git a/data/icons/word/liucheng.png b/data/icons/word/liucheng.png
deleted file mode 100644
index 8073e7e..0000000
Binary files a/data/icons/word/liucheng.png and /dev/null differ
diff --git a/data/icons/word/图片矢量化与编辑.png b/data/icons/word/图片矢量化与编辑.png
deleted file mode 100644
index be7ddd3..0000000
Binary files a/data/icons/word/图片矢量化与编辑.png and /dev/null differ
diff --git a/data/icons/word/屏幕截图 2026-03-31 144131.png b/data/icons/word/屏幕截图 2026-03-31 144131.png
deleted file mode 100644
index b059f9f..0000000
Binary files a/data/icons/word/屏幕截图 2026-03-31 144131.png and /dev/null differ
diff --git a/data/icons/word/演示文稿1.png b/data/icons/word/演示文稿1.png
deleted file mode 100644
index 79dacf4..0000000
Binary files a/data/icons/word/演示文稿1.png and /dev/null differ
diff --git a/data/icons/屏幕截图 2026-03-27 172136.png b/data/icons/屏幕截图 2026-03-27 172136.png
deleted file mode 100644
index 4509c3b..0000000
Binary files a/data/icons/屏幕截图 2026-03-27 172136.png and /dev/null differ
diff --git a/data/icons/生成软件GUI矢量图标 (2).png b/data/icons/生成软件GUI矢量图标 (2).png
deleted file mode 100644
index a03efc0..0000000
Binary files a/data/icons/生成软件GUI矢量图标 (2).png and /dev/null differ
diff --git a/data/icons/生成软件GUI矢量图标 (3).png b/data/icons/生成软件GUI矢量图标 (3).png
deleted file mode 100644
index 3fa26fc..0000000
Binary files a/data/icons/生成软件GUI矢量图标 (3).png and /dev/null differ
diff --git a/data/icons/生成软件GUI矢量图标 (4).png b/data/icons/生成软件GUI矢量图标 (4).png
deleted file mode 100644
index ea3d2f4..0000000
Binary files a/data/icons/生成软件GUI矢量图标 (4).png and /dev/null differ
diff --git a/frontend/.env.development b/frontend/.env.development
deleted file mode 100644
index 73033fe..0000000
--- a/frontend/.env.development
+++ /dev/null
@@ -1,2 +0,0 @@
-# 联调期指向本地 FastAPI dev 服务
-VITE_API_BASE_URL=http://127.0.0.1:9090
diff --git a/frontend/.gitignore b/frontend/.gitignore
deleted file mode 100644
index df26203..0000000
--- a/frontend/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-node_modules
-dist
-dist-ssr
-.vite
-*.local
-.DS_Store
-*.log
diff --git a/frontend/env.d.ts b/frontend/env.d.ts
deleted file mode 100644
index 18fd41f..0000000
--- a/frontend/env.d.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-///
-
-interface ImportMetaEnv {
- readonly VITE_API_BASE_URL?: string
-}
-
-interface ImportMeta {
- readonly env: ImportMetaEnv
-}
-
-declare module '*.vue' {
- import type { DefineComponent } from 'vue'
- const component: DefineComponent<{}, {}, any>
- export default component
-}
diff --git a/frontend/index.html b/frontend/index.html
deleted file mode 100644
index 286a51d..0000000
--- a/frontend/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- WQ_GUI · 水质反演联调控制台
-
-
-
-
-
-
diff --git a/frontend/package.json b/frontend/package.json
deleted file mode 100644
index 013abb7..0000000
--- a/frontend/package.json
+++ /dev/null
@@ -1,25 +0,0 @@
-{
- "name": "wq-gui-frontend",
- "private": true,
- "version": "0.0.1",
- "type": "module",
- "scripts": {
- "dev": "vite",
- "build": "vue-tsc --noEmit && vite build",
- "preview": "vite preview",
- "type-check": "vue-tsc --noEmit"
- },
- "dependencies": {
- "vue": "^3.4.27",
- "element-plus": "^2.7.5",
- "@element-plus/icons-vue": "^2.3.1",
- "axios": "^1.7.2"
- },
- "devDependencies": {
- "@types/node": "^20.12.12",
- "@vitejs/plugin-vue": "^5.0.4",
- "typescript": "^5.4.5",
- "vite": "^5.2.11",
- "vue-tsc": "^2.0.19"
- }
-}
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
deleted file mode 100644
index 02e62bf..0000000
--- a/frontend/src/App.vue
+++ /dev/null
@@ -1,225 +0,0 @@
-
-
-
高光谱水质反演控制台
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 开始训练
-
-
-
-
-
任务 ID: {{ trainTaskId }}
-
当前状态:
-
- {{ trainPoller?.status?.value || 'PENDING' }}
-
-
-
-
-
-
-
-
-
-
- {{ trainPoller.result.value.model_id }}
- {{ Number(trainPoller.result.value.test_r2).toFixed(4) }}
- {{ Number(trainPoller.result.value.test_rmse).toFixed(4) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 开始大图反演推断
-
-
-
-
-
任务 ID: {{ predictTaskId }}
-
当前状态:
-
- {{ predictPoller?.status?.value || 'PENDING' }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/src/api/request.ts b/frontend/src/api/request.ts
deleted file mode 100644
index c9a1693..0000000
--- a/frontend/src/api/request.ts
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * Axios 单例 + 响应拦截器
- * --------------------------------
- * 1. baseURL 默认指向本地 FastAPI dev 服务。
- * 通过 Vite 环境变量 VITE_API_BASE_URL 可覆盖, 例如:
- * .env.development: VITE_API_BASE_URL=http://127.0.0.1:8000
- * .env.production: VITE_API_BASE_URL=https://api.example.com
- *
- * 2. 响应拦截器统一 unwrap response.data, 调用方拿到的是真正的业务对象,
- * 而不是 AxiosResponse 包装。失败时统一抛 Error, message 优先取
- * FastAPI 的 detail 字段。
- *
- * 3. 类型增强: cast 成 UnwrappedAxiosInstance, 让 request.get(url)
- * 的返回类型直接是 T, 而不是 AxiosResponse, 调用方无需二次解包。
- */
-import axios, {
- type AxiosInstance,
- type AxiosRequestConfig,
-} from 'axios'
-
-// 在 Vite 下用 import.meta.env; 其它环境 (webpack/直接 ts-node) 兜底到 process.env
-type ViteEnv = { env?: Record }
-const viteEnv: ViteEnv | undefined =
- typeof import.meta !== 'undefined' ? ((import.meta as unknown) as ViteEnv) : undefined
-
-const baseURL: string =
- viteEnv?.env?.VITE_API_BASE_URL ??
- (typeof process !== 'undefined' && process.env?.VITE_API_BASE_URL) ??
- 'http://127.0.0.1:9090'
-
-const _instance: AxiosInstance = axios.create({
- baseURL,
- timeout: 15000,
- headers: {
- 'Content-Type': 'application/json',
- },
- // FastAPI 开发期 CORS 是 allow_origins=["*"], 不需要带 cookie
- withCredentials: false,
-})
-
-// ----- 请求拦截器: 预留 token / 日志位 -----
-_instance.interceptors.request.use(
- (config) => {
- // const token = localStorage.getItem('token')
- // if (token) config.headers.Authorization = `Bearer ${token}`
- return config
- },
- (error) => Promise.reject(error),
-)
-
-// ----- 响应拦截器: unwrap data + 统一错误 message -----
-_instance.interceptors.response.use(
- (response) => response.data,
- (error) => {
- const detail = error?.response?.data?.detail
- const message =
- (typeof detail === 'string' ? detail : detail?.msg) ??
- error?.response?.data?.message ??
- error?.message ??
- '请求失败'
- return Promise.reject(new Error(message))
- },
-)
-
-// ----- 类型增强: 把响应拦截器 unwrap 的事实在类型上表达出来 -----
-type UnwrappedAxiosInstance = Omit<
- AxiosInstance,
- 'get' | 'delete' | 'head' | 'options' | 'post' | 'put' | 'patch'
-> & {
- get(url: string, config?: AxiosRequestConfig): Promise
- delete(url: string, config?: AxiosRequestConfig): Promise
- head(url: string, config?: AxiosRequestConfig): Promise
- options(url: string, config?: AxiosRequestConfig): Promise
- post(
- url: string,
- data?: D,
- config?: AxiosRequestConfig,
- ): Promise
- put(
- url: string,
- data?: D,
- config?: AxiosRequestConfig,
- ): Promise
- patch(
- url: string,
- data?: D,
- config?: AxiosRequestConfig,
- ): Promise
-}
-
-const request = _instance as UnwrappedAxiosInstance
-
-export default request
-export { baseURL }
diff --git a/frontend/src/api/tasks.ts b/frontend/src/api/tasks.ts
deleted file mode 100644
index a15d70f..0000000
--- a/frontend/src/api/tasks.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-/**
- * 与 FastAPI 后端对接的 API 函数
- * --------------------------------
- * 全部用 request 单例, 调用方拿到的就是业务对象 (response 拦截器已 unwrap)。
- *
- * 后端路由:
- * GET /api/algorithms
- * POST /api/process/deglint
- * POST /api/modeling/train
- * POST /api/modeling/predict (额外, 与 train 配套)
- * GET /api/tasks/{task_id}
- */
-import request from './request'
-
-// ============================================================
-// 通用类型
-// ============================================================
-
-/** 后端任务状态机 (与 app.core.task_store.TASK_STORE 保持一致) */
-export type TaskStatus = 'PENDING' | 'PROCESSING' | 'SUCCESS' | 'FAILED'
-
-/** 任务类型, 区分去耀斑 / 训练 / 推断 */
-export type TaskKind = 'deglint' | 'train' | 'predict'
-
-/** 提交后端后立即返回的最小任务凭证 */
-export interface TaskAcceptedResponse {
- task_id: string
- status: TaskStatus
- kind: TaskKind
-}
-
-/**
- * 任务详情 (与后端 TASK_STORE 里记录的字段对齐, 通用 + 各 kind 增量字段)
- * 用 [key: string]: unknown 兜底, 兼容未来后端新增字段
- */
-export interface TaskRecord {
- task_id: string
- kind: TaskKind
- status: TaskStatus
- // 去耀斑
- algorithm?: string
- input_zarr_path?: string
- output_zarr_path?: string | null
- // 训练
- model_type?: string
- target?: string
- train_data_path?: string
- feature_start?: number | string
- params?: Record
- model_id?: string | null
- model_path?: string | null
- test_r2?: number | null
- test_rmse?: number | null
- test_mae?: number | null
- n_features?: number | null
- n_samples?: number | null
- // 推断
- // (model_id / input_zarr_path / output_zarr_path 已在上方)
- // 失败
- error?: string | null
- traceback?: string | null
- // 元
- created_at?: string
- updated_at?: string
- [key: string]: unknown
-}
-
-// ============================================================
-// 1) 算法列表 GET /api/algorithms
-// ============================================================
-
-export interface AlgorithmInfo {
- name: string
- doc?: string
-}
-
-export interface AlgorithmListResponse {
- algorithms: AlgorithmInfo[]
- count: number
-}
-
-export function getAlgorithms(): Promise {
- return request.get('/api/algorithms')
-}
-
-// ============================================================
-// 2) 提交去耀斑 POST /api/process/deglint
-// ============================================================
-
-export interface DeglintParams {
- input_zarr_path: string
- output_zarr_path?: string
- /** 算法自定义参数 (D_max / band 选择等) */
- [key: string]: unknown
-}
-
-export function submitDeglint(
- method: string,
- params: DeglintParams,
-): Promise {
- return request.post(
- '/api/process/deglint',
- { method, params },
- )
-}
-
-// ============================================================
-// 3) 提交训练 POST /api/modeling/train
-// ============================================================
-
-export interface TrainRequest {
- model_type: string
- target: string
- train_data_path: string
- /** 特征起始列, int 索引或 str 列名, 默认 4 */
- feature_start?: number | string
- /** sklearn 估计器超参 */
- params?: Record
-}
-
-export function submitTrain(payload: TrainRequest): Promise {
- return request.post(
- '/api/modeling/train',
- payload,
- )
-}
-
-// ============================================================
-// 4) 提交推断 POST /api/modeling/predict (配套, 训练后才能用)
-// ============================================================
-
-export interface PredictRequest {
- model_id: string
- input_zarr_path: string
- output_zarr_path?: string
-}
-
-export function submitPredict(
- payload: PredictRequest,
-): Promise {
- return request.post(
- '/api/modeling/predict',
- payload,
- )
-}
-
-// ============================================================
-// 5) 查询任务状态 GET /api/tasks/{task_id}
-// ============================================================
-
-export function getTaskStatus(task_id: string): Promise {
- return request.get(
- `/api/tasks/${encodeURIComponent(task_id)}`,
- )
-}
diff --git a/frontend/src/composables/useTaskPoller.ts b/frontend/src/composables/useTaskPoller.ts
deleted file mode 100644
index 34349ce..0000000
--- a/frontend/src/composables/useTaskPoller.ts
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * 任务轮询 Composable (Vue 3 + TypeScript)
- * -----------------------------------------
- * 用法 1 — 静态 task_id, 立即开始轮询:
- * const { status, result, error, waitForCompletion } = useTaskPoller(taskId)
- *
- * 用法 2 — 响应式 task_id (异步拿到后赋值, 自动开始):
- * const taskId = ref(null)
- * const poller = useTaskPoller(taskId)
- * ;(async () => { taskId.value = (await submitTrain({...})).task_id })()
- * await poller.waitForCompletion()
- *
- * 用法 3 — 手动控制:
- * const poller = useTaskPoller()
- * poller.start(taskId) // 开始
- * poller.stop() // 停止
- * poller.reset() // 清空状态
- *
- * 设计要点:
- * - 终态 (SUCCESS/FAILED) 自动停止轮询
- * - 组件卸载自动清理 (onUnmounted)
- * - 网络错误不立刻终止, 计入 error.value 但继续轮询 (兼容临时抖动)
- * - waitForCompletion 是单次承诺: SUCCESS resolve(record), FAILED reject(error)
- * 外部 stop() 也会 reject
- */
-import {
- onUnmounted,
- ref,
- watch,
- type MaybeRefOrGetter,
- type Ref,
-} from 'vue'
-import { toValue } from 'vue'
-import {
- getTaskStatus,
- type TaskRecord,
- type TaskStatus,
-} from '../api/tasks'
-
-// 显式包含 'idle', 用于未开始轮询的初始态
-export type PollerStatus = TaskStatus | 'idle'
-
-export interface UseTaskPollerOptions {
- /** 轮询间隔 ms, 默认 2000 */
- intervalMs?: number
- /** task_id 变 null 时是否自动停止, 默认 true */
- autoStopOnNull?: boolean
-}
-
-export interface UseTaskPollerReturn {
- /** 当前任务状态, 初始 'idle' */
- status: Ref
- /** SUCCESS 时的完整任务记录 (含 output_zarr_path / model_id 等) */
- result: Ref
- /** FAILED 时的错误描述, 或轮询过程中网络异常的消息 */
- error: Ref
- /** 最新一次拉取到的任务记录 (含 PENDING/PROCESSING 占位字段) */
- record: Ref
- /** 是否正在轮询中 */
- isPolling: Ref
- /** 当前轮询的 task_id (可能为 null) */
- taskId: Ref
- /** 开始轮询某 task, 已轮询同一 id 时是 no-op */
- start: (taskId: string) => void
- /** 主动停止 (会 reject 未完成的 waitForCompletion) */
- stop: () => void
- /** 清空所有状态回 'idle' */
- reset: () => void
- /**
- * 等到 SUCCESS/FAILED。
- * - SUCCESS: resolve(record)
- * - FAILED : reject(Error)
- * - stop() : reject(Error('Polling stopped'))
- * - 组件卸载: reject(Error('Component unmounted'))
- * 已处于终态时立刻 resolve/reject, 不重复等待。
- */
- waitForCompletion: () => Promise
-}
-
-export function useTaskPoller(
- taskIdSource?: MaybeRefOrGetter,
- options: UseTaskPollerOptions = {},
-): UseTaskPollerReturn {
- const { intervalMs = 2000, autoStopOnNull = true } = options
-
- const status = ref('idle')
- const result = ref(null)
- const error = ref(null)
- const record = ref(null)
- const isPolling = ref(false)
- const taskId = ref(null)
-
- let timerId: ReturnType | null = null
- let inFlightTick = false
- let resolveWait: ((rec: TaskRecord) => void) | null = null
- let rejectWait: ((err: Error) => void) | null = null
-
- function clearTimer() {
- if (timerId !== null) {
- clearInterval(timerId)
- timerId = null
- }
- }
-
- function resolveOrRejectWait(rec: TaskRecord | null, err: Error | null) {
- const r = resolveWait
- const rj = rejectWait
- resolveWait = null
- rejectWait = null
- if (rec && r) r(rec)
- else if (err && rj) rj(err)
- }
-
- function applyTerminalRecord(rec: TaskRecord) {
- record.value = rec
- status.value = rec.status
- if (rec.status === 'SUCCESS') {
- result.value = rec
- error.value = null
- resolveOrRejectWait(rec, null)
- } else if (rec.status === 'FAILED') {
- result.value = null
- error.value = rec.error ?? '任务失败 (无具体错误信息)'
- resolveOrRejectWait(
- null,
- new Error(error.value ?? '任务失败'),
- )
- }
- }
-
- async function tick() {
- const currentId = taskId.value
- if (!currentId || inFlightTick) return
- inFlightTick = true
- try {
- const rec = await getTaskStatus(currentId)
- // 防止 await 期间用户 stop() / start() 了别的 task
- if (taskId.value !== currentId) return
- if (rec.status === 'SUCCESS' || rec.status === 'FAILED') {
- applyTerminalRecord(rec)
- stop()
- } else {
- // PENDING / PROCESSING 阶段, 更新 record 与 status 供 UI 展示
- record.value = rec
- status.value = rec.status
- }
- } catch (e) {
- const msg = e instanceof Error ? e.message : String(e)
- // 单次失败不立刻终止, 写入 error 但保持轮询
- error.value = `轮询异常: ${msg}`
- } finally {
- inFlightTick = false
- }
- }
-
- function start(nextId: string) {
- if (!nextId) return
- // 已在轮询同一 id, 幂等
- if (taskId.value === nextId && isPolling.value) return
- clearTimer()
- taskId.value = nextId
- status.value = 'idle'
- result.value = null
- error.value = null
- record.value = null
- isPolling.value = true
- // 立刻拉一次, 避免 2s 空窗
- void tick()
- timerId = setInterval(() => void tick(), intervalMs)
- }
-
- function stop() {
- const wasActive = isPolling.value
- clearTimer()
- isPolling.value = false
- if (wasActive) {
- resolveOrRejectWait(null, new Error('Polling stopped'))
- }
- }
-
- function reset() {
- stop()
- taskId.value = null
- status.value = 'idle'
- result.value = null
- error.value = null
- record.value = null
- }
-
- function waitForCompletion(): Promise {
- const r = record.value
- if (r && r.status === 'SUCCESS') return Promise.resolve(r)
- if (r && r.status === 'FAILED') {
- return Promise.reject(
- new Error(r.error ?? '任务失败 (无具体错误信息)'),
- )
- }
- return new Promise((resolve, reject) => {
- resolveWait = resolve
- rejectWait = reject
- })
- }
-
- // 自动模式: 监听外部 taskIdSource
- if (taskIdSource !== undefined) {
- const stopWatch = watch(
- () => toValue(taskIdSource),
- (newId, oldId) => {
- if (newId && newId !== oldId) start(newId)
- else if (!newId && autoStopOnNull) stop()
- },
- { immediate: true },
- )
- onUnmounted(() => {
- stopWatch()
- reset()
- resolveOrRejectWait(null, new Error('Component unmounted'))
- })
- } else {
- onUnmounted(() => {
- reset()
- resolveOrRejectWait(null, new Error('Component unmounted'))
- })
- }
-
- return {
- status,
- result,
- error,
- record,
- isPolling,
- taskId,
- start,
- stop,
- reset,
- waitForCompletion,
- }
-}
diff --git a/frontend/src/main.ts b/frontend/src/main.ts
deleted file mode 100644
index 9489de8..0000000
--- a/frontend/src/main.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { createApp } from 'vue'
-import ElementPlus from 'element-plus'
-import 'element-plus/dist/index.css'
-import App from './App.vue'
-
-const app = createApp(App)
-
-app.use(ElementPlus)
-app.mount('#app')
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
deleted file mode 100644
index d333bf0..0000000
--- a/frontend/tsconfig.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "compilerOptions": {
- "target": "ES2020",
- "useDefineForClassFields": true,
- "module": "ESNext",
- "moduleResolution": "bundler",
- "strict": true,
- "jsx": "preserve",
- "resolveJsonModule": true,
- "isolatedModules": true,
- "esModuleInterop": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
- "skipLibCheck": true,
- "noEmit": true,
- "allowImportingTsExtensions": true,
- "baseUrl": ".",
- "paths": {
- "@/*": ["src/*"]
- },
- "types": ["vite/client"]
- },
- "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "env.d.ts"],
- "references": [{ "path": "./tsconfig.node.json" }]
-}
diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json
deleted file mode 100644
index 1a555ac..0000000
--- a/frontend/tsconfig.node.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "compilerOptions": {
- "composite": true,
- "skipLibCheck": true,
- "module": "ESNext",
- "moduleResolution": "bundler",
- "allowSyntheticDefaultImports": true,
- "strict": true,
- "types": ["node"]
- },
- "include": ["vite.config.ts"]
-}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
deleted file mode 100644
index 46f2711..0000000
--- a/frontend/vite.config.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue'
-import { fileURLToPath, URL } from 'node:url'
-
-// Vite 配置:
-// - @ -> frontend/src
-// - dev server 监听 0.0.0.0:5173, 允许局域网内真机调试
-// - VITE_API_BASE_URL 通过 .env.development 注入, 缺省走 src/api/request.ts 内的兜底 (http://127.0.0.1:8000)
-export default defineConfig({
- plugins: [vue()],
- resolve: {
- alias: {
- '@': fileURLToPath(new URL('./src', import.meta.url)),
- },
- },
- server: {
- host: '0.0.0.0',
- port: 5173,
- strictPort: false,
- },
-})