| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- <script setup lang="ts">
- import {useLoginUserStore, useMetaStore} from "../store";
- import {computed, onMounted, ref, watch} from "vue";
- import router, {adminRoutes} from "../router";
- const { websiteMeta, fetchMeta } = useMetaStore()
- const loginUserStore = useLoginUserStore()
- const login = computed(()=>loginUserStore.loginUser.isLogin)
- const loginHandler = () => {
- router.push('/login')
- }
- const logoutHandler = () => {
- loginUserStore.logoutUser()
- router.push('/')
- }
- const menus = computed(() => {
- const userRole = loginUserStore.loginUser.user?.role
- return adminRoutes[0].children?.filter(child => {
- if (child.meta?.hidden) return false
- const allowedRoles = child.meta?.roles as string[] | undefined
- return !allowedRoles || allowedRoles.includes(userRole ?? '')
- })
- })
- const isCollapse = ref(false)
- const mobileMenuVisible = ref(false)
- onMounted(() => {
- fetchMeta()
- })
- watch(() => websiteMeta.title, (val) => {
- document.title = val
- }, { immediate: true })
- watch(() => websiteMeta.logo, (val) => {
- let link = document.querySelector<HTMLLinkElement>("link[rel*='icon']")
- if (!link) {
- link = document.createElement('link')
- link.rel = 'icon'
- document.head.appendChild(link)
- }
- link.href = val
- }, { immediate: true })
- const handleOpen = (key: string, keyPath: string[]) => {
- console.log(key, keyPath)
- }
- const handleClose = (key: string, keyPath: string[]) => {
- console.log(key, keyPath)
- }
- </script>
- <template>
- <el-container>
- <el-header class="header">
- <div class="header-left">
- <el-button class="menu-toggle" @click="mobileMenuVisible = true">☰</el-button>
- <div class="brand" @click="router.push('/')">
- <img :src="websiteMeta.logo" alt="logo" class="logo">
- <h2 class="title">{{ websiteMeta.title }}</h2>
- </div>
- </div>
- <h1 class="page-title">后台管理</h1>
- <div class="header-right">
- <template v-if="login">
- <el-dropdown placement="bottom">
- <el-button> {{ loginUserStore.loginUser?.user?.name }} </el-button>
- <template #dropdown>
- <el-dropdown-menu>
- <el-dropdown-item>个人信息</el-dropdown-item>
- <el-dropdown-item @click="logoutHandler">退出</el-dropdown-item>
- </el-dropdown-menu>
- </template>
- </el-dropdown>
- </template>
- <template v-else>
- <el-button type="primary" size="small" @click="loginHandler">登录</el-button>
- <el-button size="small">注册</el-button>
- </template>
- </div>
- </el-header>
- <el-container class="main-container">
- <el-aside class="menu">
- <el-radio-group v-model="isCollapse" style="margin-bottom: 20px;display: flex;justify-content: center">
- <el-radio-button :value="false">expand</el-radio-button>
- <el-radio-button :value="true">collapse</el-radio-button>
- </el-radio-group>
- <el-menu
- router
- class="el-menu-vertical"
- :collapse="isCollapse"
- @open="handleOpen"
- @close="handleClose"
- >
- <template v-for="menu in menus" :key="menu.path">
- <el-sub-menu v-if="menu.children?.length" :index="menu.path">
- <template #title>
- <el-icon><Component :is="menu.meta?.icon"/></el-icon>
- <span>{{menu.meta?.title}}</span>
- </template>
- <el-menu-item v-for="child in menu.children" :key="child.path" :index="child.path">
- <template #title>{{child.meta?.title}}</template>
- </el-menu-item>
- </el-sub-menu>
- <el-menu-item v-else :index="menu.path">
- <el-icon><Component :is="menu.meta?.icon"/></el-icon>
- <template #title>{{menu.meta?.title}}</template>
- </el-menu-item>
- </template>
- </el-menu>
- </el-aside>
- <el-drawer
- v-model="mobileMenuVisible"
- direction="ltr"
- size="auto"
- :with-header="false"
- >
- <el-menu router @select="mobileMenuVisible = false">
- <template v-for="menu in menus" :key="menu.path">
- <el-sub-menu v-if="menu.children?.length" :index="menu.path">
- <template #title>
- <el-icon><Component :is="menu.meta?.icon"/></el-icon>
- <span>{{menu.meta?.title}}</span>
- </template>
- <el-menu-item v-for="child in menu.children" :key="child.path" :index="child.path">
- <template #title>{{child.meta?.title}}</template>
- </el-menu-item>
- </el-sub-menu>
- <el-menu-item v-else :index="menu.path">
- <el-icon><Component :is="menu.meta?.icon"/></el-icon>
- <template #title>{{menu.meta?.title}}</template>
- </el-menu-item>
- </template>
- </el-menu>
- </el-drawer>
- <el-main>
- <router-view/>
- </el-main>
- </el-container>
- </el-container>
- </template>
- <style scoped>
- .el-container {
- height: 100vh;
- }
- .header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 20px;
- margin-bottom: 10px;
- border-bottom: 1px solid
- }
- .header-left {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .brand {
- display: flex;
- align-items: center;
- gap: 10px;
- cursor: pointer;
- }
- .logo {
- width: 40px;
- height: 40px;
- }
- .title {
- margin: 0;
- font-size: 20px;
- }
- .header-right {
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .menu{
- width: 200px;
- }
- .main-container {
- flex: 1;
- }
- .el-menu-vertical-demo:not(.el-menu--collapse) {
- width: 200px;
- min-height: 400px;
- }
- .menu-toggle {
- display: none;
- }
- .page-title {
- font-size: 20px;
- }
- @media (max-width: 768px) {
- .menu {
- display: none;
- }
- .menu-toggle {
- display: inline-flex;
- }
- .header {
- padding: 0 10px;
- }
- .header-left .title {
- font-size: 16px;
- }
- .logo {
- width: 32px;
- height: 32px;
- }
- .page-title {
- font-size: 16px;
- }
- }
- </style>
|