支持暗黑主题切换

This commit is contained in:
chaos-zhu 2024-08-17 23:43:59 +08:00
parent ad0984bf64
commit 23e0140544
17 changed files with 259 additions and 77 deletions

View File

@ -5,10 +5,12 @@
</template>
<script setup>
import { ref } from 'vue'
import { ref, getCurrentInstance } from 'vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
const { proxy: { $store } } = getCurrentInstance()
const locale = ref(zhCn)
$store.setDefaultTheme()
</script>
<style lang="scss" scoped>

View File

@ -1,18 +0,0 @@
// vue transition 动画
.list-move, /* apply transition to moving elements */
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateY(-30px);
}
/* ensure leaving items are taken out of layout flow so that moving
animations can be calculated correctly. */
.list-leave-active {
position: absolute;
}

View File

@ -1,22 +0,0 @@
// element css bug
.el-notification__content {
text-align: initial;
}
.el-date-editor {
--el-date-editor-width: 100%;
}
.el-input__wrapper {
width: 100%;
}
.el-tabs__nav-scroll .el-tabs__nav {
padding-left: 0;
}
.el-tabs__content {
padding: 0 10px;
}
// :root {
// --active-color: red;
// }

View File

@ -0,0 +1,146 @@
// $--colors: (
// "primary": (
// "base": #589ef8,
// ),
// );
// @forward "element-plus/theme-chalk/src/dark/var.scss" with (
// $colors: $--colors
// );
/** element内置黑暗主题 */
@use 'element-plus/theme-chalk/src/dark/css-vars.scss' as *;
/** 自定义黑暗主题 */
html.dark {
// * admin
--bg-color: #000;
--v-main-bg-color: #181818;
--v-border-light: 1px solid #4c4c4d;
body {
background-color: var(--bg-color) !important;
}
.login_container {
// background: rgba(171, 181, 196, 0.3);
.login_box {
background-color: var(--v-main-bg-color) !important;
}
}
.top_bar_container {
background-color: var(--bg-color) !important;
}
.router_box {
background-color: var(--v-main-bg-color) !important;
}
.aside_container {
background-color: var(--v-main-bg-color) !important;
}
.terminal_top {
border-bottom: 1px solid #454242;
}
.info_box {
border-right: 1px solid #454242;
.el-progress-bar__innerText {
span {
color: #fff !important;
}
}
}
// scroll-bar
::-webkit-scrollbar {
background-color: var(--el-scrollbar-bg-color) !important;
}
::-webkit-scrollbar-thumb {
background-color: var(--el-border-color-darker) !important;
}
.layout-sidebar-container {
background-color: var(--bg-color) !important;
}
.app-main-container {
background-color: var(--v-main-bg-color) !important;
.card {
background-color: var(--bg-color) !important;
}
}
.layout-footer-container {
color: var(--el-text-color-regular) !important;
background-color: var(--v-main-bg-color) !important;
}
.fold-unfold {
color: #fff !important;
}
// .el-menu,
// .el-sub-menu,
// .el-menu-item,
// .el-sub-menu__title {
// background-color: var(--bg-color) !important;
// &:not(.is-active) {
// color: #bdbdc0 !important;
// }
// &.is-active {
// color: #fff !important;
// background-color: #000 !important;
// }
// }
.el-menu-item:not(.is-active):hover {
color: var(--el-menu-active-color);
}
.nav-bar-container {
background-color: var(--bg-color) !important;
}
.table-header {
.el-icon {
color: #fff;
}
}
.tabs-bar-container {
background-color: var(--bg-color) !important;
border-top: 1px solid var(--el-border-color-light) !important;
.tabs-action {
.el-icon {
color: #fff !important;
}
}
.fold-unfold {
color: #fff;
}
.el-tabs__item.is-active {
color: var(--el-color-primary);
background-color: var(--v-main-bg-color) !important;
.el-icon {
color: var(--el-color-primary) !important;
}
}
.el-tabs__item:not(.is_active):hover {
background-color: var(--v-main-bg-color) !important;
.el-icon {
color: var(--el-color-primary) !important;
}
}
}
}

View File

