chore:系统管理拆分

This commit is contained in:
xiaozhu
2026-08-05 15:13:34 +08:00
parent d851f98581
commit 8c4d2c0517
6 changed files with 923 additions and 915 deletions
@@ -0,0 +1,154 @@
import { useEffect, useState, useRef } from "react";
import { type Employee } from "../../services/api";
// 多选用户下拉框组件 - 带 tag 显示
export function UserMultiSelect({
users,
selectedUserIds,
onChange,
}: {
users: Employee[];
selectedUserIds: string[];
onChange: (ids: string[]) => void;
}) {
const [isOpen, setIsOpen] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// 点击外部关闭下拉框
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (containerRef.current && !containerRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
const selectedUsers = users.filter((user) => selectedUserIds.includes(user.user_id));
const toggleUser = (userId: string) => {
if (selectedUserIds.includes(userId)) {
onChange(selectedUserIds.filter((id) => id !== userId));
} else {
onChange([...selectedUserIds, userId]);
}
};
const removeUser = (userId: string, event: React.MouseEvent) => {
event.stopPropagation();
onChange(selectedUserIds.filter((id) => id !== userId));
};
return (
<div ref={containerRef} style={{ position: "relative" }}>
{/* 选择框 - 显示 tag */}
<div
className="admin-toolbar__input"
onClick={() => setIsOpen(!isOpen)}
style={{
display: "flex",
alignItems: "center",
flexWrap: "wrap",
gap: "4px",
padding: "6px 32px 6px 8px",
minHeight: "36px",
border: "1px solid #ddd",
borderRadius: "4px",
cursor: "pointer",
backgroundColor: "#fff",
}}
>
{selectedUsers.length === 0 ? (
<span style={{ color: "#999" }}></span>
) : (
selectedUsers.map((user) => (
<span
key={user.user_id}
style={{
display: "inline-flex",
alignItems: "center",
backgroundColor: "#e6f7ff",
color: "#1890ff",
border: "1px solid #91d5ff",
borderRadius: "2px",
padding: "2px 6px",
fontSize: "13px",
}}
>
{user.display_name}
<span
onClick={(e) => removeUser(user.user_id, e)}
style={{
marginLeft: "4px",
cursor: "pointer",
fontWeight: "bold",
fontSize: "14px",
}}
>
×
</span>
</span>
))
)}
<span
style={{
position: "absolute",
right: "12px",
top: "50%",
transform: "translateY(-50%)",
color: "#999",
pointerEvents: "none",
}}
>
{isOpen ? "▲" : "▼"}
</span>
</div>
{/* 下拉列表 */}
{isOpen && (
<div
style={{
position: "absolute",
top: "100%",
left: 0,
right: 0,
maxHeight: "200px",
overflowY: "auto",
border: "1px solid #1890ff",
borderRadius: "4px",
backgroundColor: "#fff",
zIndex: 1000,
marginTop: "2px",
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
}}
>
{users.map((user) => {
const isSelected = selectedUserIds.includes(user.user_id);
return (
<div
key={user.user_id}
onClick={() => toggleUser(user.user_id)}
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
padding: "8px 12px",
cursor: "pointer",
backgroundColor: isSelected ? "#e6f7ff" : "transparent",
borderBottom: "1px solid #f0f0f0",
}}
>
<span>
{user.display_name} <span style={{ color: "#999" }}>({user.username})</span>
</span>
{isSelected && <span style={{ color: "#1890ff" }}></span>}
</div>
);
})}
</div>
)}
</div>
);
}