From d5f3548af4331203ae8dd45099f6d04f026c9a49 Mon Sep 17 00:00:00 2001 From: wood chen Date: Wed, 7 May 2025 15:52:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20GroupSwitch=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E7=9A=84=E6=A0=87=E7=AD=BE=E7=82=B9=E5=87=BB=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A7=BB=E9=99=A4=E5=86=97?= =?UTF-8?q?=E4=BD=99=E7=9A=84=E6=BB=9A=E5=8A=A8=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=E3=80=82?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=9C=A8=20Server=20=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E4=B8=AD=E5=BC=95=E5=85=A5=E7=9B=B4=E6=8E=A5=E5=9B=BD=E5=AE=B6?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=99=A8=EF=BC=8C=E6=8F=90=E5=8D=87=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BD=93=E9=AA=8C=E5=92=8C=E7=8A=B6=E6=80=81=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E7=9A=84=E6=B8=85=E6=99=B0=E5=BA=A6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DirectCountrySelect.tsx | 49 ++++++++++++++++++ src/components/GroupSwitch.tsx | 72 ++++++++------------------ src/pages/Server.tsx | 53 ++++++++----------- 3 files changed, 93 insertions(+), 81 deletions(-) create mode 100644 src/components/DirectCountrySelect.tsx diff --git a/src/components/DirectCountrySelect.tsx b/src/components/DirectCountrySelect.tsx new file mode 100644 index 0000000..27b9998 --- /dev/null +++ b/src/components/DirectCountrySelect.tsx @@ -0,0 +1,49 @@ +import { cn } from "@/lib/utils"; +import ServerFlag from "@/components/ServerFlag"; + +type DirectCountrySelectProps = { + countries: string[]; + currentCountry: string; + onChange: (country: string) => void; +}; + +// 这是一个简单的直接选择组件,避免可能的事件传播问题 +export default function DirectCountrySelect({ + countries, + currentCountry, + onChange +}: DirectCountrySelectProps) { + return ( +
+
+ + + {countries.map((country) => ( + + ))} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/GroupSwitch.tsx b/src/components/GroupSwitch.tsx index b24ee8d..3c2ad99 100644 --- a/src/components/GroupSwitch.tsx +++ b/src/components/GroupSwitch.tsx @@ -43,56 +43,28 @@ export default function GroupSwitch({ } }, []) - useEffect(() => { - const container = scrollRef.current - if (!container) return - - const isOverflowing = container.scrollWidth > container.clientWidth - if (!isOverflowing) return - - const onWheel = (e: WheelEvent) => { - e.preventDefault() - container.scrollLeft += e.deltaY + // 处理标签点击 + function handleClick(tab: string) { + // 避免重复点击当前选中的标签 + if (tab === currentTab) return; + + try { + // 直接调用父组件传递的回调 + setCurrentTab(tab); + console.log(`[${isCountrySwitch ? '国家' : '分组'}] 切换到: ${tab}`); + + // 手动滚动到可见区域 + const index = tabs.indexOf(tab); + if (index !== -1 && tagRefs.current[index]?.current) { + tagRefs.current[index].current?.scrollIntoView({ + behavior: "smooth", + block: "nearest", + inline: "center" + }); + } + } catch (error) { + console.error('切换标签出错:', error); } - - container.addEventListener("wheel", onWheel, { passive: false }) - - return () => { - container.removeEventListener("wheel", onWheel) - } - }, []) - - useEffect(() => { - const storageKey = isCountrySwitch ? "selectedCountry" : "selectedGroup" - const savedValue = sessionStorage.getItem(storageKey) - if (savedValue && tabs.includes(savedValue)) { - setCurrentTab(savedValue) - } - }, [tabs, setCurrentTab, isCountrySwitch]) - - // 当tabs变化时更新tagRefs - useEffect(() => { - tagRefs.current = tabs.map(() => createRef()) - }, [tabs]) - - // 处理选中标签的滚动逻辑 - useEffect(() => { - const currentTagIndex = tabs.indexOf(currentTab) - if (currentTagIndex === -1) return // 如果当前选中的标签不在tabs中,不执行滚动 - - const currentTagRef = tagRefs.current[currentTagIndex] - if (currentTagRef && currentTagRef.current) { - currentTagRef.current.scrollIntoView({ - behavior: "smooth", - block: "nearest", - inline: "center", - }) - } - }, [currentTab, tabs]) - - const handleTabClick = (tab: string) => { - if (tab === currentTab) return; // 如果点击的是当前选中的标签,不执行操作 - setCurrentTab(tab) } return ( @@ -109,7 +81,7 @@ export default function GroupSwitch({
handleTabClick(tab)} + onClick={() => handleClick(tab)} className={cn( "relative cursor-pointer rounded-3xl px-2.5 py-[8px] text-[13px] font-[600] transition-all duration-500", currentTab === tab ? "text-black dark:text-white" : "text-stone-400 dark:text-stone-500", diff --git a/src/pages/Server.tsx b/src/pages/Server.tsx index 761cfcc..8bf80bd 100644 --- a/src/pages/Server.tsx +++ b/src/pages/Server.tsx @@ -1,5 +1,4 @@ import GlobalMap from "@/components/GlobalMap" -import GroupSwitch from "@/components/GroupSwitch" import ServerCard from "@/components/ServerCard" import ServerOverview from "@/components/ServerOverview" import { ServiceTracker } from "@/components/ServiceTracker" @@ -17,8 +16,9 @@ import { NezhaWebsocketResponse } from "@/types/nezha-api" import { ServerGroup } from "@/types/nezha-api" import { ArrowDownIcon, ArrowUpIcon, ArrowsUpDownIcon, ChartBarSquareIcon, MapIcon } from "@heroicons/react/20/solid" import { useQuery } from "@tanstack/react-query" -import { useEffect, useRef, useState } from "react" +import { useCallback, useEffect, useRef, useState } from "react" import { useTranslation } from "react-i18next" +import DirectCountrySelect from "@/components/DirectCountrySelect" export default function Servers() { const { t } = useTranslation() @@ -52,25 +52,16 @@ export default function Servers() { } } - const handleTagChange = (newGroup: string) => { - groupRef.current = newGroup - countryRef.current = "All" // 切换组时重置国家筛选 + const handleCountryChange = useCallback((newCountry: string) => { + countryRef.current = newCountry; - setCurrentGroup(newGroup) - setCurrentCountry("All") + // 强制立即更新状态 + setCurrentCountry(newCountry); - sessionStorage.setItem("selectedGroup", newGroup) - sessionStorage.setItem("selectedCountry", "All") - sessionStorage.setItem("scrollPosition", String(containerRef.current?.scrollTop || 0)) - } - - const handleCountryChange = (newCountry: string) => { - countryRef.current = newCountry - setCurrentCountry(newCountry) - - sessionStorage.setItem("selectedCountry", newCountry) - sessionStorage.setItem("scrollPosition", String(containerRef.current?.scrollTop || 0)) - } + // 保存到会话存储 + sessionStorage.setItem("selectedCountry", newCountry); + sessionStorage.setItem("scrollPosition", String(containerRef.current?.scrollTop || 0)); + }, []); useEffect(() => { const showServicesState = localStorage.getItem("showServices") @@ -124,6 +115,7 @@ export default function Servers() { useEffect(() => { if (!lastMessage || !initializedRef.current) return; + // 保持用户选择的筛选状态 setCurrentGroup(groupRef.current) setCurrentCountry(countryRef.current) }, [lastMessage]) @@ -137,15 +129,6 @@ export default function Servers() { .sort() : [] - const groupTabs = [ - "All", - ...(groupData?.data - ?.filter((item: ServerGroup) => { - return Array.isArray(item.servers) && item.servers.some((serverId) => nezhaWsData?.servers?.some((server) => server.id === serverId)) - }) - ?.map((item: ServerGroup) => item.group.name) || []), - ] - const countryTabs = [ "All", ...availableCountries.map(code => code.toUpperCase()) @@ -347,8 +330,6 @@ export default function Servers() { })} /> - - @@ -411,7 +392,17 @@ export default function Servers() {
{showMap === "1" && } {showServices === "1" && } -
+ + {/* 优化直接国家选择器 */} +
+ tab !== "All")} + currentCountry={currentCountry} + onChange={handleCountryChange} + /> +
+ +
{filteredServers.map((serverInfo) => { // 查找服务器所属的分组 const serverGroup = groupData?.data?.find(