mirror of
https://github.com/woodchen-ink/nezha-dash-v1.git
synced 2025-07-18 01:21:56 +08:00
优化 GroupSwitch 组件的标签点击处理逻辑,移除冗余的滚动逻辑,简化代码结构。同时在 Server 页面中引入直接国家选择器,提升用户体验和状态管理的清晰度。
This commit is contained in:
parent
b800ce816a
commit
d5f3548af4
49
src/components/DirectCountrySelect.tsx
Normal file
49
src/components/DirectCountrySelect.tsx
Normal file
@ -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 (
|
||||
<div className="w-full">
|
||||
<div className="flex flex-wrap gap-1.5 pb-1">
|
||||
<button
|
||||
className={cn(
|
||||
"px-3 py-1.5 text-xs rounded-md transition-all border",
|
||||
currentCountry === "All"
|
||||
? "bg-blue-500 text-white border-blue-600 hover:bg-blue-600 shadow-sm"
|
||||
: "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
)}
|
||||
onClick={() => onChange("All")}
|
||||
>
|
||||
ALL
|
||||
</button>
|
||||
|
||||
{countries.map((country) => (
|
||||
<button
|
||||
key={country}
|
||||
className={cn(
|
||||
"px-3 py-1.5 text-xs rounded-md flex items-center gap-1.5 transition-all border",
|
||||
currentCountry === country
|
||||
? "bg-blue-500 text-white border-blue-600 hover:bg-blue-600 shadow-sm"
|
||||
: "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
)}
|
||||
onClick={() => onChange(country)}
|
||||
>
|
||||
<ServerFlag country_code={country.toLowerCase()} className="text-[12px]" />
|
||||
{country}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -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<HTMLDivElement>())
|
||||
}, [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({
|
||||
<div
|
||||
key={isCountrySwitch ? `country-${tab}` : `group-${tab}`}
|
||||
ref={tagRefs.current[index]}
|
||||
onClick={() => 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",
|
||||
|
@ -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() {
|
||||
})}
|
||||
/>
|
||||
</button>
|
||||
<GroupSwitch tabs={groupTabs} currentTab={currentGroup} setCurrentTab={handleTagChange} />
|
||||
<GroupSwitch tabs={countryTabs} currentTab={currentCountry} setCurrentTab={handleCountryChange} isCountrySwitch={true} />
|
||||
</section>
|
||||
<Popover onOpenChange={setSettingsOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
@ -411,7 +392,17 @@ export default function Servers() {
|
||||
</div>
|
||||
{showMap === "1" && <GlobalMap now={nezhaWsData.now} serverList={nezhaWsData?.servers || []} />}
|
||||
{showServices === "1" && <ServiceTracker serverList={filteredServers} />}
|
||||
<section ref={containerRef} className="grid grid-cols-1 gap-4 md:grid-cols-3 mt-6 server-card-list">
|
||||
|
||||
{/* 优化直接国家选择器 */}
|
||||
<div className="mt-4">
|
||||
<DirectCountrySelect
|
||||
countries={countryTabs.filter(tab => tab !== "All")}
|
||||
currentCountry={currentCountry}
|
||||
onChange={handleCountryChange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<section ref={containerRef} className="grid grid-cols-1 gap-4 md:grid-cols-3 mt-4 server-card-list">
|
||||
{filteredServers.map((serverInfo) => {
|
||||
// 查找服务器所属的分组
|
||||
const serverGroup = groupData?.data?.find(
|
||||
|
Loading…
x
Reference in New Issue
Block a user