From 694f07234d49a1d53b96a5d6a3f4996b31cb7df4 Mon Sep 17 00:00:00 2001
From: steve <1050403040@qq.com>
Date: Thu, 4 Sep 2025 15:53:16 +0800
Subject: [PATCH] =?UTF-8?q?feat(theme):=20=E6=B7=BB=E5=8A=A0=E4=B8=BB?=
=?UTF-8?q?=E9=A2=98=E5=88=87=E6=8D=A2=E5=8A=9F=E8=83=BD-=20=E6=96=B0?=
=?UTF-8?q?=E5=A2=9E=20ThemeToggle=20=E7=BB=84=E4=BB=B6=EF=BC=8C=E7=94=A8?=
=?UTF-8?q?=E4=BA=8E=E5=9C=A8=E7=95=8C=E9=9D=A2=E4=B8=8A=E6=98=BE=E7=A4=BA?=
=?UTF-8?q?=E4=B8=BB=E9=A2=98=E5=88=87=E6=8D=A2=E6=8C=89=E9=92=AE=20-=20?=
=?UTF-8?q?=E5=AE=9E=E7=8E=B0=20useTheme=E9=92=A9=E5=AD=90=EF=BC=8C?=
=?UTF-8?q?=E6=8F=90=E4=BE=9B=E4=B8=BB=E9=A2=98=E7=AE=A1=E7=90=86=E9=80=BB?=
=?UTF-8?q?=E8=BE=91-=20=E6=B7=BB=E5=8A=A0=E6=9A=97=E9=BB=91=E6=A8=A1?=
=?UTF-8?q?=E5=BC=8F=E5=92=8C=E4=BA=AE=E8=89=B2=E6=A8=A1=E5=BC=8F=E7=9A=84?=
=?UTF-8?q?=E5=88=87=E6=8D=A2=E6=94=AF=E6=8C=81=20-=20=E5=AE=9E=E7=8E=B0?=
=?UTF-8?q?=E6=9C=AC=E5=9C=B0=E5=AD=98=E5=82=A8=E5=92=8C=E7=B3=BB=E7=BB=9F?=
=?UTF-8?q?=E4=B8=BB=E9=A2=98=E7=9A=84=E8=87=AA=E5=8A=A8=E6=A3=80=E6=B5=8B?=
=?UTF-8?q?=20-=20=E6=B7=BB=E5=8A=A0=E4=B8=BB=E9=A2=98=E5=88=87=E6=8D=A2?=
=?UTF-8?q?=E6=97=B6=E7=9A=84=E4=BA=8B=E4=BB=B6=E5=B9=BF=E6=92=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/ThemeToggle.vue | 33 ++++++++
src/composables/useTheme.js | 141 +++++++++++++++++++++++++++++++++
2 files changed, 174 insertions(+)
create mode 100644 src/components/ThemeToggle.vue
create mode 100644 src/composables/useTheme.js
diff --git a/src/components/ThemeToggle.vue b/src/components/ThemeToggle.vue
new file mode 100644
index 0000000..2daad6f
--- /dev/null
+++ b/src/components/ThemeToggle.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/composables/useTheme.js b/src/composables/useTheme.js
new file mode 100644
index 0000000..111b355
--- /dev/null
+++ b/src/composables/useTheme.js
@@ -0,0 +1,141 @@
+import { ref, onMounted, onUnmounted } from 'vue'
+
+// 全局响应式主题状态
+const isDark = ref(false)
+
+// 检查当前主题状态
+const checkCurrentTheme = () => {
+ return document.documentElement.classList.contains('dark') ||
+ document.documentElement.getAttribute('data-theme') === 'dark'
+}
+
+// 更新响应式状态
+const updateReactiveState = () => {
+ isDark.value = checkCurrentTheme()
+}
+
+// 主题管理逻辑
+export function useTheme() {
+ let mutationObserver = null
+
+ // 初始化主题
+ const initTheme = () => {
+ const savedTheme = localStorage.getItem('theme')
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
+
+ if (savedTheme === 'dark' || (!savedTheme && prefersDark)) {
+ setDarkTheme()
+ } else {
+ setLightTheme()
+ }
+
+ // 立即更新响应式状态
+ updateReactiveState()
+ }
+
+ // 设置深色主题
+ const setDarkTheme = () => {
+ document.documentElement.classList.add('dark')
+ document.documentElement.setAttribute('data-theme', 'dark')
+ document.body.classList.add('dark')
+ document.body.setAttribute('data-theme', 'dark')
+ localStorage.setItem('theme', 'dark')
+
+ // 立即更新响应式状态
+ isDark.value = true
+
+ // 触发主题更新事件
+ window.dispatchEvent(new CustomEvent('theme-change', { detail: { isDark: true } }))
+ }
+
+ // 设置浅色主题
+ const setLightTheme = () => {
+ document.documentElement.classList.remove('dark')
+ document.documentElement.removeAttribute('data-theme')
+ document.body.classList.remove('dark')
+ document.body.removeAttribute('data-theme')
+ localStorage.setItem('theme', 'light')
+
+ // 立即更新响应式状态
+ isDark.value = false
+
+ // 触发主题更新事件
+ window.dispatchEvent(new CustomEvent('theme-change', { detail: { isDark: false } }))
+ }
+
+ // 切换主题
+ const toggleTheme = () => {
+ if (isDark.value) {
+ setLightTheme()
+ } else {
+ setDarkTheme()
+ }
+ }
+
+ // 监听系统主题变化
+ const setupSystemThemeListener = () => {
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
+ mediaQuery.addListener(() => {
+ const savedTheme = localStorage.getItem('theme')
+ // 只有在用户没有手动设置主题时才跟随系统
+ if (!savedTheme) {
+ initTheme()
+ }
+ })
+ }
+
+ // 设置DOM变化监听器(确保响应式状态同步)
+ const setupDOMObserver = () => {
+ if (typeof window !== 'undefined') {
+ mutationObserver = new MutationObserver(() => {
+ updateReactiveState()
+ })
+
+ // 监听documentElement和body的变化
+ mutationObserver.observe(document.documentElement, {
+ attributes: true,
+ attributeFilter: ['class', 'data-theme']
+ })
+
+ mutationObserver.observe(document.body, {
+ attributes: true,
+ attributeFilter: ['class', 'data-theme']
+ })
+ }
+ }
+
+ // 清理监听器
+ const cleanup = () => {
+ if (mutationObserver) {
+ mutationObserver.disconnect()
+ mutationObserver = null
+ }
+ }
+
+ // 获取当前主题
+ const getCurrentTheme = () => {
+ return isDark.value ? 'dark' : 'light'
+ }
+
+ // 组件挂载时初始化
+ onMounted(() => {
+ setupDOMObserver()
+ updateReactiveState()
+ })
+
+ // 组件卸载时清理
+ onUnmounted(() => {
+ cleanup()
+ })
+
+ return {
+ isDark,
+ initTheme,
+ toggleTheme,
+ setDarkTheme,
+ setLightTheme,
+ setupSystemThemeListener,
+ getCurrentTheme,
+ updateReactiveState
+ }
+}
\ No newline at end of file