@ -0,0 +1,37 @@
// $--colors: (
// "primary": (
// "base": green,
// ),
// "success": (
// "base": #21ba45,
// ),
// "warning": (
// "base": #f2711c,
// ),
// "danger": (
// "base": #db2828,
// ),
// "error": (
// "base": #db2828,
// ),
// "info": (
// "base": #42b8dd,
// ),
// );
// You should use them in scss, because we calculate it by sass.
// comment next lines to use default color
// @forward "element-plus/theme-chalk/src/common/var.scss" with (
// // do not use same name, it will override.
// // $colors: $--colors,
// // $button-padding-horizontal: ("default": 50px)
// );
// if you want to import all
// @use "element-plus/theme-chalk/src/index.scss" as *;
// You can comment it to hide debug info.
// @debug $--colors;
// custom dark variables
@use "./dark.scss";

View File

@ -3,16 +3,24 @@
<div class="bar_wrap">
<h2>{{ title }}</h2>
<!-- <el-icon><UserFilled /></el-icon> -->
<el-switch
v-model="isDark"
inline-prompt
:active-icon="Moon"
:inactive-icon="Sunny"
class="dark_switch"
@change="setTheme"
/>
<el-button
type="info"
class="about_btn top_text"
class="about_btn"
link
@click="visible = true"
>
关于 <span class="new_version">{{ isNew ? `(新版本可用)` : '' }}</span>
</el-button>
<el-dropdown trigger="click">
<span class="username top_text"><el-icon><User /></el-icon> {{ user }}</span>
<span class="username"><el-icon><User /></el-icon> {{ user }}</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="handleLogout">
@ -45,7 +53,7 @@
<script setup>
import { ref, getCurrentInstance, computed } from 'vue'
import { User } from '@element-plus/icons-vue'
import { User, Sunny, Moon } from '@element-plus/icons-vue'
import packageJson from '../../package.json'
const { proxy: { $router, $store, $message } } = getCurrentInstance()
@ -54,11 +62,24 @@ let visible = ref(false)
let checkVersionErr = ref(false)
let currentVersion = ref(`v${ packageJson.version }`)
let latestVersion = ref(null)
// let isDark = ref(true)
let isNew = computed(() => {
return latestVersion.value && latestVersion.value !== currentVersion.value
let isNew = computed(() => latestVersion.value && latestVersion.value !== currentVersion.value)
let user = computed(() => $store.user)
let title = computed(() => $store.title)
let isDark = computed({
get: () => $store.isDark,
set: (value) => {
$store.setTheme({ isDark: value })
}
})
const handleLogout = () => {
$store.clearJwtToken()
$message({ type: 'success', message: '已安全退出', center: true })
$router.push('/login')
}
async function checkLatestVersion() {
const timeout = 3000
try {
@ -92,19 +113,6 @@ async function checkLatestVersion() {
checkLatestVersion()
let user = computed(() => {
return $store.user
})
let title = computed(() => {
return $store.title
})
const handleLogout = () => {
$store.clearJwtToken()
$message({ type: 'success', message: '已安全退出', center: true })
$router.push('/login')
}
</script>
<style lang="scss" scoped>
@ -125,16 +133,19 @@ const handleLogout = () => {
font-size: 18px;
margin-right: auto;
}
.username {
margin-left: 10px;
cursor: pointer;
.dark_switch {
margin-right: 15px;
}
.top_text {
.about_btn {
margin-right: 15px;
font-size: 14px;
.new_version {
color: red;
}
}
.username {
cursor: pointer;
}
}
.about_content {
h1 {

View File

@ -10,8 +10,6 @@ import api from './api'
import App from './app.vue'
import './assets/scss/reset.scss'
import './assets/scss/global.scss'
import './assets/scss/element-ui.scss'
import './assets/scss/animate.scss'
const app = createApp(App)
elementPlugins(app)

View File

@ -15,7 +15,8 @@ const useStore = defineStore({
HostStatusSocket: null,
user: localStorage.getItem('user') || null,
token: sessionStorage.getItem('token') || localStorage.getItem('token') || null,
title: ''
title: '',
isDark: false
}),
actions: {
async setJwtToken(token, isSession = true) {
@ -109,6 +110,26 @@ const useStore = defineStore({
socketInstance.on('connect_error', (message) => {
console.error('clients websocket 连接出错: ', message)
})
},
setTheme(isDark) {
// $store.setThemeConfig({ isDark: val })
const html = document.documentElement
if (isDark) html.setAttribute('class', 'dark')
else html.setAttribute('class', '')
localStorage.setItem('isDark', isDark)
this.$patch({ isDark })
},
setDefaultTheme() {
let isDark = false
if (localStorage.getItem('isDark')) {
isDark = localStorage.getItem('isDark') === 'true' ? true : false
} else {
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)')
const systemTheme = prefersDarkScheme.matches
// console.log('当前系统使用的是深色模式:', systemTheme ? '是' : '否')
isDark = systemTheme
}
this.setTheme(isDark)
}
}
})

View File

@ -52,7 +52,7 @@ onBeforeMount(async () => {
min-height: calc(100vh - 60px - 20px);
background-color: #fff;
border-radius: 6px;
margin: 10px;
margin: 0 10px 10px;
}
}
}

View File

@ -161,7 +161,6 @@ onMounted(async () => {
padding: 20px;
border-radius: 6px;
background-color: #ffffff;
border: 1px solid #ebedef;
h2 {
text-align: center;

View File

@ -40,7 +40,7 @@
</el-table-column>
<el-table-column prop="command" label="指令" show-overflow-tooltip>
<template #default="{ row }">
<span style="letter-spacing: 2px;background: rgba(227, 230, 235, 0.7);color: rgb(54, 52, 52);"> {{ row.command }} </span>
<span> {{ row.command }} </span>
</template>
</el-table-column>
<el-table-column prop="status" label="执行结果" show-overflow-tooltip>
@ -436,14 +436,13 @@ onActivated(async () => {
position: sticky;
top: 0;
z-index: 1;
background-color: #fff;
}
.detail_content_box {
max-height: 200px;
overflow: auto;
white-space: pre-line;
line-height: 1.1;
background: rgba(227, 230, 235, .7);
// background: rgba(227, 230, 235, .7);
padding: 25px;
border-radius: 3px;
}

View File

@ -146,7 +146,7 @@ const handleOnekey = async (row) => {
}
let defaultSortLocal = localStorage.getItem('host_table_sort')
defaultSortLocal = defaultSortLocal ? JSON.parse(defaultSortLocal) : { prop: 'index', order: 'ascending' }
defaultSortLocal = defaultSortLocal ? JSON.parse(defaultSortLocal) : { prop: 'index', order: null } // 'ascending' or 'descending'
let defaultSort = ref(defaultSortLocal)
const handleSortChange = (sortObj) => {

View File

@ -242,6 +242,9 @@ let hostFormClosed = () => {
}
.server_group_collapse {
:deep(.el-card__body) {
padding: 0;
}
:deep(.el-collapse-item__header) {
padding: 0 35px;
}

View File

@ -323,8 +323,7 @@ onBeforeUnmount(() => {
.info_container {
// border-top: 1px solid var(--el-border-color);
flex-shrink: 0;
overflow: scroll;
background-color: #fff; //#E0E2EF;
// overflow: scroll;
transition: all 0.15s;
// header {
@ -412,7 +411,7 @@ onBeforeUnmount(() => {
display: flex;
span {
color: #000;
color: #434343;
}
}
}

View File

@ -429,6 +429,7 @@ defineExpose({
:deep(.xterm-screen) {
padding: 0 0 0 10px;
border-radius: var(--el-border-radius-base);
// background-color: rgba(0, 0, 0, 0.6);
}
}
.terminal_command_history {

View File

@ -405,8 +405,6 @@ const handleInputCommand = async (command) => {
padding: 0 15px;
position: sticky;
top: 0;
border-bottom: 1px solid #fff;
// background-color: #fff;
background: var(--el-fill-color-light);
color: var(--el-text-color-regular);
z-index: 3;
@ -462,6 +460,7 @@ const handleInputCommand = async (command) => {
overflow: auto;
display: flex;
flex-direction: column;
border: var(--el-descriptions-table-border);
}
.terminals_sftp_wrap {

View File

@ -59,6 +59,13 @@ export default defineConfig({
deleteOriginFile: false
}),
],
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "@/assets/scss/element/index.scss" as *;'
}
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL(