Files
senmesh-zc/src/components/move-file-dialog.tsx
2026-02-06 10:10:26 +08:00

237 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, 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<any[]>([]);
// 文件目录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">
<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>
);
}