import { 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" import { Label } from "~/components/ui/label" import type { MappingInfo } from "~/lib/api" import { getParentPath, resolveLocalPath } from "~/lib/tfs" /** * 分支弹窗:把源路径分叉到目标路径(pendBranch 产生挂起更改,签入后服务器生效)。 * 目标路径父目录必须已映射(分支内容需要落盘到本地工作区);可选基于指定 changeset。 */ export function BranchDialog({ sourceServerPath, mappings, onConfirm, onClose, }: { sourceServerPath: string mappings: MappingInfo[] /** 确认分支,由编排层调用接口并刷新,返回是否成功。 */ onConfirm: (targetServerPath: string, changeset: number | undefined) => Promise onClose: () => void }) { const [target, setTarget] = useState(`${sourceServerPath}-branch`) const [changesetInput, setChangesetInput] = useState("") const [busy, setBusy] = useState(false) const trimmed = target.trim() const parentPath = trimmed.length > 2 ? getParentPath(trimmed) : null const parentMapped = parentPath != null && resolveLocalPath(mappings, parentPath) != null const targetLocalPath = trimmed.length > 2 ? resolveLocalPath(mappings, trimmed) : null const changesetTrimmed = changesetInput.trim() const changesetValid = changesetTrimmed.length === 0 || /^\d+$/.test(changesetTrimmed) const validation = !trimmed.startsWith("$/") || trimmed.length <= 2 ? "目标路径必须以 $/ 开头" : trimmed === sourceServerPath ? "目标路径不能与源路径相同" : trimmed.startsWith(`${sourceServerPath}/`) ? "目标路径不能位于源路径内部" : !parentMapped ? "目标路径所在父目录未映射,请先在工作区中映射" : !changesetValid ? "changeset 必须是数字" : null /** * 执行分支:成功后由上层刷新并关闭弹窗。 */ const handleConfirm = () => { void (async () => { setBusy(true) const ok = await onConfirm( trimmed, changesetTrimmed.length === 0 ? undefined : Number(changesetTrimmed), ) setBusy(false) if (ok) { onClose() } })() } return ( !open && !busy && onClose()}> 分支 从源路径创建分支,结果为 branch 挂起更改,签入后服务器生效

{sourceServerPath}

setTarget(event.target.value)} disabled={busy} className="font-mono text-xs" /> {targetLocalPath != null && validation == null && (

本地落盘:{targetLocalPath}

)}
setChangesetInput(event.target.value)} placeholder="留空表示最新版本" disabled={busy} inputMode="numeric" className="w-56 font-mono text-xs" />
{validation != null &&

{validation}

}

分支只产生挂起更改,可在挂起更改面板审查、撤销,签入后目标路径才会出现在服务器上。

) }