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:
55
markbase-tauri/src-tauri/Cargo.lock
generated
55
markbase-tauri/src-tauri/Cargo.lock
generated
@@ -637,15 +637,6 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
@@ -656,18 +647,6 @@ dependencies = [
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
@@ -800,6 +779,18 @@ version = "2.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.4.1"
|
||||
@@ -1993,7 +1984,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"dirs",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
@@ -2336,12 +2327,6 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "option-ext"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.3"
|
||||
@@ -2992,6 +2977,20 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
|
||||
dependencies = [
|
||||
"bitflags 2.13.0",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
|
||||
@@ -21,10 +21,10 @@ tauri = { version = "1.8.3", features = ["fs-all", "path-all", "http-all", "shel
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
sqlx = { version = "0.7", features = ["runtime-tokio", "sqlite"] }
|
||||
sysinfo = "0.30"
|
||||
dirs = "5.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
rusqlite = { version = "0.30", features = ["bundled"] }
|
||||
|
||||
[features]
|
||||
custom-protocol = [ "tauri/custom-protocol" ]
|
||||
|
||||
@@ -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