training_data/apps/web/src/components/models/term/term-select.tsx

200 lines
4.8 KiB
TypeScript
Executable File

import { TreeSelect, TreeSelectProps } from "antd";
import React, {
useEffect,
useState,
useCallback,
useRef,
ReactElement,
JSXElementConstructor,
} from "react";
import { getUniqueItems } from "@nice/common";
import { api } from "@nice/client";
import { DefaultOptionType } from "antd/es/select";
interface TermSelectProps {
defaultValue?: string | string[];
value?: string | string[];
onChange?: (value: string | string[]) => void;
placeholder?: string;
multiple?: boolean;
taxonomyId?: string;
disabled?: boolean;
className?: string;
domainId?: string;
dropdownStyle?: React.CSSProperties;
style?: React.CSSProperties;
open?: boolean;
showSearch?: boolean;
dropdownRender?: (
menu: ReactElement<any, string | JSXElementConstructor<any>>
) => ReactElement<any, string | JSXElementConstructor<any>>;
}
export default function TermSelect({
defaultValue,
value,
onChange,
className,
placeholder = "选择分类",
multiple = false,
taxonomyId,
open = undefined,
showSearch = true,
domainId,
dropdownStyle,
style,
disabled = false,
dropdownRender,
}: TermSelectProps) {
const utils = api.useUtils();
const [listTreeData, setListTreeData] = useState<
Omit<DefaultOptionType, "label">[]
>([]);
const fetchParentTerms = useCallback(
async (termIds: string | string[], taxonomyId?: string) => {
const idsArray = Array.isArray(termIds)
? termIds
: [termIds].filter(Boolean);
try {
return await utils.term.getParentSimpleTree.fetch({
termIds: idsArray,
taxonomyId,
domainId,
});
} catch (error) {
console.error(
"Error fetching parent departments for deptIds",
idsArray,
":",
error
);
throw error;
}
},
[utils]
);
const fetchTerms = useCallback(async () => {
try {
const rootDepts = await utils.term.getChildSimpleTree.fetch({
taxonomyId,
domainId,
});
let combinedDepts = [...rootDepts];
if (defaultValue) {
const defaultDepts = await fetchParentTerms(
defaultValue,
taxonomyId
);
combinedDepts = getUniqueItems(
[...listTreeData, ...combinedDepts, ...defaultDepts] as any,
"id"
);
}
if (value) {
const valueDepts = await fetchParentTerms(value, taxonomyId);
combinedDepts = getUniqueItems(
[...listTreeData, ...combinedDepts, ...valueDepts] as any,
"id"
);
}
setListTreeData(combinedDepts);
} catch (error) {
console.error("Error fetching departments:", error);
}
}, [defaultValue, value, taxonomyId, utils, fetchParentTerms]);
useEffect(() => {
fetchTerms();
}, [defaultValue, value, taxonomyId, fetchTerms]);
const handleChange = (newValue: any) => {
if (onChange) {
const processedValue =
multiple && Array.isArray(newValue)
? newValue.map((item) => item.value)
: newValue;
onChange(processedValue);
}
};
const onLoadData: TreeSelectProps["loadData"] = async ({ id }) => {
try {
const result = await utils.term.getChildSimpleTree.fetch({
termIds: [id],
taxonomyId,
domainId,
});
const newItems = getUniqueItems([...listTreeData, ...result], "id");
setListTreeData(newItems);
} catch (error) {
console.error(
"Error loading data for node with id",
id,
":",
error
);
}
};
const handleExpand = async (keys: React.Key[]) => {
try {
const allKeyIds =
keys.map((key) => key.toString()).filter(Boolean) || [];
const expandedNodes = await utils.term.getChildSimpleTree.fetch({
termIds: allKeyIds,
taxonomyId,
domainId,
});
const flattenedNodes = expandedNodes.flat();
const newItems = getUniqueItems(
[...listTreeData, ...flattenedNodes],
"id"
);
setListTreeData(newItems);
} catch (error) {
console.error("Error expanding nodes with keys", keys, ":", error);
}
};
const handleDropdownVisibleChange = async (open: boolean) => {
if (open) {
// This will attempt to expand all nodes and fetch their children when the dropdown opens
const allKeys = listTreeData.map((item) => item.id);
await handleExpand(allKeys);
}
};
return (
<TreeSelect
treeDataSimpleMode
disabled={disabled}
showSearch={showSearch}
allowClear
style={style}
// ref={selectRef}
dropdownStyle={{
width: "300px", // 固定宽度
minWidth: "200px", // 最小宽度
maxWidth: "600px", // 最大宽度
...dropdownStyle,
}}
dropdownRender={dropdownRender}
defaultValue={defaultValue}
value={value}
open={open}
className={className}
placeholder={placeholder}
onChange={handleChange}
loadData={onLoadData}
treeData={listTreeData}
treeCheckable={multiple}
showCheckedStrategy={TreeSelect.SHOW_ALL}
treeCheckStrictly={multiple}
onClear={() => handleChange(multiple ? [] : undefined)}
onTreeExpand={handleExpand}
onDropdownVisibleChange={handleDropdownVisibleChange}
/>
);
}