collect-system/apps/web/src/components/models/term/term-list.tsx

335 lines
8.4 KiB
TypeScript
Executable File

import { useCallback, useContext, useEffect, useMemo, useState } from "react";
import { Empty, Tree, Button, message, TreeProps } from "antd";
import {
DeleteOutlined,
DownOutlined,
EditFilled,
EllipsisOutlined,
ImportOutlined,
PlusOutlined,
} from "@ant-design/icons";
import { CrudOperation, emitDataChange, useTerm } from "@nice/client";
import { ObjectType, Term, TreeDataNode } from "@nice/common";
import DepartmentSelect from "../department/department-select";
import { TermEditorContext } from "./term-editor";
import { useAuth } from "@web/src/providers/auth-provider";
import { api } from "@nice/client";
import { Menu, MenuItem } from "../../presentation/dropdown-menu";
import AgServerTable from "../../presentation/ag-server-table";
import { CustomCellRendererProps } from "ag-grid-react";
import { ColDef, SortDirection } from "@ag-grid-community/core";
const OpreationRenderer = ({ props }: { props: CustomCellRendererProps }) => {
const { setEditId, setTermModalOpen, setImportModalOpen, setParentId } =
useContext(TermEditorContext);
const { softDeleteByIds } = useTerm();
return (
<Menu
node={
<EllipsisOutlined className=" hover:bg-textHover p-1 rounded" />
}>
<MenuItem
label="导入子节点"
icon={<ImportOutlined></ImportOutlined>}
onClick={() => {
setParentId(props?.data?.id);
// setEditId(data?.id)
setImportModalOpen(true);
}}></MenuItem>
<MenuItem
label="添加子节点"
icon={<PlusOutlined></PlusOutlined>}
onClick={() => {
setParentId(props?.data?.id);
// setEditId(data?.id)
setTermModalOpen(true);
}}></MenuItem>
<MenuItem
label="编辑"
icon={<EditFilled></EditFilled>}
onClick={() => {
setEditId(props?.data?.id);
setTermModalOpen(true);
}}></MenuItem>
<MenuItem
label="移除"
onClick={() => {
softDeleteByIds.mutateAsync(
{
ids: [props?.data?.id],
},
{
onSettled: () => {
message.success("删除成功");
emitDataChange(
ObjectType.TERM,
props.data as any,
CrudOperation.DELETED
);
},
}
);
}}
icon={<DeleteOutlined></DeleteOutlined>}></MenuItem>
</Menu>
);
};
export default function TermList() {
const {
domainId,
setDomainId,
taxonomyId,
canManageAnyTerm,
setTermModalOpen,
setImportModalOpen,
} = useContext(TermEditorContext);
const { user } = useAuth();
useEffect(() => {
if (user) {
setDomainId(user.domainId);
}
}, [user]);
const [params, setParams] = useState({
parentId: null,
domainId: null,
taxonomyId: null,
});
useEffect(() => {
if (taxonomyId) {
setParams((prev) => ({ ...prev, taxonomyId }));
}
if (domainId) {
setParams((prev) => ({ ...prev, domainId }));
} else {
setParams((prev) => ({ ...prev, domainId: null }));
}
}, [taxonomyId, domainId]);
const columnDefs = useMemo<ColDef[]>(() => {
return [
{
field: "order",
hide: true,
sort: "asc" as SortDirection,
},
{
headerName: "操作",
// sortable: true,
cellRenderer: (props: CustomCellRendererProps) => (
<OpreationRenderer props={props} />
),
maxWidth: 80,
},
].filter(Boolean);
}, []);
const autoGroupColumnDef = useMemo(
() => ({
rowDrag: true,
headerName: "术语名",
field: "name",
filter: "agTextColumnFilter",
}),
[]
);
const getServerSideGroupKey = useCallback((item) => item.id, []);
const isServerSideGroup = useCallback((item) => item.has_children, []);
return (
<div className="flex flex-col w-full">
<div className=" justify-between flex items-center gap-4 border-b p-2">
<span className=" text-secondary"></span>
<div className=" flex items-center gap-4">
<DepartmentSelect
disabled={!canManageAnyTerm}
className="w-48"
domain={true}
value={domainId}
onChange={(value) =>
setDomainId(value as string)
}></DepartmentSelect>
<Button
type="primary"
icon={<ImportOutlined />}
onClick={() => {
setImportModalOpen(true);
}}
ghost>
</Button>
<Button
icon={<PlusOutlined></PlusOutlined>}
type="primary"
onClick={() => {
setTermModalOpen(true);
}}>
</Button>
</div>
</div>
<AgServerTable
height="calc(100vh - 49px - 48px - 49px)"
columnDefs={columnDefs}
objectType={ObjectType.TERM}
treeData={true}
params={params} // 使用 state 中的 params
getServerSideGroupKey={getServerSideGroupKey}
isServerSideGroup={isServerSideGroup}
autoGroupColumnDef={autoGroupColumnDef}></AgServerTable>
{/* <div
className="p-2"
style={{ height: "calc(100vh - 49px - 48px - 49px)" }}>
{treeData.length > 0 ? (
<Tree
style={{ minWidth: 400 }}
treeData={treeData}
draggable
blockNode
onDragEnter={onDragEnter}
onDrop={async (info) => {
try {
console.log(
"Drag and Drop operation initiated with info:",
info
);
const dropKey = info.node.key;
const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split("-");
const dropPosition =
info.dropPosition -
Number(dropPos[dropPos.length - 1]);
console.debug(
`Calculated drop position: ${dropPosition}`
);
const data = [...treeData];
let dragObj;
console.debug(
"Starting tree visitor to find and remove drag object."
);
treeVisitor(
data,
dragKey,
(item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
console.debug(
`Removed dragged node: `,
dragObj
);
}
);
let parentNodeId = null;
let siblings = [];
if (!info.dropToGap) {
console.debug(
"Drop onto node action detected."
);
treeVisitor(data, dropKey, (item) => {
item.children = item.children || [];
item.children.unshift(dragObj);
parentNodeId = item.key;
siblings = item.children;
console.debug(
`Added drag node as a child of node: ${parentNodeId}`
);
});
} else if (
(info.node.children || []).length > 0 &&
info.node.expanded &&
dropPosition === 1
) {
console.debug(
"Drop after expanded node with children detected."
);
treeVisitor(data, dropKey, (item) => {
item.children = item.children || [];
item.children.unshift(dragObj);
parentNodeId = item.key;
siblings = item.children;
console.debug(
`Added drag node as a child of node: ${parentNodeId}`
);
});
} else {
console.debug("Drop in gap detected.");
let ar = [];
let i = 0;
treeVisitor(
data,
dropKey,
(item, index, arr) => {
ar = arr;
i = index;
}
);
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
parentNodeId = ar[0].parentId || null;
siblings = ar;
console.debug(
`Inserted drag node at position: ${i}, under parentNodeId: ${parentNodeId}`
);
}
setTreeData(data);
console.debug(
"Tree data updated with new structure."
);
console.log(siblings);
const { id } = dragObj;
const updatePromises = siblings.map(
(sibling, idx) => {
return update.mutateAsync({
id: sibling.id,
order: idx,
parentId: parentNodeId,
});
}
);
console.debug(
"Starting update of siblings' order and parentId."
);
await Promise.all(updatePromises);
console.log(
`Updated node ${id} and its siblings with new order and parentId ${parentNodeId}`
);
} catch (error) {
console.error(
"An error occurred during the drag and drop operation:",
error
);
}
}}
checkable
checkStrictly
titleRender={titleRender}
showLine={{ showLeafIcon: false }}
switcherIcon={<DownOutlined />}
/>
) : (
<div className="pt-32">
<Empty />
</div>
)}
</div> */}
</div>
);
}