11<template >
22 <div class =" absolute right-3 sm:right-6 top-4 flex flex-col items-end" >
3- <button @click =" showSettingPanel = !showSettingPanel" >
3+ <button @click.stop =" showSettingPanel = !showSettingPanel" >
44 <IconSettings class =" text-gray-500 hover:text-black w-6 h-6 transition-colors" />
55 </button >
66 <Transition name =" popup-right" >
77 <div
88 v-show =" showSettingPanel"
9- class =" border shadow-md rounded-lg px-4 py-3 z-50 bg-white mt-2 min-w-[200px]"
9+ class =" setting-panel border shadow-md rounded-lg px-4 py-3 z-50 bg-white mt-2 min-w-[200px]"
1010 >
1111 <h2 class =" font-bold text-lg" >
1212 设置
1313 </h2 >
14- <div class =" flex items-center justify-between " >
15- 布局模式
16- <div class =" rounded-lg overflow-hidden border h-[30px]" >
17- <button
18- class =" p-1 hover:bg-gray-200 text-gray-500 transition-colors"
19- :class =" {
20- '!bg-gray-300 !text-black': settings.layout === 'grid',
21- }"
22- @click =" settings.layout = 'grid'"
23- >
24- <IconLayoutGrid class =" w-5 h-5" />
25- </button >
26- <button
27- class =" p-1 hover:bg-gray-200 text-gray-500 transition-colors"
28- :class =" {
29- '!bg-gray-300 !text-black': settings.layout === 'flex',
30- }"
31- @click =" settings.layout = 'flex'"
32- >
33- <IconLayoutFlex class =" w-5 h-5" />
34- </button >
35- <button
36- class =" p-1 hover:bg-gray-200 text-gray-500 transition-colors"
37- :class =" {
38- '!bg-gray-300 !text-black': settings.layout === 'list',
39- }"
40- @click =" settings.layout = 'list'"
41- >
42- <IconLayoutList class =" w-5 h-5" />
43- </button >
44- </div >
45- </ div >
46- < div class = " flex items-center justify-between " >
47- 精简显示
48- <Switch v-model = " settings.compactMode " / >
14+ <div class =" flex flex-col gap-1 " >
15+ < SettingItem title = " 布局模式" >
16+ <div class =" rounded-lg overflow-hidden border h-[30px]" >
17+ <button
18+ class =" p-1 hover:bg-gray-200 text-gray-500 transition-colors"
19+ :class =" {
20+ '!bg-gray-300 !text-black': settings.layout === 'grid',
21+ }"
22+ @click =" settings.layout = 'grid'"
23+ >
24+ <IconLayoutGrid class =" w-5 h-5" />
25+ </button >
26+ <button
27+ class =" p-1 hover:bg-gray-200 text-gray-500 transition-colors"
28+ :class =" {
29+ '!bg-gray-300 !text-black': settings.layout === 'flex',
30+ }"
31+ @click =" settings.layout = 'flex'"
32+ >
33+ <IconLayoutFlex class =" w-5 h-5" />
34+ </button >
35+ <button
36+ class =" p-1 hover:bg-gray-200 text-gray-500 transition-colors"
37+ :class =" {
38+ '!bg-gray-300 !text-black': settings.layout === 'list',
39+ }"
40+ @click =" settings.layout = 'list'"
41+ >
42+ <IconLayoutList class =" w-5 h-5" />
43+ </button >
44+ </div >
45+ </ SettingItem >
46+ < SettingItem title = " 精简显示 " >
47+ < Switch v-model = " settings.compactMode " />
48+ </ SettingItem >
4949 </div >
5050 </div >
5151 </Transition >
9494import { useLocalStorage , useWindowSize } from ' @vueuse/core'
9595import type { ServerData } from ' ./types'
9696
97- const jsonApi = ' /json/stats.json'
98-
99- const ServerCardWidth = 350
97+ const JSON_API = ' /json/stats.json'
98+ const CARD_WIDTH = 350
10099
101100const { width : WindowWidth } = useWindowSize ()
102101
102+ const settings = useLocalStorage (' sstl-settings' , {
103+ layout: ' grid' ,
104+ compactMode: false ,
105+ }, {
106+ mergeDefaults: true ,
107+ })
108+
103109const serverData = ref <{
104110 updated: number
105111 servers: ServerData []
106112}>()
107113const loading = ref (true )
108114const error = ref (false )
109115const fetching = ref (false )
110-
111- const settings = useLocalStorage (' sstl-settings' , {
112- layout: ' grid' ,
113- compactMode: false ,
114- }, {
115- mergeDefaults: true ,
116- })
117116const showSettingPanel = ref (false )
118117
119118const serverCardCount = computed (() => {
120- return Math .floor (WindowWidth .value / ServerCardWidth ) || 1
119+ return Math .floor (WindowWidth .value / CARD_WIDTH ) || 1
121120})
122121
123122onMounted (() => {
124- fetch (jsonApi )
123+ document .addEventListener (' click' , (e ) => {
124+ if (showSettingPanel .value && ! (e .target as HTMLElement ).closest (' .setting-panel' ))
125+ showSettingPanel .value = false
126+ })
127+ fetch (JSON_API )
125128 .then (res => res .json ())
126129 .then ((data ) => {
127130 serverData .value = data
@@ -141,7 +144,7 @@ function fetchData() {
141144 if (fetching .value )
142145 return
143146 fetching .value = true
144- fetch (jsonApi )
147+ fetch (JSON_API )
145148 .then (res => res .json ())
146149 .then ((data ) => {
147150 serverData .value = data
0 commit comments