import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { getDeployCommands, addDeployCommand, removeDeployCommand, executeDeploy, type DeployCommand, } from "../../lib/commands"; import { useUIStore } from "../../store/ui"; interface Props { projectId: string; projectName: string; projectPath?: string; // Windows 路径(win_path) projectWslPath?: string; // WSL 路径(wsl_path),作为 WSL 命令的 fallback cwd onClose: () => void; } export function DeployModal({ projectId, projectName, projectPath, projectWslPath, onClose }: Props) { const queryClient = useQueryClient(); const showToast = useUIStore((s) => s.showToast); const [newName, setNewName] = useState(""); const [newCmd, setNewCmd] = useState(""); const [newRunIn, setNewRunIn] = useState<"windows" | "wsl">("windows"); // 正在执行的命令 id const [runningId, setRunningId] = useState(null); // 执行输出 const [output, setOutput] = useState<{ id: string; text: string; ok: boolean } | null>(null); const { data: commands = [] } = useQuery({ queryKey: ["deployCommands", projectId], queryFn: () => getDeployCommands(projectId), }); const addMutation = useMutation({ mutationFn: () => addDeployCommand(projectId, newName, newCmd, newRunIn), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["deployCommands", projectId] }); setNewName(""); setNewCmd(""); }, onError: (e) => showToast(`添加失败: ${e}`), }); const removeMutation = useMutation({ mutationFn: (id: string) => removeDeployCommand(id), onSuccess: () => queryClient.invalidateQueries({ queryKey: ["deployCommands", projectId] }), }); async function handleRun(cmd: DeployCommand) { setRunningId(cmd.id); setOutput(null); try { // WSL 命令优先用 projectPath(Windows 路径,WSL 自动映射 /mnt/...) // 若无 win_path,则将 wsl_path 通过 `cd && ` 前缀传入命令 let effectiveCommand = cmd.command; let cwd: string | undefined = projectPath; if (cmd.run_in === "wsl" && !projectPath && projectWslPath) { effectiveCommand = `cd ${JSON.stringify(projectWslPath)} && ${cmd.command}`; cwd = undefined; } const result = await executeDeploy(effectiveCommand, cmd.run_in, cwd); setOutput({ id: cmd.id, text: result, ok: true }); } catch (e) { setOutput({ id: cmd.id, text: String(e), ok: false }); } finally { setRunningId(null); } } const canAdd = newName.trim() && newCmd.trim(); return (
{/* 标题 */}

部署命令

{projectName}

{/* 已有命令列表 */} {commands.length === 0 && (

暂无部署命令

)} {commands.map((cmd) => (
{cmd.name} {cmd.run_in.toUpperCase()}
{cmd.command} {/* 执行输出 */} {output?.id === cmd.id && (
{output.text}
)}
))}
{/* 添加新命令 */}

添加命令

setNewName(e.target.value)} className="flex-1 bg-zinc-800 text-white text-sm rounded-lg px-3 py-2 outline-none focus:ring-1 focus:ring-blue-500 placeholder:text-zinc-600" />
setNewCmd(e.target.value)} onKeyDown={(e) => e.key === "Enter" && canAdd && addMutation.mutate()} className="flex-1 bg-zinc-800 text-white text-sm rounded-lg px-3 py-2 outline-none focus:ring-1 focus:ring-blue-500 placeholder:text-zinc-600 font-mono" />
); }