Skip to content

Commit 4a18233

Browse files
committed
feat: 添加暗黑主题支持
1 parent aacc10c commit 4a18233

6 files changed

Lines changed: 496 additions & 36 deletions

File tree

frontend/src/components/QuickSftpFileManager.vue

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div class="sftp-file-manager">
2+
<div class="sftp-file-manager" :class="{ 'dark-theme': isDark }">
33
<!-- 顶部工具栏 -->
44
<div class="toolbar">
55
<div class="toolbar-buttons">
@@ -176,12 +176,17 @@
176176

177177
<script setup>
178178
import { ref, onMounted, computed, watch } from 'vue';
179+
import { storeToRefs } from 'pinia';
179180
import { ElMessage, ElMessageBox } from 'element-plus';
180181
import { VueMonacoEditor } from '@guolao/vue-monaco-editor';
181182
import {
182183
ArrowUp, Refresh, FolderAdd, Upload, Delete, Loading,
183184
Folder, Document, Right
184185
} from '@element-plus/icons-vue'
186+
import { useThemeStore } from '@/stores/theme'
187+
188+
const themeStore = useThemeStore()
189+
const { isDark } = storeToRefs(themeStore)
185190
186191
const props = defineProps({
187192
sftp: {
@@ -445,6 +450,11 @@ onMounted(() => {
445450
display: flex;
446451
flex-direction: column;
447452
color: #303133;
453+
transition: all 0.3s ease;
454+
}
455+
456+
.dark-theme .sftp-file-manager {
457+
color: #ffffff;
448458
}
449459
450460
.toolbar {
@@ -454,6 +464,12 @@ onMounted(() => {
454464
gap: 10px;
455465
align-items: center;
456466
flex-wrap: wrap;
467+
transition: all 0.3s ease;
468+
}
469+
470+
.dark-theme .toolbar {
471+
background-color: rgba(30, 30, 30, 0.8);
472+
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
457473
}
458474
459475
.toolbar-buttons {
@@ -508,20 +524,40 @@ onMounted(() => {
508524
background-color: #ffffff;
509525
}
510526
527+
.dark-theme .file-item {
528+
background-color: rgba(255, 255, 255, 0.08);
529+
color: #ffffff;
530+
border: 1px solid rgba(255, 255, 255, 0.1);
531+
}
532+
511533
.file-item:hover {
512534
background-color: #f5f7fa;
513535
border-color: #e4e7ed;
514536
}
515537
538+
.dark-theme .file-item:hover {
539+
background-color: rgba(255, 255, 255, 0.12);
540+
border-color: rgba(255, 255, 255, 0.2);
541+
}
542+
516543
.file-item.selected {
517544
background-color: #ecf5ff;
518545
border-color: #409eff;
519546
}
520547
548+
.dark-theme .file-item.selected {
549+
background-color: rgba(0, 122, 255, 0.2);
550+
border-color: #007AFF;
551+
}
552+
521553
.file-item.directory {
522554
font-weight: bold;
523555
}
524556
557+
.dark-theme .file-item.directory .file-name {
558+
color: #ffffff;
559+
}
560+
525561
.file-icon {
526562
margin-right: 10px;
527563
font-size: 20px;
@@ -538,13 +574,21 @@ onMounted(() => {
538574
color: #303133;
539575
}
540576
577+
.dark-theme .file-name {
578+
color: #ffffff !important;
579+
}
580+
541581
.file-details {
542582
display: flex;
543583
gap: 15px;
544584
font-size: 12px;
545585
color: #606266;
546586
}
547587
588+
.dark-theme .file-details {
589+
color: #98989d;
590+
}
591+
548592
.file-actions {
549593
opacity: 0;
550594
transition: opacity 0.2s;
@@ -560,6 +604,10 @@ onMounted(() => {
560604
color: #909399;
561605
}
562606
607+
.dark-theme .empty-directory {
608+
color: #98989d;
609+
}
610+
563611
.editor-container {
564612
height: 600px;
565613
display: flex;
@@ -572,6 +620,13 @@ onMounted(() => {
572620
display: flex;
573621
justify-content: space-between;
574622
align-items: center;
623+
transition: all 0.3s ease;
624+
}
625+
626+
.dark-theme .editor-header {
627+
background-color: rgba(30, 30, 30, 0.8);
628+
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
629+
color: #ffffff;
575630
}
576631
577632
.file-editor-monaco {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<template>
2+
<el-tooltip :content="isDark ? '切换到亮色模式' : '切换到暗黑模式'" placement="bottom">
3+
<el-button
4+
class="theme-toggle-btn"
5+
:class="{ 'dark': isDark }"
6+
text
7+
circle
8+
size="small"
9+
@click.stop="toggleTheme"
10+
>
11+
<el-icon v-if="isDark"><Sunny /></el-icon>
12+
<el-icon v-else><Moon /></el-icon>
13+
</el-button>
14+
</el-tooltip>
15+
</template>
16+
17+
<script setup>
18+
import { storeToRefs } from 'pinia'
19+
import { Sunny, Moon } from '@element-plus/icons-vue'
20+
import { useThemeStore } from '@/stores/theme'
21+
22+
const themeStore = useThemeStore()
23+
const { isDark } = storeToRefs(themeStore)
24+
25+
const toggleTheme = () => {
26+
console.log('Toggle theme clicked, current isDark:', isDark.value)
27+
themeStore.toggleTheme()
28+
console.log('After toggle, isDark:', isDark.value)
29+
}
30+
</script>
31+
32+
<style scoped>
33+
.theme-toggle-btn {
34+
color: #007AFF;
35+
background-color: rgba(0, 122, 255, 0.08);
36+
border: 1px solid rgba(0, 122, 255, 0.2);
37+
transition: all 0.2s ease;
38+
width: 32px !important;
39+
height: 32px !important;
40+
display: flex;
41+
align-items: center;
42+
justify-content: center;
43+
}
44+
45+
.theme-toggle-btn:hover {
46+
color: #ffffff;
47+
background-color: #007AFF;
48+
border-color: #007AFF;
49+
transform: scale(1.05);
50+
box-shadow: 0 2px 8px rgba(0, 122, 255, 0.3);
51+
}
52+
53+
.theme-toggle-btn:active {
54+
transform: scale(0.95);
55+
}
56+
57+
.theme-toggle-btn.dark {
58+
color: #007AFF;
59+
background-color: rgba(255, 255, 255, 0.08);
60+
border: 1px solid rgba(0, 122, 255, 0.4);
61+
}
62+
63+
.theme-toggle-btn.dark:hover {
64+
color: #ffffff;
65+
background-color: #007AFF;
66+
border-color: #007AFF;
67+
transform: scale(1.05);
68+
box-shadow: 0 2px 8px rgba(0, 122, 255, 0.4);
69+
}
70+
</style>

frontend/src/stores/theme.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { defineStore } from 'pinia'
2+
import { ref, watch } from 'vue'
3+
4+
export const useThemeStore = defineStore('theme', () => {
5+
const THEME_KEY = 'webssh_theme'
6+
const THEME_VERSION_KEY = 'webssh_theme_version'
7+
const CURRENT_VERSION = '1.0' // 版本号,用于重置主题设置
8+
9+
// 检查版本,如果版本不一致则重置为亮色模式
10+
const savedVersion = localStorage.getItem(THEME_VERSION_KEY)
11+
const shouldResetTheme = savedVersion !== CURRENT_VERSION
12+
13+
// 从 localStorage 读取主题设置,默认为 'light'
14+
const savedTheme = shouldResetTheme ? 'light' : (localStorage.getItem(THEME_KEY) || 'light')
15+
const isDark = ref(savedTheme === 'dark')
16+
17+
// 保存版本号
18+
if (shouldResetTheme) {
19+
localStorage.setItem(THEME_VERSION_KEY, CURRENT_VERSION)
20+
localStorage.setItem(THEME_KEY, 'light')
21+
}
22+
23+
// 切换主题
24+
const toggleTheme = () => {
25+
isDark.value = !isDark.value
26+
}
27+
28+
// 设置主题
29+
const setTheme = (dark) => {
30+
isDark.value = dark
31+
}
32+
33+
// 监听主题变化,保存到 localStorage 并更新 document class
34+
watch(isDark, (newVal) => {
35+
localStorage.setItem(THEME_KEY, newVal ? 'dark' : 'light')
36+
37+
if (newVal) {
38+
document.documentElement.classList.add('dark')
39+
} else {
40+
document.documentElement.classList.remove('dark')
41+
}
42+
}, { immediate: true })
43+
44+
return {
45+
isDark,
46+
toggleTheme,
47+
setTheme
48+
}
49+
})

0 commit comments

Comments
 (0)