diff --git a/Yi.Ai.Vue3/.claude/settings.local.json b/Yi.Ai.Vue3/.claude/settings.local.json
index 13e6d318..d3606838 100644
--- a/Yi.Ai.Vue3/.claude/settings.local.json
+++ b/Yi.Ai.Vue3/.claude/settings.local.json
@@ -3,7 +3,8 @@
"allow": [
"Bash(npx vue-tsc --noEmit)",
"Bash(timeout 60 npx vue-tsc:*)",
- "Bash(npm run dev:*)"
+ "Bash(npm run dev:*)",
+ "Bash(xargs:*)"
],
"deny": [],
"ask": []
diff --git a/Yi.Ai.Vue3/README.md b/Yi.Ai.Vue3/README.md
index 71e163b2..80fcf996 100644
--- a/Yi.Ai.Vue3/README.md
+++ b/Yi.Ai.Vue3/README.md
@@ -70,3 +70,87 @@ dotnet run
- [ ] 文件上传
- [ ] 其他...
+深色主题样式编写指南
+
+1. 在 src/styles/dark-theme.scss 中添加样式
+
+所有深色主题样式都使用 [data-theme="dark"] 选择器包裹:
+
+/* ========== 组件名称深色样式 ========== */
+[data-theme="dark"] {
+.your-component-class {
+background-color: #1f2937 !important;
+color: #e5e7eb !important;
+border-color: #374151 !important;
+}
+}
+
+2. 常用深色主题颜色值
+ ┌─────────────────────────┬───────────────────┐
+ │ 用途 │ 颜色值 │
+ ├─────────────────────────┼───────────────────┤
+ │ 主背景 │ #111827 │
+ ├─────────────────────────┼───────────────────┤
+ │ 次级背景(卡片、弹窗) │ #1f2937 │
+ ├─────────────────────────┼───────────────────┤
+ │ 三级背景(hover、表头) │ #374151 │
+ ├─────────────────────────┼───────────────────┤
+ │ 主文字 │ #f3f4f6 / #f9fafb │
+ ├─────────────────────────┼───────────────────┤
+ │ 次级文字 │ #e5e7eb │
+ ├─────────────────────────┼───────────────────┤
+ │ 三级文字 │ #9ca3af │
+ ├─────────────────────────┼───────────────────┤
+ │ 边框浅色 │ #374151 │
+ ├─────────────────────────┼───────────────────┤
+ │ 边框深色 │ #4b5563 │
+ ├─────────────────────────┼───────────────────┤
+ │ 主色调 │ #60a5fa │
+ └─────────────────────────┴───────────────────┘
+3. 覆盖 Tailwind 工具类
+
+[data-theme="dark"] {
+.bg-white {
+background-color: #374151 !important;
+}
+
+ .text-gray-700 {
+ color: #e5e7eb !important;
+ }
+}
+
+4. 覆盖 Element Plus 组件
+
+[data-theme="dark"] {
+.el-card {
+background-color: #1f2937 !important;
+border-color: #374151 !important;
+}
+}
+
+5. 使用 CSS 变量(推荐)
+
+在 src/styles/var.scss 的 [data-theme="dark"] 块中定义变量,然后在组件中使用:
+
+// var.scss
+[data-theme="dark"] {
+--my-component-bg: #1f2937;
+}
+
+// dark-theme.scss
+[data-theme="dark"] {
+.my-component {
+background-color: var(--my-component-bg) !important;
+}
+}
+
+6. 组件内动态颜色(JS 方式)
+
+如果需要在 JS 中动态获取颜色:
+
+
diff --git a/Yi.Ai.Vue3/src/components/WelecomeText/index.vue b/Yi.Ai.Vue3/src/components/WelecomeText/index.vue
index 1d626475..0cf5d1dd 100644
--- a/Yi.Ai.Vue3/src/components/WelecomeText/index.vue
+++ b/Yi.Ai.Vue3/src/components/WelecomeText/index.vue
@@ -1,13 +1,18 @@
@@ -22,7 +27,7 @@ const username = computed(() => userStore.userInfo?.username ?? '意心Ai,一
interval: 45,
}"
:is-fog="{
- bgColor: '#fff',
+ bgColor: fogBgColor,
}"
/>
diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue b/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue
index 975fd521..972881d5 100644
--- a/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue
+++ b/Yi.Ai.Vue3/src/layouts/components/Header/components/ThemeBtn.vue
@@ -3,16 +3,58 @@ import { useColorMode } from '@vueuse/core';
// 使用 VueUse 的 useColorMode
const mode = useColorMode({
- attribute: 'class',
+ attribute: 'data-theme',
modes: {
light: 'light',
dark: 'dark',
},
+ storageKey: 'yi-theme',
+ initialValue: 'light',
});
-// 切换主题
-function toggleTheme() {
- mode.value = mode.value === 'dark' ? 'light' : 'dark';
+// 是否正在切换动画中
+const isAnimating = ref(false);
+
+// 切换主题(带动画)
+async function toggleTheme(event: MouseEvent) {
+ // 如果正在动画中,不执行
+ if (isAnimating.value) return;
+
+ const newTheme = mode.value === 'dark' ? 'light' : 'dark';
+
+ // 检查浏览器是否支持 View Transitions API
+ if (!document.startViewTransition) {
+ mode.value = newTheme;
+ return;
+ }
+
+ isAnimating.value = true;
+
+ // 获取点击位置
+ const x = event.clientX;
+ const y = event.clientY;
+
+ // 计算最大半径(从点击位置到最远角落的距离)
+ const maxRadius = Math.hypot(
+ Math.max(x, window.innerWidth - x),
+ Math.max(y, window.innerHeight - y),
+ );
+
+ // 设置 CSS 变量用于动画
+ document.documentElement.style.setProperty('--theme-transition-x', `${x}px`);
+ document.documentElement.style.setProperty('--theme-transition-y', `${y}px`);
+ document.documentElement.style.setProperty('--theme-transition-radius', `${maxRadius}px`);
+
+ // 使用 View Transitions API
+ const transition = document.startViewTransition(() => {
+ mode.value = newTheme;
+ });
+
+ try {
+ await transition.finished;
+ } finally {
+ isAnimating.value = false;
+ }
}
// 主题图标
@@ -24,20 +66,29 @@ const themeIcon = computed(() => {
const themeTitle = computed(() => {
return mode.value === 'dark' ? '切换到浅色模式' : '切换到深色模式';
});
+
+// 是否为深色模式
+const isDark = computed(() => mode.value === 'dark');
-
-
-
-
-
主题
+
+
+
+
+
+
+
+
+
+
{{ isDark ? '深色' : '浅色' }}
@@ -53,29 +104,68 @@ const themeTitle = computed(() => {
gap: 6px;
cursor: pointer;
padding: 8px 12px;
- border-radius: 6px;
- transition: all 0.2s;
+ border-radius: 8px;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
color: var(--el-text-color-regular);
+ background-color: transparent;
&:hover {
background-color: var(--el-fill-color-light);
color: var(--el-color-primary);
}
+ &.is-dark {
+ .icon-wrapper {
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
+ }
+ }
+
+ .icon-wrapper {
+ position: relative;
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ overflow: hidden;
+ }
+
.theme-icon {
- font-size: 18px;
- transition: transform 0.3s;
+ position: absolute;
+ font-size: 16px;
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
- &:hover .theme-icon {
- transform: rotate(20deg);
+ .sun-icon {
+ color: #f59e0b;
+ transform: translateY(30px) rotate(-90deg);
+ opacity: 0;
+
+ &.active {
+ transform: translateY(0) rotate(0deg);
+ opacity: 1;
+ }
+ }
+
+ .moon-icon {
+ color: #6366f1;
+ transform: translateY(0) rotate(0deg);
+ opacity: 1;
+
+ &.active {
+ transform: translateY(-30px) rotate(90deg);
+ opacity: 0;
+ }
}
- // PC端显示文字
.pc-text {
display: inline;
font-size: 14px;
font-weight: 500;
+ transition: color 0.3s;
}
}
}
diff --git a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue
index 1910cfe3..a68df3cf 100644
--- a/Yi.Ai.Vue3/src/layouts/components/Header/index.vue
+++ b/Yi.Ai.Vue3/src/layouts/components/Header/index.vue
@@ -155,9 +155,9 @@ function toggleMobileMenu() {
-
-