Files
senmesh-zc/src/components/move-file-dialog.tsx

238 lines
6.5 KiB
TypeScript
Raw Normal View History

2026-02-05 18:12:49 +08:00
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { Spinner } from "@/components/ui/spinner";
import { FileType, getFilePage } from "@/apis/file";
import { Folder, FolderInput, CircleAlert, MoveRight } from "lucide-react";
import clsx from "clsx";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { DirectoryBreadcrumb } from "@/components/directory-breadcrumb";
import { DataTable } from "@/components/data-table";
import { Pagination } from "@/components/pagination";
import { useMoveFile } from "@/hooks/use-move-flie";
import { useEffect, useMemo, useState } from "react";
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
import { useQuery } from "@tanstack/react-query";
export function useColumns(handleChangeParentId: any) {
return [
{
accessorKey: "fileName",
header: "目录名",
size: 200,
cell: ({ row }: any) => (
<div
className={clsx(
"w-full flex items-center gap-1 break-all",
"cursor-pointer hover:underline hover:text-primary group",
)}
onClick={() => {
handleChangeParentId(row?.original);
}}
>
<Folder size={18} className="text-amber-500 group-hover:hidden" />
<FolderInput
size={18}
className="text-amber-500 group-hover:block hidden"
/>
<span>{row.getValue("fileName")}</span>
</div>
),
},
];
}
type MoveFileDialogProps = {
open: boolean;
setOpen: (open: boolean) => void;
data?: any;
refetch?: () => void;
params: any;
};
export function MoveFileDialog({
open,
setOpen,
data,
refetch,
params,
}: MoveFileDialogProps) {
// 选中行
const [rowSelection, setRowSelection] = useState({});
// 当前打开的目录层级
const [openDirectorys, setOpenDirectorys] = useState([]);
// 文件目录ID
const [parentId, setParentId] = useState(0);
// 分页
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 10,
});
// 查询数据
const query = useQuery<any>({
queryKey: ["moveFiles", pagination, parentId, params],
queryFn: () =>
getFilePage({
...params,
parentId: parentId || 0,
pageSize: pagination.pageSize,
pageNo: pagination.pageIndex + 1,
fileType: FileType.DIRECTORY,
}).then((res) => res.data),
enabled: open,
});
// 切换上级ID
const handleChangeParentId = (record: any) => {
setParentId(record.id);
// 重置分页
table.setPageIndex(0);
// @ts-ignore
setOpenDirectorys((prev: any) => [...prev, record]);
// 重置选中数据
table.resetRowSelection();
};
// 过滤当前目录
const list = useMemo(
() =>
query?.data?.entityList?.filter((item: any) => item.id !== data?.id) ||
[],
[query, data],
);
// 表格
const columns = useColumns(handleChangeParentId);
const table = useReactTable({
data: list,
columns,
getCoreRowModel: getCoreRowModel(),
manualPagination: false,
rowCount: query?.data?.totalCount || 0,
pageCount: query?.data?.pageCount || 0,
onPaginationChange: setPagination,
autoResetPageIndex: false,
onRowSelectionChange: setRowSelection,
state: {
pagination,
rowSelection,
},
});
const changeOpenDirectorys = (record?: any) => {
setParentId(record?.id);
// 重置分页
table.setPageIndex(0);
// @ts-ignore
setOpenDirectorys((prev: any[]) => {
const index = prev.findIndex((item: any) => item.id === record?.id);
if (index > -1) {
return [...prev.slice(0, index + 1)];
}
return [];
});
// 重置选中数据
table.resetRowSelection();
};
// 重置
const reset = () => {
setParentId(0);
// 重置分页
table.setPageIndex(0);
// @ts-ignore
setOpenDirectorys([]);
// 重置选中数据
table.resetRowSelection();
};
const { move, moveIsPending } = useMoveFile({
onSuccess: () => {
refetch?.();
setOpen(false);
},
});
const handleSubmit = () => {
move({
...data,
parentId,
});
};
useEffect(() => {
if (open) {
reset();
}
}, [open]);
const currentDirectory = useMemo(
() => openDirectorys?.[openDirectorys?.length - 1],
[openDirectorys],
);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="w-[680px]">
<DialogHeader>
<DialogTitle>
{data?.fileType === FileType.DIRECTORY ? "移动目录" : "移动文件"}
</DialogTitle>
</DialogHeader>
<div className="overflow-hidden space-y-2">
<DirectoryBreadcrumb
directorys={openDirectorys}
onChangeDirectorys={changeOpenDirectorys}
/>
<div className="max-h-[400px] overflow-y-auto relative border rounded-lg">
<DataTable table={table} query={query} />
<Pagination
table={table}
simple
className="p-2 justify-end sticky bottom-0 left-0 right-0 z-10 bg-white"
/>
</div>
</div>
<div className="flex items-center gap-2 text-sm overflow-hidden">
<span className="flex-1 overflow-hidden text-primary clamp-1 truncate p-2 rounded-lg bg-gray-100">
{data?.fileName}
</span>
<MoveRight size={16} className="flex-none" />
<span className="flex-1 overflow-hidden text-amber-500 clamp-1 truncate inline-flex items-center gap-1 p-2 rounded-lg bg-gray-100">
<Folder size={16} className="flex-none " />
{currentDirectory?.fileName || "根目录"}
</span>
</div>
<Alert className="!bg-amber-50 !border-amber-200">
<CircleAlert />
<AlertTitle>
您确定要将{data?.fileType === FileType.DIRECTORY ? "目录" : "文件"}
移动到该目录下吗?
</AlertTitle>
<AlertDescription>
如果是目录,则目录下的所有文件和子目录都会被移动
</AlertDescription>
</Alert>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">取消</Button>
</DialogClose>
<Button type="submit" disabled={moveIsPending} onClick={handleSubmit}>
{moveIsPending && <Spinner />}确定
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}