vue cli3 改造完成

This commit is contained in:
dqjdda
2019-11-20 21:12:47 +08:00
parent 2e7cce299c
commit e096447c2e
267 changed files with 3678 additions and 3130 deletions

View File

@@ -2,11 +2,11 @@
<section class="app-main">
<transition name="fade-transform" mode="out-in">
<keep-alive :include="cachedViews">
<router-view :key="key"/>
<router-view :key="key" />
</keep-alive>
</transition>
<div v-if="$store.state.settings.showFooter" id="el-main-footer">
<span v-html="$store.state.settings.footerTxt"/>
<span v-html="$store.state.settings.footerTxt" />
<span> </span>
<a href="http://www.beian.miit.gov.cn" target="_blank">{{ $store.state.settings.caseNumber }}</a>
</div>
@@ -21,35 +21,42 @@ export default {
return this.$store.state.tagsView.cachedViews
},
key() {
return this.$route.fullPath
return this.$route.path
}
}
}
</script>
<style lang="scss" scoped>
.app-main {
/* 50= navbar 50 */
min-height: calc(100vh - 50px);
width: 100%;
position: relative;
overflow: hidden;
}
.fixed-header+.app-main {
padding-top: 50px;
}
.hasTagsView {
.app-main {
z-index: 88;
/*84 = navbar + tags-view = 50 +34 */
/* 84 = navbar + tags-view = 50 + 34 */
min-height: calc(100vh - 84px);
width: 100%;
position: relative;
overflow: hidden;
padding-bottom: 12px;
}
.fixed-header+.app-main {
padding-top: 50px;
}
.hasTagsView {
.app-main {
min-height: calc(100vh - 84px);
}
.fixed-header+.app-main {
padding-top: 85px;
}
padding-top: 84px;
}
}
</style>
<style lang="scss">
// fix css style bug in open el-dialog
.el-popup-parent--hidden {
.fixed-header {
padding-right: 15px;
}
}
</style>

View File

