优化: 所有剩余UI/UX待办一批完成(持久化+AuditLog+解耦+total+原12大改+P2)

持久化(P1-c):新建 usePersistedRef composable,Tasks/AuditLog/ProjectDetail 等接入 localStorage

AuditLog(P1-d):后端 list_tool_executions 加 WHERE 筛选+返 {items,total,has_more},前端对接+长列折叠+筛选持久化

数据源解耦(P1-g):ProjectDetail projectTasks 按 project_id 独立加载 + ChatInput @项目联想独立加载(不读 store.tasks 当前页)

GitChanges(12a):后端 get_module_commits 加 git rev-list --count 返 total,前端显真实总数

原12大改:Dashboard 统计卡压底行(1)/Projects 列表卡片视图(2)/project_event 埋点排序(3)/TaskDetail 重设计(4)/IdeaDetail 重设计(5)/KnowledgeDetail 重设计(6)/界面持久化+侧栏Ctrl+B+审批数字键(7)/ProjectDetail 三栏改两栏(10)

P2打磨:文件浏览器(FileTree去重/FilePreview行号.md Diff/selectedFilePath归位)/settings反馈(假保存/端口校验)/AI会话(try-catch/scrollIntoView)/后端计数(move_queue事件/timeline total/workflow分页/import_batch分块)/杂项(TopBar/ConfirmDialog键盘/CIStatus i18n/ToolResultBody/ModuleNode/ApprovalDialog全选)
This commit is contained in:
lxy
2026-08-02 13:11:06 +08:00
parent caaabf0c15
commit f736f435bc
70 changed files with 2645 additions and 576 deletions
+71 -10
View File
@@ -9,11 +9,11 @@
//!
//! 依赖 audit/utils.rs 的 `truncate_chars` 做参数/结果截断,通过 `super::truncate_chars` 引用。
use serde::Serialize;
use serde::{Deserialize, Serialize};
use tauri::State;
use df_ai::ai_tools::RiskLevel;
use df_storage::crud::AiToolExecutionRepo;
use df_storage::crud::{AiToolExecutionRepo, AuditQuery};
use df_storage::models::AiToolExecutionRecord;
use df_types::types::new_id;
@@ -128,24 +128,78 @@ pub struct ToolExecutionDto {
pub decided_by: Option<String>,
}
/// 审批历史查询入参(前端透传,空值=不过滤)。
///
/// 复用 [`AuditQuery`](`df_storage::crud::AuditQuery`) 的字段语义:status/risk_level 精确匹配,
/// tool_keyword 走 tool_name LIKE。limit/offset 默认 50/0,storage 层钳制 limit ≤ 200。
///
/// `Deserialize`:Tauri IPC 从前端 JSON 反序列化。
#[derive(Debug, Clone, Default, Deserialize)]
pub struct ToolExecQuery {
/// 状态精确匹配(pending/approved/rejected/executing/completed/failed/interrupted)
pub status: Option<String>,
/// 风险等级精确匹配(low/medium/high)
pub risk_level: Option<String>,
/// 工具名关键词(tool_name LIKE %kw%)
pub tool_keyword: Option<String>,
pub limit: Option<u32>,
pub offset: Option<u32>,
}
impl From<ToolExecQuery> for AuditQuery {
fn from(q: ToolExecQuery) -> Self {
AuditQuery {
status: q.status,
risk_level: q.risk_level,
tool_keyword: q.tool_keyword,
limit: q.limit,
offset: q.offset,
}
}
}
/// 审批历史分页结果(对标项目通用 `{items,total,has_more}` 结构)。
///
/// - `items`:当前页审计 DTO 列表
/// - `total`:满足筛选条件的总行数(忽略分页裁剪,前端用于"第 N 页 / 共 M 条"展示)
/// - `has_more`:基于 `loaded < total` 推断,而非"本页是否满 limit"启发式
#[derive(Debug, Clone, Serialize)]
pub struct ToolExecutionPage {
pub items: Vec<ToolExecutionDto>,
pub total: i64,
pub has_more: bool,
}
/// 审批历史面板查询:按 requested_at 倒序(最新在前)分页返回工具调用审计记录。
///
/// 支持 status / risk_level / 工具名关键词筛选(WHERE 在后端收口,非前端 filter 当前页)。
/// 默认 limit=50 / offset=0(第一页)。limit 在 storage 层钳制 ≤200 防滥用。
/// 敏感字段(arguments/result)截断成摘要返回,完整原值仍留库。
///
/// 返回 `{items,total,has_more}`:total 为满足筛选条件的真实总数(独立 COUNT 查询),
/// has_more 基于 `offset + items.len() < total` 推断。
#[tauri::command]
pub async fn list_tool_executions(
state: State<'_, AppState>,
limit: Option<u32>,
offset: Option<u32>,
) -> Result<Vec<ToolExecutionDto>, String> {
let limit = limit.unwrap_or(50);
let offset = offset.unwrap_or(0);
query: Option<ToolExecQuery>,
) -> Result<ToolExecutionPage, String> {
let q = query.unwrap_or_default();
let limit = q.limit.unwrap_or(50);
let offset = q.offset.unwrap_or(0);
let audit_q = AuditQuery::from(q);
let records = state
.ai_tool_executions
.list_recent(limit, offset)
.list_by_query(&audit_q)
.await
.map_err(err_str)?;
Ok(records
let total = state
.ai_tool_executions
.count_by_query(&audit_q)
.await
.map_err(err_str)?;
let items: Vec<ToolExecutionDto> = records
.into_iter()
.map(|r| ToolExecutionDto {
id: r.id,
@@ -160,5 +214,12 @@ pub async fn list_tool_executions(
executed_at: r.executed_at,
decided_by: r.decided_by,
})
.collect())
.collect();
let has_more = (offset as i64 + items.len() as i64) < total;
Ok(ToolExecutionPage {
items,
total,
has_more,
})
}