diff --git a/README.md b/README.md
index e5d40c8..1b6a2b5 100644
--- a/README.md
+++ b/README.md
@@ -5,11 +5,10 @@
## 技术栈
- Vue 3 - 渐进式 JavaScript 框架
-- Vite - 下一代前端构建工具
-- Vue Router - 官方路由管理器
-- Pinia - Vue 的官方状态管理库
- TypeScript - JavaScript 的超集
-- Tailwind CSS - 实用优先的 CSS 框架
+- Element Plus - 基于 Vue 3 的组件库
+- Vite - 下一代前端构建工具
+- Sass - CSS 预处理器
## 功能特性
diff --git a/dist.zip b/dist.zip
new file mode 100644
index 0000000..23bed64
Binary files /dev/null and b/dist.zip differ
diff --git a/package.json b/package.json
index 2e33ffd..e38523b 100644
--- a/package.json
+++ b/package.json
@@ -48,10 +48,19 @@
"vue-router": "^4.5.0"
},
"devDependencies": {
- "@vitejs/plugin-vue": "^5.2.1",
- "sass": "^1.86.3",
+ "@vitejs/plugin-vue": "^5.0.4",
+ "@types/node": "^20.11.19",
+ "@vue/compiler-sfc": "^3.4.21",
+ "autoprefixer": "^10.4.17",
+ "postcss": "^8.4.35",
+ "postcss-import": "^16.0.1",
+ "postcss-preset-env": "^9.3.0",
+ "sass": "^1.71.1",
"sass-embedded": "^1.86.3",
- "vite": "^6.2.0",
- "vite-plugin-mkcert": "^1.17.8"
+ "typescript": "^5.3.3",
+ "vite": "^5.1.4",
+ "vite-plugin-mkcert": "^1.17.8",
+ "vite-plugin-vue-devtools": "^7.0.19",
+ "vue-tsc": "^1.8.27"
}
}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..0fa6f18
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,12 @@
+export default {
+ plugins: {
+ 'postcss-import': {},
+ 'postcss-preset-env': {
+ stage: 3,
+ features: {
+ 'nesting-rules': true
+ }
+ },
+ autoprefixer: {}
+ }
+}
\ No newline at end of file
diff --git a/src/App.vue b/src/App.vue
index 7de1880..f762c1e 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,40 +1,58 @@
-
\ No newline at end of file
diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue
deleted file mode 100644
index 546ebbc..0000000
--- a/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
- {{ msg }}
-
-
-
-
- Edit
- components/HelloWorld.vue
to test HMR
-
-
-
-
- Check out
- create-vue, the official Vue + Vite starter
-
-
- Learn more about IDE Support for Vue in the
- Vue Docs Scaling up Guide.
-
- Click on the Vite and Vue logos to learn more
-
-
-
diff --git a/src/components/chat/ModeSetting.vue b/src/components/chat/ModeSetting.vue
deleted file mode 100644
index 4a9a614..0000000
--- a/src/components/chat/ModeSetting.vue
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
-
-
-
-
-
- 编辑设置
-
-
-
- 重置设置
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/components/settings/SettingsPanel.vue b/src/components/settings/SettingsPanel.vue
new file mode 100644
index 0000000..7bcb87a
--- /dev/null
+++ b/src/components/settings/SettingsPanel.vue
@@ -0,0 +1,243 @@
+
+
+
整体外观
+
+
+ 系统默认
+
+
+ 浅色
+
+
+ 深色
+
+
+
+
+
+
主题
+
+
+
+
{{ getThemeLabel(name) }}
+
+
+
+
+
+
+
界面设置
+
+ 启用动画效果
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/settings/ThemeSwitcher.vue b/src/components/settings/ThemeSwitcher.vue
new file mode 100644
index 0000000..2e37058
--- /dev/null
+++ b/src/components/settings/ThemeSwitcher.vue
@@ -0,0 +1,56 @@
+
+
+
+ 主题切换
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index 66d3ead..241e295 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,21 +1,39 @@
import { createApp } from "vue";
-import "./style.css";
+import "@/styles/style.css";
+// 使用自定义主题
+import '@/styles/element-variables.scss'
import App from "./App.vue";
import {router} from "@/router/index";
import { createPinia } from "pinia";
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
+import { useSettingsStore, applyDisplayMode } from '@/store/settings'; // Import store and helper
+import { setTheme } from '@/styles/theme'; // Import setTheme
// 引入 Element Plus
import ElementPlus from 'element-plus'
-// 使用自定义主题
-import './styles/element-variables.scss'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate)
const app = createApp(App);
-app.use(pinia);
+app.use(pinia); // Pinia is now available
+
+// Access the store *after* app.use(pinia)
+// The store state will be hydrated from localStorage here by the persistence plugin
+const settingsStore = useSettingsStore();
+
+// Apply persisted/initial settings
+applyDisplayMode(settingsStore.displayMode);
+setTheme(settingsStore.theme);
+// Apply initial animation setting
+if (!settingsStore.animationsEnabled) {
+ document.body.classList.add('no-animations');
+} else {
+ // Ensure the class is not present if animations are enabled initially
+ document.body.classList.remove('no-animations');
+}
+
app.use(router);
app.use(ElementPlus, {
locale: zhCn,
diff --git a/src/router/index.js b/src/router/index.js
index c8350cf..b478c06 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -18,17 +18,11 @@ const routes = [
},
{
path: "/chat",
- component: () => import("@/layouts/MainLayout.vue"),
- children: [
- {
- path: "",
- name: "chat",
- meta: {
- icon: ChatRound
- },
- component: () => import("@/views/chat/index.vue"),
- },
- ],
+ name: "chat",
+ meta: {
+ icon: ChatRound
+ },
+ component: () => import("@/views/chat/index.vue"),
},
];
diff --git a/src/store/settings.js b/src/store/settings.js
index c8ce361..136404b 100644
--- a/src/store/settings.js
+++ b/src/store/settings.js
@@ -1,34 +1,40 @@
import { defineStore } from 'pinia'
+import { setTheme, themes } from '@/styles/theme'
+
+// Helper function to apply display mode
+export function applyDisplayMode(mode) {
+ const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
+ let darkModeEnabled = false;
+
+ if (mode === 'dark') {
+ darkModeEnabled = true;
+ } else if (mode === 'system') {
+ darkModeEnabled = prefersDark;
+ } // else mode === 'light', darkModeEnabled remains false
+
+ if (darkModeEnabled) {
+ document.documentElement.classList.add('dark');
+ } else {
+ document.documentElement.classList.remove('dark');
+ }
+}
export const useSettingsStore = defineStore('settings', {
state: () => ({
// 侧边栏状态
sidebarCollapsed: false,
-
- // 界面配置
- interface: {
- // 是否显示时间戳
- showTimestamp: true,
- // 消息气泡最大宽度(px)
- messageMaxWidth: 800,
- // 字体大小(px)
- fontSize: 14
- },
-
- // 主题配置
- theme: {
- // 主色调
- primary: '#07c160',
- // 背景色
- background: '#f5f5f5',
- // 文字颜色
- textColor: '#333333'
- }
+ // 显示模式: 'light', 'dark', 'system'
+ displayMode: 'system', // Default to system preference
+ // 主题配置 (e.g., 'default', 'fresh', 'warm')
+ theme: 'default',
+ // 动画效果开关
+ animationsEnabled: true,
}),
persist: {
key: 'settings',
- paths: ['sidebarCollapsed']
+ // Persist displayMode and theme
+ paths: ['sidebarCollapsed', 'displayMode', 'theme', 'animationsEnabled']
},
actions: {
@@ -37,25 +43,54 @@ export const useSettingsStore = defineStore('settings', {
this.sidebarCollapsed = !this.sidebarCollapsed
},
- // 更新界面配置
- updateInterface(config) {
- this.interface = {
- ...this.interface,
- ...config
+ // 切换动画状态
+ toggleAnimations(enabled) {
+ if (typeof enabled === 'boolean') {
+ this.animationsEnabled = enabled;
+ } else {
+ this.animationsEnabled = !this.animationsEnabled;
+ }
+ // Apply animation class to body
+ if (this.animationsEnabled) {
+ document.body.classList.remove('no-animations');
+ } else {
+ document.body.classList.add('no-animations');
}
},
- // 更新主题配置
- updateTheme(config) {
- this.theme = {
- ...this.theme,
- ...config
+ // 更新显示模式
+ updateDisplayMode(newMode) {
+ if (['light', 'dark', 'system'].includes(newMode)) {
+ this.displayMode = newMode;
+ applyDisplayMode(newMode); // Apply the change immediately
+ // Optional: Re-apply theme colors if they differ significantly in dark mode
+ // setTheme(this.theme); // Uncomment if needed
+ } else {
+ console.warn(`Invalid display mode: ${newMode}`);
+ }
+ },
+
+ // 更新主题颜色配置
+ updateTheme(newTheme) {
+ if (themes.hasOwnProperty(newTheme)) { // Check if the theme exists
+ this.theme = newTheme;
+ setTheme(newTheme); // Apply the theme colors
+ } else {
+ console.warn(`Invalid theme: ${newTheme}`);
}
},
// 重置所有设置
resetSettings() {
this.$reset()
+ // Re-apply settings after reset
+ setTheme(this.theme);
+ applyDisplayMode(this.displayMode);
+ this.toggleAnimations(this.animationsEnabled); // Re-apply animation class
}
}
-})
\ No newline at end of file
+})
+
+// --- Helper Function to apply animation class ---
+// Moved applying logic into the toggleAnimations action directly
+// export function applyAnimationSetting(enabled) { ... }
\ No newline at end of file
diff --git a/src/style.css b/src/styles/style.css
similarity index 81%
rename from src/style.css
rename to src/styles/style.css
index 71c27b1..c701ab1 100644
--- a/src/style.css
+++ b/src/styles/style.css
@@ -1,3 +1,22 @@
+/* 基础样式 */
+:root {
+ --primary-color: #409eff;
+ --success-color: #67c23a;
+ --warning-color: #e6a23c;
+ --danger-color: #f56c6c;
+ --info-color: #909399;
+}
+
+/* 全局样式 */
+body {
+ margin: 0;
+ padding: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
@@ -58,10 +77,6 @@ button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
-.card {
- padding: 2em;
-}
-
#app {
/* max-width: 100vh; */
height: 100%;
diff --git a/src/styles/theme.js b/src/styles/theme.js
new file mode 100644
index 0000000..5094f1c
--- /dev/null
+++ b/src/styles/theme.js
@@ -0,0 +1,38 @@
+// 使用 import.meta.glob 动态导入 themes 目录下的所有 .js 文件
+// { eager: true } 表示同步导入,立即获取模块内容
+const themeModules = import.meta.glob('./themes/*.js', { eager: true });
+
+// 构建 themes 对象
+export const themes = {};
+for (const path in themeModules) {
+ // 从路径中提取主题名称 (e.g., './themes/default.js' -> 'default')
+ const themeName = path.replace('./themes/', '').replace('.js', '');
+ // 获取模块的默认导出 (即主题配置对象)
+ if (themeModules[path].default) {
+ themes[themeName] = themeModules[path].default;
+ }
+}
+
+// 主题切换函数
+export function setTheme(themeName) {
+ const theme = themes[themeName] || themes.default // Fallback to default
+ if (!theme) {
+ console.error(`Theme "${themeName}" not found.`);
+ return;
+ }
+
+ const root = document.documentElement
+
+ // 设置主题色变量
+ Object.keys(theme).forEach(key => {
+ root.style.setProperty(`--el-color-${key}`, theme[key])
+ })
+
+ // 设置主题类名
+ // 查找 body 上是否已有 theme- 开头的类
+ const currentThemeClass = Array.from(document.body.classList).find(cls => cls.startsWith('theme-'));
+ if (currentThemeClass) {
+ document.body.classList.remove(currentThemeClass);
+ }
+ document.body.classList.add(`theme-${themeName}`);
+}
\ No newline at end of file
diff --git a/src/styles/themes/business.js b/src/styles/themes/business.js
new file mode 100644
index 0000000..21a5887
--- /dev/null
+++ b/src/styles/themes/business.js
@@ -0,0 +1,7 @@
+export default {
+ primary: '#2C3E50',
+ success: '#27AE60',
+ warning: '#F39C12',
+ danger: '#E74C3C',
+ info: '#3498DB'
+};
\ No newline at end of file
diff --git a/src/styles/themes/dark.js b/src/styles/themes/dark.js
new file mode 100644
index 0000000..9a17238
--- /dev/null
+++ b/src/styles/themes/dark.js
@@ -0,0 +1,7 @@
+export default {
+ primary: '#5CACEE',
+ success: '#85CE61',
+ warning: '#E6A23C',
+ danger: '#F78989',
+ info: '#A6A9AD'
+};
\ No newline at end of file
diff --git a/src/styles/themes/default.js b/src/styles/themes/default.js
new file mode 100644
index 0000000..0eca09b
--- /dev/null
+++ b/src/styles/themes/default.js
@@ -0,0 +1,7 @@
+export default {
+ primary: '#409EFF',
+ success: '#67C23A',
+ warning: '#E6A23C',
+ danger: '#F56C6C',
+ info: '#909399'
+};
\ No newline at end of file
diff --git a/src/styles/themes/fresh.js b/src/styles/themes/fresh.js
new file mode 100644
index 0000000..07570c0
--- /dev/null
+++ b/src/styles/themes/fresh.js
@@ -0,0 +1,7 @@
+export default {
+ primary: '#36D1DC',
+ success: '#5CB85C',
+ warning: '#F0AD4E',
+ danger: '#D9534F',
+ info: '#5BC0DE'
+};
\ No newline at end of file
diff --git a/src/styles/themes/warm.js b/src/styles/themes/warm.js
new file mode 100644
index 0000000..3810ed8
--- /dev/null
+++ b/src/styles/themes/warm.js
@@ -0,0 +1,7 @@
+export default {
+ primary: '#FF6B6B',
+ success: '#4ECDC4',
+ warning: '#FFE66D',
+ danger: '#FF6B6B',
+ info: '#45B7D1'
+};
\ No newline at end of file
diff --git a/src/views/chat/ChatHeader.vue b/src/views/chat/ChatHeader.vue
index 31206a4..2038356 100644
--- a/src/views/chat/ChatHeader.vue
+++ b/src/views/chat/ChatHeader.vue
@@ -3,7 +3,6 @@