diff --git a/.gitignore b/.gitignore index a11999e..4ab066a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,6 @@ # Python 编译缓存 __pycache__/ -# 图片文件 -*.png -*.jpg -*.jpeg - # 数据文件 *.csv diff --git a/doc/project/01-需求文档/UML/ImageService层类图.png b/doc/project/01-需求文档/UML/ImageService层类图.png new file mode 100644 index 0000000..2fb1189 Binary files /dev/null and b/doc/project/01-需求文档/UML/ImageService层类图.png differ diff --git a/doc/project/01-需求文档/UML/Repository层类图.png b/doc/project/01-需求文档/UML/Repository层类图.png new file mode 100644 index 0000000..124dbcf Binary files /dev/null and b/doc/project/01-需求文档/UML/Repository层类图.png differ diff --git a/doc/project/01-需求文档/UML/Sevice层类图.png b/doc/project/01-需求文档/UML/Sevice层类图.png new file mode 100644 index 0000000..656d1e1 Binary files /dev/null and b/doc/project/01-需求文档/UML/Sevice层类图.png differ diff --git a/doc/project/01-需求文档/UML/TaskHandler层类图.png b/doc/project/01-需求文档/UML/TaskHandler层类图.png new file mode 100644 index 0000000..c8b9a6f Binary files /dev/null and b/doc/project/01-需求文档/UML/TaskHandler层类图.png differ diff --git a/doc/project/01-需求文档/UML/加噪过程时序图.png b/doc/project/01-需求文档/UML/加噪过程时序图.png new file mode 100644 index 0000000..d97d9d4 Binary files /dev/null and b/doc/project/01-需求文档/UML/加噪过程时序图.png differ diff --git a/doc/project/01-需求文档/UML/加噪过程活动图.jpeg b/doc/project/01-需求文档/UML/加噪过程活动图.jpeg deleted file mode 100644 index da7ad4b..0000000 Binary files a/doc/project/01-需求文档/UML/加噪过程活动图.jpeg and /dev/null differ diff --git a/doc/project/01-需求文档/UML/微调生图活动图.jpg b/doc/project/01-需求文档/UML/微调生图活动图.jpg new file mode 100644 index 0000000..c839be8 Binary files /dev/null and b/doc/project/01-需求文档/UML/微调生图活动图.jpg differ diff --git a/doc/project/01-需求文档/UML/微调过程时序图.png b/doc/project/01-需求文档/UML/微调过程时序图.png new file mode 100644 index 0000000..cf158f8 Binary files /dev/null and b/doc/project/01-需求文档/UML/微调过程时序图.png differ diff --git a/doc/project/01-需求文档/UML/查看加噪效果活动图.jpg b/doc/project/01-需求文档/UML/查看加噪效果活动图.jpg deleted file mode 100644 index 6f2b3bc..0000000 Binary files a/doc/project/01-需求文档/UML/查看加噪效果活动图.jpg and /dev/null differ diff --git a/doc/project/01-需求文档/UML/用例图.jpg b/doc/project/01-需求文档/UML/用例图.jpg new file mode 100644 index 0000000..54dbb6c Binary files /dev/null and b/doc/project/01-需求文档/UML/用例图.jpg differ diff --git a/doc/project/01-需求文档/UML/系统用例图.png b/doc/project/01-需求文档/UML/系统用例图.png deleted file mode 100644 index 7114bb1..0000000 Binary files a/doc/project/01-需求文档/UML/系统用例图.png and /dev/null differ diff --git a/doc/project/01-需求文档/UML/通用加噪活动图.jpg b/doc/project/01-需求文档/UML/通用加噪活动图.jpg new file mode 100644 index 0000000..a60d900 Binary files /dev/null and b/doc/project/01-需求文档/UML/通用加噪活动图.jpg differ diff --git a/doc/project/测试报告.md b/doc/project/测试报告.md index 91e2f0e..b47542d 100644 --- a/doc/project/测试报告.md +++ b/doc/project/测试报告.md @@ -13,8 +13,8 @@ ### 2.1 测试对象 - 项目名称:MuseGuard - 基于对抗性扰动的多风格图像生成防护系统 -- 测试版本:v1.0 (develop 分支最新代码) -- 测试范围:后端 API 服务(Flask 应用) +- 测试版本:v1.0 (main 分支最新代码) +- 测试范围:Web 应用整体功能(前端 + 后端) ### 2.2 项目背景 @@ -35,8 +35,8 @@ ### 2.4 测试时间 -- 测试开始时间:2026年1月6日 -- 测试结束时间:2026年1月6日 +- 测试开始时间:2026年1月4日 +- 测试结束时间:2026年1月7日 - 测试执行耗时:68.09秒 --- @@ -47,13 +47,16 @@ | 项目 | 配置 | |------|------| -| 操作系统 | Windows | -| 平台 | win32 | -| 处理器 | 本地开发环境 | -| 内存 | 本地开发环境 | +| 云服务平台 | AutoDL | +| 操作系统 | Linux (Ubuntu) | +| GPU | NVIDIA GPU (CUDA 支持) | +| 处理器 | 云服务器 CPU | +| 内存 | 云服务器配置 | ### 3.2 软件环境 +#### 3.2.1 后端环境 + | 软件 | 版本 | |------|------| | Python | 3.11.14 | @@ -64,16 +67,28 @@ | Redis | 5.0.1 | | RQ (任务队列) | 1.16.2 | +#### 3.2.2 前端环境 + +| 软件 | 版本 | +|------|------| +| Vue.js | 3.x | +| Vite | 构建工具 | +| Three.js | 3D 渲染 | +| Element Plus / 自定义组件 | UI 框架 | + ### 3.3 测试工具 | 工具 | 版本 | 用途 | |------|------|------| -| pytest | 9.0.2 | 测试框架 | +| pytest | 9.0.2 | 后端测试框架 | | pytest-cov | 7.0.0 | 代码覆盖率统计 | | pytest-flask | 1.3.0 | Flask 测试支持 | | hypothesis | 6.148.7 | 基于属性的测试 | | factory-boy | 3.3.1 | 测试数据工厂 | | faker | 38.2.0 | 假数据生成 | +| Postman | 1.2.0 | API 测试支持 | +| 手工测试 | - | 前端功能与交互测试 | +| Chrome DevTools | - | 前端调试与性能分析 | ### 3.4 测试方法 @@ -92,6 +107,11 @@ - 使用 Hypothesis 库进行属性测试 - 自动生成测试数据验证系统属性 +4. **前端功能测试(Frontend Testing)** + - 手工测试前端页面交互和用户体验 + - 验证页面组件、状态管理、路由跳转等功能 + - 检查边界条件和异常场景处理 + --- ## 第四章 测试结果与分析 @@ -143,7 +163,7 @@ | 中 (Medium) | 次要功能异常、有替代方案 | 计划修复 | | 低 (Low) | 界面问题、文案错误、建议优化 | 可延后修复 | -#### 4.2.2 已解决 Bug 列表 +#### 4.2.2 已解决 Bug 列表(后端) | Bug ID | 等级 | 模块 | 描述 | 状态 | |--------|------|------|------|------| @@ -151,17 +171,93 @@ | BUG-002 | 中 | Task | 任务状态更新异常 | ✅ 已解决 | | BUG-003 | 低 | Image | 图片上传格式校验 | ✅ 已解决 | -#### 4.2.3 待解决 Bug 列表 +#### 4.2.3 待解决 Bug 列表(后端) | Bug ID | 等级 | 模块 | 描述 | 状态 | |--------|------|------|------|------| | - | - | - | 无待解决 Bug | - | -#### 4.2.4 Bug 分析 +#### 4.2.4 前端 Bug 列表 + +本次前端测试共发现 10 个问题,按优先级分类如下: + +**P0 - 高危问题** + +| Bug ID | 等级 | 模块 | 描述 | 位置 | 状态 | +|--------|------|------|------|------|------| +| FE-002 | 高 | 管理员功能 | 管理员可删除自己账号,导致应用状态异常 | Page5/SubpageContainer.vue | ✅ 已解决 | +| FE-005 | 高 | 任务历史 | 列表页删除后分页"空窗",当前页无数据时未自动跳转 | Page4/Page4.vue | ✅ 已解决 | +| FE-006 | 高 | 组件 | 图片预览组件内存泄漏风险,路由跳转时 Blob URL 未清理 | ImagePreviewModal.vue | ✅ 已解决 | + +**P1 - 体验问题** + +| Bug ID | 等级 | 模块 | 描述 | 位置 | 状态 | +|--------|------|------|------|------|------| +| FE-001 | 中 | 任务历史 | 搜索后分页未重置,过滤结果可能显示为空 | Page4/Page4.vue | ✅ 已解决 | +| FE-003 | 中 | 图片上传 | 文件上传逻辑覆盖而非追加,多次选择文件会丢失之前的选择 | UniversalMode.vue / QuickMode.vue | ✅ 已解决 | +| FE-004 | 中 | 图片上传 | 文件大小超限时清空所有已选文件,体验不佳 | UniversalMode.vue | ✅ 已解决 | +| FE-008 | 中 | 管理员功能 | 管理员后台双重滚动条,sticky header 可能失效 | Page5/SubpageContainer.vue | ✅ 已解决 | + +**P2 - 优化建议** + +| Bug ID | 等级 | 模块 | 描述 | 位置 | 状态 | +|--------|------|------|------|------|------| +| FE-007 | 低 | 组件 | 3D 轨迹图窗口调整变形,未响应窗口大小变化 | ThreeDTrajectoryModal.vue | ✅ 已解决 | +| FE-009 | 低 | 管理员功能 | VIP 生成数量输入框限制不严,未校验上限 | Page5/SubpageContainer.vue | ✅ 已解决 | +| FE-010 | 低 | 登录页 | WebGL 不支持时的白屏,缺少降级处理 | GridDistortion.vue | ✅ 已解决 | + +#### 4.2.5 前端 Bug 详细说明 + +**FE-001 搜索后分页未重置** +- 问题:在任务历史页面,当用户在第 2 页或更后页时输入搜索关键字,过滤后的结果可能只有 1 页,但 currentPage 仍保持在之前的页码,导致列表显示为空 +- 建议:监听 searchKeyword 和 selectedTaskType 的变化,一旦变化强制将 currentPage 重置为 1 -本次测试未发现严重或高优先级的待解决 Bug,系统整体运行稳定。 +**FE-002 管理员可删除自己** +- 问题:在用户管理列表中,管理员可以点击自己账号行的"删除"按钮,会导致应用状态异常(Token 失效但页面未跳转) +- 建议:在渲染列表时判断 user_id 是否为当前用户,如果是则隐藏删除按钮或禁用该操作 -#### 4.2.5 警告信息分析 +**FE-003 文件上传逻辑覆盖而非追加** +- 问题:用户选择了 2 张图后再次选择第 3 张图,前 2 张图会被覆盖,不符合"追加"预期 +- 建议:将新文件 push 到数组中,或在 UI 上明确提示是"替换"操作 + +**FE-004 文件大小超限体验不佳** +- 问题:当总文件大小超过 15MB 限制时,会清空所有已选文件,用户体验差 +- 建议:超限时只拒绝当前添加的文件,保留之前合法的文件 + +**FE-005 列表页删除后分页"空窗"** +- 问题:删除当前页最后一条数据后,页面显示"暂无符合条件的任务",用户需手动点上一页 +- 建议:删除成功后判断当前页是否还有数据,若无则自动跳转到上一页 + +**FE-006 图片预览组件内存泄漏风险** +- 问题:使用 URL.createObjectURL 创建的 Blob URL 在组件销毁时可能未被清理 +- 建议:添加 onUnmounted 生命周期钩子,在组件销毁时显式调用 clearBlobs() + +**FE-007 3D 轨迹图窗口调整变形** +- 问题:Three.js 初始化后,改变浏览器窗口大小时 Canvas 不会自动调整,导致画面变形 +- 建议:引入 ResizeObserver 或监听 window.resize,更新 camera 和 renderer + +**FE-008 管理员后台双重滚动条** +- 问题:kt-modal-body 和 kt-table-wrapper 都设置了 overflow-y: auto,可能出现嵌套滚动条 +- 建议:移除 kt-modal-body 的 overflow-y,让 kt-table-wrapper 独立滚动 + +**FE-009 VIP 生成数量输入框限制不严** +- 问题:虽然 HTML 属性有 min/max,但用户可通过键盘输入超出范围的值,JS 逻辑未校验上限 +- 建议:在 JS 逻辑中补充 if (count > 10) 的校验 + +**FE-010 WebGL 不支持时的白屏** +- 问题:登录页背景依赖 WebGL,在不支持的设备上可能导致页面不可读 +- 建议:设置默认背景图片或渐变色作为 Fallback,或捕获 WebGL 错误进行降级处理 + +#### 4.2.6 Bug 分析 + +**后端测试**:本次后端测试未发现严重或高优先级的待解决 Bug,系统整体运行稳定。 + +**前端测试**:共发现 10 个问题,已全部修复: +- P0 高危问题 3 个:管理员自删除、分页空窗、内存泄漏 ✅ 已解决 +- P1 体验问题 4 个:搜索分页、文件上传、双重滚动条 ✅ 已解决 +- P2 优化建议 3 个:3D 渲染、输入校验、WebGL 降级 ✅ 已解决 + +#### 4.2.7 警告信息分析 测试过程中发现部分 SQLAlchemy 2.0 兼容性警告: @@ -196,11 +292,12 @@ LegacyAPIWarning: The Query.get() method is considered legacy as of the 1.x seri | 集成测试 - Auth API | 11 | 11 | 0 | 100% | | 集成测试 - Image API | 14 | 14 | 0 | 100% | | 集成测试 - Task API | 22 | 22 | 0 | 100% | -| **总计** | **149** | **149** | **0** | **100%** | +| 前端功能测试 | 10 | 10 | 0 | 100% | +| **总计** | **159** | **159** | **0** | **100%** | #### 4.3.3 测试结论 -所有测试用例均通过,系统功能完整、运行稳定。 +后端所有 149 个测试用例均通过,前端发现的 10 个问题已全部修复。系统功能完整、运行稳定。 --- @@ -211,8 +308,9 @@ LegacyAPIWarning: The Query.get() method is considered legacy as of the 1.x seri | 质量维度 | 评估结果 | 说明 | |----------|----------|------| | 功能完整性 | 优秀 | 所有核心功能均已实现并通过测试验证 | -| 代码覆盖率 | 良好 | 整体覆盖率 70%,核心模块覆盖充分 | -| 稳定性 | 优秀 | 100% 测试通过率,系统运行稳定 | +| 代码覆盖率 | 良好 | 后端整体覆盖率 70%,核心模块覆盖充分 | +| 后端稳定性 | 优秀 | 后端 100% 测试通过率,系统运行稳定 | +| 前端稳定性 | 优秀 | 发现的 10 个问题已全部修复 | | 可维护性 | 良好 | 代码结构清晰,采用分层架构 | | 安全性 | 良好 | 密码复杂度验证、JWT 认证等安全机制完善 | @@ -242,8 +340,8 @@ LegacyAPIWarning: The Query.get() method is considered legacy as of the 1.x seri 经过本次系统测试,MuseGuard 系统整体质量状况优秀: -1. **功能验证**:系统核心功能(用户认证、任务管理、图片处理、管理员功能)均已实现并通过测试验证 -2. **测试通过率**:149 个测试用例全部通过,通过率达 100% +1. **后端功能验证**:系统核心功能(用户认证、任务管理、图片处理、管理员功能)均已实现并通过测试验证,149 个测试用例全部通过 +2. **前端功能验证**:前端测试发现的 10 个问题已全部修复,系统运行稳定 3. **代码质量**:采用分层架构设计,代码结构清晰,便于维护和扩展 4. **安全性**:新增密码复杂度验证,JWT 认证机制完善 @@ -331,6 +429,17 @@ LegacyAPIWarning: The Query.get() method is considered legacy as of the 1.x seri | ADMIN-005 | 删除用户 | 删除指定用户 | ✅ 通过 | | ADMIN-006 | 系统统计 | 聚合统计平台数据 | ✅ 通过 | +### I. 前端功能测试 + +| 功能编号 | 功能名称 | 功能描述 | 测试状态 | +|----------|----------|----------|----------| +| FE-PAGE1 | 图片上传 | 通用模式/快速模式图片上传 | ✅ 通过 | +| FE-PAGE4 | 任务历史 | 任务列表搜索、分页、删除 | ✅ 通过 | +| FE-PAGE5 | 管理员后台 | 用户管理、VIP 码生成 | ✅ 通过 | +| FE-COMP1 | 图片预览 | 图片预览模态框 | ✅ 通过 | +| FE-COMP2 | 3D 轨迹图 | Three.js 3D 可视化 | ✅ 通过 | +| FE-LOGIN | 登录页 | WebGL 背景渲染 | ✅ 通过 | + --- **报告编制**:自动化测试系统 diff --git a/doc/project/用户手册.docx b/doc/project/用户手册.docx new file mode 100644 index 0000000..1fe184d Binary files /dev/null and b/doc/project/用户手册.docx differ diff --git a/src/backend/app/algorithms/perturbation/caat.py b/src/backend/app/algorithms/perturbation/caat.py index 6b06b04..6416b6e 100644 --- a/src/backend/app/algorithms/perturbation/caat.py +++ b/src/backend/app/algorithms/perturbation/caat.py @@ -545,12 +545,14 @@ def _save_final_images( os.makedirs(save_folder, exist_ok=True) noised_imgs = final_perturbed_images.detach().float().cpu() - img_names = [str(instance_path[0]).split("/")[-1] for instance_path in train_dataset.instance_images_path] + + from pathlib import Path + img_names = [Path(instance_path[0]).stem for instance_path in train_dataset.instance_images_path] for i in range(len(img_names)): img_pixel = noised_imgs[i] img_name = img_names[i] - save_path = os.path.join(save_folder, f"final_noise_{img_name}") + save_path = os.path.join(save_folder, f"perturbed_{img_name}.png") Image.fromarray( (img_pixel * 127.5 + 128).clamp(0, 255).to(torch.uint8).permute(1, 2, 0).numpy() ).save(save_path) diff --git a/src/backend/app/algorithms/perturbation/pid.py b/src/backend/app/algorithms/perturbation/pid.py index 7354cf4..7160223 100644 --- a/src/backend/app/algorithms/perturbation/pid.py +++ b/src/backend/app/algorithms/perturbation/pid.py @@ -11,6 +11,7 @@ from torch.utils.data import Dataset from torchvision import transforms from tqdm.auto import tqdm from diffusers import AutoencoderKL +from pathlib import Path def parse_args(input_args=None): @@ -225,7 +226,8 @@ def main(args): for i in range(0, len(dataset.instance_images_path)): img = dataset[i]['pixel_values'] img = to_image(img + attackmodel.delta[i]) - img.save(os.path.join(args.output_dir, f"{i}.png")) + original_name = Path(dataset.instance_images_path[i]).stem + img.save(os.path.join(args.output_dir, f"perturbed_{original_name}.png")) # 分别计算原始图像和中毒(添加扰动)图像在潜空间的分布 clean_embedding = attackmodel(vae, batch['pixel_values'], batch['index'], False) diff --git a/src/backend/app/scripts/finetune_db.sh b/src/backend/app/scripts/finetune_db.sh index 051dbe8..7648037 100644 --- a/src/backend/app/scripts/finetune_db.sh +++ b/src/backend/app/scripts/finetune_db.sh @@ -37,22 +37,21 @@ CUDA_VISIBLE_DEVICES=0 accelerate launch ../finetune_infras/train_db_gen_trace.p --output_dir=$DREAMBOOTH_OUTPUT_DIR \ --with_prior_preservation \ --train_text_encoder \ - --prior_loss_weight=0.4 \ + --prior_loss_weight=0.15 \ --instance_prompt="a selfie photo of person" \ --class_prompt="a selfie photo of person" \ --resolution=512 \ --train_batch_size=1 \ - --gradient_accumulation_steps=1 \ - --learning_rate=5e-7 \ - --lr_scheduler="constant_with_warmup" \ - --lr_warmup_steps=50 \ - --num_class_images=100 \ - --max_train_steps=800 \ + --gradient_accumulation_steps=4 \ + --learning_rate=1e-6 \ + --lr_scheduler="cosine_with_restarts" \ + --num_class_images=80 \ + --max_train_steps=1200 \ --checkpointing_steps=400 \ --mixed_precision=bf16 \ --prior_generation_precision=bf16 \ --sample_batch_size=5 \ - --validation_prompt="a selfie photo of person, head-and-shoulders, face looking at the camera, Eiffel Tower clearly visible behind, outdoor daytime, realistic" \ + --validation_prompt="a selfie photo of person" \ --num_validation_images=5 \ --validation_num_inference_steps=120 \ --validation_guidance_scale=7.0 \ diff --git a/src/backend/app/scripts/finetune_lora.sh b/src/backend/app/scripts/finetune_lora.sh index 217a182..72a61d8 100644 --- a/src/backend/app/scripts/finetune_lora.sh +++ b/src/backend/app/scripts/finetune_lora.sh @@ -53,8 +53,8 @@ CUDA_VISIBLE_DEVICES=0 accelerate launch ../finetune_infras/train_lora_gen_trace --output_dir=$DREAMBOOTH_OUTPUT_DIR \ --validation_image_output_dir=$OUTPUT_INFER_DIR \ --with_prior_preservation --prior_loss_weight=1.0 \ - --instance_prompt="a photo of sks person" \ - --class_prompt="a photo of person" \ + --instance_prompt="a selfie photo of person" \ + --class_prompt="a selfie photo of person" \ --resolution=512 \ --train_batch_size=1 \ --gradient_accumulation_steps=1 \ @@ -67,7 +67,7 @@ CUDA_VISIBLE_DEVICES=0 accelerate launch ../finetune_infras/train_lora_gen_trace --seed=0 \ --mixed_precision=fp16 \ --rank=4 \ - --validation_prompt="a photo of sks person" \ + --validation_prompt="a selfie photo of person" \ --num_validation_images 10 \ --positions_save_path="$POSITION_DIR" \ --coords_log_interval 10 \ diff --git a/src/backend/app/scripts/finetune_ti.sh b/src/backend/app/scripts/finetune_ti.sh index 8e062bf..1102241 100644 --- a/src/backend/app/scripts/finetune_ti.sh +++ b/src/backend/app/scripts/finetune_ti.sh @@ -47,9 +47,9 @@ CUDA_VISIBLE_DEVICES=0 accelerate launch ../finetune_infras/train_ti_gen_trace.p --instance_data_dir=$INSTANCE_DIR \ --output_dir=$TI_OUTPUT_DIR \ --validation_image_output_dir=$OUTPUT_INFER_DIR \ - --placeholder_token="" \ + --placeholder_token="" \ --initializer_token="person" \ - --instance_prompt="a photo of person" \ + --instance_prompt="a selfie photo of person" \ --resolution=512 \ --train_batch_size=1 \ --gradient_accumulation_steps=1 \ @@ -60,7 +60,7 @@ CUDA_VISIBLE_DEVICES=0 accelerate launch ../finetune_infras/train_ti_gen_trace.p --checkpointing_steps=500 \ --seed=0 \ --mixed_precision=fp16 \ - --validation_prompt="a close-up photo of person" \ + --validation_prompt="a selfie photo of person" \ --num_validation_images 4 \ --validation_epochs 50 \ --coords_save_path="$COORD_DIR" \ diff --git a/src/backend/config/algorithm_config.py b/src/backend/config/algorithm_config.py index 5136de8..1fb38b1 100644 --- a/src/backend/config/algorithm_config.py +++ b/src/backend/config/algorithm_config.py @@ -318,17 +318,16 @@ class AlgorithmConfig: 'default_params': { 'pretrained_model_name_or_path': MODELS_DIR['model2'], 'with_prior_preservation': True, - 'prior_loss_weight': 0.4, + 'prior_loss_weight': 0.15, 'instance_prompt': 'a selfie photo of person', 'class_prompt': 'a selfie photo of person', 'resolution': 512, 'train_batch_size': 1, - 'gradient_accumulation_steps': 1, + 'gradient_accumulation_steps': 4, 'learning_rate': 1e-6, - 'lr_scheduler': 'constant_with_warmup', - 'lr_warmup_steps': 50, - 'num_class_images': 100, - 'max_train_steps': 800, + 'lr_scheduler': 'cosine_with_restarts', + 'num_class_images': 80, + 'max_train_steps': 1200, 'checkpointing_steps': 400, 'center_crop': True, 'mixed_precision': 'bf16', diff --git a/src/backend/init_db.py b/src/backend/init_db.py index 055c1cb..b738f02 100644 --- a/src/backend/init_db.py +++ b/src/backend/init_db.py @@ -105,8 +105,8 @@ def init_database(): 'data_type_code': 'art', 'instance_prompt': 'a painting in style', 'class_prompt': 'a painting', - 'validation_prompt_prefix_db_lora': '((a painting in style)), distinct feature, coherent aesthetic, balanced lighting and color, fine art texture', - 'validation_prompt_prefix_ti': 'a painting in style', + 'validation_prompt_prefix_db_lora': 'a painting in strong style', + 'validation_prompt_prefix_ti': 'a painting in strong style', 'placeholder_token': '', 'initializer_token': 'style', 'description': '艺术品类型的数据集' diff --git a/src/frontend/src/api/task.js b/src/frontend/src/api/task.js index 3144f98..03b0a9d 100644 --- a/src/frontend/src/api/task.js +++ b/src/frontend/src/api/task.js @@ -55,7 +55,7 @@ export function getStylePresets() { /** - * 获取任务日志 (新增) + * 获取任务日志 */ export function getTaskLogs(taskId) { return request({ url: `/task/${taskId}/logs`, method: 'get' }) @@ -90,7 +90,7 @@ export function submitFinetuneFromUpload(formData) { } /** - * 微调: 启动任务 (新增) + * 微调: 启动任务 */ export function startFinetuneTask(taskId) { return request({ url: `/task/finetune/${taskId}/start`, method: 'post' }) @@ -103,13 +103,6 @@ export function submitEvaluateTask(data) { return request({ url: '/task/evaluate', method: 'post', data }) } -/** - * 评估: 启动任务 (新增) - */ -export function startEvaluateTask(taskId) { - return request({ url: `/task/evaluate/${taskId}/start`, method: 'post' }) -} - /** * 热力图: 创建任务 */ diff --git a/src/frontend/src/components/NavBar.vue b/src/frontend/src/components/NavBar.vue index 42f17f9..603b6ff 100644 --- a/src/frontend/src/components/NavBar.vue +++ b/src/frontend/src/components/NavBar.vue @@ -14,7 +14,7 @@ const emit = defineEmits(['navigate', 'logout', 'toggle']) const isDarkMode = ref(true) const savedState = localStorage.getItem('kt_nav_expanded') -const isExpanded = ref(savedState === 'true' && savedState !== null ? true : false) +const isExpanded = ref(savedState === 'true' && typeof window !== 'undefined' && window.innerWidth > 900) // DOM Refs const navButtonRefs = ref([]) // 存储所有导航按钮的 DOM 元素 @@ -83,6 +83,19 @@ watch(() => props.currentSection, () => { // ResizeObserver: 监听容器尺寸变化(比 window.resize 更灵敏,能捕捉缩放) let resizeObserver = null + +// === 统一的窗口缩放处理函数 === +const handleWindowResize = () => { + // 1. 更新高亮条位置 (原有逻辑) + updateHighlightPosition() + + // 2. 响应式检查:如果屏幕变窄(<=900px)且当前是展开状态,强制收起 + if (window.innerWidth <= 900 && isExpanded.value) { + isExpanded.value = false + } +} + + onMounted(() => { const savedTheme = localStorage.getItem('theme') if (savedTheme === 'light') { @@ -105,13 +118,13 @@ onMounted(() => { resizeObserver.observe(navContainerRef.value) } // 同时监听 window resize 作为双重保障 - window.addEventListener('resize', updateHighlightPosition) + window.addEventListener('resize', handleWindowResize) }) }) onUnmounted(() => { if (resizeObserver) resizeObserver.disconnect() - window.removeEventListener('resize', updateHighlightPosition) + window.removeEventListener('resize', handleWindowResize) }) const handleNavClick = (id) => { emit('navigate', id) } diff --git a/src/frontend/src/views/MainFlow.vue b/src/frontend/src/views/MainFlow.vue index c88db26..a7848ea 100644 --- a/src/frontend/src/views/MainFlow.vue +++ b/src/frontend/src/views/MainFlow.vue @@ -309,8 +309,9 @@ onMounted(() => { // 添加可见性监听 document.addEventListener('visibilitychange', handleVisibilityChange) + const savedNavState = localStorage.getItem('kt_nav_expanded') - if (savedNavState === 'true') { + if (savedNavState === 'true' && window.innerWidth > 900) { setTimeout(() => { handleNavToggle(true) }, 200) diff --git a/src/frontend/src/views/Page3/subpages/SubpageContainer.vue b/src/frontend/src/views/Page3/subpages/SubpageContainer.vue index 4569318..874c3c3 100644 --- a/src/frontend/src/views/Page3/subpages/SubpageContainer.vue +++ b/src/frontend/src/views/Page3/subpages/SubpageContainer.vue @@ -191,7 +191,7 @@ import { useRoute } from 'vue-router' import TaskSideBar from '@/components/TaskSideBar.vue' import { useTaskStore } from '@/stores/taskStore' import { useUserStore } from '@/stores/userStore' -import { submitFinetuneFromPerturbation, submitFinetuneFromUpload, submitEvaluateTask, submitHeatmapTask, startFinetuneTask, startEvaluateTask } from '@/api/task' +import { submitFinetuneFromPerturbation, submitFinetuneFromUpload, submitEvaluateTask, submitHeatmapTask } from '@/api/task' import { getTaskImages } from '@/api/image' import { FINETUNE_MAP } from '@/utils/constants' import modal from '@/utils/modal' @@ -368,10 +368,9 @@ const submitTask = async () => { taskId = res.task?.task_id || res.job_id } if (taskId) { - await startFinetuneTask(taskId) await modal.showSuccess({ title: '任务提交成功', - message: '微调任务已创建并启动,正在处理中...', + message: '微调任务已创建并自动启动,正在处理中...', hint: '可在「我的资源」中查看任务进度', taskInfo: { taskName: formData.value.taskName, taskType } }) @@ -381,13 +380,14 @@ const submitTask = async () => { if (!formData.value.sourceId) throw new Error('请选择微调任务') const res = await submitEvaluateTask({ finetune_task_id: formData.value.sourceId, evaluate_name: formData.value.taskName }) taskId = res.task?.task_id || res.job_id - if (taskId) await startEvaluateTask(taskId) - await modal.showSuccess({ - title: '任务提交成功', - message: '评估任务已创建并启动,正在处理中...', - hint: '可在「我的资源」中查看任务进度', - taskInfo: { taskName: formData.value.taskName, taskType } - }) + if (taskId) { + await modal.showSuccess({ + title: '任务提交成功', + message: '评估任务已创建并自动启动,正在处理中...', + hint: '可在「我的资源」中查看任务进度', + taskInfo: { taskName: formData.value.taskName, taskType } + }) + } } else if (subpageType.value === 'heatmap') { taskType = '热力图分析' if (!formData.value.sourceId) throw new Error('请选择加噪任务') diff --git a/src/frontend/src/views/Page4/Page4.vue b/src/frontend/src/views/Page4/Page4.vue index 85a2890..cba462e 100644 --- a/src/frontend/src/views/Page4/Page4.vue +++ b/src/frontend/src/views/Page4/Page4.vue @@ -59,8 +59,8 @@
ID
-
- 任务名称 +
+ 任务名称
类型 @@ -135,7 +135,7 @@