11<script setup lang="ts">
22import { ref , computed , watch , onMounted } from ' vue'
3+ import { applyTheme , getStoredTheme } from ' @/util/theme'
34// 使用 defineAsyncComponent 导入没有默认导出的组件
45// const Dashboard = defineAsyncComponent(() => import("@/components/pages/dashboard/Dashboard.vue"))
56// const SendLogs = defineAsyncComponent(() => import("@/components/pages/sendLogs/SendLogs.vue"))
@@ -56,7 +57,7 @@ const applyThemeFromPreference = () => {
5657 } else {
5758 document .documentElement .classList .remove (' dark' )
5859 }
59- try { localStorage .setItem (' themePreference' , themePreference .value ) } catch {}
60+ try { localStorage .setItem (' themePreference' , themePreference .value ) } catch { }
6061}
6162
6263const toggleTheme = () => {
@@ -91,23 +92,23 @@ const updateUserAccount = () => {
9192}
9293
9394// 更新favicon
94- const updateFavicon = (logoSvg : string ) => {
95- if (logoSvg ) {
96- // 查找现有的favicon链接
97- let link = document .querySelector (" link[rel*='icon']" ) as HTMLLinkElement
98- if (! link ) {
99- // 如果不存在,创建新的favicon链接
100- link = document .createElement (' link' )
101- link .rel = ' icon'
102- document .head .appendChild (link )
103- }
104- // 将SVG转换为data URL
105- const svgBlob = new Blob ([logoSvg ], { type: ' image/svg+xml' })
106- const svgUrl = URL .createObjectURL (svgBlob )
107- link .href = svgUrl
108- link .type = ' image/svg+xml'
95+ const updateFavicon = (logoSvg : string ) => {
96+ if (logoSvg ) {
97+ // 查找现有的favicon链接
98+ let link = document .querySelector (" link[rel*='icon']" ) as HTMLLinkElement
99+ if (! link ) {
100+ // 如果不存在,创建新的favicon链接
101+ link = document .createElement (' link' )
102+ link .rel = ' icon'
103+ document .head .appendChild (link )
109104 }
105+ // 将SVG转换为data URL
106+ const svgBlob = new Blob ([logoSvg ], { type: ' image/svg+xml' })
107+ const svgUrl = URL .createObjectURL (svgBlob )
108+ link .href = svgUrl
109+ link .type = ' image/svg+xml'
110110 }
111+ }
111112
112113// 获取本地配置
113114const getLocalConfig = () => {
@@ -127,7 +128,11 @@ const getLocalConfig = () => {
127128 if (localConfig .logo ) {
128129 updateFavicon (localConfig .logo )
129130 }
130-
131+ // 更新主题色
132+ if (localConfig .theme_color ) {
133+ applyTheme (localConfig .theme_color )
134+ }
135+
131136 }
132137 } catch (error ) {
133138 console .error (' 获取本地配置失败:' , error )
@@ -152,7 +157,11 @@ const getLatestConfig = async () => {
152157 if (latestConfig .logo ) {
153158 updateFavicon (latestConfig .logo )
154159 }
155-
160+ // 更新主题色
161+ if (latestConfig .theme_color ) {
162+ applyTheme (latestConfig .theme_color )
163+ }
164+
156165 }
157166 } catch (error ) {
158167 console .error (' 获取最新配置失败:' , error )
@@ -181,6 +190,8 @@ const logout = () => {
181190
182191// 监听localStorage变化
183192onMounted (() => {
193+ // 应用存储的主题色
194+ applyTheme (getStoredTheme ())
184195 // 初始化主题并监听系统主题变化
185196 applyThemeFromPreference ()
186197 try {
@@ -193,21 +204,21 @@ onMounted(() => {
193204 media .addEventListener (' change' , handleSystemChange )
194205 } else if ((media as any ).addListener ) {
195206 // 兼容旧浏览器
196- ;(media as any ).addListener (handleSystemChange )
207+ ; (media as any ).addListener (handleSystemChange )
197208 }
198- } catch {}
209+ } catch { }
199210
200211 // 初始化用户账号信息
201212 updateUserAccount ();
202-
213+
203214 // 初始化配置信息
204215 getLocalConfig ();
205-
216+
206217 // 如果已认证,获取最新配置
207218 if (isAuthenticated .value ) {
208219 getLatestConfig ();
209220 }
210-
221+
211222 // 定期检查token状态
212223 const checkAuth = () => {
213224 const wasAuthenticated = isAuthenticated .value ;
@@ -228,7 +239,7 @@ onMounted(() => {
228239 window .addEventListener (' storage' , checkAuth );
229240 // 定期检查(处理同一页面内的变化)
230241 const interval = setInterval (checkAuth , 1000 );
231-
242+
232243 // 点击外部关闭用户菜单
233244 const handleClickOutside = (event : Event ) => {
234245 const target = event .target as Element ;
@@ -237,7 +248,7 @@ onMounted(() => {
237248 }
238249 };
239250 document .addEventListener (' click' , handleClickOutside );
240-
251+
241252 // 清理函数
242253 return () => {
243254 window .removeEventListener (' storage' , checkAuth );
@@ -262,7 +273,7 @@ const activeTab = ref('Dashboard');
262273// 处理标签点击事件,跳转到对应路由
263274const handleTabClick = (tab : TabRoute ) => {
264275 activeTab .value = tab .name ;
265-
276+
266277
267278 // 使用 Vue Router 进行导航,保持单页应用状态
268279 // 对于根路径,直接导航;对于其他路径,使用相对路径
@@ -331,13 +342,13 @@ const siteTitle = computed(() => {
331342 <button v-for =" tab in tabRoutes" :key =" tab.name" @click =" handleTabClick(tab)" :class =" [
332343 'relative py-1.5 px-2 text-sm font-medium transition-all duration-200 rounded-md whitespace-nowrap',
333344 activeTab === tab.name
334- ? 'text-blue-600 bg-blue-50 dark:text-blue-400 dark:bg-blue-400 /10'
335- : 'text-gray-600 hover:text-blue-600 hover:bg-gray-50 dark:text-gray-300 dark:hover:text-blue-400 dark:hover:bg-white/5'
345+ ? 'text-brand bg-brand/10 dark:text-brand dark:bg-brand /10'
346+ : 'text-gray-600 hover:text-brand hover:bg-gray-50 dark:text-gray-300 dark:hover:text-brand dark:hover:bg-white/5'
336347 ]" >
337348 {{ tab.name }}
338349 <!-- 活动状态指示器 -->
339350 <span v-if =" activeTab === tab.name"
340- class =" absolute bottom-0 left-1/2 transform -translate-x-1/2 w-1 h-1 bg-blue-600 rounded-full" ></span >
351+ class =" absolute bottom-0 left-1/2 transform -translate-x-1/2 w-1 h-1 bg-brand rounded-full" ></span >
341352 </button >
342353 </div >
343354
@@ -347,21 +358,24 @@ const siteTitle = computed(() => {
347358 <TooltipProvider >
348359 <Tooltip >
349360 <TooltipTrigger >
350- <button @click =" toggleTheme" class =" hidden md:inline-flex p-2 rounded-md text-gray-600 hover:text-blue-600 hover:bg-gray-50 transition-colors dark:text-gray-300 dark:hover:text-blue-400 dark:hover:bg-white/5" >
361+ <button @click =" toggleTheme"
362+ class =" hidden md:inline-flex p-2 rounded-md text-gray-600 hover:text-brand hover:bg-gray-50 transition-colors dark:text-gray-300 dark:hover:text-brand dark:hover:bg-white/5" >
351363 <MonitorIcon v-if =" themePreference === ' system' " class="w-5 h-5" />
352- <svg v-else-if =" theme === 'dark'" class =" w-5 h-5" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" >
353- <path d =" M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
364+ <svg v-else-if =" theme === 'dark'" class =" w-5 h-5" viewBox =" 0 0 24 24" fill =" none"
365+ stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" >
366+ <path d =" M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
354367 </svg >
355- <svg v-else class =" w-5 h-5" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" >
356- <circle cx =" 12" cy =" 12" r =" 5" />
357- <line x1 =" 12" y1 =" 1" x2 =" 12" y2 =" 3" />
358- <line x1 =" 12" y1 =" 21" x2 =" 12" y2 =" 23" />
359- <line x1 =" 4.22" y1 =" 4.22" x2 =" 5.64" y2 =" 5.64" />
360- <line x1 =" 18.36" y1 =" 18.36" x2 =" 19.78" y2 =" 19.78" />
361- <line x1 =" 1" y1 =" 12" x2 =" 3" y2 =" 12" />
362- <line x1 =" 21" y1 =" 12" x2 =" 23" y2 =" 12" />
363- <line x1 =" 4.22" y1 =" 19.78" x2 =" 5.64" y2 =" 18.36" />
364- <line x1 =" 18.36" y1 =" 5.64" x2 =" 19.78" y2 =" 4.22" />
368+ <svg v-else class =" w-5 h-5" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2"
369+ stroke-linecap =" round" stroke-linejoin =" round" >
370+ <circle cx =" 12" cy =" 12" r =" 5" />
371+ <line x1 =" 12" y1 =" 1" x2 =" 12" y2 =" 3" />
372+ <line x1 =" 12" y1 =" 21" x2 =" 12" y2 =" 23" />
373+ <line x1 =" 4.22" y1 =" 4.22" x2 =" 5.64" y2 =" 5.64" />
374+ <line x1 =" 18.36" y1 =" 18.36" x2 =" 19.78" y2 =" 19.78" />
375+ <line x1 =" 1" y1 =" 12" x2 =" 3" y2 =" 12" />
376+ <line x1 =" 21" y1 =" 12" x2 =" 23" y2 =" 12" />
377+ <line x1 =" 4.22" y1 =" 19.78" x2 =" 5.64" y2 =" 18.36" />
378+ <line x1 =" 18.36" y1 =" 5.64" x2 =" 19.78" y2 =" 4.22" />
365379 </svg >
366380 </button >
367381 </TooltipTrigger >
@@ -372,34 +386,42 @@ const siteTitle = computed(() => {
372386 </TooltipProvider >
373387 <!-- 用户账号下拉菜单 -->
374388 <div class =" relative user-menu-container" >
375- <button @click =" toggleUserMenu" class =" flex items-center space-x-2 p-2 rounded-md hover:bg-muted transition-colors dark:hover:bg-white/5" >
376- <div class =" w-8 h-8 bg-blue-100 dark:bg-muted border border-border rounded-full flex items-center justify-center" >
377- <svg class =" w-4 h-4 text-blue-600 dark:text-blue-300" fill =" currentColor" viewBox =" 0 0 20 20" >
378- <path fill-rule =" evenodd" d =" M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule =" evenodd" />
389+ <button @click =" toggleUserMenu"
390+ class =" flex items-center space-x-2 p-2 rounded-md hover:bg-muted transition-colors dark:hover:bg-white/5" >
391+ <div
392+ class =" w-8 h-8 bg-brand/10 dark:bg-muted border border-border rounded-full flex items-center justify-center" >
393+ <svg class =" w-4 h-4 text-brand dark:text-brand" fill =" currentColor" viewBox =" 0 0 20 20" >
394+ <path fill-rule =" evenodd" d =" M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
395+ clip-rule =" evenodd" />
379396 </svg >
380397 </div >
381398 <span class =" hidden sm:block text-sm font-medium text-foreground" >{{ userAccount }}</span >
382- <svg class =" w-4 h-4 text-gray-400 dark:text-muted-foreground" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
399+ <svg class =" w-4 h-4 text-gray-400 dark:text-muted-foreground" fill =" none" stroke =" currentColor"
400+ viewBox =" 0 0 24 24" >
383401 <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M19 9l-7 7-7-7" />
384402 </svg >
385403 </button >
386-
404+
387405 <!-- 下拉菜单 -->
388- <div v-if =" isUserMenuOpen" class =" absolute right-0 mt-2 w-52 bg-card text-foreground rounded-md shadow-lg border border-border z-50" >
406+ <div v-if =" isUserMenuOpen"
407+ class =" absolute right-0 mt-2 w-52 bg-card text-foreground rounded-md shadow-lg border border-border z-50" >
389408 <div class =" py-1" >
390409 <div class =" px-4 py-2 text-sm text-muted-foreground border-b border-border" >
391410 <div class =" font-medium text-foreground" >{{ userAccount }}</div >
392411 <!-- <div class="text-xs">当前登录账号</div> -->
393412 </div >
394413 <!-- 移动端主题切换入口 -->
395- <button @click =" toggleTheme" class =" md:hidden w-full text-left px-4 py-2 text-sm hover:bg-muted dark:hover:bg-white/5 flex items-center justify-between" >
414+ <button @click =" toggleTheme"
415+ class =" md:hidden w-full text-left px-4 py-2 text-sm hover:bg-muted dark:hover:bg-white/5 flex items-center justify-between" >
396416 <span class =" truncate" >外观</span >
397417 <span class =" text-xs text-muted-foreground flex-shrink-0" >{{ themeLabel }}</span >
398418 </button >
399- <button @click =" logout" class =" w-full text-left px-4 py-2 text-sm text-destructive hover:bg-muted dark:hover:bg-white/5 transition-colors" >
419+ <button @click =" logout"
420+ class =" w-full text-left px-4 py-2 text-sm text-destructive hover:bg-muted dark:hover:bg-white/5 transition-colors" >
400421 <div class =" flex items-center space-x-2" >
401422 <svg class =" w-4 h-4" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
402- <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2" d =" M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
423+ <path stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2"
424+ d =" M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
403425 </svg >
404426 <span >退出登录</span >
405427 </div >
@@ -410,7 +432,7 @@ const siteTitle = computed(() => {
410432
411433 <!-- 移动端菜单按钮 -->
412434 <button @click =" toggleMobileMenu"
413- class =" md:hidden p-2 rounded-md text-gray-600 hover:text-blue-600 hover:bg-gray-50 transition-colors" >
435+ class =" md:hidden p-2 rounded-md text-gray-600 hover:text-brand hover:bg-gray-50 transition-colors" >
414436 <svg class =" w-5 h-5" fill =" none" stroke =" currentColor" viewBox =" 0 0 24 24" >
415437 <path v-if =" !isMobileMenuOpen" stroke-linecap =" round" stroke-linejoin =" round" stroke-width =" 2"
416438 d =" M4 6h16M4 12h16M4 18h16" />
@@ -427,8 +449,8 @@ const siteTitle = computed(() => {
427449 :class =" [
428450 'text-left py-3 px-4 text-sm font-medium transition-all duration-200 rounded-md',
429451 activeTab === tab.name
430- ? 'text-blue-600 bg-blue-50 dark:text-blue-400 dark:bg-blue-400 /10'
431- : 'text-muted-foreground hover:text-blue-600 hover:bg-muted dark:hover:bg-white/5'
452+ ? 'text-brand bg-brand/10 dark:text-brand dark:bg-brand /10'
453+ : 'text-muted-foreground hover:text-brand hover:bg-muted dark:hover:bg-white/5'
432454 ]" >
433455 {{ tab.name }}
434456 </button >
0 commit comments