import { useEffect, useRef, useState } from "react"; import { SearchOutlined } from "@ant-design/icons"; import { searchStaff, type StaffSearchVO } from "@/api/staff"; interface Props { value: string; onChange: (name: string, staffId: number | null) => void; disabled?: boolean; required?: boolean; placeholder?: string; } const fieldControl: React.CSSProperties = { flex: 1, minWidth: 0, height: "var(--input-h)", border: "1px solid var(--border-input)", background: "var(--bg-input)", padding: "0 26px 0 6px", fontSize: 12, color: "var(--text)", borderRadius: 0, outline: "none", fontFamily: "inherit", }; export default function StaffPicker({ value, onChange, disabled, required, placeholder, }: Props) { const [open, setOpen] = useState(false); const [items, setItems] = useState([]); const [loading, setLoading] = useState(false); const wrapRef = useRef(null); const debounce = useRef(null); useEffect(() => { function onDocClick(e: MouseEvent) { if (wrapRef.current && !wrapRef.current.contains(e.target as Node)) { setOpen(false); } } document.addEventListener("mousedown", onDocClick); return () => document.removeEventListener("mousedown", onDocClick); }, []); const fetchSuggestions = async (kw: string) => { setLoading(true); try { const list = await searchStaff(kw, 20); setItems(list); } catch { setItems([]); } finally { setLoading(false); } }; const onFocus = () => { if (disabled) return; setOpen(true); void fetchSuggestions(value); }; const onInputChange = (next: string) => { // Typing clears any previously bound staff id; user must re-select onChange(next, null); setOpen(true); if (debounce.current) window.clearTimeout(debounce.current); debounce.current = window.setTimeout(() => fetchSuggestions(next), 200); }; const select = (s: StaffSearchVO) => { onChange(s.sStaffName, s.iIncrement); setOpen(false); }; return (
onInputChange(e.target.value)} onFocus={onFocus} disabled={disabled} placeholder={placeholder} style={{ ...fieldControl, ...(disabled ? { background: "var(--bg-disabled)", color: "var(--text-muted)" } : {}), ...(required && !disabled ? { background: "#d4e8f7" } : {}), }} /> {open && !disabled && (
{loading && (
加载中…
)} {!loading && items.length === 0 && (
没有匹配的职员
)} {!loading && items.map((s) => (
{ e.preventDefault(); select(s); }} style={{ padding: "6px 12px", fontSize: 12, cursor: "pointer", display: "flex", alignItems: "center", gap: 8, borderBottom: "1px solid #f0f2f5", }} onMouseEnter={(e) => { e.currentTarget.style.background = "var(--bg-row-hover)"; }} onMouseLeave={(e) => { e.currentTarget.style.background = "transparent"; }} > {s.sStaffName} {s.sStaffNo} {s.sDepartment ?? ""}
))}
)}
); }