Skip to content

Commit

Permalink
feat(contextmenu): 支持自定义右键菜单配置
Browse files Browse the repository at this point in the history
  • Loading branch information
bhuh12 committed Jul 29, 2020
1 parent cb2b424 commit d475ebe
Show file tree
Hide file tree
Showing 22 changed files with 382 additions and 177 deletions.
15 changes: 13 additions & 2 deletions lib/RouterTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@ import restore from './mixins/restore'
import RouterAlive from './components/RouterAlive.vue'
import TabItem from './components/TabItem.vue'
import TabScroll from './components/TabScroll.vue'
import TabContextmenu from './components/Contextmenu.vue'

// RouterTab 组件
const RouterTab = {
name: 'RouterTab',
mixins: [contextmenu, i18n, iframe, operate, pageLeave, scroll, restore],
components: { RouterAlive, TabItem, TabScroll },
components: { RouterAlive, TabItem, TabScroll, TabContextmenu },

// 注入子组件
provide() {
return { RouterTab: this }
return { $tabs: this }
},

props: {
Expand Down Expand Up @@ -67,6 +68,16 @@ const RouterTab = {
default: 'zh-CN'
},

/**
* 自定义右键菜单
* 1. 为 false 时禁用
* 2. 为数组时可自定义右键菜单
*/
contextmenu: {
type: [Boolean, Array],
default: true
},

// 默认是否缓存,可通过路由 meta.keepAlive 重置
keepAlive: {
type: Boolean,
Expand Down
60 changes: 5 additions & 55 deletions lib/RouterTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,63 +64,13 @@

<!-- 右键菜单 -->
<transition name="router-tab-zoom">
<div
v-if="contextmenu.id"
class="router-tab__contextmenu"
:style="`left: ${contextmenu.left}px; top: ${contextmenu.top}px;`"
>
<a
class="router-tab__contextmenu-item"
@click="refreshTab(contextmenu.id)"
>
{{ lang.contextmenu.refresh }}
</a>

<a
class="router-tab__contextmenu-item"
:disabled="items.length < 2"
@click="items.length > 1 && refreshAll()"
>
{{ lang.contextmenu.refreshAll }}
</a>

<a
class="router-tab__contextmenu-item"
:disabled="!isContextTabCanBeClosed"
@click="isContextTabCanBeClosed && closeTab(contextmenu.id)"
>
{{ lang.contextmenu.close }}
</a>

<a
class="router-tab__contextmenu-item"
:disabled="!tabsLeft.length"
@click="tabsLeft.length && closeMulti(tabsLeft)"
>
{{ lang.contextmenu.closeLefts }}
</a>

<a
class="router-tab__contextmenu-item"
:disabled="!tabsRight.length"
@click="tabsRight.length && closeMulti(tabsRight)"
>
{{ lang.contextmenu.closeRights }}
</a>

<a
class="router-tab__contextmenu-item"
:disabled="!tabsOther.length"
@click="tabsOther.length && closeMulti(tabsOther)"
>
{{ lang.contextmenu.closeOthers }}
</a>
</div>
<tab-contextmenu
v-if="contextmenu !== false && contextData.id"
:data="contextData"
:menu="contextMenu"
/>
</transition>
</div>
</template>

<script src="./RouterTab.js"></script>

<style lang="scss" src="./scss/routerTab.scss"></style>
<style lang="scss" src="./scss/transition.scss"></style>
104 changes: 104 additions & 0 deletions lib/components/Contextmenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<template>
<div
class="router-tab__contextmenu"
:class="{ 'has-icon': hasIcon }"
:style="{
left: `${data.left}px`,
top: `${data.top}px`
}"
>
<tab-contextmenu-item
v-for="item in menuList"
:key="item.id"
:data="item"
/>
</div>
</template>

<script>
import TabContextmenuItem from './ContextmenuItem.vue'
import menuMap, { defaultMenu } from '../config/contextmenu'
export default {
name: 'TabContextmenu',
inject: ['$tabs'],
components: { TabContextmenuItem },
props: {
// 右键数据
data: {
type: [Boolean, Object]
},
// 菜单配置
menu: {
type: Array,
default: () => defaultMenu
}
},
computed: {
// 激活菜单的页签
target() {
return this.$tabs.$refs.tab[this.data.index]
},
// 菜单选项
menuList() {
return this.menu
.map(item => {
if (typeof item === 'string') {
// 内置菜单
return menuMap[item]
} else if (item && item.id) {
// 扩展内置菜单
let origin = menuMap[item.id]
return origin ? { ...origin, ...item } : item
}
})
.filter(item => item)
},
// 是否显示图标
hasIcon() {
return this.menuList.some(item => item.icon)
},
// 页签
tabs() {
return this.$tabs.$refs.tab
},
// 左侧可关闭的页签
lefts() {
return this.tabs.slice(0, this.data.index).filter(item => item.closable)
},
// 左侧可关闭的页签
rights() {
return this.tabs.slice(this.data.index + 1).filter(item => item.closable)
},
// 其他可关闭的页签
others() {
return this.tabs.filter(item => item.closable && this.data.id !== item.id)
}
},
methods: {
// 关闭多个页签
async closeMulti(tabs) {
for (let { id } of tabs) {
try {
await this.$tabs.removeTab(id)
} catch (e) {}
}
// 当前页签如已关闭,则打开右键选中页签
if (!this.$tabs.activeTab) {
this.$router.replace(this.target.to)
}
}
}
}
</script>
61 changes: 61 additions & 0 deletions lib/components/ContextmenuItem.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<template>
<a
v-if="visible"
class="router-tab__contextmenu-item"
:class="menuClass"
:data-action="id"
:disabled="!enable"
:title="tips"
@click="enable && data.handler(context)"
>
<i v-if="icon" class="router-tab__contextmenu-icon" :class="icon"></i>
{{ title }}
</a>
</template>

<script>
import { mapGetters } from '../util'
export default {
name: 'ContextmenuItem',
inject: ['$tabs'],
props: {
// 菜单数据
data: {
type: Object,
required: true
}
},
computed: {
// 参数
context() {
const { $tabs, $parent: $menu } = this
const { target, data } = $menu
return { $tabs, $menu, target, data }
},
// 从 this.data 提取计算属性
...mapGetters(
'data',
{
id: '',
// 菜单标题
title() {
return this.$tabs.lang.contextmenu[this.id]
},
icon: '',
tips: '',
class: {
default: '',
alias: 'menuClass'
},
visible: true, // 是否显示
enable: true // 是否启用
},
'context'
)
}
}
</script>
14 changes: 7 additions & 7 deletions lib/components/TabItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
:class="{
'router-tab__item': true,
[tabClass || '']: true,
'is-active': RouterTab.activeTabId === id,
'is-active': $tabs.activeTabId === id,
'is-closable': closable,
'is-contextmenu': RouterTab.contextmenu.id === id
'is-contextmenu': $tabs.contextData.id === id
}"
:to="to"
>
Expand All @@ -28,7 +28,7 @@ import { mapGetters } from '../util'
// 页签项
export default {
name: 'TabItem',
inject: ['RouterTab'],
inject: ['$tabs'],
props: {
// 页签原始数据,方便 slot 插槽自定义数据
data: {
Expand All @@ -46,12 +46,12 @@ export default {
// 国际化
i18nText() {
return this.RouterTab.i18nText
return this.$tabs.i18nText
},
// 未命名页签
untitled() {
return this.RouterTab.lang.tab.untitled
return this.$tabs.lang.tab.untitled
},
// 页签标题
Expand All @@ -66,15 +66,15 @@ export default {
// 是否可关闭
closable() {
const { keepLastTab, items } = this.RouterTab
const { keepLastTab, items } = this.$tabs
return this.data.closable !== false && !(keepLastTab && items.length < 2)
}
},
methods: {
// 关闭当前页签
close() {
this.RouterTab.closeTab(this.id)
this.$tabs.closeTab(this.id)
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion lib/components/TabScroll.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
<div
ref="thumb"
class="router-tab__scrollbar-thumb"
:style="`width: ${thumbWidth}px; transform: translateX(${thumbLeft}px)`"
:style="{
width: `${thumbWidth}px`,
transform: `translateX(${thumbLeft}px`
}"
@mousedown.prevent="onDragStart"
/>
</div>
Expand Down
Loading

0 comments on commit d475ebe

Please sign in to comment.