@@ -1,23 +1,25 @@
<template>
<div class="navbar">
<hamburger :toggle-click="toggleSideBar" :is-active="sidebar.opened" class="hamburger-container"/>
<breadcrumb class="breadcrumb-container"/>
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
<div class="right-menu">
<template v-if="device!=='mobile'">
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<Github class="screenfull right-menu-item"/>
<search id="header-search" class="right-menu-item" />
<screenfull id="screenfull" class="right-menu-item hover-effect" />
<el-tooltip content="Global Size" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" />
</el-tooltip>
</template>
<template v-if="device!=='mobile'">
<el-tooltip content="全屏" effect="dark" placement="bottom">
<screenfull class="screenfull right-menu-item"/>
</el-tooltip>
</template>
<el-dropdown class="avatar-container right-menu-item" trigger="click">
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
<div class="avatar-wrapper">
<img :src="user.avatar ? baseApi + '/avatar/' + user.avatar : Avatar" class="user-avatar">
<i class="el-icon-caret-bottom"/>
<i class="el-icon-caret-bottom" />
</div>
<el-dropdown-menu slot="dropdown">
<a target="_blank" href="https://docs.auauz.net/">
@@ -51,14 +53,17 @@ import { mapGetters } from 'vuex'
import Breadcrumb from '@/components/Breadcrumb'
import Hamburger from '@/components/Hamburger'
import Screenfull from '@/components/Screenfull'
import Github from '@/components/Github'
import Avatar from '@/assets/avatar/avatar.png'
import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import Avatar from '@/assets/images/avatar.png'
export default {
components: {
Breadcrumb,
Hamburger,
Screenfull,
Github
SizeSelect,
Search
},
data() {
return {
@@ -69,23 +74,26 @@ export default {
computed: {
...mapGetters([
'sidebar',
'user',
'device',
'user',
'baseApi'
]),
show: {
get() {
return this.$store.state.settings.showRightPanel
return this.$store.state.settings.showSettings
},
set(val) {
this.$store.dispatch('changeSetting', {
key: 'showRightPanel',
this.$store.dispatch('settings/changeSetting', {
key: 'showSettings',
value: val
})
}
}
},
methods: {
toggleSideBar() {
this.$store.dispatch('app/toggleSideBar')
},
open() {
this.$confirm('确定注销并退出系统吗?', '提示', {
confirmButtonText: '确定',
@@ -95,77 +103,95 @@ export default {
this.logout()
})
},
toggleSideBar() {
this.$store.dispatch('ToggleSideBar')
},
logout() {
this.dialogVisible = false
this.$store.dispatch('LogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug
location.reload()
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.navbar {
height: 50px;
<style lang="scss" scoped>
.navbar {
height: 50px;
overflow: hidden;
position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08);
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;
cursor: pointer;
transition: background .3s;
-webkit-tap-highlight-color:transparent;
&:hover {
background: rgba(0, 0, 0, .025)
}
}
.breadcrumb-container {
float: left;
}
.errLog-container {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
height: 100%;
line-height: 50px;
border-radius: 0px !important;
.hamburger-container {
line-height: 58px;
height: 50px;
float: left;
padding: 0 10px;
&:focus {
outline: none;
}
.breadcrumb-container{
float: left;
}
.errLog-container {
.right-menu-item {
display: inline-block;
vertical-align: top;
}
.right-menu {
float: right;
padding: 0 8px;
height: 100%;
&:focus{
outline: none;
font-size: 18px;
color: #5a5e66;
vertical-align: text-bottom;
&.hover-effect {
cursor: pointer;
transition: background .3s;
&:hover {
background: rgba(0, 0, 0, .025)
}
}
.right-menu-item {
display: inline-block;
margin: 0 8px;
}
.screenfull {
height: 20px;
}
.international{
vertical-align: top;
}
.theme-switch {
vertical-align: 15px;
}
.avatar-container {
height: 50px;
margin-right: 30px;
.avatar-wrapper {
margin-top: 5px;
position: relative;
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
.avatar-container {
margin-right: 30px;
.avatar-wrapper {
margin-top: 5px;
position: relative;
.user-avatar {
cursor: pointer;
width: 40px;
height: 40px;
border-radius: 10px;
}
.el-icon-caret-bottom {
cursor: pointer;
position: absolute;
right: -20px;
top: 25px;
font-size: 12px;
}
}
}
}
}
</style>

View File

@@ -1,33 +1,42 @@
<template>
<div class="drawer-container">
<div>
<h3 class="drawer-title">系统布局</h3>
<h3 class="drawer-title">系统布局</h3>
<div class="drawer-item">
<span>显示 Logo</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" />
<span>主题颜色</span>
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
</div>
<div class="drawer-item">
<span>固定 Header</span>
<el-switch v-model="fixedHeader" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>开启 Tags-Views</span>
<span>打开标签</span>
<el-switch v-model="tagsView" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>显示 SettingButton</span>
<el-switch v-model="settingBtn" class="drawer-switch" />
<span>固定头部</span>
<el-switch v-model="fixedHeader" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>菜单 UniqueOpened</span>
<span>显示LOGO</span>
<el-switch v-model="sidebarLogo" class="drawer-switch" />
</div>
<div class="drawer-item">
<span>菜单UniqueOpened</span>
<el-switch v-model="uniqueOpened" class="drawer-switch" />
</div>
</div>
</div>
</template>
<script>
import ThemePicker from '@/components/ThemePicker'
export default {
components: { ThemePicker },
data() {
return {}
},
@@ -37,7 +46,7 @@ export default {
return this.$store.state.settings.fixedHeader
},
set(val) {
this.$store.dispatch('changeSetting', {
this.$store.dispatch('settings/changeSetting', {
key: 'fixedHeader',
value: val
})
@@ -48,7 +57,7 @@ export default {
return this.$store.state.settings.tagsView
},
set(val) {
this.$store.dispatch('changeSetting', {
this.$store.dispatch('settings/changeSetting', {
key: 'tagsView',
value: val
})
@@ -59,34 +68,31 @@ export default {
return this.$store.state.settings.sidebarLogo
},
set(val) {
this.$store.dispatch('changeSetting', {
this.$store.dispatch('settings/changeSetting', {
key: 'sidebarLogo',
value: val
})
}
},
settingBtn: {
get() {
return this.$store.state.settings.settingBtn
},
set(val) {
this.$store.dispatch('changeSetting', {
key: 'settingBtn',
value: val
})
}
},
uniqueOpened: {
get() {
return this.$store.state.settings.uniqueOpened
},
set(val) {
this.$store.dispatch('changeSetting', {
this.$store.dispatch('settings/changeSetting', {
key: 'uniqueOpened',
value: val
})
}
}
},
methods: {
themeChange(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'theme',
value: val
})
}
}
}
</script>

View File

@@ -5,16 +5,16 @@ export default {
}
},
mounted() {
// In order to fix the click on menu on the ios device will trigger the mouseeleave bug
// In order to fix the click on menu on the ios device will trigger the mouseleave bug
// https://github.com/PanJiaChen/vue-element-admin/issues/1135
this.fixBugIniOS()
},
methods: {
fixBugIniOS() {
const $submenu = this.$refs.submenu
if ($submenu) {
const handleMouseleave = $submenu.handleMouseleave
$submenu.handleMouseleave = (e) => {
const $subMenu = this.$refs.subMenu
if ($subMenu) {
const handleMouseleave = $subMenu.handleMouseleave
$subMenu.handleMouseleave = (e) => {
if (this.device === 'mobile') {
return
}

View File

@@ -1,13 +1,13 @@
<template>
<!-- eslint-disable vue/require-component-is-->
<!-- eslint-disable vue/require-component-is -->
<component v-bind="linkProps(to)">
<slot/>
<slot />
</component>
</template>
<script>
import { isExternal } from '@/utils'
import { isExternal } from '@/utils/validate'
export default {
props: {
@@ -17,11 +17,8 @@ export default {
}
},
methods: {
isExternalLink(routePath) {
return isExternal(routePath)
},
linkProps(url) {
if (this.isExternalLink(url)) {
if (isExternal(url)) {
return {
is: 'a',
href: url,

View File

@@ -1,39 +1,81 @@
<template>
<el-menu-item v-if="sidebarLogo" :class="{'submenu-title-noDropdown':isCollapse}" index="0" style="pointer-events: none;">
<!-- 缩小时显示的logo可以自定义这里直接使用图标库中的 -->
<svg-icon v-if="isCollapse" icon-class="run" />
<!--正常状态下显示的可以使用本地的logoImg-->
<div class="logo-con">
<img src="https://aurora-1255840532.cos.ap-chengdu.myqcloud.com/logo.png">
<!--<img :src="logoImg">-->
</div>
</el-menu-item>
<div class="sidebar-logo-container" :class="{'collapse':collapse}">
<transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo">
<h1 v-else class="sidebar-title">{{ title }} </h1>
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo">
<h1 class="sidebar-title">{{ title }} </h1>
</router-link>
</transition>
</div>
</template>
<script>
// import logoImg from '@/assets/logo/logo.png'
import { mapState } from 'vuex'
export default {
name: 'Logo',
name: 'SidebarLogo',
props: {
isCollapse: {
collapse: {
type: Boolean,
default: false
required: true
}
},
data() {
return {
// logoImg: logoImg
title: 'EL-ADMIN 后台管理',
logo: 'https://wpimg.wallstcn.com/69a1c46c-eb1c-4b46-8bd4-e9e686ef5251.png'
}
},
computed: {
...mapState({
sidebarLogo: state => state.settings.sidebarLogo
})
}
}
</script>
<style scoped>
<style lang="scss" scoped>
.sidebarLogoFade-enter-active {
transition: opacity 1.5s;
}
.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
opacity: 0;
}
.sidebar-logo-container {
position: relative;
width: 100%;
height: 50px;
line-height: 50px;
text-align: center;
overflow: hidden;
& .sidebar-logo-link {
height: 100%;
width: 100%;
& .sidebar-logo {
width: 32px;
height: 32px;
vertical-align: middle;
margin-right: 12px;
}
& .sidebar-title {
display: inline-block;
margin: 0;
color: #fff;
font-weight: 600;
line-height: 50px;
font-size: 14px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
&.collapse {
.sidebar-logo {
margin-right: 0px;
}
}
}
</style>

View File

@@ -1,41 +1,32 @@
<template>
<div v-if="!item.hidden&&item.children" class="menu-wrapper">
<div v-if="!item.hidden" class="menu-wrapper">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link :to="resolvePath(onlyOneChild.path)">
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item v-if="onlyOneChild.meta" :icon="onlyOneChild.meta.icon||item.meta.icon" :title="onlyOneChild.meta.title" />
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="submenu" :index="resolvePath(item.path)">
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta.icon" :title="item.meta.title" />
</template>
<template v-for="child in item.children" v-if="!child.hidden">
<sidebar-item
v-if="child.children&&child.children.length>0"
:is-nest="true"
:item="child"
:key="child.path"
:base-path="resolvePath(child.path)"
class="nest-menu" />
<app-link v-else :to="resolvePath(child.path)" :key="child.name">
<el-menu-item :index="resolvePath(child.path)">
<item v-if="child.meta" :icon="child.meta.icon" :title="child.meta.title" />
</el-menu-item>
</app-link>
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</div>
</template>
<script>
import path from 'path'
import { isExternal } from '@/utils'
import { isExternal } from '@/utils/validate'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
@@ -60,12 +51,13 @@ export default {
}
},
data() {
return {
onlyOneChild: null
}
// To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
// TODO: refactor with render function
this.onlyOneChild = null
return {}
},
methods: {
hasOneShowingChild(children, parent) {
hasOneShowingChild(children = [], parent) {
const showingChildren = children.filter(item => {
if (item.hidden) {
return false
@@ -90,13 +82,13 @@ export default {
return false
},
resolvePath(routePath) {
if (this.isExternalLink(routePath)) {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
},
isExternalLink(routePath) {
return isExternal(routePath)
}
}
}

View File

@@ -1,25 +1,29 @@
<template>
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:show-timeout="200"
:default-active="$route.path"
:collapse="isCollapse"
:unique-opened="$store.state.settings.uniqueOpened"
mode="vertical"
background-color="#304156"
text-color="#bfcbd9"
active-text-color="#409EFF"
>
<Logo :is-collapse="isCollapse"/>
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/>
</el-menu>
</el-scrollbar>
<div :class="{'has-logo':showLogo}">
<logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="$store.state.settings.uniqueOpened"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/assets/styles/variables.scss'
export default {
components: { SidebarItem, Logo },
computed: {
@@ -27,9 +31,30 @@ export default {
'permission_routers',
'sidebar'
]),
activeMenu() {
const route = this.$route
const { meta, path } = route
// if set path, the sidebar will highlight the path you set
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
<style scoped>
/deep/ .el-scrollbar__wrap{
margin-right: -22px !important;
}
</style>

View File

@@ -0,0 +1,85 @@
<template>
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
<slot />
</el-scrollbar>
</template>
<script>
const tagAndTagSpacing = 4 // tagAndTagSpacing
export default {
name: 'ScrollPane',
data() {
return {
left: 0
}
},
computed: {
scrollWrapper() {
return this.$refs.scrollContainer.$refs.wrap
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 40
const $scrollWrapper = this.scrollWrapper
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
},
moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el
const $containerWidth = $container.offsetWidth
const $scrollWrapper = this.scrollWrapper
const tagList = this.$parent.$refs.tag
let firstTag = null
let lastTag = null
// find first tag and last tag
if (tagList.length > 0) {
firstTag = tagList[0]
lastTag = tagList[tagList.length - 1]
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
} else {
// find preTag and nextTag
const currentIndex = tagList.findIndex(item => item === currentTag)
const prevTag = tagList[currentIndex - 1]
const nextTag = tagList[currentIndex + 1]
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
}
}
}
}
}
</script>
<style lang="scss" scoped>
.scroll-container {
white-space: nowrap;
position: relative;
overflow: hidden;
width: 100%;
/deep/ {
.el-scrollbar__bar {
bottom: 0px;
}
.el-scrollbar__wrap {
height: 49px;
}
}
}
</style>

View File

@@ -1,33 +1,34 @@
<template>
<div class="tags-view-container">
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
<router-link
v-for="tag in visitedViews"
ref="tag"
:key="tag.path"
:class="isActive(tag)?'active':''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
:key="tag.path"
tag="span"
class="tags-view-item"
@click.middle.native="closeSelectedTag(tag)"
@contextmenu.prevent.native="openMenu(tag,$event)">
@contextmenu.prevent.native="openMenu(tag,$event)"
>
{{ tag.title }}
<span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">刷新</li>
<li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">关闭</li>
<li @click="closeOthersTags">关闭其他</li>
<li @click="closeAllTags(selectedTag)">关闭所有</li>
<li @click="refreshSelectedTag(selectedTag)">Refresh</li>
<li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">Close</li>
<li @click="closeOthersTags">Close Others</li>
<li @click="closeAllTags(selectedTag)">Close All</li>
</ul>
</div>
</template>
<script>
import ScrollPane from '@/components/ScrollPane'
import ScrollPane from './ScrollPane'
import path from 'path'
import { constantRouterMap } from '@/router/routers'
export default {
components: { ScrollPane },
data() {
@@ -35,18 +36,21 @@ export default {
visible: false,
top: 0,
left: 0,
affixTags: [],
selectedTag: {}
selectedTag: {},
affixTags: []
}
},
computed: {
visitedViews() {
return this.$store.state.tagsView.visitedViews
},
routes() {
return this.$store.state.permission.routers
}
},
watch: {
$route() {
this.addViewTags()
this.addTags()
this.moveToCurrentTag()
},
visible(value) {
@@ -59,7 +63,7 @@ export default {
},
mounted() {
this.initTags()
this.addViewTags()
this.addTags()
},
methods: {
isActive(route) {
@@ -87,18 +91,18 @@ export default {
return tags
},
initTags() {
const affixTags = this.affixTags = this.filterAffixTags(constantRouterMap)
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
for (const tag of affixTags) {
// Must have tag name
if (tag.name) {
this.$store.dispatch('addVisitedView', tag)
this.$store.dispatch('tagsView/addVisitedView', tag)
}
}
},
addViewTags() {
addTags() {
const { name } = this.$route
if (name) {
this.$store.dispatch('addView', this.$route)
this.$store.dispatch('tagsView/addView', this.$route)
}
return false
},
@@ -108,19 +112,17 @@ export default {
for (const tag of tags) {
if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag)
// when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) {
this.$store.dispatch('updateVisitedView', this.$route)
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
}
break
}
}
})
},
refreshSelectedTag(view) {
this.$store.dispatch('delCachedView', view).then(() => {
this.$store.dispatch('tagsView/delCachedView', view).then(() => {
const { fullPath } = view
this.$nextTick(() => {
this.$router.replace({
@@ -130,25 +132,20 @@ export default {
})
},
closeSelectedTag(view) {
this.$store.dispatch('delView', view).then(({ visitedViews }) => {
this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
if (this.isActive(view)) {
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
this.$router.push(latestView)
} else {
this.$router.push('/')
}
this.toLastView(visitedViews, view)
}
})
},
closeOthersTags() {
this.$router.push(this.selectedTag)
this.$store.dispatch('delOthersViews', this.selectedTag).then(() => {
this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
this.moveToCurrentTag()
})
},
closeAllTags(view) {
this.$store.dispatch('delAllViews').then(({ visitedViews }) => {
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
if (this.affixTags.some(tag => tag.path === view.path)) {
return
}
@@ -162,7 +159,7 @@ export default {
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === '首页') {
if (view.name === 'Dashboard') {
// to reload home page
this.$router.replace({ path: '/redirect' + view.fullPath })
} else {
@@ -182,8 +179,8 @@ export default {
} else {
this.left = left
}
this.top = e.clientY
this.top = e.clientY
this.visible = true
this.selectedTag = tag
},
@@ -194,7 +191,7 @@ export default {
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
<style lang="scss" scoped>
.tags-view-container {
height: 34px;
width: 100%;
@@ -241,7 +238,7 @@ export default {
.contextmenu {
margin: 0;
background: #fff;
z-index: 100;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
@@ -262,7 +259,7 @@ export default {
}
</style>
<style rel="stylesheet/scss" lang="scss">
<style lang="scss">
//reset element css of el-icon-close
.tags-view-wrapper {
.tags-view-item {

View File

@@ -1,5 +1,5 @@
export { default as Navbar } from './Navbar'
export { default as Sidebar } from './Sidebar'
export { default as AppMain } from './AppMain'
export { default as TagsView } from './TagsView'
export { default as Navbar } from './Navbar'
export { default as Settings } from './Settings'
export { default as Sidebar } from './Sidebar/index.vue'
export { default as TagsView } from './TagsView/index.vue'

View File

@@ -8,7 +8,7 @@
<tags-view v-if="needTagsView" />
</div>
<app-main />
<right-panel>
<right-panel v-if="showSettings">
<settings />
</right-panel>
</div>
@@ -26,16 +26,17 @@ export default {
components: {
AppMain,
Navbar,
RightPanel,
Settings,
Sidebar,
TagsView,
RightPanel
TagsView
},
mixins: [ResizeMixin],
computed: {
...mapState({
sidebar: state => state.app.sidebar,
device: state => state.app.device,
showSettings: state => state.settings.showSettings,
needTagsView: state => state.settings.tagsView,
fixedHeader: state => state.settings.fixedHeader
}),
@@ -50,15 +51,15 @@ export default {
},
methods: {
handleClickOutside() {
this.$store.dispatch('closeSideBar', { withoutAnimation: false })
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
}
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
@import "~@/styles/variables.scss";
@import "~@/assets/styles/mixin.scss";
@import "~@/assets/styles/variables.scss";
.app-wrapper {
@include clearfix;
@@ -86,18 +87,16 @@ export default {
position: fixed;
top: 0;
right: 0;
background: #ffffff;
z-index: 99;
z-index: 9;
width: calc(100% - #{$sideBarWidth});
transition: width 0.28s;
}
.hideSidebar .fixed-header {
width: calc(100% - #{$hideSidebarWidth})
width: calc(100% - 54px)
}
.mobile .fixed-header {
width: 100%;
}
</style>

View File

@@ -1,39 +1,43 @@
import store from '@/store'
const { body } = document
const WIDTH = 1024
const RATIO = 3
const WIDTH = 992 // refer to Bootstrap's responsive design
export default {
watch: {
$route(route) {
if (this.device === 'mobile' && this.sidebar.opened) {
store.dispatch('closeSideBar', { withoutAnimation: false })
store.dispatch('app/closeSideBar', { withoutAnimation: false })
}
}
},
beforeMount() {
window.addEventListener('resize', this.resizeHandler)
window.addEventListener('resize', this.$_resizeHandler)
},
beforeDestroy() {
window.removeEventListener('resize', this.$_resizeHandler)
},
mounted() {
const isMobile = this.isMobile()
const isMobile = this.$_isMobile()
if (isMobile) {
store.dispatch('toggleDevice', 'mobile')
store.dispatch('closeSideBar', { withoutAnimation: true })
store.dispatch('app/toggleDevice', 'mobile')
store.dispatch('app/closeSideBar', { withoutAnimation: true })
}
},
methods: {
isMobile() {
// use $_ for mixins properties
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
$_isMobile() {
const rect = body.getBoundingClientRect()
return rect.width - RATIO < WIDTH
return rect.width - 1 < WIDTH
},
resizeHandler() {
$_resizeHandler() {
if (!document.hidden) {
const isMobile = this.isMobile()
store.dispatch('toggleDevice', isMobile ? 'mobile' : 'desktop')
const isMobile = this.$_isMobile()
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
if (isMobile) {
store.dispatch('closeSideBar', { withoutAnimation: true })
store.dispatch('app/closeSideBar', { withoutAnimation: true })
}
}
}