doctor-mail/apps/web/src/components/models/term/term-select.tsx

304 lines
7.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 { TreeSelect, TreeSelectProps } from "antd";
import React, { useEffect, useState, useCallback, useRef } from "react";
import { getUniqueItems } from "@nice/common";
import { api } from "@nice/client";
import { DefaultOptionType } from "antd/es/select";
import "./TermSelect.css";
interface TermSelectProps {
defaultValue?: string | string[];
value?: string | string[];
onChange?: (value: string | string[]) => void;
placeholder?: string;
multiple?: boolean;
// rootId?: string;
// domain?: boolean;
taxonomyId?: string;
disabled?: boolean;
className?: string;
domainId?: string;
}
export default function TermSelect({
defaultValue,
value,
onChange,
className,
placeholder = "选择术语",
multiple = false,
taxonomyId,
domainId,
disabled = false,
}: 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 terms:", 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[]) => {
// console.log(keys);
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);
}
};
// 检测旧版本浏览器
const isLegacyBrowser = () => {
const chromeMatch = navigator.userAgent.match(/Chrome\/(\d+)/);
if (chromeMatch) {
const version = parseInt(chromeMatch[1], 10);
return version <= 87;
}
return false;
};
// 为降级版本准备的状态
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
// 处理点击外部关闭下拉框
useEffect(() => {
if (isLegacyBrowser()) {
const handleClickOutside = (event: MouseEvent) => {
if (
dropdownRef.current &&
!dropdownRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () =>
document.removeEventListener("mousedown", handleClickOutside);
}
}, []);
if (isLegacyBrowser()) {
return (
<div className="legacy-select-container" ref={dropdownRef}>
<div
className="legacy-select-trigger"
onClick={() => setIsOpen(!isOpen)}>
{multiple ? (
<div className="legacy-select-multiple-value">
{Array.isArray(value) && value.length > 0 ? (
value.map((v) => {
const item = listTreeData.find(
(i) => i.id === v
);
return (
<span key={v} className="legacy-tag">
{item?.title}
<span
className="legacy-tag-close"
onClick={(e) => {
e.stopPropagation();
const newValue =
value.filter(
(val: string) =>
val !== v
);
handleChange(newValue);
}}>
×
</span>
</span>
);
})
) : (
<span className="legacy-placeholder">
{placeholder}
</span>
)}
</div>
) : (
<div className="legacy-select-single-value">
{value ? (
listTreeData.find((i) => i.id === value)?.title
) : (
<span className="legacy-placeholder">
{placeholder}
</span>
)}
</div>
)}
<span className={`legacy-arrow ${isOpen ? "open" : ""}`}>
</span>
</div>
{isOpen && (
<div className="legacy-select-dropdown">
{listTreeData.map((item) => (
<div
key={item.id}
className={`legacy-select-option ${
multiple
? Array.isArray(value) &&
value.includes(item.id)
: value === item.id
? "selected"
: ""
}`}
onClick={() => {
if (multiple) {
const newValue = Array.isArray(value)
? value.includes(item.id)
? value.filter(
(v) => v !== item.id
)
: [...value, item.id]
: [item.id];
handleChange(newValue);
} else {
handleChange(item.id);
setIsOpen(false);
}
}}>
{multiple && (
<span className="legacy-checkbox">
{Array.isArray(value) &&
value.includes(item.id) &&
"✓"}
</span>
)}
{item.title}
</div>
))}
</div>
)}
</div>
);
}
return (
<TreeSelect
treeDataSimpleMode
disabled={disabled}
showSearch
allowClear
defaultValue={defaultValue}
value={value}
// className={`custom-tree-select ${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}
// 添加以下属性来优化显示
/>
);
}