style: 支持深色主题
This commit is contained in:
@@ -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": []
|
||||
|
||||
@@ -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 中动态获取颜色:
|
||||
|
||||
<script setup>
|
||||
import { useColorMode } from '@vueuse/core';
|
||||
|
||||
const mode = useColorMode();
|
||||
const bgColor = computed(() => mode.value === 'dark' ? '#1f2937' : '#ffffff');
|
||||
</script>
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
<!-- 欢迎提示词 -->
|
||||
<script setup lang="ts">
|
||||
import { Typewriter } from 'vue-element-plus-x';
|
||||
import { useColorMode } from '@vueuse/core';
|
||||
import { useTimeGreeting } from '@/hooks/useTimeGreeting';
|
||||
import { useUserStore } from '@/stores';
|
||||
|
||||
const greeting = useTimeGreeting();
|
||||
const userStore = useUserStore();
|
||||
const mode = useColorMode();
|
||||
|
||||
const username = computed(() => userStore.userInfo?.username ?? '意心Ai,一心只为打造更良心的Ai平台');
|
||||
|
||||
// 根据主题动态设置雾化背景色
|
||||
const fogBgColor = computed(() => mode.value === 'dark' ? '#111827' : '#fff');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -22,7 +27,7 @@ const username = computed(() => userStore.userInfo?.username ?? '意心Ai,一
|
||||
interval: 45,
|
||||
}"
|
||||
:is-fog="{
|
||||
bgColor: '#fff',
|
||||
bgColor: fogBgColor,
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -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');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="theme-btn-container" data-tour="theme-btn">
|
||||
<div
|
||||
class="theme-btn"
|
||||
:class="{ 'is-dark': isDark }"
|
||||
:title="themeTitle"
|
||||
@click="toggleTheme"
|
||||
>
|
||||
<!-- PC端显示文字 + 图标 -->
|
||||
<el-icon class="theme-icon">
|
||||
<component :is="`i-ep-${themeIcon}`" />
|
||||
</el-icon>
|
||||
<span class="pc-text">主题</span>
|
||||
<!-- 图标容器 -->
|
||||
<div class="icon-wrapper">
|
||||
<el-icon class="theme-icon sun-icon" :class="{ active: isDark }">
|
||||
<Sunny />
|
||||
</el-icon>
|
||||
<el-icon class="theme-icon moon-icon" :class="{ active: !isDark }">
|
||||
<Moon />
|
||||
</el-icon>
|
||||
</div>
|
||||
<span class="pc-text">{{ isDark ? '深色' : '浅色' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +155,9 @@ function toggleMobileMenu() {
|
||||
<BuyBtn :is-menu-item="true" />
|
||||
</el-menu-item>
|
||||
|
||||
<!-- 主题切换(暂不显示) -->
|
||||
<el-menu-item v-if="false" class="custom-menu-item" index="no-route">
|
||||
<ThemeBtn :is-menu-item="true" />
|
||||
<!-- 主题切换 -->
|
||||
<el-menu-item class="custom-menu-item" index="no-route">
|
||||
<ThemeBtn />
|
||||
</el-menu-item>
|
||||
|
||||
<!-- 用户头像 -->
|
||||
|
||||
1207
Yi.Ai.Vue3/src/styles/dark-theme.scss
Normal file
1207
Yi.Ai.Vue3/src/styles/dark-theme.scss
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
@use './element-plus';
|
||||
@use './elx';
|
||||
@use './guide-tour';
|
||||
@use './dark-theme';
|
||||
body{
|
||||
//overflow: hidden;
|
||||
}
|
||||
|
||||
@@ -219,24 +219,96 @@
|
||||
|
||||
/* ========== 暗色模式变量 ========== */
|
||||
[data-theme="dark"] {
|
||||
--sidebar-background-color: #1f2937;
|
||||
--header-background-color: #111827;
|
||||
/* ========== 颜色系统 ========== */
|
||||
--color-primary: #60a5fa;
|
||||
--color-primary-light: #93c5fd;
|
||||
--color-primary-lighter: #bfdbfe;
|
||||
--color-primary-dark: #3b82f6;
|
||||
--color-primary-darker: #2563eb;
|
||||
|
||||
/* 辅助色 */
|
||||
--color-success: #34d399;
|
||||
--color-warning: #fbbf24;
|
||||
--color-danger: #f87171;
|
||||
--color-info: #9ca3af;
|
||||
|
||||
/* 中性色 */
|
||||
--color-white: #ffffff;
|
||||
--color-black: #000000;
|
||||
--color-gray-50: #111827;
|
||||
--color-gray-100: #1f2937;
|
||||
--color-gray-200: #374151;
|
||||
--color-gray-300: #4b5563;
|
||||
--color-gray-400: #6b7280;
|
||||
--color-gray-500: #9ca3af;
|
||||
--color-gray-600: #d1d5db;
|
||||
--color-gray-700: #e5e7eb;
|
||||
--color-gray-800: #f3f4f6;
|
||||
--color-gray-900: #f9fafb;
|
||||
|
||||
/* ========== 头部区域 ========== */
|
||||
--header-background-color: #1f2937;
|
||||
--header-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
|
||||
--header-text-color: #f3f4f6;
|
||||
--header-icon-color: #9ca3af;
|
||||
--header-border: 1px solid #374151;
|
||||
|
||||
/* ========== 侧边栏区域 ========== */
|
||||
--sidebar-background-color: #111827;
|
||||
--sidebar-active-bg-color: #374151;
|
||||
--sidebar-active-text-color: #f9fafb;
|
||||
--sidebar-hover-bg-color: #374151;
|
||||
--sidebar-text-color: #d1d5db;
|
||||
--sidebar-icon-color: #9ca3af;
|
||||
--sidebar-border-color: rgba(255, 255, 255, 0.08);
|
||||
|
||||
/* ========== 主要内容区域 ========== */
|
||||
--content-background-color: #111827;
|
||||
|
||||
/* ========== 聊天区域 ========== */
|
||||
--chat-container-bg: #111827;
|
||||
--chat-sender-bg: #1f2937;
|
||||
--chat-bubble-ai-bg: #374151;
|
||||
--chat-sender-border: 1px solid #374151;
|
||||
--chat-bubble-user-bg: #3b82f6;
|
||||
--chat-bubble-user-text: #ffffff;
|
||||
--chat-bubble-ai-bg: #1f2937;
|
||||
--chat-bubble-ai-text: #e5e7eb;
|
||||
--chat-typing-indicator-color: #6b7280;
|
||||
|
||||
/* ========== 登录弹框 ========== */
|
||||
--login-dialog-logo-background: #1f2937;
|
||||
--login-dialog-logo-text-color: #f3f4f6;
|
||||
--login-dialog-form-bg: #1f2937;
|
||||
--login-dialog-input-border: 1px solid #374151;
|
||||
--login-dialog-input-focus-border: 1px solid #60a5fa;
|
||||
|
||||
/* 文字颜色 */
|
||||
--text-color-primary: #f9fafb;
|
||||
--text-color-secondary: #e5e7eb;
|
||||
--text-color-tertiary: #9ca3af;
|
||||
--text-color-placeholder: #6b7280;
|
||||
--text-color-disabled: #4b5563;
|
||||
--text-color-inverse: #111827;
|
||||
|
||||
/* 背景颜色 */
|
||||
--bg-color-primary: #111827;
|
||||
--bg-color-secondary: #1f2937;
|
||||
--bg-color-tertiary: #374151;
|
||||
--bg-color-overlay: rgba(0, 0, 0, 0.7);
|
||||
--bg-color-mask: rgba(0, 0, 0, 0.6);
|
||||
|
||||
/* ========== 边框 ========== */
|
||||
--border-color-light: #374151;
|
||||
--border-color-default: #4b5563;
|
||||
--border-color-dark: #6b7280;
|
||||
|
||||
/* ========== 阴影系统 ========== */
|
||||
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
|
||||
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3);
|
||||
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.3);
|
||||
--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
|
||||
--shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* ========== 响应式断点 ========== */
|
||||
|
||||
Reference in New Issue
Block a user