Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/gui/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import App from './view/App.vue'
import DsContainer from './view/components/container'
import routes from './view/router'
import 'ant-design-vue/dist/reset.css'
import './view/style/theme/variables.scss'
import './view/style/index.scss'
import './view/style/theme/dark.scss' // 暗色主题

try {
window.onerror = (message, source, lineno, colno, error) => {
Expand Down
132 changes: 15 additions & 117 deletions packages/gui/src/view/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as Icons from '@ant-design/icons-vue';
import { ipcRenderer } from 'electron'
import createMenus from '@/view/router/menu'
import zhCN from 'ant-design-vue/es/locale/zh_CN'
import { colorTheme } from './composables/theme'
import { appliedTheme, initTheme, getAntThemeConfig } from './composables/theme'

export default {
name: 'App',
Expand All @@ -32,11 +32,11 @@ export default {
},

computed: {
themeClass() {
return `theme-${this.theme}`
},
theme() {
return colorTheme.value
return appliedTheme.value
},
themeConfig() {
return getAntThemeConfig(this.theme === 'dark')
},
// 将菜单数据转换为 items 格式
menuItems() {
Expand Down Expand Up @@ -125,20 +125,21 @@ export default {
await this.configReadyPromise
}

// 初始化主题系统
const appConfig = (this.config && this.config.app) || (this.$global && this.$global.config && this.$global.config.app) || {}
let theme = appConfig.theme || 'dark'
if (appConfig.theme === 'system') {
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}

colorTheme.value = theme
const initialThemeMode = appConfig.theme || 'dark'
this.cleanupTheme = initTheme(initialThemeMode)

// 设置默认选中的菜单项
this.updateSelectedKeys(this.$route.fullPath)
},

beforeUnmount() {
ipcRenderer.removeListener('config.changed', this.onConfigChanged)
// 清理主题监听器
if (this.cleanupTheme) {
this.cleanupTheme()
}
},

methods: {
Expand Down Expand Up @@ -258,10 +259,10 @@ export default {
</script>

<template>
<a-config-provider :locale="locale">
<div class="ds_layout" :class="themeClass">
<a-config-provider :locale="locale" :theme="themeConfig">
<div class="ds_layout">
<a-layout>
<a-layout-sider :theme="theme" style="overflow-y: auto">
<a-layout-sider :style="{ background: theme === 'dark' ? '#1e1f22' : '#ffffff', overflowY: 'auto' }">
<div class="logo" />
<div class="aside">
<a-menu
Expand Down Expand Up @@ -318,106 +319,3 @@ export default {
</a-config-provider>
</template>

<style lang="scss">
body {
height: 100%;
}
.ds_layout {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
height: 100%;
.ant-layout-has-sider {
border: 1px solid #eee;
}
.ant-layout-sider {
flex: 0 0 200px;
max-width: 200px;
min-width: 200px;
width: 200px;
}
.ant-layout-sider-children {
border-right: 1px solid #eee;
}
> .ant-layout {
height: 100%;
}
> .ant-layout > .ant-layout {
display: flex;
flex-direction: column;
min-height: 0;
}
.ant-layout-content {
flex: 1 1 auto;
min-height: 0;
display: flex;
flex-direction: column;
}
.content-inner {
flex: 1 1 auto;
min-height: 0;
overflow: auto;
}
.logo {
padding: 5px;
border-bottom: #eee solid 1px;
height: 60px;
background-image: url('../../public/logo/logo-lang.svg');
background-size: auto 50px;
background-repeat: no-repeat;
background-position: 5px center;
}
.ant-layout-footer {
padding: 10px;
text-align: center;
border-top: #d6d4d4 solid 1px;
flex: 0 0 auto;
background: #fff;
position: relative;
z-index: 1;
}
.ant-menu-inline,
.ant-menu-vertical,
.ant-menu-vertical-left {
border: 0;
}

.pre-release-banner {
margin: 0 12px 12px;
padding: 10px 12px;
border: 1px solid #ffa940;
background: #fff7e6;
color: #ad4e00;
font-weight: 600;
border-radius: 6px;
text-align: center;
}

.pre-release-tag {
display: inline-block;
margin-left: 8px;
padding: 1px 8px;
border-radius: 999px;
border: 1px solid #ffa940;
background: #fff7e6;
color: #ad4e00;
font-size: 12px;
line-height: 20px;
}

.search-bar {
padding: 12px;
border-bottom: 1px solid #eee;
background: #fff;
}
}
.search-bar-highlight {
background-color: #ef0fff;
color: #fdfdfd;

&.selected-highlight {
background-color: #17a450;
}
}
</style>
140 changes: 138 additions & 2 deletions packages/gui/src/view/composables/theme.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,139 @@
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { theme } from 'ant-design-vue'

export const colorTheme = ref('dark')
// 主题类型:light-亮色, dark-暗色, system-跟随系统
export const themeMode = ref('dark')

// 实际应用的主题(system模式下会根据系统主题计算)
export const appliedTheme = ref('dark')

// 系统主题媒体查询
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')

/**
* 获取系统主题
* @returns {'dark' | 'light'}
*/
function getSystemTheme() {
return mediaQuery.matches ? 'dark' : 'light'
}

/**
* 应用主题到 DOM
* @param {'dark' | 'light'} theme
*/
function applyThemeToDOM(theme) {
appliedTheme.value = theme
document.documentElement.setAttribute('data-theme', theme)
}

/**
* 更新主题
* 根据 themeMode 的值计算并应用实际主题
*/
export function updateTheme() {
const actualTheme = themeMode.value === 'system'
? getSystemTheme()
: themeMode.value

applyThemeToDOM(actualTheme)
}

/**
* 设置主题模式
* @param {'light' | 'dark' | 'system'} mode
*/
export function setThemeMode(mode) {
themeMode.value = mode
updateTheme()
}

/**
* 初始化主题系统
* 应在应用挂载时调用
* @param {'light' | 'dark' | 'system'} initialMode - 初始主题模式
*/
export function initTheme(initialMode = 'dark') {
themeMode.value = initialMode
updateTheme()

// 监听系统主题变化
const handleSystemThemeChange = () => {
if (themeMode.value === 'system') {
updateTheme()
}
}

// 添加监听器
if (mediaQuery.addEventListener) {
mediaQuery.addEventListener('change', handleSystemThemeChange)
} else {
// 兼容旧版浏览器
mediaQuery.addListener(handleSystemThemeChange)
}

// 返回清理函数
return () => {
if (mediaQuery.removeEventListener) {
mediaQuery.removeEventListener('change', handleSystemThemeChange)
} else {
mediaQuery.removeListener(handleSystemThemeChange)
}
}
}

/**
* 获取 Ant Design 主题配置
* @param {boolean} isDarkTheme - 是否为暗色主题
* @returns {Object} Ant Design 主题配置对象
*/
export function getAntThemeConfig(isDarkTheme) {
return {
algorithm: isDarkTheme
? theme.darkAlgorithm
: theme.defaultAlgorithm,
token: isDarkTheme
? {
colorBgBase: '#1e1f22',
colorTextBase: '#dddddd',
colorBgContainer: '#1e1f22',
}
: {
colorBgBase: '#ffffff',
colorTextBase: '#333333',
colorBgContainer: '#ffffff',
},
}
}

/**
* 在组件中使用主题的组合式函数
* 提供响应式的主题状态和切换方法
*/
export function useTheme() {
// 计算属性:是否为暗色主题
const isDark = computed(() => appliedTheme.value === 'dark')

// 计算属性:是否为亮色主题
const isLight = computed(() => appliedTheme.value === 'light')

// 计算属性:当前是否为跟随系统模式
const isSystem = computed(() => themeMode.value === 'system')

// Ant Design 主题配置
const antThemeConfig = computed(() => getAntThemeConfig(isDark.value))

return {
themeMode,
appliedTheme,
isDark,
isLight,
isSystem,
setThemeMode,
updateTheme,
antThemeConfig,
}
}

// 为了兼容旧代码,保留 colorTheme 导出
export const colorTheme = appliedTheme
22 changes: 11 additions & 11 deletions packages/gui/src/view/pages/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -422,23 +422,23 @@ export default {
height: 100px;
border-radius: 100px;
transition: all 0.3s ease;
border: 2px solid #d9d9d9;
background-color: #fff;
border: 2px solid var(--btn-border);
background-color: var(--btn-bg);

&:hover {
border-color: #40a9ff;
box-shadow: 0 0 8px rgba(24, 144, 255, 0.2);
border-color: var(--accent-hover);
box-shadow: 0 0 8px var(--accent-shadow);
}

/* 激活状态 */
&.is-active {
background-color: #1890ff;
border-color: #1890ff;
box-shadow: 0 0 12px rgba(24, 144, 255, 0.4);
background-color: var(--accent-color);
border-color: var(--accent-color);
box-shadow: 0 0 12px var(--accent-shadow);

&:hover {
background-color: #40a9ff;
border-color: #40a9ff;
background-color: var(--accent-hover);
border-color: var(--accent-hover);
}
}
}
Expand All @@ -452,11 +452,11 @@ export default {
margin-top: 10px;
font-size: 14px;
font-weight: 500;
color: #666;
color: var(--text-secondary);
transition: color 0.3s ease;

&.is-active {
color: #1890ff;
color: var(--accent-color);
font-weight: 600;
}
}
Expand Down
Loading
Loading