import { useEffect, useState } from "react" import { Check, Copy, Loader2 } from "lucide-react" import { Badge } from "~/components/ui/badge" import { Button } from "~/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "~/components/ui/dialog" import { api } from "~/lib/api" import type { ItemInfo } from "~/lib/api" import type { FileTarget } from "~/lib/tfs" import { statusBadgeClass, statusLabel } from "~/lib/tfs" import { formatDateTime } from "~/lib/utils" /** * 把字节数格式化为可读大小(B / KB / MB)。 */ function formatSize(size: number | null): string { if (size == null) { return "—" } if (size < 1024) { return `${size} B` } if (size < 1024 * 1024) { return `${(size / 1024).toFixed(1)} KB` } return `${(size / 1024 / 1024).toFixed(2)} MB` } /** * 对象属性弹窗:展示服务器路径、本地路径、映射与挂起状态, * 以及服务器侧最新版本信息(changeset / 时间 / 提交人),文件附带大小与编码。 */ export function PropertiesDialog({ target, onClose, }: { target: FileTarget onClose: () => void }) { const [info, setInfo] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) useEffect(() => { let active = true void (async () => { setLoading(true) const result = await api.getItemInfo({ serverPath: target.serverPath }) if (!active) { return } setLoading(false) if (!result.ok || !result.data) { setError(result.errorMessage ?? "属性加载失败") return } setError(null) setInfo(result.data.item) })() return () => { active = false } }, [target.serverPath]) return ( !open && onClose()}> 属性 {target.serverPath}
{target.mapped ? (target.mappingRoot ? "映射根目录" : "已映射") : "未映射"} } /> {statusLabel(target.pendingStatus)} ) : ( "无挂起更改" ) } muted={target.pendingStatus == null} />
{loading ? (
正在加载服务器信息…
) : error ? (
{error}
) : info ? ( <> {!info.folder && ( <> )} ) : null}
) } /** * 单行属性:左侧标签 + 右侧值,可选等宽字体与复制按钮。 */ function PropertyRow({ label, value, mono, copyable, muted, }: { label: string value: React.ReactNode mono?: boolean copyable?: boolean muted?: boolean }) { const [copied, setCopied] = useState(false) return (
{label} {value} {copyable && typeof value === "string" && ( )}
) }