mirror of https://gitee.com/xiaonuobase/snowy
【更新】调整多页签交互,增加右键操作
parent
e42c4ddc3a
commit
09c4fb24b5
|
@ -0,0 +1,88 @@
|
|||
<template>
|
||||
<div :style="style" v-show="show" @mousedown.stop @contextmenu.prevent>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'XnContextMenu',
|
||||
props: {
|
||||
target: null,
|
||||
show: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
triggerShowFn: () => {},
|
||||
triggerHideFn: () => {},
|
||||
x: null,
|
||||
y: null,
|
||||
style: {},
|
||||
binded: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(show) {
|
||||
if (show) {
|
||||
this.bindHideEvents()
|
||||
} else {
|
||||
this.unbindHideEvents()
|
||||
}
|
||||
},
|
||||
target(target) {
|
||||
this.bindEvents()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.bindEvents()
|
||||
},
|
||||
methods: {
|
||||
// 初始化事件
|
||||
bindEvents() {
|
||||
this.$nextTick(() => {
|
||||
if (!this.target || this.binded) return
|
||||
this.triggerShowFn = this.contextMenuHandler.bind(this)
|
||||
this.target.addEventListener('contextmenu', this.triggerShowFn)
|
||||
this.binded = true
|
||||
})
|
||||
},
|
||||
// 取消绑定事件
|
||||
unbindEvents() {
|
||||
if (!this.target) return
|
||||
this.target.removeEventListener('contextmenu', this.triggerShowFn)
|
||||
},
|
||||
// 绑定隐藏菜单事件
|
||||
bindHideEvents() {
|
||||
this.triggerHideFn = this.clickDocumentHandler.bind(this)
|
||||
document.addEventListener('mousedown', this.triggerHideFn)
|
||||
document.addEventListener('mousewheel', this.triggerHideFn)
|
||||
},
|
||||
// 取消绑定隐藏菜单事件
|
||||
unbindHideEvents() {
|
||||
document.removeEventListener('mousedown', this.triggerHideFn)
|
||||
document.removeEventListener('mousewheel', this.triggerHideFn)
|
||||
},
|
||||
// 鼠标按压事件处理器
|
||||
clickDocumentHandler(e) {
|
||||
this.$emit('update:show', false)
|
||||
},
|
||||
// 右键事件事件处理
|
||||
contextMenuHandler(e) {
|
||||
this.x = e.clientX
|
||||
this.y = e.clientY
|
||||
this.layout()
|
||||
this.$emit('update:show', true)
|
||||
this.$emit('get-context-menu', e)
|
||||
e.preventDefault()
|
||||
},
|
||||
// 布局
|
||||
layout() {
|
||||
this.style = {
|
||||
left: this.x + 'px',
|
||||
top: this.y + 'px',
|
||||
display: 'block'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,10 +1,42 @@
|
|||
<template>
|
||||
<div class="snowy-tags">
|
||||
<xn-context-menu
|
||||
class="right-menu"
|
||||
:target="contextMenuTarget"
|
||||
:show="contextMenuVisible"
|
||||
@update:show="(show) => (contextMenuVisible = show)"
|
||||
@get-context-menu="handleTabContextMenu"
|
||||
>
|
||||
<div class="right-menu-item" @click="refreshTab">
|
||||
<reload-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">刷新</div>
|
||||
</div>
|
||||
|
||||
<div class="right-menu-item" @click="closeTabs">
|
||||
<close-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">关闭</div>
|
||||
</div>
|
||||
|
||||
<div class="right-menu-item" @click="closeOtherTabs">
|
||||
<close-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">关闭其他标签</div>
|
||||
</div>
|
||||
|
||||
<div class="right-menu-item" @click="maximize">
|
||||
<expand-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">最大化</div>
|
||||
</div>
|
||||
<div class="right-menu-item" @click="openWindow">
|
||||
<select-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">新窗口打开</div>
|
||||
</div>
|
||||
</xn-context-menu>
|
||||
<a-tabs
|
||||
v-model:activeKey="activeKey"
|
||||
type="editable-card"
|
||||
class="snowy-admin-tabs"
|
||||
hide-add
|
||||
ref="tabs"
|
||||
@edit="onTabRemove"
|
||||
@tabClick="onTabClick"
|
||||
>
|
||||
|
@ -17,41 +49,8 @@
|
|||
<div class="snowy-admin-tabs-arrow" @click="scrollRight">
|
||||
<right-outlined />
|
||||
</div>
|
||||
|
||||
<a-dropdown>
|
||||
<div class="snowy-admin-tabs-drop">
|
||||
<DownOutlined />
|
||||
</div>
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item>
|
||||
<div class="layout-items-center" @click="refreshTab">
|
||||
<reload-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">刷新</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<div class="layout-items-center" @click="closeOtherTabs">
|
||||
<close-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">关闭其他标签</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<div class="layout-items-center" @click="maximize">
|
||||
<expand-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">最大化</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<div class="layout-items-center" @click="openWindow">
|
||||
<select-outlined class="snowy-header-tags-right" />
|
||||
<div class="pl-3">在新的窗口中打开</div>
|
||||
</div>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<a-tab-pane v-for="tag in tagList" :key="tag.fullPath" :tab="tag.meta.title" :closable="!tag.meta.affix">
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
|
@ -59,22 +58,26 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'
|
||||
import Sortable from 'sortablejs'
|
||||
import tool from '@/utils/tool'
|
||||
import XnContextMenu from '@/components/XnContextMenu/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'Tags',
|
||||
components: { XnContextMenu },
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
tagList: this.$store.state.viewTags.viewTags,
|
||||
activeKey: this.$route.fullPath
|
||||
activeKey: this.$route.fullPath,
|
||||
maxTabs: 20,
|
||||
contextMenuTarget: null,
|
||||
contextMenuVisible: false,
|
||||
currentContextMenuTabIndex: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route(e) {
|
||||
this.addViewTags(e)
|
||||
$route(to) {
|
||||
this.addViewTags(to)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -88,12 +91,27 @@
|
|||
this.addViewTags(this.$route)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const tabNavList = document.querySelector('.ant-tabs-nav-list')
|
||||
if (tabNavList) {
|
||||
this.contextMenuTarget = tabNavList
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTabContextMenu(evt) {
|
||||
evt.preventDefault()
|
||||
let target = evt.target
|
||||
if (target.classList.contains('ant-tabs-tab-btn')) {
|
||||
target = target.parentNode
|
||||
}
|
||||
const tabList = document.querySelectorAll('.ant-tabs-nav-list .ant-tabs-tab')
|
||||
this.currentContextMenuTabIndex = Array.from(tabList).findIndex((tab) => tab === target)
|
||||
},
|
||||
onTabClick(tab) {
|
||||
this.$router.push(tab)
|
||||
},
|
||||
getCurrentTag() {
|
||||
return this.tagList.find((tag) => tag.fullPath === this.activeKey)
|
||||
return this.tagList[this.currentContextMenuTabIndex]
|
||||
},
|
||||
onTabRemove(tabKey, action) {
|
||||
if (action === 'remove') {
|
||||
|
@ -132,10 +150,15 @@
|
|||
// 增加tag
|
||||
addViewTags(route) {
|
||||
this.activeKey = route.fullPath
|
||||
|
||||
if (route.name && !route.meta.fullpage) {
|
||||
this.$store.commit('pushViewTags', route)
|
||||
this.$store.commit('pushKeepLive', route.name)
|
||||
}
|
||||
if (this.tagList.length - 1 > this.maxTabs) {
|
||||
const firstTag = this.tagList[1]
|
||||
this.$store.commit('removeViewTags', firstTag)
|
||||
}
|
||||
},
|
||||
// 高亮tag
|
||||
isActive(route) {
|
||||
|
@ -157,6 +180,7 @@
|
|||
},
|
||||
// TAB 刷新
|
||||
refreshTab() {
|
||||
this.contextMenuVisible = false
|
||||
const nowTag = this.getCurrentTag()
|
||||
// 判断是否当前路由,否的话跳转
|
||||
// eslint-disable-next-line eqeqeq
|
||||
|
@ -178,6 +202,7 @@
|
|||
},
|
||||
// TAB 关闭
|
||||
closeTabs() {
|
||||
this.contextMenuVisible = false
|
||||
const nowTag = this.getCurrentTag()
|
||||
if (!nowTag.meta.affix) {
|
||||
this.closeSelectedTag(nowTag)
|
||||
|
@ -185,6 +210,7 @@
|
|||
},
|
||||
// TAB 关闭其他
|
||||
closeOtherTabs() {
|
||||
this.contextMenuVisible = false
|
||||
const nowTag = this.getCurrentTag()
|
||||
// 判断是否当前路由,否的话跳转
|
||||
// eslint-disable-next-line eqeqeq
|
||||
|
@ -206,6 +232,7 @@
|
|||
},
|
||||
// TAB 最大化(包括标签栏)
|
||||
maximize() {
|
||||
this.contextMenuVisible = false
|
||||
const nowTag = this.getCurrentTag()
|
||||
// 判断是否当前路由,否的话跳转
|
||||
// eslint-disable-next-line eqeqeq
|
||||
|
@ -219,6 +246,7 @@
|
|||
},
|
||||
// 新窗口打开
|
||||
openWindow() {
|
||||
this.contextMenuVisible = false
|
||||
const nowTag = this.getCurrentTag()
|
||||
const url = nowTag.href || '/'
|
||||
if (!nowTag.meta.affix) {
|
||||
|
@ -284,4 +312,25 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
.right-menu {
|
||||
position: fixed;
|
||||
background: #fff;
|
||||
z-index: 999;
|
||||
border: 1px solid #eee;
|
||||
box-shadow: 0 0.5em 1em 0 rgb(0 0 0 / 10%);
|
||||
border-radius: 1px;
|
||||
}
|
||||
.snowy-tags {
|
||||
.right-menu-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 10px 20px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: var(--primary-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue