import CardGiftcardOutlined from "@mui/icons-material/CardGiftcardOutlined"; import Inventory2Outlined from "@mui/icons-material/Inventory2Outlined"; import SearchOutlined from "@mui/icons-material/SearchOutlined"; import Checkbox from "@mui/material/Checkbox"; import InputAdornment from "@mui/material/InputAdornment"; import Tab from "@mui/material/Tab"; import Tabs from "@mui/material/Tabs"; import TextField from "@mui/material/TextField"; import { useMemo, useState } from "react"; import { resourcePriceTypeLabels, resourceTypeFilters, resourceTypeLabels, } from "@/features/resources/constants.js"; import { Button } from "@/shared/ui/Button.jsx"; import { SideDrawer } from "@/shared/ui/SideDrawer.jsx"; import styles from "@/features/resources/components/GiftResourceSelectDrawer.module.css"; const drawerZIndex = 1600; export function GiftResourceSelectField({ disabled = false, drawerTitle = "选择资源", excludedResourceIds = [], giftTypes = [], gifts = [], label = "资源", loading = false, onChange = () => {}, placeholder = "请选择资源", required = true, resources = [], selectedResourceIds = [], }) { const [open, setOpen] = useState(false); const [query, setQuery] = useState(""); const [activeType, setActiveType] = useState(""); const selectedIds = useMemo(() => selectedResourceIds.map((id) => String(id)), [selectedResourceIds]); const selectedIdSet = useMemo(() => new Set(selectedIds), [selectedIds]); const excludedIds = useMemo(() => excludedResourceIds.map((id) => String(id)), [excludedResourceIds]); const excludedIdSet = useMemo(() => new Set(excludedIds), [excludedIds]); const giftsByResourceId = useMemo(() => { const items = new Map(); gifts.forEach((gift) => { if (gift?.resourceId) { items.set(String(gift.resourceId), gift); } }); return items; }, [gifts]); const resourceItems = useMemo( () => buildResourceItems(resources, giftsByResourceId), [giftsByResourceId, resources], ); const visibleResourceItems = useMemo( () => resourceItems.filter((item) => { const resourceId = String(item.resource.resourceId); return !excludedIdSet.has(resourceId) || selectedIdSet.has(resourceId); }), [excludedIdSet, resourceItems, selectedIdSet], ); const visibleResources = useMemo(() => visibleResourceItems.map((item) => item.resource), [visibleResourceItems]); const itemCounts = useMemo(() => countResourceItems(visibleResourceItems), [visibleResourceItems]); const categoryOptions = useMemo( () => buildCategoryOptions(giftTypes, visibleResources, visibleResourceItems), [giftTypes, visibleResourceItems, visibleResources], ); const activeCategory = categoryOptions.some((item) => item.value === activeType) ? activeType : categoryOptions.find((item) => itemCounts.get(item.value))?.value || categoryOptions[0]?.value || ""; const resourcesById = useMemo(() => { const items = new Map(); resourceItems.forEach((item) => { items.set(String(item.resource.resourceId), item); }); return items; }, [resourceItems]); const displayValue = useMemo(() => { if (!selectedIds.length) { return ""; } const labels = selectedIds.map((resourceId) => resourceName(resourcesById.get(resourceId), resourceId)); if (labels.length <= 3) { return labels.join(","); } return `${labels.slice(0, 3).join(",")} 等 ${labels.length} 个资源`; }, [resourcesById, selectedIds]); const filteredResources = useMemo(() => { const keyword = query.trim().toLowerCase(); return visibleResourceItems.filter((item) => { if (activeCategory && item.categoryValue !== activeCategory) { return false; } if (!keyword) { return true; } const resource = item.resource || {}; const gift = item.gift || {}; const haystack = [ resource.name, resource.resourceCode, resource.resourceId, resource.resourceType, gift.name, gift.giftId, gift.giftTypeCode, ] .filter(Boolean) .join(" ") .toLowerCase(); return haystack.includes(keyword); }); }, [activeCategory, query, visibleResourceItems]); const openDrawer = () => { if (!disabled && !loading) { setOpen(true); } }; const toggleResource = (item) => { if (!item?.resource?.resourceId || disabled) { return; } const resourceId = String(item.resource.resourceId); if (selectedIdSet.has(resourceId)) { onChange(selectedIds.filter((selectedId) => selectedId !== resourceId)); return; } onChange([...selectedIds, resourceId]); }; return ( <> { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); openDrawer(); } }} /> setOpen(false)}> 完成 } className={styles.drawer} contentClassName={styles.drawerBody} drawerProps={{ sx: { zIndex: drawerZIndex } }} open={open} title={drawerTitle} width="wide" onClose={() => setOpen(false)} >
), }, }} value={query} onChange={(event) => setQuery(event.target.value)} /> {categoryOptions.length ? ( setActiveType(value)} > {categoryOptions.map((item) => ( ))} ) : null}
{filteredResources.map((item) => { const resourceId = String(item.resource.resourceId); const selected = selectedIdSet.has(resourceId); const name = resourceName(item); return ( ); })} {!filteredResources.length ?
当前无可选资源
: null}
); } function buildResourceItems(resources, giftsByResourceId) { const seen = new Set(); return resources .filter((resource) => resource?.resourceId) .filter((resource) => { const resourceId = String(resource.resourceId); if (seen.has(resourceId)) { return false; } seen.add(resourceId); return true; }) .map((resource) => { const gift = giftsByResourceId.get(String(resource.resourceId)); const category = categoryForResource(resource, gift); return { categoryLabel: category.label, categoryValue: category.value, gift, resource, }; }); } function buildCategoryOptions(giftTypes, resources, resourceItems) { const options = []; const resourceTypes = new Set(resources.map((resource) => resource?.resourceType).filter(Boolean)); resourceTypeFilters.forEach(([type, label], index) => { if (!type) { return; } if (type === "gift") { options.push(...buildGiftCategoryOptions(giftTypes, resourceItems, index)); if (resourceItems.some((item) => item.categoryValue === "resource:gift")) { options.push({ label: resourceTypeLabels.gift || "礼物", order: index * 100, value: "resource:gift" }); } return; } options.push({ label, order: index * 100, value: `resource:${type}` }); resourceTypes.delete(type); }); resourceTypes.forEach((type) => { options.push({ label: resourceTypeLabels[type] || type, order: options.length * 100, value: `resource:${type}`, }); }); return dedupeCategories(options); } function buildGiftCategoryOptions(giftTypes, resourceItems, resourceTypeOrder) { const options = []; const knownGiftTypes = new Set(); giftTypes .filter((item) => item?.status !== "disabled") .forEach((item) => { const value = String(item.tabKey || item.typeCode || "").trim(); if (!value) { return; } knownGiftTypes.add(value); options.push({ label: item.displayName || item.tabName || value, order: resourceTypeOrder * 100 + Number(item.sortOrder || 0), value: `gift:${value}`, }); }); resourceItems.forEach((item) => { const value = String(item.gift?.giftTypeCode || "").trim(); if (value && !knownGiftTypes.has(value)) { knownGiftTypes.add(value); options.push({ label: value, order: resourceTypeOrder * 100 + 1000 + options.length, value: `gift:${value}`, }); } }); return options; } function dedupeCategories(options) { const byValue = new Map(); options.forEach((item) => { if (!byValue.has(item.value)) { byValue.set(item.value, item); } }); return Array.from(byValue.values()).sort((left, right) => left.order - right.order); } function categoryForResource(resource, gift) { if (resource.resourceType === "gift" && gift?.giftTypeCode) { return { value: `gift:${gift.giftTypeCode}` }; } const type = resource.resourceType || "unknown"; return { label: resourceTypeLabels[type] || type, value: `resource:${type}`, }; } function categoryLabel(item, categoryOptions) { return categoryOptions.find((option) => option.value === item.categoryValue)?.label || item.categoryLabel || "资源"; } function countResourceItems(resourceItems) { const counts = new Map(); resourceItems.forEach((item) => { counts.set(item.categoryValue, (counts.get(item.categoryValue) || 0) + 1); }); return counts; } function ResourceThumb({ item }) { const imageUrl = imageURL(item.resource.previewUrl || item.resource.assetUrl || item.resource.animationUrl); return ( {imageUrl ? ( ) : item.resource.resourceType === "gift" ? ( ) : ( )} ); } function resourceName(item, fallbackResourceId = "") { const resource = item?.resource || {}; const gift = item?.gift || {}; return gift.name || resource.name || resource.resourceCode || `资源 #${fallbackResourceId || resource.resourceId || "-"}`; } function resourcePriceLabel(item = {}) { const resource = item.resource || {}; const gift = item.gift || {}; if (resource.resourceType === "gift" && (gift.chargeAssetType === "COIN" || Number(gift.coinPrice || 0) > 0)) { return `${formatNumber(gift.coinPrice)}金币`; } if (resource.priceType === "coin") { return `${formatNumber(resource.coinPrice)}金币`; } if (resource.priceType === "free") { return resourcePriceTypeLabels.free; } return "未配置价格"; } function imageURL(value) { const url = String(value || "").trim(); return /\.(avif|gif|jpe?g|png|webp)(\?|#|$)/i.test(url) ? url : ""; } function formatNumber(value) { return Number(value || 0).toLocaleString("zh-CN"); }