import { useMemo, useState, useEffect, useLayoutEffect, useRef } from "react" import { useWindowVirtualizer } from "@tanstack/react-virtual" import rawModCodes from "@/data/modification-codes.json" function useColumnCount() { const getCount = () => { if (window.innerWidth >= 1024) return 4 if (window.innerWidth >= 768) return 3 if (window.innerWidth >= 640) return 2 return 1 } const [cols, setCols] = useState(getCount) useEffect(() => { const handler = () => setCols(getCount()) window.addEventListener("resize", handler) return () => window.removeEventListener("resize", handler) }, []) return cols } type ModCodeSource = { weapon: string "modification-code": string mode?: string tags?: string[] note?: string price?: number } type ModCode = { id: string weapon: string code: string mode?: string tags: string[] note?: string price?: number } const MOD_CODES: ModCode[] = (rawModCodes as ModCodeSource[]).map((item, index) => ({ id: `mod-${index + 1}`, weapon: item.weapon, code: item["modification-code"], mode: item.mode, tags: item.tags ?? [], note: item.note, price: item.price, })) export default function ModCodes() { const [keyword, setKeyword] = useState("") const [activeTag, setActiveTag] = useState("全部") const [activeWeapon, setActiveWeapon] = useState("全部") const [copiedId, setCopiedId] = useState(null) const [copyErrorId, setCopyErrorId] = useState(null) const handleCopy = async (item: ModCode) => { try { await navigator.clipboard.writeText(item.code) setCopyErrorId(null) setCopiedId(item.id) window.setTimeout(() => { setCopiedId((current) => (current === item.id ? null : current)) }, 1500) } catch { setCopiedId(null) setCopyErrorId(item.id) window.setTimeout(() => { setCopyErrorId((current) => (current === item.id ? null : current)) }, 1500) } } const allWeapons = useMemo(() => { const weapons = new Set() MOD_CODES.forEach((item) => weapons.add(item.weapon)) return ["全部", ...Array.from(weapons)] }, []) const allTags = useMemo(() => { const tags = new Set() MOD_CODES.forEach((item) => { item.tags.forEach((tag) => tags.add(tag)) }) return ["全部", ...Array.from(tags)] }, []) const filtered = useMemo(() => { const q = keyword.trim().toLowerCase() return MOD_CODES.filter((item) => { const matchWeapon = activeWeapon === "全部" || item.weapon === activeWeapon const matchTag = activeTag === "全部" || item.tags.includes(activeTag) const matchKeyword = q.length === 0 || item.weapon.toLowerCase().includes(q) || item.code.toLowerCase().includes(q) || (item.mode?.toLowerCase().includes(q) ?? false) || item.tags.some((tag) => tag.toLowerCase().includes(q)) return matchWeapon && matchTag && matchKeyword }) }, [activeWeapon, activeTag, keyword]) const colCount = useColumnCount() const rows = useMemo(() => { const result: ModCode[][] = [] for (let i = 0; i < filtered.length; i += colCount) { result.push(filtered.slice(i, i + colCount)) } return result }, [filtered, colCount]) const listRef = useRef(null) const scrollMarginRef = useRef(0) useLayoutEffect(() => { scrollMarginRef.current = listRef.current?.offsetTop ?? 0 }) const rowVirtualizer = useWindowVirtualizer({ count: rows.length, estimateSize: () => 220, overscan: 3, scrollMargin: scrollMarginRef.current, }) return (

支持按武器、tag 与关键字筛选。关键字可匹配枪械名称、改枪码或 tag。

{allTags.map((tag) => { const selected = tag === activeTag return ( ) })}
{rowVirtualizer.getVirtualItems().map((virtualRow) => (
{rows[virtualRow.index].map((item) => (

{item.weapon}

{item.mode ? ( {item.mode} ) : ( ID: {item.id} )}
{item.code}
{copyErrorId === item.id ? (

复制失败,请手动选中复制。

) : null} {item.price ?

$ {item.price.toLocaleString()}

: null} {item.note ?

{item.note}

: null}
{item.tags.map((tag) => ( ))}
))}
))}
{filtered.length === 0 ? (
未找到匹配的改枪码,请尝试其他 tag 或关键字。
) : null}
) }