219 lines
6.7 KiB
Rust
219 lines
6.7 KiB
Rust
use crate::db::pool;
|
||
use rusqlite::params;
|
||
use serde::{Deserialize, Serialize};
|
||
use std::process::Command as StdCommand;
|
||
|
||
// ── 数据结构 ──────────────────────────────────────────────────────────────────
|
||
|
||
#[derive(Debug, Serialize)]
|
||
pub struct ProjectTool {
|
||
pub id: String,
|
||
pub project_id: String,
|
||
pub name: String,
|
||
pub icon: String,
|
||
pub exe_path: String,
|
||
pub args: Option<String>,
|
||
pub sort_order: i32,
|
||
}
|
||
|
||
#[derive(Debug, Deserialize)]
|
||
pub struct ProjectToolInput {
|
||
pub name: String,
|
||
pub icon: String,
|
||
pub exe_path: String,
|
||
pub args: Option<String>,
|
||
pub sort_order: Option<i32>,
|
||
}
|
||
|
||
// ── Tauri 命令 ────────────────────────────────────────────────────────────────
|
||
|
||
/// 获取某个项目的所有工具
|
||
#[tauri::command]
|
||
pub fn get_project_tools(project_id: String) -> Result<Vec<ProjectTool>, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let mut stmt = conn
|
||
.prepare(
|
||
"SELECT id, project_id, name, icon, exe_path, args, sort_order
|
||
FROM project_tools
|
||
WHERE project_id = ?1
|
||
ORDER BY sort_order ASC, rowid ASC",
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
let rows = stmt
|
||
.query_map(params![project_id], |row| {
|
||
Ok(ProjectTool {
|
||
id: row.get(0)?,
|
||
project_id: row.get(1)?,
|
||
name: row.get(2)?,
|
||
icon: row.get(3)?,
|
||
exe_path: row.get(4)?,
|
||
args: row.get(5)?,
|
||
sort_order: row.get(6)?,
|
||
})
|
||
})
|
||
.map_err(|e| e.to_string())?
|
||
.collect::<Result<Vec<_>, _>>()
|
||
.map_err(|e| e.to_string());
|
||
rows
|
||
}
|
||
|
||
/// 添加工具到项目
|
||
#[tauri::command]
|
||
pub fn add_project_tool(project_id: String, input: ProjectToolInput) -> Result<String, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let id = uuid::Uuid::new_v4().to_string();
|
||
conn.execute(
|
||
"INSERT INTO project_tools (id, project_id, name, icon, exe_path, args, sort_order)
|
||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||
params![
|
||
id,
|
||
project_id,
|
||
input.name,
|
||
input.icon,
|
||
input.exe_path,
|
||
input.args,
|
||
input.sort_order.unwrap_or(0),
|
||
],
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(id)
|
||
}
|
||
|
||
/// 更新工具信息
|
||
#[tauri::command]
|
||
pub fn update_project_tool(id: String, input: ProjectToolInput) -> Result<(), String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
conn.execute(
|
||
"UPDATE project_tools
|
||
SET name = ?1, icon = ?2, exe_path = ?3, args = ?4, sort_order = ?5
|
||
WHERE id = ?6",
|
||
params![
|
||
input.name,
|
||
input.icon,
|
||
input.exe_path,
|
||
input.args,
|
||
input.sort_order.unwrap_or(0),
|
||
id,
|
||
],
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 删除项目工具
|
||
#[tauri::command]
|
||
pub fn remove_project_tool(id: String) -> Result<(), String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
conn.execute("DELETE FROM project_tools WHERE id = ?1", params![id])
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(())
|
||
}
|
||
|
||
// ── 全局快启命令(写入 global_tools 表,无项目 FK 约束)────────────────────────
|
||
|
||
#[tauri::command]
|
||
pub fn get_global_tools() -> Result<Vec<ProjectTool>, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let mut stmt = conn
|
||
.prepare(
|
||
"SELECT id, name, icon, exe_path, args, sort_order
|
||
FROM global_tools
|
||
ORDER BY sort_order ASC, rowid ASC",
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
let rows = stmt
|
||
.query_map([], |row| {
|
||
Ok(ProjectTool {
|
||
id: row.get(0)?,
|
||
project_id: "__global__".to_string(),
|
||
name: row.get(1)?,
|
||
icon: row.get(2)?,
|
||
exe_path: row.get(3)?,
|
||
args: row.get(4)?,
|
||
sort_order: row.get(5)?,
|
||
})
|
||
})
|
||
.map_err(|e| e.to_string())?
|
||
.collect::<Result<Vec<_>, _>>()
|
||
.map_err(|e| e.to_string());
|
||
rows
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn add_global_tool(input: ProjectToolInput) -> Result<String, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let id = uuid::Uuid::new_v4().to_string();
|
||
conn.execute(
|
||
"INSERT INTO global_tools (id, name, icon, exe_path, args, sort_order)
|
||
VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
||
params![
|
||
id,
|
||
input.name,
|
||
input.icon,
|
||
input.exe_path,
|
||
input.args,
|
||
input.sort_order.unwrap_or(0),
|
||
],
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(id)
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn update_global_tool(id: String, input: ProjectToolInput) -> Result<(), String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
conn.execute(
|
||
"UPDATE global_tools
|
||
SET name = ?1, icon = ?2, exe_path = ?3, args = ?4, sort_order = ?5
|
||
WHERE id = ?6",
|
||
params![
|
||
input.name,
|
||
input.icon,
|
||
input.exe_path,
|
||
input.args,
|
||
input.sort_order.unwrap_or(0),
|
||
id,
|
||
],
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(())
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn remove_global_tool(id: String) -> Result<(), String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
conn.execute("DELETE FROM global_tools WHERE id = ?1", params![id])
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 启动工具(通用:exe 路径 + 可选参数)
|
||
#[tauri::command]
|
||
pub fn launch_tool(exe_path: String, args: Option<String>) -> Result<(), String> {
|
||
#[cfg(target_os = "windows")]
|
||
{
|
||
use std::os::windows::process::CommandExt;
|
||
let mut cmd = StdCommand::new("cmd");
|
||
cmd.args(["/c", &exe_path]);
|
||
if let Some(a) = &args {
|
||
let trimmed = a.trim();
|
||
if !trimmed.is_empty() {
|
||
cmd.raw_arg(trimmed);
|
||
}
|
||
}
|
||
cmd.spawn().map_err(|e| format!("启动失败: {e}"))?;
|
||
}
|
||
#[cfg(not(target_os = "windows"))]
|
||
{
|
||
let mut cmd = StdCommand::new(&exe_path);
|
||
if let Some(a) = &args {
|
||
let trimmed = a.trim();
|
||
if !trimmed.is_empty() {
|
||
cmd.args(trimmed.split_whitespace());
|
||
}
|
||
}
|
||
cmd.spawn().map_err(|e| format!("启动失败: {e}"))?;
|
||
}
|
||
Ok(())
|
||
}
|