import { useMemo, useState } from "react" import { Button } from "~/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "~/components/ui/dialog" import { Input } from "~/components/ui/input" // Windows / TFS 路径名非法字符。 const INVALID_NAME_PATTERN = /[\\/:*?"<>|]/ /** * 重命名弹窗:同目录改名,校验非空、非法字符与同名; * 确认后产生 rename 挂起更改(由编排层调用接口并刷新)。 */ export function RenameDialog({ serverPath, folder, onConfirm, onClose, }: { serverPath: string folder: boolean /** 确认重命名,由编排层执行 API 与刷新,返回是否成功。 */ onConfirm: (newName: string) => Promise onClose: () => void }) { const currentName = useMemo( () => serverPath.slice(serverPath.lastIndexOf("/") + 1), [serverPath], ) const [name, setName] = useState(currentName) const [busy, setBusy] = useState(false) const trimmed = name.trim() const validation = trimmed.length === 0 ? "名称不能为空" : INVALID_NAME_PATTERN.test(trimmed) ? '名称不能包含 / \\ : * ? " < > | 字符' : trimmed === currentName ? "新名称与当前名称相同" : null return ( !open && !busy && onClose()}> 重命名{folder ? "目录" : "文件"} {serverPath}
setName(event.target.value)} autoFocus onFocus={(event) => { // 文件默认选中主名部分,便于直接输入新名称。 const dot = folder ? -1 : currentName.lastIndexOf(".") event.target.setSelectionRange(0, dot > 0 ? dot : currentName.length) }} onKeyDown={(event) => { if (event.key === "Enter" && validation == null && !busy) { event.preventDefault() void (async () => { setBusy(true) const ok = await onConfirm(trimmed) setBusy(false) if (ok) { onClose() } })() } }} /> {validation != null && trimmed !== currentName && (

{validation}

)}

重命名会产生挂起更改,签入后服务器才会生效;签入前可在挂起更改面板撤销。

) }