This commit is contained in:
aaron 2025-05-23 15:12:19 +08:00
parent 0c0ea54d18
commit 280f6ec6e3
5 changed files with 243 additions and 25 deletions

View File

@ -5,7 +5,7 @@ services:
build:
context: .
dockerfile: Dockerfile
image: tradus-web:1.3.9
image: tradus-web:1.3.10
container_name: tradus-web
ports:
- '6000:80'

View File

@ -17,35 +17,69 @@
--vt-c-text-light-2: var(--vt-c-white-soft);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: var(--vt-c-white-soft);
/* 通用颜色 */
--color-accent: #3355ff;
--color-accent-hover: #2244ee;
--color-accent-light: rgba(51, 85, 255, 0.08);
--color-accent-rgb: 51, 85, 255;
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
/* 深色主题变量 - 默认 */
:root,
html[data-theme='dark'] {
--color-bg-primary: #050505;
--color-bg-secondary: rgba(255, 255, 255, 0.05);
--color-text-primary: rgba(255, 255, 255, 0.9);
--color-text-secondary: rgba(255, 255, 255, 0.6);
--color-border: rgba(255, 255, 255, 0.1);
--color-border-hover: rgba(255, 255, 255, 0.2);
--color-shadow: rgba(0, 0, 0, 0.3);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-background: var(--color-bg-primary);
--color-background-soft: var(--color-bg-secondary);
--color-background-mute: #111111;
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-1);
--color-heading: var(--color-text-primary);
--color-text: var(--color-text-primary);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
/* 浅色主题变量 */
html[data-theme='light'] {
--color-bg-primary: #ffffff;
--color-bg-secondary: rgba(0, 0, 0, 0.03);
--color-text-primary: rgba(0, 0, 0, 0.9);
--color-text-secondary: rgba(0, 0, 0, 0.6);
--color-border: rgba(0, 0, 0, 0.1);
--color-border-hover: rgba(0, 0, 0, 0.2);
--color-shadow: rgba(0, 0, 0, 0.1);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-background: var(--color-bg-primary);
--color-background-soft: var(--color-bg-secondary);
--color-background-mute: #f5f5f5;
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
--color-heading: var(--color-text-primary);
--color-text: var(--color-text-primary);
}
/* 深色主题变量 - 通过data-theme属性设置 */
html[data-theme='dark'] {
--color-bg-primary: #050505;
--color-bg-secondary: rgba(255, 255, 255, 0.05);
--color-text-primary: rgba(255, 255, 255, 0.9);
--color-text-secondary: rgba(255, 255, 255, 0.6);
--color-border: rgba(255, 255, 255, 0.1);
--color-border-hover: rgba(255, 255, 255, 0.2);
--color-shadow: rgba(0, 0, 0, 0.3);
--color-background: var(--color-bg-primary);
--color-background-soft: var(--color-bg-secondary);
--color-background-mute: #111111;
--color-heading: var(--color-text-primary);
--color-text: var(--color-text-primary);
}
*,
@ -73,8 +107,8 @@ body {
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
color 0.3s ease,
background-color 0.3s ease;
line-height: 1.6;
font-family:
Inter,

View File

@ -5,10 +5,29 @@ import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import { useThemeStore } from './stores/theme'
// 设置默认主题attribute以防止闪烁
const applyInitialTheme = () => {
const savedTheme = localStorage.getItem('theme') || 'dark'
document.documentElement.setAttribute('data-theme', savedTheme)
}
// 在应用挂载前应用初始主题,避免闪烁
applyInitialTheme()
const app = createApp(App)
app.use(createPinia())
const pinia = createPinia()
app.use(pinia)
app.use(router)
// 在pinia初始化后确保theme store被创建并应用主题设置
app.config.globalProperties.$initTheme = () => {
useThemeStore() // 只需创建实例watch会自动处理主题设置
}
// 挂载应用
app.mount('#app')
// 确保主题store被初始化
app.config.globalProperties.$initTheme()

54
src/stores/theme.ts Normal file
View File

@ -0,0 +1,54 @@
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
type ThemeMode = 'dark' | 'light'
// 设置主题到DOM
const applyThemeToDOM = (theme: ThemeMode) => {
document.documentElement.setAttribute('data-theme', theme)
}
export const useThemeStore = defineStore('theme', () => {
// 获取存储的主题或默认为深色
const storedTheme = localStorage.getItem('theme') as ThemeMode | null
const currentTheme = ref<ThemeMode>(storedTheme || 'dark')
// 立即应用当前主题到DOM
applyThemeToDOM(currentTheme.value)
// 监听主题变化并应用
watch(
currentTheme,
(newTheme) => {
// 保存到本地存储
localStorage.setItem('theme', newTheme)
// 设置文档根元素的主题属性
applyThemeToDOM(newTheme)
},
{ immediate: true },
)
// 切换主题
function toggleTheme() {
currentTheme.value = currentTheme.value === 'dark' ? 'light' : 'dark'
}
// 设置特定主题
function setTheme(theme: ThemeMode) {
currentTheme.value = theme
}
// 判断是否为深色主题
const isDarkTheme = ref(currentTheme.value === 'dark')
watch(currentTheme, (newTheme) => {
isDarkTheme.value = newTheme === 'dark'
})
return {
currentTheme,
isDarkTheme,
toggleTheme,
setTheme,
}
})

View File

@ -1,9 +1,12 @@
<script setup lang="ts">
import { computed, ref, onMounted } from 'vue'
import { computed, ref, onMounted, watch } from 'vue'
import { useUserStore } from '../stores/user'
import { useThemeStore } from '../stores/theme'
const userStore = useUserStore()
const themeStore = useThemeStore()
const isAuthenticated = computed(() => userStore.isAuthenticated)
const isDarkTheme = computed(() => themeStore.isDarkTheme)
const emit = defineEmits(['openAuth'])
@ -11,6 +14,24 @@ const openAuthModal = (mode: 'login' | 'register') => {
emit('openAuth', mode)
}
//
const toggleTheme = () => {
themeStore.toggleTheme()
}
onMounted(() => {
//
document.documentElement.setAttribute('data-theme', themeStore.currentTheme)
})
// UI
watch(
() => themeStore.currentTheme,
(newTheme) => {
document.documentElement.setAttribute('data-theme', newTheme)
},
)
//
const apiBaseUrl =
import.meta.env.MODE === 'development' ? 'http://127.0.0.1:8000' : 'https://api.ibtc.work'
@ -97,6 +118,46 @@ onMounted(() => {
<template>
<div class="home-view">
<section class="hero-section">
<!-- 主题切换按钮 -->
<div class="theme-toggle">
<button
class="theme-toggle-button"
@click="toggleTheme"
:title="isDarkTheme ? '切换到浅色模式' : '切换到深色模式'"
>
<svg
v-if="isDarkTheme"
class="theme-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<!-- 太阳图标 -->
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
<svg
v-else
class="theme-icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<!-- 月亮图标 -->
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
</svg>
</button>
</div>
<div class="hero-content">
<h1 class="hero-title">
<span class="accent">tradus</span>
@ -517,6 +578,22 @@ onMounted(() => {
.deepseek-name {
font-size: 0.8rem;
}
.theme-toggle {
top: 16px;
right: 16px;
}
.theme-toggle-button {
width: 32px;
height: 32px;
padding: 4px;
}
.theme-icon {
width: 16px;
height: 16px;
}
}
/* 删除底部悬浮的模型支持信息样式 */
@ -570,4 +647,38 @@ onMounted(() => {
background-clip: text;
text-fill-color: transparent;
}
/* 主题切换按钮样式 */
.theme-toggle {
position: absolute;
top: 20px;
right: 20px;
z-index: 10;
}
.theme-toggle-button {
background: none;
border: none;
cursor: pointer;
padding: 6px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--color-bg-secondary);
color: var(--color-text-secondary);
transition: all 0.3s ease;
width: 38px;
height: 38px;
}
.theme-toggle-button:hover {
background-color: var(--color-accent-light);
color: var(--color-accent);
}
.theme-icon {
width: 20px;
height: 20px;
}
</style>