import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { open } from "@tauri-apps/plugin-dialog"; import { getGlobalTools, addGlobalTool, updateGlobalTool, removeGlobalTool, type GlobalTool, type GlobalToolInput, } from "../../lib/commands"; import { useUIStore } from "../../store/ui"; import { Dialog } from "../ui/Dialog"; export const GLOBAL_TOOLS_QUERY_KEY = ["globalTools"]; const COMMON_ICONS = ["🔧", "🎬", "🎨", "📷", "🗄️", "📮", "🌐", "📊", "🎵", "📐", "🔬", "⚙️", "📁", "🖨️", "🎮"]; const EMPTY: GlobalToolInput = { name: "", icon: "🔧", exe_path: "", args: "" }; interface Props { onClose: () => void; defaultOpenAdd?: boolean; } export function GlobalToolsModal({ onClose, defaultOpenAdd }: Props) { const showToast = useUIStore((s) => s.showToast); const qc = useQueryClient(); const { data: tools = [], refetch } = useQuery({ queryKey: GLOBAL_TOOLS_QUERY_KEY, queryFn: () => getGlobalTools(), }); const [editing, setEditing] = useState(null); // null = 新增模式 const [form, setForm] = useState(EMPTY); const [showForm, setShowForm] = useState(defaultOpenAdd ?? false); const [saving, setSaving] = useState(false); const openAdd = () => { setEditing(null); setForm(EMPTY); setShowForm(true); }; const openEdit = (tool: GlobalTool) => { setEditing(tool); setForm({ name: tool.name, icon: tool.icon, exe_path: tool.exe_path, args: tool.args ?? "" }); setShowForm(true); }; const set = (k: keyof GlobalToolInput, v: string) => setForm((f) => ({ ...f, [k]: v })); const pickExe = async () => { const selected = await open({ directory: false, multiple: false, filters: [{ name: "可执行文件", extensions: ["exe", "cmd", "bat", "lnk"] }], }); if (!selected) return; const path = selected as string; set("exe_path", path); // 如果名称还是空的,从文件名推断 if (!form.name.trim()) { const parts = path.replace(/\\/g, "/").split("/"); const filename = parts[parts.length - 1]; const name = filename.replace(/\.(exe|cmd|bat|lnk)$/i, ""); set("name", name); } }; const handleSave = async () => { if (!form.name.trim()) { showToast("❌ 工具名称不能为空"); return; } if (!form.exe_path.trim()) { showToast("❌ 程序路径不能为空"); return; } setSaving(true); try { const input: GlobalToolInput = { ...form, args: form.args?.trim() || undefined, sort_order: editing?.sort_order ?? tools.length, }; if (editing) { await updateGlobalTool(editing.id, input); } else { await addGlobalTool(input); } await refetch(); qc.invalidateQueries({ queryKey: GLOBAL_TOOLS_QUERY_KEY }); setShowForm(false); showToast(editing ? "已更新 ✓" : "已添加 ✓"); } catch (e) { showToast(`❌ ${e}`); } finally { setSaving(false); } }; const handleDelete = async (id: string) => { try { await removeGlobalTool(id); await refetch(); qc.invalidateQueries({ queryKey: GLOBAL_TOOLS_QUERY_KEY }); } catch (e) { showToast(`❌ ${e}`); } }; const inputCls = "w-full border border-gray-200 rounded-lg px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-blue-200"; return (
{/* 工具列表 */} {tools.length > 0 ? (
{tools.map((tool) => (
{tool.icon}

{tool.name}

{tool.exe_path}

{tool.args && (

{tool.args}

)}
))}
) : !showForm ? (

🧰

还没有配置工具

添加常用软件,一键从项目卡片启动

) : null} {/* 添加/编辑表单 */} {showForm ? (

{editing ? "编辑工具" : "添加工具"}

{/* 图标选择 */}
{COMMON_ICONS.map((ic) => ( ))} set("icon", e.target.value)} placeholder="…" maxLength={2} title="自定义 emoji" />
{/* 名称 + 路径 */}
set("name", e.target.value)} placeholder="RealityCapture" />
set("args", e.target.value)} placeholder="-load project.rcproj" />
set("exe_path", e.target.value)} placeholder="C:\Program Files\...\app.exe" />
) : ( )}
); }