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 @@ - - - - - - - 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, - }, -})