feat: implement dark theme with Element Plus

feature/20260628/refactor-frontend-modernization
Lxy 3 weeks ago
parent aaeba22f66
commit 6762d22d8e

@ -11,8 +11,14 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElInput: typeof import('element-plus/es')['ElInput']
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Sidebar: typeof import('./components/layout/Sidebar/index.vue')['default']
}
}

@ -0,0 +1,114 @@
<template>
<aside class="sidebar">
<div class="sidebar-header">
<h2 class="logo">RuoYi</h2>
</div>
<nav class="sidebar-menu">
<div
v-for="item in menuItems"
:key="item.path"
class="menu-item"
:class="{ active: currentPath === item.path }"
@click="navigate(item.path)"
>
<el-icon v-if="item.icon">
<component :is="item.icon" />
</el-icon>
<span class="menu-text">{{ item.name }}</span>
</div>
</nav>
</aside>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import {
HomeFilled,
Setting,
User,
Menu,
Document,
} from '@element-plus/icons-vue'
const router = useRouter()
const route = useRoute()
const currentPath = ref(route.path)
const menuItems = [
{ path: '/dashboard', name: '仪表盘', icon: HomeFilled },
{ path: '/system', name: '系统管理', icon: Setting },
{ path: '/monitor', name: '系统监控', icon: Menu },
{ path: '/log', name: '日志管理', icon: Document },
{ path: '/user', name: '用户管理', icon: User },
]
function navigate(path: string) {
currentPath.value = path
router.push(path)
}
</script>
<style scoped lang="scss">
.sidebar {
width: var(--sidebar-width);
height: 100vh;
background: var(--bg1);
border-right: 1px solid var(--bd);
display: flex;
flex-direction: column;
transition: width var(--transition-normal);
.sidebar-header {
height: var(--header-height);
display: flex;
align-items: center;
padding: 0 16px;
border-bottom: 1px solid var(--bd);
.logo {
font-size: 18px;
font-weight: 600;
color: var(--blu);
margin: 0;
}
}
.sidebar-menu {
flex: 1;
padding: 8px 0;
overflow-y: auto;
}
.menu-item {
display: flex;
align-items: center;
padding: 12px 16px;
cursor: pointer;
color: var(--t2);
transition: all var(--transition-fast);
gap: 12px;
&:hover {
background: var(--bg3);
color: var(--t1);
}
&.active {
background: rgba(88, 166, 255, 0.1);
color: var(--blu);
border-left: 3px solid var(--blu);
}
.el-icon {
font-size: 18px;
}
.menu-text {
font-size: 14px;
white-space: nowrap;
}
}
}
</style>

@ -0,0 +1,12 @@
import { ref } from 'vue'
export function useTheme() {
const isDark = ref(true)
function toggleTheme() {
isDark.value = !isDark.value
document.documentElement.setAttribute('theme', isDark.value ? 'dark' : 'light')
}
return { isDark, toggleTheme }
}

@ -1,13 +1,15 @@
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import 'element-plus/theme-chalk/dark/css-vars.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import '@/styles/variables.scss'
import '@/styles/index.scss'
const app = createApp(App)
@ -18,6 +20,6 @@ for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.use(createPinia())
app.use(router)
app.use(ElementPlus)
app.use(ElementPlus, { locale: zhCn })
app.mount('#app')

@ -0,0 +1,28 @@
// Global styles (variables.scss is auto-imported via vite.config.ts additionalData)
//
body {
margin: 0;
background-color: var(--bg0);
color: var(--t1);
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
#app {
min-height: 100vh;
}
//
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-thumb {
background: var(--bd);
border-radius: 3px;
}
::-webkit-scrollbar-track {
background: var(--bg1);
}

@ -1,41 +1,56 @@
// demo-dashboard-heatmap.html
:root {
// Background colors
--bg0: #0d1117;
--bg1: #161b22;
--bg2: #1c2333;
--bg3: #2a3040;
//
--bg0: #0d1117; //
--bg1: #161b22; //
--bg2: #1c2333; //
--bg3: #2a3040; // hover
// Border color
//
--bd: #30363d;
// Text colors
--t1: #e6edf3;
--t2: #8b949e;
--tm: #6e7681;
//
--t1: #e6edf3; //
--t2: #8b949e; //
--tm: #6e7681; //
// Accent colors
--red: #f85149;
--grn: #3fb950;
--org: #d29922;
--blu: #58a6ff;
//
--red: #f85149; //
--grn: #3fb950; // 绿
--org: #d29922; //
--blu: #58a6ff; //
// Semantic colors
//
--color-success: var(--grn);
--color-warning: var(--org);
--color-danger: var(--red);
--color-info: var(--blu);
// Layout
//
--header-height: 56px;
--sidebar-width: 220px;
--sidebar-collapsed-width: 64px;
// Transitions
//
--transition-fast: 0.15s ease;
--transition-normal: 0.3s ease;
}
// Global styles
// Element Plus
.el-button--primary {
--el-button-bg-color: var(--blu);
--el-button-border-color: var(--blu);
}
.el-table {
--el-table-bg-color: var(--bg2);
--el-table-tr-bg-color: var(--bg2);
--el-table-header-bg-color: var(--bg1);
--el-table-text-color: var(--t1);
--el-table-border-color: var(--bd);
}
//
* {
box-sizing: border-box;
}
@ -47,7 +62,7 @@ html, body {
color: var(--t1);
}
// Scrollbar styling
//
::-webkit-scrollbar {
width: 8px;
height: 8px;

@ -27,6 +27,13 @@ export default defineConfig(({ mode }) => {
'@': resolve(__dirname, 'src'),
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";\n`
}
}
},
server: {
port: 80,
host: true,
@ -51,10 +58,16 @@ export default defineConfig(({ mode }) => {
chunkSizeWarningLimit: 1500,
rollupOptions: {
output: {
manualChunks: {
'element-plus': ['element-plus'],
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'echarts': ['echarts', 'vue-echarts'],
manualChunks(id) {
if (id.includes('element-plus')) {
return 'element-plus'
}
if (id.includes('node_modules') && (id.includes('vue') || id.includes('vue-router') || id.includes('pinia'))) {
return 'vue-vendor'
}
if (id.includes('echarts')) {
return 'echarts'
}
},
},
},

Loading…
Cancel
Save