init
This commit is contained in:
3
src/layouts/EmptyLayout.vue
Normal file
3
src/layouts/EmptyLayout.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
46
src/layouts/components/VabAd/index.vue
Normal file
46
src/layouts/components/VabAd/index.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<div class="vab-ad">
|
||||
<el-carousel v-if="adList" :autoplay="true" :interval="3000" direction="vertical" height="30px" indicator-position="none">
|
||||
<el-carousel-item v-for="(item, index) in adList" :key="index">
|
||||
<el-tag type="warning">Ad</el-tag>
|
||||
<a :href="item.url" target="_blank">{{ item.title }}</a>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getList } from '@/api/ad'
|
||||
|
||||
export default {
|
||||
name: 'VabAd',
|
||||
data() {
|
||||
return {
|
||||
nodeEnv: process.env.NODE_ENV,
|
||||
adList: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
async fetchData() {
|
||||
const { data } = await getList()
|
||||
this.adList = data
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.vab-ad {
|
||||
height: 30px;
|
||||
padding-right: $base-padding;
|
||||
padding-left: $base-padding;
|
||||
margin-bottom: -20px;
|
||||
line-height: 30px;
|
||||
cursor: pointer;
|
||||
|
||||
a {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
107
src/layouts/components/VabAppMain/index.vue
Normal file
107
src/layouts/components/VabAppMain/index.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div v-if="routerView" class="app-main-container">
|
||||
<!-- <vab-github-corner /> -->
|
||||
<transition mode="out-in" name="fade-transform">
|
||||
<keep-alive :include="cachedRoutes" :max="keepAliveMaxNum">
|
||||
<router-view :key="key" class="app-main-height" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
<footer v-show="footerCopyright" class="footer-copyright">
|
||||
Copyright
|
||||
<vab-icon :icon="['fas', 'copyright']"></vab-icon>
|
||||
{{ titleName }} {{ fullYear }}
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import { copyright, footerCopyright, keepAliveMaxNum, title } from '@/config'
|
||||
|
||||
export default {
|
||||
name: 'VabAppMain',
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
fullYear: new Date().getFullYear(),
|
||||
copyright,
|
||||
title,
|
||||
keepAliveMaxNum,
|
||||
routerView: true,
|
||||
footerCopyright,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
visitedRoutes: 'tabsBar/visitedRoutes',
|
||||
device: 'settings/device',
|
||||
titleName: 'settings/title',
|
||||
}),
|
||||
cachedRoutes() {
|
||||
const cachedRoutesArr = []
|
||||
this.visitedRoutes.forEach((item) => {
|
||||
if (!item.meta.noKeepAlive) {
|
||||
cachedRoutesArr.push(item.name)
|
||||
}
|
||||
})
|
||||
return cachedRoutesArr
|
||||
},
|
||||
key() {
|
||||
return this.$route.path
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route: {
|
||||
handler(route) {
|
||||
if ('mobile' === this.device) this.foldSideBar()
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
const handleReloadRouterView = () => {
|
||||
this.routerView = false
|
||||
this.$nextTick(() => {
|
||||
this.routerView = true
|
||||
})
|
||||
};
|
||||
|
||||
//重载所有路由
|
||||
this.$baseEventBus.$on('reload-router-view', handleReloadRouterView)
|
||||
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
this.$baseEventBus.$off('reload-router-view', handleReloadRouterView);
|
||||
});
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
...mapActions({
|
||||
foldSideBar: 'settings/foldSideBar',
|
||||
}),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-main-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.vab-keel {
|
||||
margin: $base-padding;
|
||||
}
|
||||
|
||||
.app-main-height {
|
||||
min-height: $base-app-main-height;
|
||||
}
|
||||
|
||||
.footer-copyright {
|
||||
min-height: 55px;
|
||||
line-height: 55px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
text-align: center;
|
||||
border-top: 1px dashed $base-border-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
100
src/layouts/components/VabAvatar/index.vue
Normal file
100
src/layouts/components/VabAvatar/index.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<el-dropdown @command="handleCommand">
|
||||
<span class="avatar-dropdown">
|
||||
<!--<el-avatar class="user-avatar" :src="avatar"></el-avatar>-->
|
||||
<img :src="avatar" alt="" class="user-avatar" />
|
||||
<div class="user-name">
|
||||
{{ username }}
|
||||
<i class="el-icon-arrow-down el-icon--right"></i>
|
||||
</div>
|
||||
</span>
|
||||
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="github">github地址</el-dropdown-item>
|
||||
<el-dropdown-item command="gitee" divided>码云地址</el-dropdown-item>
|
||||
<el-dropdown-item command="pro" divided>pro付费版地址</el-dropdown-item>
|
||||
<el-dropdown-item command="plus" divided>plus付费版地址</el-dropdown-item>
|
||||
<el-dropdown-item command="shop" divided>shop-vite付费版地址</el-dropdown-item>
|
||||
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import { recordRoute } from '@/config'
|
||||
|
||||
export default {
|
||||
name: 'VabAvatar',
|
||||
computed: {
|
||||
...mapGetters({
|
||||
avatar: 'user/avatar',
|
||||
username: 'user/username',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
handleCommand(command) {
|
||||
switch (command) {
|
||||
case 'logout':
|
||||
this.logout()
|
||||
break
|
||||
case 'personalCenter':
|
||||
this.personalCenter()
|
||||
break
|
||||
case 'github':
|
||||
window.open('https://github.com/zxwk1998/vue-admin-better')
|
||||
break
|
||||
case 'gitee':
|
||||
window.open('https://gitee.com/chu1204505056/vue-admin-better')
|
||||
break
|
||||
case 'pro':
|
||||
window.open('https://vuejs-core.cn/admin-pro/')
|
||||
break
|
||||
case 'plus':
|
||||
window.open('https://vuejs-core.cn/admin-plus/')
|
||||
case 'shop':
|
||||
window.open('https://vuejs-core.cn/shop-vite/')
|
||||
}
|
||||
},
|
||||
personalCenter() {
|
||||
this.$router.push('/personalCenter/personalCenter')
|
||||
},
|
||||
logout() {
|
||||
this.$baseConfirm('您确定要退出' + this.$baseTitle + '吗?', null, async () => {
|
||||
await this.$store.dispatch('user/logout')
|
||||
if (recordRoute) {
|
||||
const fullPath = this.$route.fullPath
|
||||
this.$router.push(`/login?redirect=${fullPath}`)
|
||||
} else {
|
||||
this.$router.push('/login')
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.avatar-dropdown {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
justify-items: center;
|
||||
height: 50px;
|
||||
padding: 0;
|
||||
|
||||
.user-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
61
src/layouts/components/VabBreadcrumb/index.vue
Normal file
61
src/layouts/components/VabBreadcrumb/index.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<el-breadcrumb class="breadcrumb-container" separator=">">
|
||||
<el-breadcrumb-item v-for="item in list" :key="item.path">
|
||||
{{ item.meta.title }}
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'VabBreadcrumb',
|
||||
data() {
|
||||
return {
|
||||
list: this.getBreadcrumb(),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.list = this.getBreadcrumb()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getBreadcrumb() {
|
||||
return this.$route.matched.filter((item) => item.name && item.meta.title)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.breadcrumb-container {
|
||||
height: $base-nav-bar-height;
|
||||
font-size: $base-font-size-default;
|
||||
line-height: $base-nav-bar-height;
|
||||
|
||||
::v-deep {
|
||||
.el-breadcrumb__item {
|
||||
.el-breadcrumb__inner {
|
||||
a {
|
||||
display: flex;
|
||||
float: left;
|
||||
font-weight: normal;
|
||||
color: #515a6e;
|
||||
|
||||
i {
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
.el-breadcrumb__inner {
|
||||
a {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
85
src/layouts/components/VabLogo/index.vue
Normal file
85
src/layouts/components/VabLogo/index.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div :class="'logo-container-' + layout">
|
||||
<router-link to="/">
|
||||
<!-- 这里是logo变更的位置 -->
|
||||
<vab-remix-icon v-if="logo" :icon-class="logo" class="logo" />
|
||||
<span :class="{ 'hidden-xs-only': layout === 'horizontal' }" :title="title" class="title">
|
||||
{{ title }}
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'VabLogo',
|
||||
computed: {
|
||||
...mapGetters({
|
||||
logo: 'settings/logo',
|
||||
layout: 'settings/layout',
|
||||
title: 'settings/title',
|
||||
}),
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@mixin container {
|
||||
position: relative;
|
||||
height: $base-top-bar-height;
|
||||
overflow: hidden;
|
||||
line-height: $base-top-bar-height;
|
||||
background: $base-menu-background;
|
||||
}
|
||||
|
||||
@mixin logo {
|
||||
display: inline-block;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
margin-right: 3px;
|
||||
color: $base-title-color;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@mixin title {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
font-size: 24px;
|
||||
line-height: 55px;
|
||||
color: $base-title-color;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.logo-container-horizontal {
|
||||
@include container;
|
||||
|
||||
.logo {
|
||||
@include logo;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include title;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-container-vertical {
|
||||
@include container;
|
||||
|
||||
height: $base-logo-height;
|
||||
line-height: $base-logo-height;
|
||||
text-align: center;
|
||||
|
||||
.logo {
|
||||
@include logo;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include title;
|
||||
|
||||
max-width: calc(#{$base-left-menu-width} - 60px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
136
src/layouts/components/VabNavBar/index.vue
Normal file
136
src/layouts/components/VabNavBar/index.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div class="nav-bar-container">
|
||||
<el-row :gutter="15">
|
||||
<el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="4">
|
||||
<div class="left-panel">
|
||||
<i
|
||||
:class="collapse ? 'el-icon-s-unfold' : 'el-icon-s-fold'"
|
||||
:title="collapse ? '展开' : '收起'"
|
||||
class="fold-unfold"
|
||||
@click="handleCollapse"
|
||||
></i>
|
||||
<vab-breadcrumb class="hidden-xs-only" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :lg="12" :md="12" :sm="12" :xl="12" :xs="20">
|
||||
<div class="right-panel">
|
||||
<vab-error-log />
|
||||
<vab-full-screen-bar @refresh="refreshRoute" />
|
||||
<vab-theme-bar class="hidden-xs-only" />
|
||||
<vab-icon :icon="['fas', 'redo']" :pulse="pulse" title="重载所有路由" @click="refreshRoute" />
|
||||
<vab-avatar />
|
||||
<!-- <vab-icon
|
||||
title="退出系统"
|
||||
:icon="['fas', 'sign-out-alt']"
|
||||
@click="logout"
|
||||
/>-->
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'VabNavBar',
|
||||
data() {
|
||||
return {
|
||||
pulse: false,
|
||||
timeOutID: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
collapse: 'settings/collapse',
|
||||
visitedRoutes: 'tabsBar/visitedRoutes',
|
||||
device: 'settings/device',
|
||||
routes: 'routes/routes',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
changeCollapse: 'settings/changeCollapse',
|
||||
}),
|
||||
handleCollapse() {
|
||||
this.changeCollapse()
|
||||
},
|
||||
async refreshRoute() {
|
||||
this.$baseEventBus.$emit('reload-router-view')
|
||||
this.pulse = true
|
||||
this.timeOutID = setTimeout(() => {
|
||||
this.pulse = false
|
||||
}, 1000)
|
||||
},
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
clearTimeout(this.timeOutID);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.nav-bar-container {
|
||||
position: relative;
|
||||
height: $base-nav-bar-height;
|
||||
padding-right: $base-padding;
|
||||
padding-left: $base-padding;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
background: $base-color-white;
|
||||
box-shadow: $base-box-shadow;
|
||||
|
||||
.left-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
height: $base-nav-bar-height;
|
||||
|
||||
.fold-unfold {
|
||||
color: $base-color-gray;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.breadcrumb-container {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right-panel {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
height: $base-nav-bar-height;
|
||||
|
||||
::v-deep {
|
||||
svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
margin-right: 15px;
|
||||
font-size: $base-font-size-small;
|
||||
color: $base-color-gray;
|
||||
cursor: pointer;
|
||||
fill: $base-color-gray;
|
||||
}
|
||||
|
||||
button {
|
||||
svg {
|
||||
margin-right: 0;
|
||||
color: $base-color-white;
|
||||
cursor: pointer;
|
||||
fill: $base-color-white;
|
||||
}
|
||||
}
|
||||
|
||||
.el-badge {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
259
src/layouts/components/VabThemeBar/index.vue
Normal file
259
src/layouts/components/VabThemeBar/index.vue
Normal file
@@ -0,0 +1,259 @@
|
||||
<template>
|
||||
<span v-if="themeBar">
|
||||
<vab-icon :icon="['fas', 'palette']" title="主题配置" @click="handleOpenThemeBar" />
|
||||
<div class="theme-bar-setting">
|
||||
<div @click="handleOpenThemeBar">
|
||||
<vab-icon :icon="['fas', 'palette']" />
|
||||
<p>主题配置</p>
|
||||
</div>
|
||||
<div @click="handleGetCode">
|
||||
<vab-icon :icon="['fas', 'laptop-code']"></vab-icon>
|
||||
<p>拷贝源码</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer :visible.sync="drawerVisible" append-to-body direction="rtl" size="300px" title="主题配置">
|
||||
<el-scrollbar style="height: 80vh; overflow: hidden">
|
||||
<div class="el-drawer__body">
|
||||
<el-form ref="form" :model="theme" label-position="top">
|
||||
<el-form-item label="主题">
|
||||
<el-radio-group v-model="theme.name">
|
||||
<el-radio-button label="default">默认</el-radio-button>
|
||||
<el-radio-button label="green">绿荫草场</el-radio-button>
|
||||
<el-radio-button label="glory">荣耀典藏</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="布局">
|
||||
<el-radio-group v-model="theme.layout">
|
||||
<el-radio-button label="vertical">纵向布局</el-radio-button>
|
||||
<el-radio-button label="horizontal">横向布局</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="头部">
|
||||
<el-radio-group v-model="theme.header">
|
||||
<el-radio-button label="fixed">固定头部</el-radio-button>
|
||||
<el-radio-button label="noFixed">不固定头部</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="多标签">
|
||||
<el-radio-group v-model="theme.tabsBar">
|
||||
<el-radio-button label="true">开启</el-radio-button>
|
||||
<el-radio-button label="false">不开启</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<div class="el-drawer__footer">
|
||||
<el-button type="primary" @click="handleSaveTheme">保存</el-button>
|
||||
<el-button type="" @click="drawerVisible = false">取消</el-button>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import { layout as defaultLayout } from '@/config'
|
||||
|
||||
export default {
|
||||
name: 'VabThemeBar',
|
||||
data() {
|
||||
return {
|
||||
drawerVisible: false,
|
||||
theme: {
|
||||
name: 'default',
|
||||
layout: '',
|
||||
header: 'fixed',
|
||||
tabsBar: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
layout: 'settings/layout',
|
||||
header: 'settings/header',
|
||||
tabsBar: 'settings/tabsBar',
|
||||
themeBar: 'settings/themeBar',
|
||||
}),
|
||||
},
|
||||
created() {
|
||||
const handleTheme = () => {
|
||||
this.handleOpenThemeBar()
|
||||
}
|
||||
|
||||
this.$baseEventBus.$on('theme', handleTheme)
|
||||
const theme = localStorage.getItem('vue-admin-better-theme')
|
||||
if (null !== theme) {
|
||||
this.theme = JSON.parse(theme)
|
||||
this.handleSetTheme()
|
||||
} else {
|
||||
this.theme.layout = this.layout
|
||||
this.theme.header = this.header
|
||||
this.theme.tabsBar = this.tabsBar
|
||||
}
|
||||
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
this.$baseEventBus.$off('theme', handleTheme)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
changeLayout: 'settings/changeLayout',
|
||||
changeHeader: 'settings/changeHeader',
|
||||
changeTabsBar: 'settings/changeTabsBar',
|
||||
}),
|
||||
handleIsMobile() {
|
||||
return document.body.getBoundingClientRect().width - 1 < 992
|
||||
},
|
||||
handleOpenThemeBar() {
|
||||
this.drawerVisible = true
|
||||
},
|
||||
handleSetTheme() {
|
||||
let { name, layout, header, tabsBar } = this.theme
|
||||
localStorage.setItem(
|
||||
'vue-admin-better-theme',
|
||||
`{
|
||||
"name":"${name}",
|
||||
"layout":"${layout}",
|
||||
"header":"${header}",
|
||||
"tabsBar":"${tabsBar}"
|
||||
}`
|
||||
)
|
||||
if (!this.handleIsMobile()) this.changeLayout(layout)
|
||||
this.changeHeader(header)
|
||||
this.changeTabsBar(tabsBar)
|
||||
document.getElementsByTagName('body')[0].className = `vue-admin-better-theme-${name}`
|
||||
this.drawerVisible = false
|
||||
},
|
||||
handleSaveTheme() {
|
||||
this.handleSetTheme()
|
||||
},
|
||||
handleSetDfaultTheme() {
|
||||
let { name } = this.theme
|
||||
document.getElementsByTagName('body')[0].classList.remove(`vue-admin-better-theme-${name}`)
|
||||
localStorage.removeItem('vue-admin-better-theme')
|
||||
this.$refs['form'].resetFields()
|
||||
Object.assign(this.$data, this.$options.data())
|
||||
this.changeHeader(defaultLayout)
|
||||
this.theme.name = 'default'
|
||||
this.theme.layout = this.layout
|
||||
this.theme.header = this.header
|
||||
this.theme.tabsBar = this.tabsBar
|
||||
this.drawerVisible = false
|
||||
location.reload()
|
||||
},
|
||||
handleGetCode() {
|
||||
const url = 'https://github.com/zxwk1998/vue-admin-better/tree/master/src/views'
|
||||
let path = this.$route.path + '/index.vue'
|
||||
if (path === '/vab/menu1/menu1-1/menu1-1-1/index.vue') {
|
||||
path = '/vab/nested/menu1/menu1-1/menu1-1-1/index.vue'
|
||||
}
|
||||
if (path === '/vab/icon/awesomeIcon/index.vue') {
|
||||
path = '/vab/icon/index.vue'
|
||||
}
|
||||
if (path === '/vab/icon/remixIcon/index.vue') {
|
||||
path = '/vab/icon/remixIcon.vue'
|
||||
}
|
||||
if (path === '/vab/icon/colorfulIcon/index.vue') {
|
||||
path = '/vab/icon/colorfulIcon.vue'
|
||||
}
|
||||
if (path === '/vab/table/comprehensiveTable/index.vue') {
|
||||
path = '/vab/table/index.vue'
|
||||
}
|
||||
if (path === '/vab/table/inlineEditTable/index.vue') {
|
||||
path = '/vab/table/inlineEditTable.vue'
|
||||
}
|
||||
window.open(url + path)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@mixin right-bar {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
z-index: $base-z-index;
|
||||
width: 60px;
|
||||
min-height: 60px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background: $base-color-blue;
|
||||
border-radius: $base-border-radius;
|
||||
|
||||
> div {
|
||||
padding-top: 10px;
|
||||
border-bottom: 0 !important;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
& + div {
|
||||
border-top: 1px solid $base-color-white;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: $base-font-size-small;
|
||||
line-height: 30px;
|
||||
color: $base-color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.theme-bar-setting {
|
||||
@include right-bar;
|
||||
|
||||
top: calc((100vh - 110px) / 2);
|
||||
|
||||
::v-deep {
|
||||
svg:not(:root).svg-inline--fa {
|
||||
display: block;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
color: $base-color-white;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
display: block;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
font-size: 20px;
|
||||
color: $base-color-white;
|
||||
fill: $base-color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-drawer__body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.el-drawer__footer {
|
||||
border-top: 1px solid #dedede;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 10px 0 0 20px;
|
||||
height: 50px;
|
||||
}
|
||||
</style>
|
||||
<style lang="scss">
|
||||
.el-drawer__wrapper {
|
||||
outline: none !important;
|
||||
|
||||
* {
|
||||
outline: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.vab-color-picker {
|
||||
.el-color-dropdown__link-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
25
src/layouts/export.js
Normal file
25
src/layouts/export.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @author https://github.com/zxwk1998/vue-admin-better (不想保留author可删除)
|
||||
* @description 公共布局及样式自动引入
|
||||
*/
|
||||
|
||||
import Vue from 'vue'
|
||||
|
||||
const requireComponents = require.context('./components', true, /\.vue$/)
|
||||
requireComponents.keys().forEach((fileName) => {
|
||||
const componentConfig = requireComponents(fileName)
|
||||
const componentName = componentConfig.default.name
|
||||
Vue.component(componentName, componentConfig.default || componentConfig)
|
||||
})
|
||||
|
||||
const requireZxLayouts = require.context('layouts', true, /\.vue$/)
|
||||
requireZxLayouts.keys().forEach((fileName) => {
|
||||
const componentConfig = requireZxLayouts(fileName)
|
||||
const componentName = componentConfig.default.name
|
||||
Vue.component(componentName, componentConfig.default || componentConfig)
|
||||
})
|
||||
|
||||
const requireThemes = require.context('@/styles/themes', true, /\.scss$/)
|
||||
requireThemes.keys().forEach((fileName) => {
|
||||
require(`@/styles/themes/${fileName.slice(2)}`)
|
||||
})
|
||||
298
src/layouts/index.vue
Normal file
298
src/layouts/index.vue
Normal file
@@ -0,0 +1,298 @@
|
||||
<template>
|
||||
<div :class="classObj" class="vue-admin-better-wrapper">
|
||||
<div
|
||||
v-if="'horizontal' === layout"
|
||||
:class="{
|
||||
fixed: header === 'fixed',
|
||||
'no-tabs-bar': tabsBar === 'false' || tabsBar === false,
|
||||
}"
|
||||
class="layout-container-horizontal"
|
||||
>
|
||||
<div :class="header === 'fixed' ? 'fixed-header' : ''">
|
||||
<vab-top-bar />
|
||||
<div v-if="tabsBar === 'true' || tabsBar === true" :class="{ 'tag-view-show': tabsBar }">
|
||||
<div class="vab-main">
|
||||
<vab-tabs-bar />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vab-main main-padding">
|
||||
<!-- <vab-ad /> -->
|
||||
<vab-app-main />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
:class="{
|
||||
fixed: header === 'fixed',
|
||||
'no-tabs-bar': tabsBar === 'false' || tabsBar === false,
|
||||
}"
|
||||
class="layout-container-vertical"
|
||||
>
|
||||
<div v-if="device === 'mobile' && collapse === false" class="mask" @click="handleFoldSideBar" />
|
||||
<vab-side-bar />
|
||||
<div :class="collapse ? 'is-collapse-main' : ''" class="vab-main">
|
||||
<div :class="header === 'fixed' ? 'fixed-header' : ''">
|
||||
<vab-nav-bar />
|
||||
<vab-tabs-bar v-if="tabsBar === 'true' || tabsBar === true" />
|
||||
</div>
|
||||
<!-- <vab-ad /> -->
|
||||
<vab-app-main />
|
||||
</div>
|
||||
</div>
|
||||
<el-backtop />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import { tokenName } from '@/config'
|
||||
|
||||
export default {
|
||||
name: 'Layout',
|
||||
data() {
|
||||
return {
|
||||
oldLayout: '',
|
||||
controller: new window.AbortController(),
|
||||
timeOutID: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
layout: 'settings/layout',
|
||||
tabsBar: 'settings/tabsBar',
|
||||
collapse: 'settings/collapse',
|
||||
header: 'settings/header',
|
||||
device: 'settings/device',
|
||||
}),
|
||||
classObj() {
|
||||
return {
|
||||
mobile: this.device === 'mobile',
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
this.controller.abort()
|
||||
clearTimeout(this.timeOutID)
|
||||
},
|
||||
mounted() {
|
||||
this.oldLayout = this.layout
|
||||
const userAgent = navigator.userAgent
|
||||
const isMobile = this.handleIsMobile()
|
||||
if (isMobile) {
|
||||
if (isMobile) {
|
||||
//横向布局时如果是手机端访问那么改成纵向版
|
||||
this.$store.dispatch('settings/changeLayout', 'vertical')
|
||||
} else {
|
||||
this.$store.dispatch('settings/changeLayout', this.oldLayout)
|
||||
}
|
||||
this.$store.dispatch('settings/toggleDevice', 'mobile')
|
||||
this.timeOutID = setTimeout(() => {
|
||||
this.$store.dispatch('settings/foldSideBar')
|
||||
}, 2000)
|
||||
} else {
|
||||
this.$store.dispatch('settings/openSideBar')
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
window.addEventListener(
|
||||
'storage',
|
||||
(e) => {
|
||||
if (e.key === tokenName || e.key === null) window.location.reload()
|
||||
if (e.key === tokenName && e.value === null) window.location.reload()
|
||||
},
|
||||
{
|
||||
capture: false,
|
||||
signal: this.controller?.signal,
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
...mapActions({
|
||||
handleFoldSideBar: 'settings/foldSideBar',
|
||||
}),
|
||||
handleIsMobile() {
|
||||
return document.body.getBoundingClientRect().width - 1 < 992
|
||||
},
|
||||
handleResize() {
|
||||
if (!document.hidden) {
|
||||
const isMobile = this.handleIsMobile()
|
||||
if (isMobile) {
|
||||
//横向布局时如果是手机端访问那么改成纵向版
|
||||
this.$store.dispatch('settings/changeLayout', 'vertical')
|
||||
} else {
|
||||
this.$store.dispatch('settings/changeLayout', this.oldLayout)
|
||||
}
|
||||
|
||||
this.$store.dispatch('settings/toggleDevice', isMobile ? 'mobile' : 'desktop')
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@mixin fix-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: $base-z-index - 2;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.vue-admin-better-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.layout-container-horizontal {
|
||||
position: relative;
|
||||
|
||||
&.fixed {
|
||||
padding-top: calc(#{$base-top-bar-height} + #{$base-tabs-bar-height});
|
||||
}
|
||||
|
||||
&.fixed.no-tabs-bar {
|
||||
padding-top: $base-top-bar-height;
|
||||
}
|
||||
|
||||
::v-deep {
|
||||
.vab-main {
|
||||
width: 88%;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
@include fix-header;
|
||||
}
|
||||
|
||||
.tag-view-show {
|
||||
background: $base-color-white;
|
||||
box-shadow: $base-box-shadow;
|
||||
}
|
||||
|
||||
.nav-bar-container {
|
||||
.fold-unfold {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.main-padding {
|
||||
.app-main-container {
|
||||
margin-top: $base-padding;
|
||||
margin-bottom: $base-padding;
|
||||
background: $base-color-white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-container-vertical {
|
||||
position: relative;
|
||||
|
||||
.mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $base-z-index - 1;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
background: #000;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&.fixed {
|
||||
padding-top: calc(#{$base-nav-bar-height} + #{$base-tabs-bar-height});
|
||||
}
|
||||
|
||||
&.fixed.no-tabs-bar {
|
||||
padding-top: $base-nav-bar-height;
|
||||
}
|
||||
|
||||
.vab-main {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
margin-left: $base-left-menu-width;
|
||||
background: #f6f8f9;
|
||||
transition: $base-transition;
|
||||
|
||||
::v-deep {
|
||||
.fixed-header {
|
||||
@include fix-header;
|
||||
|
||||
left: $base-left-menu-width;
|
||||
width: $base-right-content-width;
|
||||
box-shadow: $base-box-shadow;
|
||||
transition: $base-transition;
|
||||
}
|
||||
|
||||
.nav-bar-container {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tabs-bar-container {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.app-main-container {
|
||||
width: calc(100% - #{$base-padding} - #{$base-padding});
|
||||
margin: $base-padding auto;
|
||||
background: $base-color-white;
|
||||
border-radius: $base-border-radius;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-collapse-main {
|
||||
margin-left: $base-left-menu-width-min;
|
||||
|
||||
::v-deep {
|
||||
.fixed-header {
|
||||
left: $base-left-menu-width-min;
|
||||
width: calc(100% - 65px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端开始 */
|
||||
&.mobile {
|
||||
::v-deep {
|
||||
.el-pager,
|
||||
.el-pagination__jump {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-container-vertical {
|
||||
.el-scrollbar.side-bar-container.is-collapse {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.vab-main {
|
||||
width: 100%;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.vab-main {
|
||||
.fixed-header {
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端结束 */
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user