mirror of
https://github.com/iczer/vue-antd-admin
synced 2025-04-06 04:00:06 +08:00
feat: add function of fixing the head of tabs; ⭐
新增:固定页签头功能。
This commit is contained in:
parent
9df2666304
commit
83576d88d7
@ -11,6 +11,7 @@ module.exports = {
|
|||||||
layout: 'side', //导航布局,可选 side 和 head,分别为侧边导航和顶部导航
|
layout: 'side', //导航布局,可选 side 和 head,分别为侧边导航和顶部导航
|
||||||
fixedHeader: false, //固定头部状态栏,true:固定,false:不固定
|
fixedHeader: false, //固定头部状态栏,true:固定,false:不固定
|
||||||
fixedSideBar: true, //固定侧边栏,true:固定,false:不固定
|
fixedSideBar: true, //固定侧边栏,true:固定,false:不固定
|
||||||
|
fixedTabs: false, //固定页签头,true:固定,false:不固定
|
||||||
pageWidth: 'fixed', //内容区域宽度,fixed:固定宽度,fluid:流式宽度
|
pageWidth: 'fixed', //内容区域宽度,fixed:固定宽度,fluid:流式宽度
|
||||||
weekMode: false, //色弱模式,true:开启,false:不开启
|
weekMode: false, //色弱模式,true:开启,false:不开启
|
||||||
multiPage: false, //多页签模式,true:开启,false:不开启
|
multiPage: false, //多页签模式,true:开启,false:不开启
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
<setting />
|
<setting />
|
||||||
</drawer>
|
</drawer>
|
||||||
<a-layout class="admin-layout-main beauty-scroll">
|
<a-layout class="admin-layout-main beauty-scroll">
|
||||||
<admin-header :style="headerStyle" :menuData="headMenuData" :collapsed="collapsed" @toggleCollapse="toggleCollapse"/>
|
<admin-header :class="[{'fixed-tabs': fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage}]" :style="headerStyle" :menuData="headMenuData" :collapsed="collapsed" @toggleCollapse="toggleCollapse"/>
|
||||||
<a-layout-header v-if="fixedHeader"></a-layout-header>
|
<a-layout-header :class="['virtual-header', {'fixed-tabs' : fixedTabs, 'fixed-header': fixedHeader, 'multi-page': multiPage}]" v-show="fixedHeader"></a-layout-header>
|
||||||
<a-layout-content class="admin-layout-content">
|
<a-layout-content class="admin-layout-content">
|
||||||
<div :style="`min-height: ${minHeight}px; position: relative`">
|
<div :style="`min-height: ${minHeight}px; position: relative`">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
@ -47,6 +47,11 @@ export default {
|
|||||||
drawerOpen: false
|
drawerOpen: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
provide() {
|
||||||
|
return {
|
||||||
|
adminLayout: this
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route(val) {
|
$route(val) {
|
||||||
this.setActivated(val)
|
this.setActivated(val)
|
||||||
@ -62,7 +67,7 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState('setting', ['isMobile', 'theme', 'layout', 'footerLinks', 'copyright', 'fixedHeader', 'fixedSideBar',
|
...mapState('setting', ['isMobile', 'theme', 'layout', 'footerLinks', 'copyright', 'fixedHeader', 'fixedSideBar',
|
||||||
'hideSetting']),
|
'fixedTabs', 'hideSetting', 'multiPage']),
|
||||||
...mapGetters('setting', ['firstMenu', 'subMenu', 'menuData']),
|
...mapGetters('setting', ['firstMenu', 'subMenu', 'menuData']),
|
||||||
sideMenuWidth() {
|
sideMenuWidth() {
|
||||||
return this.collapsed ? '80px' : '256px'
|
return this.collapsed ? '80px' : '256px'
|
||||||
@ -70,8 +75,7 @@ export default {
|
|||||||
headerStyle() {
|
headerStyle() {
|
||||||
let width = (this.fixedHeader && this.layout !== 'head' && !this.isMobile) ? `calc(100% - ${this.sideMenuWidth})` : '100%'
|
let width = (this.fixedHeader && this.layout !== 'head' && !this.isMobile) ? `calc(100% - ${this.sideMenuWidth})` : '100%'
|
||||||
let position = this.fixedHeader ? 'fixed' : 'static'
|
let position = this.fixedHeader ? 'fixed' : 'static'
|
||||||
let transition = this.fixedHeader ? 'transition: width 0.2s' : ''
|
return `width: ${width}; position: ${position};`
|
||||||
return `width: ${width}; position: ${position}; ${transition}`
|
|
||||||
},
|
},
|
||||||
headMenuData() {
|
headMenuData() {
|
||||||
const {layout, menuData, firstMenu} = this
|
const {layout, menuData, firstMenu} = this
|
||||||
@ -127,10 +131,22 @@ export default {
|
|||||||
.virtual-side{
|
.virtual-side{
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
.virtual-header{
|
||||||
|
transition: all 0.2s;
|
||||||
|
opacity: 0;
|
||||||
|
&.fixed-tabs.multi-page:not(.fixed-header){
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
.admin-layout-main{
|
.admin-layout-main{
|
||||||
.admin-header{
|
.admin-header{
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.2s;
|
||||||
|
&.fixed-tabs.multi-page:not(.fixed-header){
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.admin-layout-content{
|
.admin-layout-content{
|
||||||
|
155
src/layouts/tabs/TabsHead.vue
Normal file
155
src/layouts/tabs/TabsHead.vue
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<template>
|
||||||
|
<div :class="['tabs-head', layout, pageWidth]">
|
||||||
|
<a-tabs
|
||||||
|
type="editable-card"
|
||||||
|
:class="['tabs-container', layout, pageWidth, {'affixed' : affixed, 'fixed-header' : fixedHeader, 'collapsed' : adminLayout.collapsed}]"
|
||||||
|
:active-key="active"
|
||||||
|
:hide-add="true"
|
||||||
|
@change="onChange"
|
||||||
|
@edit="onEdit"
|
||||||
|
@contextmenu="onContextmenu"
|
||||||
|
>
|
||||||
|
<a-tooltip placement="left" :title="lockTitle" slot="tabBarExtraContent">
|
||||||
|
<a-icon
|
||||||
|
theme="filled"
|
||||||
|
@click="onLockClick"
|
||||||
|
class="header-lock"
|
||||||
|
:type="fixedTabs ? 'lock' : 'unlock'"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tab-pane v-for="page in pageList" :key="page.fullPath">
|
||||||
|
<span slot="tab" :pagekey="page.fullPath">{{pageName(page)}}</span>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
<div v-if="affixed" class="virtual-tabs"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapState, mapMutations} from 'vuex'
|
||||||
|
import {getI18nKey} from '@/utils/routerUtil'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TabsHead',
|
||||||
|
i18n: {
|
||||||
|
messages: {
|
||||||
|
CN: {
|
||||||
|
lock: '点击锁定页签头',
|
||||||
|
unlock: '点击解除锁定',
|
||||||
|
},
|
||||||
|
HK: {
|
||||||
|
lock: '點擊鎖定頁簽頭',
|
||||||
|
unlock: '點擊解除鎖定',
|
||||||
|
},
|
||||||
|
US: {
|
||||||
|
lock: 'click to lock the tabs head',
|
||||||
|
unlock: 'click to unlock',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
pageList: Array,
|
||||||
|
active: String,
|
||||||
|
fixed: Boolean
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
affixed: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
inject:['adminLayout'],
|
||||||
|
watch: {
|
||||||
|
'adminLayout.collapsed': (val) => {
|
||||||
|
console.log(val)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.affixed = this.fixedTabs
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState('setting', ['layout', 'pageWidth', 'fixedHeader', 'fixedTabs']),
|
||||||
|
lockTitle() {
|
||||||
|
return this.$t(this.fixedTabs ? 'unlock' : 'lock')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapMutations('setting', ['setFixedTabs']),
|
||||||
|
onLockClick() {
|
||||||
|
this.setFixedTabs(!this.fixedTabs)
|
||||||
|
if (this.fixedTabs) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.affixed = true
|
||||||
|
}, 200)
|
||||||
|
} else {
|
||||||
|
this.affixed = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onChange(key) {
|
||||||
|
this.$emit('change', key)
|
||||||
|
},
|
||||||
|
onEdit(key, action) {
|
||||||
|
if (action === 'remove') {
|
||||||
|
this.$emit('close', key)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onContextmenu(e) {
|
||||||
|
this.$emit('contextmenu', e)
|
||||||
|
},
|
||||||
|
pageName(page) {
|
||||||
|
return this.$t(getI18nKey(page.keyPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.tabs-head{
|
||||||
|
margin: 0 auto;
|
||||||
|
&.head.fixed{
|
||||||
|
width: 1400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tabs-container{
|
||||||
|
margin: -16px auto 8px;
|
||||||
|
transition: top,left 0.2s;
|
||||||
|
.header-lock{
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: @primary-3;
|
||||||
|
&:hover{
|
||||||
|
color: @primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.affixed{
|
||||||
|
margin: 0 auto;
|
||||||
|
top: 0px;
|
||||||
|
padding: 8px 24px 0;
|
||||||
|
position: fixed;
|
||||||
|
height: 48px;
|
||||||
|
z-index: 1;
|
||||||
|
background-color: @layout-body-background;
|
||||||
|
&.side,&.mix{
|
||||||
|
right: 0;
|
||||||
|
left: 256px;
|
||||||
|
&.collapsed{
|
||||||
|
left: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.head{
|
||||||
|
width: inherit;
|
||||||
|
padding: 8px 0 0;
|
||||||
|
&.fluid{
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 8px 24px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.fixed-header{
|
||||||
|
top: 64px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.virtual-tabs{
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,20 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<admin-layout>
|
<admin-layout>
|
||||||
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
|
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect" />
|
||||||
<a-tabs
|
<tabs-head
|
||||||
v-if="multiPage"
|
v-if="multiPage"
|
||||||
type="editable-card"
|
:active="activePage"
|
||||||
:active-key="activePage"
|
:page-list="pageList"
|
||||||
:class="['tabs-view', layout, pageWidth]"
|
|
||||||
:hide-add="true"
|
|
||||||
@change="changePage"
|
@change="changePage"
|
||||||
@edit="editPage"
|
@close="remove"
|
||||||
@contextmenu="onContextmenu"
|
@contextmenu="onContextmenu"
|
||||||
>
|
/>
|
||||||
<a-tab-pane :key="page.fullPath" v-for="page in pageList">
|
|
||||||
<span slot="tab" :pagekey="page.fullPath">{{pageName(page)}}</span>
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
|
||||||
<div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
|
<div :class="['tabs-view-content', layout, pageWidth]" :style="`margin-top: ${multiPage ? -24 : 0}px`">
|
||||||
<page-toggle-transition :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
|
<page-toggle-transition :disabled="animate.disabled" :animate="animate.name" :direction="animate.direction">
|
||||||
<a-keep-alive v-if="multiPage" v-model="clearCaches">
|
<a-keep-alive v-if="multiPage" v-model="clearCaches">
|
||||||
@ -33,11 +27,12 @@ import PageToggleTransition from '@/components/transition/PageToggleTransition'
|
|||||||
import {mapState, mapMutations} from 'vuex'
|
import {mapState, mapMutations} from 'vuex'
|
||||||
import {getI18nKey} from '@/utils/routerUtil'
|
import {getI18nKey} from '@/utils/routerUtil'
|
||||||
import AKeepAlive from '@/components/cache/AKeepAlive'
|
import AKeepAlive from '@/components/cache/AKeepAlive'
|
||||||
|
import TabsHead from '@/layouts/tabs/TabsHead'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TabsView',
|
name: 'TabsView',
|
||||||
i18n: require('./i18n'),
|
i18n: require('./i18n'),
|
||||||
components: { PageToggleTransition, Contextmenu, AdminLayout , AKeepAlive },
|
components: {TabsHead, PageToggleTransition, Contextmenu, AdminLayout , AKeepAlive },
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
clearCaches: [],
|
clearCaches: [],
|
||||||
|
@ -91,6 +91,9 @@ export default {
|
|||||||
},
|
},
|
||||||
setActivatedFirst(state, activatedFirst) {
|
setActivatedFirst(state, activatedFirst) {
|
||||||
state.activatedFirst = activatedFirst
|
state.activatedFirst = activatedFirst
|
||||||
|
},
|
||||||
|
setFixedTabs(state, fixedTabs) {
|
||||||
|
state.fixedTabs = fixedTabs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user