Phase 2.7完成:文件浏览模块完善(SQLite查询 + Tree展示)
Phase 2.7.1成果: - 实现SQLite数据库查询(file_registry/file_nodes表) - get_tree():构建完整虚拟Tree结构 - list_files():列出文件节点 - search_files():文件名模糊搜索 - download_file():查询物理文件路径 - build_tree():递归构建Tree辅助函数 Phase 2.7.2成果: - Element Plus Tree组件集成 - 双虚拟目录切换(中文/英文) - 文件节点点击打开功能 - 文件大小格式化显示(KB/MB/GB) - 文件夹/文件图标区分 技术实现: - 添加rusqlite依赖到Cargo.toml - 修复Tauri features配置 - Home.vue完整Tree展示UI - 编译成功(8警告,0错误) 状态:Phase 2总进度98%完成
This commit is contained in:
@@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use tauri::State;
|
||||
use std::sync::Mutex;
|
||||
use rusqlite::Connection;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct TreeNode {
|
||||
@@ -31,15 +32,37 @@ pub async fn get_tree(
|
||||
return Err(format!("Database not found: {:?}", db_path));
|
||||
}
|
||||
|
||||
let tree_node = TreeNode {
|
||||
id: "root".to_string(),
|
||||
name: "Root".to_string(),
|
||||
node_type: "directory".to_string(),
|
||||
size: None,
|
||||
children: vec![],
|
||||
};
|
||||
let conn = Connection::open(&db_path)
|
||||
.map_err(|e| format!("Failed to open database: {}", e))?;
|
||||
|
||||
Ok(tree_node)
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT node_id, label, node_type, parent_id, file_size
|
||||
FROM file_nodes
|
||||
WHERE tree_type = ?1
|
||||
ORDER BY sort_order ASC"
|
||||
).map_err(|e| format!("Failed to prepare statement: {}", e))?;
|
||||
|
||||
let nodes = stmt.query_map([&tree_type], |row| {
|
||||
Ok((
|
||||
row.get::<_, String>(0)?, // node_id
|
||||
row.get::<_, String>(1)?, // label
|
||||
row.get::<_, String>(2)?, // node_type
|
||||
row.get::<_, Option<String>>(3)?, // parent_id
|
||||
row.get::<_, Option<i64>>(4)?, // file_size
|
||||
))
|
||||
}).map_err(|e| format!("Failed to query nodes: {}", e))?;
|
||||
|
||||
let mut all_nodes: Vec<(String, String, String, Option<String>, Option<i64>)> = vec
|
||||
![];
|
||||
|
||||
for node in nodes {
|
||||
let node_data = node.map_err(|e| format!("Failed to get node: {}", e))?;
|
||||
all_nodes.push(node_data);
|
||||
}
|
||||
|
||||
let root_node = build_tree(&all_nodes, None)?;
|
||||
|
||||
Ok(root_node)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -55,7 +78,35 @@ pub async fn list_files(
|
||||
return Err(format!("Database not found: {:?}", db_path));
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
let conn = Connection::open(&db_path)
|
||||
.map_err(|e| format!("Failed to open database: {}", e))?;
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT node_id, label, node_type, file_size
|
||||
FROM file_nodes
|
||||
WHERE tree_type = ?1 AND parent_id = ?2
|
||||
ORDER BY sort_order ASC"
|
||||
).map_err(|e| format!("Failed to prepare statement: {}", e))?;
|
||||
|
||||
let files = stmt.query_map([&tree_type, &parent_id], |row| {
|
||||
Ok(TreeNode {
|
||||
id: row.get::<_, String>(0)?,
|
||||
name: row.get::<_, String>(1)?,
|
||||
node_type: row.get::<_, String>(2)?,
|
||||
size: row.get::<_, Option<i64>>(3)?.map(|s| s as u64),
|
||||
children: vec
|
||||
![],
|
||||
})
|
||||
}).map_err(|e| format!("Failed to query files: {}", e))?;
|
||||
|
||||
let mut result: Vec<TreeNode> = vec
|
||||
![];
|
||||
for file in files {
|
||||
let file_node = file.map_err(|e| format!("Failed to get file: {}", e))?;
|
||||
result.push(file_node);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -85,7 +136,36 @@ pub async fn search_files(
|
||||
return Err(format!("Database not found: {:?}", db_path));
|
||||
}
|
||||
|
||||
Ok(vec![])
|
||||
let conn = Connection::open(&db_path)
|
||||
.map_err(|e| format!("Failed to open database: {}", e))?;
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT node_id, label, node_type, file_size
|
||||
FROM file_nodes
|
||||
WHERE tree_type = ?1 AND label LIKE ?2
|
||||
ORDER BY label ASC"
|
||||
).map_err(|e| format!("Failed to prepare statement: {}", e))?;
|
||||
|
||||
let search_pattern = format!("%{}%", query);
|
||||
let files = stmt.query_map([&tree_type, &search_pattern], |row| {
|
||||
Ok(TreeNode {
|
||||
id: row.get::<_, String>(0)?,
|
||||
name: row.get::<_, String>(1)?,
|
||||
node_type: row.get::<_, String>(2)?,
|
||||
size: row.get::<_, Option<i64>>(3)?.map(|s| s as u64),
|
||||
children: vec
|
||||
![],
|
||||
})
|
||||
}).map_err(|e| format!("Failed to search files: {}", e))?;
|
||||
|
||||
let mut result: Vec<TreeNode> = vec
|
||||
![];
|
||||
for file in files {
|
||||
let file_node = file.map_err(|e| format!("Failed to get file: {}", e))?;
|
||||
result.push(file_node);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -93,7 +173,26 @@ pub async fn download_file(
|
||||
user_id: String,
|
||||
file_uuid: String,
|
||||
) -> Result<String, String> {
|
||||
Ok(format!("/downloads/{}", file_uuid))
|
||||
let db_path = PathBuf::from("data/users")
|
||||
.join(format!("{}.sqlite", user_id));
|
||||
|
||||
if !db_path.exists() {
|
||||
return Err(format!("Database not found: {:?}", db_path));
|
||||
}
|
||||
|
||||
let conn = Connection::open(&db_path)
|
||||
.map_err(|e| format!("Failed to open database: {}", e))?;
|
||||
|
||||
let file_path: String = conn.query_row(
|
||||
"SELECT fr.file_path
|
||||
FROM file_registry fr
|
||||
JOIN file_nodes fn ON fr.file_uuid = fn.file_uuid
|
||||
WHERE fn.node_id = ?1",
|
||||
[&file_uuid],
|
||||
|row| row.get(0)
|
||||
).map_err(|e| format!("Failed to query file path: {}", e))?;
|
||||
|
||||
Ok(file_path)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -125,4 +224,43 @@ pub async fn open_file(file_path: String) -> Result<(), String> {
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_tree(
|
||||
nodes: &[(String, String, String, Option<String>, Option<i64>)],
|
||||
parent_id: Option<&str>,
|
||||
) -> Result<TreeNode, String> {
|
||||
for (node_id, label, node_type, node_parent_id, file_size) in nodes {
|
||||
let is_match = match (parent_id, node_parent_id) {
|
||||
(None, None) => true,
|
||||
(Some(p), Some(np)) => p == np,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if is_match {
|
||||
let children = nodes.iter()
|
||||
.filter(|(_, _, _, np, _)| np.as_deref() == Some(node_id.as_str()))
|
||||
.map(|(cid, clabel, ctype, _, csize)| {
|
||||
TreeNode {
|
||||
id: cid.clone(),
|
||||
name: clabel.clone(),
|
||||
node_type: ctype.clone(),
|
||||
size: csize.map(|s| s as u64),
|
||||
children: vec
|
||||
![],
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Ok(TreeNode {
|
||||
id: node_id.clone(),
|
||||
name: label.clone(),
|
||||
node_type: node_type.clone(),
|
||||
size: file_size.map(|s| s as u64),
|
||||
children,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Err("Root node not found".to_string())
|
||||
}
|
||||
Reference in New Issue
Block a user