feat: 接入两端用户名登录和独立学习空间 (#2)
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="basic-layout">
|
||||
<slot name="wrapper" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BasicLayout'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.basic-layout{
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
<template><section class="app-main"><router-view :key="$store.state.generation" /></section></template>
|
||||
<script>export default { name: 'AppMain' }</script>
|
||||
<style scoped>.app-main { min-height: calc(100vh - 56px); width: 100%; position: relative; overflow: hidden; background-color: #f0f2f5; }</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<div :class="classObj" class="app-wrapper">
|
||||
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside" />
|
||||
<aside class="sidebar-container">
|
||||
<div class="brand">{{ sidebar.opened ? 'LexGo 管理' : 'LG' }}</div>
|
||||
<el-menu default-active="/accounts" :collapse="!sidebar.opened" router>
|
||||
<el-menu-item index="/accounts"><span>账号管理</span></el-menu-item>
|
||||
</el-menu>
|
||||
</aside>
|
||||
<div class="main-container">
|
||||
<header class="navbar">
|
||||
<button class="menu-toggle" aria-label="切换导航" @click="$store.dispatch('app/toggleSideBar')"><hamburger :is-active="sidebar.opened" /></button>
|
||||
<span>账号管理</span>
|
||||
<div class="right-menu"><span>{{ $store.state.user?.username }}</span><el-button @click="logout">退出登录</el-button></div>
|
||||
</header>
|
||||
<app-main />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import ResizeMixin from './mixin/ResizeHandler'
|
||||
import Hamburger from '../components/Hamburger/index.vue'
|
||||
import AppMain from './components/AppMain.vue'
|
||||
import { session } from '../store'
|
||||
import { ElMessage } from 'element-plus'
|
||||
export default {
|
||||
name: 'MainLayout', components: { Hamburger, AppMain }, mixins: [ResizeMixin],
|
||||
computed: {
|
||||
...mapState({ sidebar: state => state.app.sidebar, device: state => state.app.device }),
|
||||
classObj() { return { hideSidebar: !this.sidebar.opened, openSidebar: this.sidebar.opened, withoutAnimation: this.sidebar.withoutAnimation, mobile: this.device === 'mobile' } }
|
||||
},
|
||||
watch: { '$store.state.user'(user) { if (!user) this.$router.replace('/login') } },
|
||||
methods: {
|
||||
handleClickOutside() { this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) },
|
||||
async logout() {
|
||||
try { await session.logout() } catch { ElMessage.warning('本地已退出;服务器尚未确认退出,原会话可能仍有效。') }
|
||||
this.$router.replace('/login')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@use '@/styles/mixin.scss' as *;
|
||||
.app-wrapper { @include clearfix; position: relative; height: 100%; width: 100%; &.mobile.openSidebar { position: fixed; top: 0; } }
|
||||
.drawer-bg { background: #000; opacity: .3; width: 100%; top: 0; height: 100%; position: absolute; z-index: 999; }
|
||||
.navbar { height: 56px; display: flex; align-items: center; gap: 12px; background: white; padding-right: 16px; box-shadow: 0 1px 4px #00152914; }
|
||||
.right-menu { margin-left: auto; display: flex; align-items: center; gap: 12px; }
|
||||
.brand { height: 64px; display: flex; align-items: center; justify-content: center; font-size: 18px; color: white; font-weight: 600; }
|
||||
.menu-toggle { background: transparent; border: 0; cursor: pointer; }
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
import store from '@/store'
|
||||
|
||||
const { body } = document
|
||||
const WIDTH = 992 // refer to Bootstrap's responsive design
|
||||
|
||||
export default {
|
||||
watch: {
|
||||
$route() {
|
||||
if (this.device === 'mobile' && this.sidebar.opened) {
|
||||
store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
window.addEventListener('resize', this.$_resizeHandler)
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('resize', this.$_resizeHandler)
|
||||
},
|
||||
mounted() {
|
||||
const isMobile = this.$_isMobile()
|
||||
if (isMobile) {
|
||||
store.dispatch('app/toggleDevice', 'mobile')
|
||||
store.dispatch('app/closeSideBar', { withoutAnimation: true })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// use $_ for mixins properties
|
||||
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
|
||||
$_isMobile() {
|
||||
const rect = body.getBoundingClientRect()
|
||||
return rect.width - 1 < WIDTH
|
||||
},
|
||||
$_resizeHandler() {
|
||||
if (!document.hidden) {
|
||||
const isMobile = this.$_isMobile()
|
||||
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
|
||||
|
||||
if (isMobile) {
|
||||
store.dispatch('app/closeSideBar', { withoutAnimation: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user