✨ 支持暗黑主题切换
This commit is contained in:
parent
ad0984bf64
commit
23e0140544
@ -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>
|
||||
|
18
web/src/assets/scss/animate.scss
vendored
18
web/src/assets/scss/animate.scss
vendored
@ -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;
|
||||
}
|
@ -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;
|
||||
// }
|
146
web/src/assets/scss/element/dark.scss
Normal file
146
web/src/assets/scss/element/dark.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
37
web/src/assets/scss/element/index.scss
Normal file
37
web/src/assets/scss/element/index.scss
Normal 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";
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -52,7 +52,7 @@ onBeforeMount(async () => {
|
||||
min-height: calc(100vh - 60px - 20px);
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
margin: 10px;
|
||||
margin: 0 10px 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,6 @@ onMounted(async () => {
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #ebedef;
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
@ -242,6 +242,9 @@ let hostFormClosed = () => {
|
||||
}
|
||||
|
||||
.server_group_collapse {
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
:deep(.el-collapse-item__header) {
|
||||
padding: 0 35px;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user