fix(ui): 修复阶段 2/3 错显扫描结果,新增开始/继续/取消按钮
- 阶段 1 进行时,阶段 2/3 改为'等待阶段 1 扫描完成'占位,不再错显'已抽检 0/0 正在处理'
- 新增独立'扫描控制'区:▶ 开始扫描 / ⏩ 继续扫描 / ⏸ 取消 三个按钮
- 续扫进度文件存在时才启用'继续扫描',避免误用
- start_inspection 增加 clear_progress 参数:true=全新扫描,false=续扫
- runner 在 walker 返回后增加取消检查,避免扫描被取消后仍进入抽样/抽检
- '当前文件'在扫描控制区统一显示,便于查看实时进度
This commit is contained in:
@@ -164,6 +164,15 @@ pub async fn run(
|
|||||||
// 扫完清空 ETA
|
// 扫完清空 ETA
|
||||||
if let Ok(mut e) = scan_eta.lock() { e.clear(); }
|
if let Ok(mut e) = scan_eta.lock() { e.clear(); }
|
||||||
|
|
||||||
|
// 中断检查:若用户在扫描阶段点了"取消",直接退出,避免继续进入抽样/抽检
|
||||||
|
if cancel.load(Ordering::Relaxed) {
|
||||||
|
push_log("⏹ 已取消(扫描阶段)".into());
|
||||||
|
set_state(RunState::Cancelled);
|
||||||
|
set_step(String::new());
|
||||||
|
set_current(None);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
if candidates_count == 0 {
|
if candidates_count == 0 {
|
||||||
push_log("⚠ 没有可抽检的文件,请检查扫描范围/白名单".into());
|
push_log("⚠ 没有可抽检的文件,请检查扫描范围/白名单".into());
|
||||||
set_state(RunState::Done);
|
set_state(RunState::Done);
|
||||||
|
|||||||
175
src/ui/home.rs
175
src/ui/home.rs
@@ -12,29 +12,12 @@ use crate::ui::material;
|
|||||||
|
|
||||||
/// 主面板
|
/// 主面板
|
||||||
pub fn draw(ui: &mut egui::Ui, app: &mut App) {
|
pub fn draw(ui: &mut egui::Ui, app: &mut App) {
|
||||||
// 1. 主控制
|
// 0. 扫描控制(开始/继续/取消)
|
||||||
material::group(ui, "主控制", |ui| {
|
draw_scan_controls(ui, app);
|
||||||
ui.horizontal(|ui| {
|
|
||||||
let can_start = matches!(app.state, RunState::Idle | RunState::Done | RunState::Cancelled | RunState::Error(_));
|
|
||||||
let can_cancel = matches!(app.state, RunState::Scanning | RunState::Sampling | RunState::Inspecting | RunState::Reporting);
|
|
||||||
|
|
||||||
if ui.add_enabled(can_start, material::primary_button("▶ 开始检测")).clicked() {
|
|
||||||
start_inspection(app);
|
|
||||||
}
|
|
||||||
if ui.add_enabled(can_cancel, material::danger_button("⏹ 取消")).clicked() {
|
|
||||||
app.cancel_flag.store(true, Ordering::Relaxed);
|
|
||||||
app.state = RunState::Cancelled;
|
|
||||||
}
|
|
||||||
if ui.button("📂 打开报告目录").clicked() {
|
|
||||||
let _ = std::fs::create_dir_all(&app.config.report.output_dir);
|
|
||||||
let _ = open_in_explorer(&app.config.report.output_dir);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// 2. 状态卡片
|
// 1. 状态卡片
|
||||||
material::group(ui, "当前状态", |ui| {
|
material::group(ui, "当前状态", |ui| {
|
||||||
let state_str = match &app.state {
|
let state_str = match &app.state {
|
||||||
RunState::Idle => "● 空闲".to_string(),
|
RunState::Idle => "● 空闲".to_string(),
|
||||||
@@ -65,7 +48,7 @@ pub fn draw(ui: &mut egui::Ui, app: &mut App) {
|
|||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// 3. 阶段 1:扫描进度
|
// 2. 阶段 1:扫描进度
|
||||||
material::group(ui, "阶段 1/3:扫描全盘候选文件", |ui| {
|
material::group(ui, "阶段 1/3:扫描全盘候选文件", |ui| {
|
||||||
let scanned = app.scan_scanned.load(Ordering::Relaxed);
|
let scanned = app.scan_scanned.load(Ordering::Relaxed);
|
||||||
let found = app.scan_found.load(Ordering::Relaxed);
|
let found = app.scan_found.load(Ordering::Relaxed);
|
||||||
@@ -122,8 +105,18 @@ pub fn draw(ui: &mut egui::Ui, app: &mut App) {
|
|||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// 4. 阶段 2/3:抽样结果 + 抽检进度
|
// 3. 阶段 2/3:抽样结果 + 抽检进度
|
||||||
material::group(ui, "阶段 2/3:抽样结果 / 阶段 3/3:抽检", |ui| {
|
material::group(ui, "阶段 2/3:抽样结果 / 阶段 3/3:抽检", |ui| {
|
||||||
|
// 阶段 1 还在进行时,阶段 2/3 信息无意义,给出占位提示
|
||||||
|
if matches!(app.state, RunState::Idle | RunState::Scanning) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(egui::RichText::new("⏳ 等待阶段 1 扫描完成……")
|
||||||
|
.strong().size(14.0)
|
||||||
|
.color(material::ON_SURFACE_DIM));
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let p = app.progress.load(Ordering::Relaxed);
|
let p = app.progress.load(Ordering::Relaxed);
|
||||||
let t = app.total.load(Ordering::Relaxed);
|
let t = app.total.load(Ordering::Relaxed);
|
||||||
let frac = if t == 0 { 0.0 } else { p as f32 / t as f32 };
|
let frac = if t == 0 { 0.0 } else { p as f32 / t as f32 };
|
||||||
@@ -165,14 +158,16 @@ pub fn draw(ui: &mut egui::Ui, app: &mut App) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 当前正在处理
|
// 当前正在处理(仅在阶段 2/3 时显示;阶段 1 的当前文件由上面"阶段 1"组显示)
|
||||||
if let Ok(cur) = app.current_file.lock() {
|
if matches!(app.state, RunState::Sampling | RunState::Inspecting) {
|
||||||
if let Some(f) = cur.as_ref() {
|
if let Ok(cur) = app.current_file.lock() {
|
||||||
ui.add_space(4.0);
|
if let Some(f) = cur.as_ref() {
|
||||||
ui.horizontal(|ui| {
|
ui.add_space(4.0);
|
||||||
ui.label("📄 正在处理:");
|
ui.horizontal(|ui| {
|
||||||
ui.label(egui::RichText::new(shorten_path(f, 100)).monospace().color(material::PRIMARY_DARK));
|
ui.label("📄 正在处理:");
|
||||||
});
|
ui.label(egui::RichText::new(shorten_path(f, 100)).monospace().color(material::PRIMARY_DARK));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -251,8 +246,126 @@ fn classify_log(line: &str) -> (egui::Color32, &'static str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 扫描控制区:开始扫描 / 继续扫描 / 取消
|
||||||
|
fn draw_scan_controls(ui: &mut egui::Ui, app: &mut App) {
|
||||||
|
material::group(ui, "扫描控制", |ui| {
|
||||||
|
// 状态判断
|
||||||
|
let running = matches!(
|
||||||
|
app.state,
|
||||||
|
RunState::Scanning | RunState::Sampling | RunState::Inspecting | RunState::Reporting
|
||||||
|
);
|
||||||
|
let can_start = !running;
|
||||||
|
let can_cancel = running;
|
||||||
|
|
||||||
|
// 续扫进度文件是否存在
|
||||||
|
let pf = crate::scan::progress_store::progress_file();
|
||||||
|
let has_progress = pf.exists();
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
// ▶ 开始扫描:全新扫描(先清空旧续扫进度)
|
||||||
|
if ui
|
||||||
|
.add_enabled(can_start, material::primary_button("▶ 开始扫描"))
|
||||||
|
.on_hover_text("从头开始扫描:先清空旧续扫进度,再启动新的扫描流程")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
start_inspection(app, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⏩ 继续扫描:使用已有续扫进度跳过已扫过的文件
|
||||||
|
if ui
|
||||||
|
.add_enabled(can_start && has_progress, material::primary_button("⏩ 继续扫描"))
|
||||||
|
.on_hover_text("使用已有的续扫进度,跳过上次已扫过的文件")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
start_inspection(app, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ⏸ 取消:中断当前正在运行的扫描/抽检
|
||||||
|
if ui
|
||||||
|
.add_enabled(can_cancel, material::danger_button("⏸ 取消"))
|
||||||
|
.on_hover_text("中断当前正在进行的扫描/抽检(可点'继续扫描'从断点恢复)")
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
app.cancel_flag.store(true, Ordering::Relaxed);
|
||||||
|
// 不立刻覆盖 state:让后台任务在合适的检查点写 Cancelled,避免 UI 与后端不一致
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
if ui.button("📂 打开报告目录").clicked() {
|
||||||
|
let _ = std::fs::create_dir_all(&app.config.report.output_dir);
|
||||||
|
let _ = open_in_explorer(&app.config.report.output_dir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 续扫进度提示
|
||||||
|
if has_progress {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(
|
||||||
|
egui::RichText::new("✔ 检测到续扫进度文件")
|
||||||
|
.color(material::SUCCESS)
|
||||||
|
.size(12.0),
|
||||||
|
);
|
||||||
|
ui.label(
|
||||||
|
egui::RichText::new(format!("({})", pf.display()))
|
||||||
|
.color(material::ON_SURFACE_DIM)
|
||||||
|
.size(11.0),
|
||||||
|
);
|
||||||
|
if ui.small_button("🗑 清空进度").clicked() {
|
||||||
|
let _ = std::fs::remove_file(&pf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ui.add_space(2.0);
|
||||||
|
ui.label(
|
||||||
|
egui::RichText::new("✖ 暂无续扫进度;点击'开始扫描'会从零开始")
|
||||||
|
.color(material::ON_SURFACE_DIM)
|
||||||
|
.size(11.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当前正在扫描/处理的文件(统一显示入口)
|
||||||
|
if running {
|
||||||
|
if let Ok(cur) = app.current_file.lock() {
|
||||||
|
if let Some(f) = cur.as_ref() {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(
|
||||||
|
egui::RichText::new("📄 当前文件:")
|
||||||
|
.strong()
|
||||||
|
.color(material::PRIMARY_DARK)
|
||||||
|
.size(13.0),
|
||||||
|
);
|
||||||
|
ui.label(
|
||||||
|
egui::RichText::new(shorten_path(f, 100))
|
||||||
|
.monospace()
|
||||||
|
.color(material::ON_SURFACE)
|
||||||
|
.size(12.0),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// 启动后台抽检任务
|
/// 启动后台抽检任务
|
||||||
fn start_inspection(app: &mut App) {
|
///
|
||||||
|
/// - `clear_progress`:true 表示全新扫描(先清空续扫进度文件);false 表示续扫(保留续扫进度)
|
||||||
|
fn start_inspection(app: &mut App, clear_progress: bool) {
|
||||||
|
// 全新扫描:先删掉续扫进度文件,确保不会跳过任何文件
|
||||||
|
if clear_progress {
|
||||||
|
let pf = crate::scan::progress_store::progress_file();
|
||||||
|
if pf.exists() {
|
||||||
|
if let Err(e) = std::fs::remove_file(&pf) {
|
||||||
|
tracing::warn!("清空续扫进度失败:{}", e);
|
||||||
|
} else {
|
||||||
|
tracing::info!("已清空续扫进度:{}", pf.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
app.state = RunState::Scanning;
|
app.state = RunState::Scanning;
|
||||||
app.progress.store(0, Ordering::Relaxed);
|
app.progress.store(0, Ordering::Relaxed);
|
||||||
app.total.store(0, Ordering::Relaxed);
|
app.total.store(0, Ordering::Relaxed);
|
||||||
|
|||||||
Reference in New Issue
Block a user