feat(ui): add theme manager and dark mode

This commit is contained in:
Tim
2025-07-07 13:09:33 +08:00
parent 57863aadf8
commit 06af358989
4 changed files with 89 additions and 3 deletions

View File

@@ -5,19 +5,36 @@
--header-height: 60px;
--header-background-color: white;
--header-border-color: lightgray;
--header-text-color: black;
--menu-background-color: white;
--menu-border-color: lightgray;
--menu-selected-background-color: rgba(208, 250, 255, 0.659);
--menu-text-color: black;
--scroller-background-color: rgba(130, 175, 180, 0.5);
--normal-background-color: rgb(241, 241, 241);
--text-color: black;
--menu-width: 200px;
--page-max-width: 1200px;
}
[data-theme='dark'] {
--header-background-color: #2b2b2b;
--header-border-color: #555;
--header-text-color: white;
--menu-background-color: #333;
--menu-border-color: #555;
--menu-selected-background-color: rgba(255, 255, 255, 0.1);
--menu-text-color: white;
--normal-background-color: #121212;
--text-color: #eee;
}
body {
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
background-color: var(--normal-background-color);
color: var(--text-color);
}

View File

@@ -21,8 +21,8 @@
</div>
<div class="menu-footer">
<div class="menu-footer-btn" @click="$emit('toggle-dark-mode')">
<i class="fas fa-moon"></i>
<div class="menu-footer-btn" @click="cycleTheme">
<i :class="iconClass"></i>
</div>
</div>
</nav>
@@ -30,6 +30,7 @@
</template>
<script>
import { themeState, cycleTheme, ThemeMode } from '../utils/theme'
export default {
name: 'MenuComponent',
props: {
@@ -37,7 +38,20 @@ export default {
type: Boolean,
default: true
}
}
},
computed: {
iconClass() {
switch (themeState.mode) {
case ThemeMode.DARK:
return 'fas fa-moon'
case ThemeMode.LIGHT:
return 'fas fa-sun'
default:
return 'fas fa-desktop'
}
}
},
methods: { cycleTheme }
}
</script>

View File

@@ -6,6 +6,7 @@ import Toast, { POSITION } from 'vue-toastification'
import 'vue-toastification/dist/index.css'
import { useToast } from 'vue-toastification'
import { checkToken, clearToken } from './utils/auth'
import { initTheme } from './utils/theme'
// Configurable API domain and port
export const API_DOMAIN = 'http://127.0.0.1'
@@ -14,6 +15,8 @@ export const API_BASE_URL = API_PORT ? `${API_DOMAIN}:${API_PORT}` : API_DOMAIN
export const GOOGLE_CLIENT_ID = '777830451304-nt8afkkap18gui4f9entcha99unal744.apps.googleusercontent.com'
export const toast = useToast()
initTheme()
const app = createApp(App)
app.use(router)
app.use(Toast, { position: POSITION.TOP_RIGHT })

View File

@@ -0,0 +1,52 @@
import { reactive } from 'vue'
export const ThemeMode = {
SYSTEM: 'system',
LIGHT: 'light',
DARK: 'dark'
}
const THEME_KEY = 'theme-mode'
export const themeState = reactive({
mode: ThemeMode.SYSTEM
})
function apply(mode) {
const root = document.documentElement
if (mode === ThemeMode.SYSTEM) {
root.dataset.theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
} else {
root.dataset.theme = mode
}
}
export function initTheme() {
const saved = localStorage.getItem(THEME_KEY)
if (saved && Object.values(ThemeMode).includes(saved)) {
themeState.mode = saved
}
apply(themeState.mode)
}
export function setTheme(mode) {
if (!Object.values(ThemeMode).includes(mode)) return
themeState.mode = mode
localStorage.setItem(THEME_KEY, mode)
apply(mode)
}
export function cycleTheme() {
const modes = [ThemeMode.SYSTEM, ThemeMode.LIGHT, ThemeMode.DARK]
const index = modes.indexOf(themeState.mode)
const next = modes[(index + 1) % modes.length]
setTheme(next)
}
if (window.matchMedia) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (themeState.mode === ThemeMode.SYSTEM) {
apply(ThemeMode.SYSTEM)
}
})
}