| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- <script>
- import events from './events'
- export default {
- name: 'MultiTab',
- data () {
- return {
- fullPathList: [],
- pages: [],
- activeKey: '',
- newTabIndex: 0
- }
- },
- created () {
- // bind event
- events.$on('open', val => {
- if (!val) {
- throw new Error(`multi-tab: open tab ${val} err`)
- }
- this.activeKey = val
- }).$on('close', val => {
- if (!val) {
- this.closeThat(this.activeKey)
- return
- }
- this.closeThat(val)
- }).$on('rename', ({ key, name }) => {
- console.log('rename', key, name)
- try {
- const item = this.pages.find(item => item.path === key)
- item.meta.customTitle = name
- this.$forceUpdate()
- } catch (e) {
- }
- })
- this.pages.push(this.$route)
- this.fullPathList.push(this.$route.fullPath)
- this.selectedLastPath()
- // 当前页面的保活由全局混入在组件 mounted 时自动注册,这里无需手动处理
- },
- methods: {
- onEdit (targetKey, action) {
- this[action](targetKey)
- },
- remove (targetKey) {
- const closedPage = this.pages.find(page => page.fullPath === targetKey)
- this.pages = this.pages.filter(page => page.fullPath !== targetKey)
- this.fullPathList = this.fullPathList.filter(path => path !== targetKey)
- // 判断当前标签是否关闭,若关闭则跳转到最后一个还存在的标签页
- const needNavigate = !this.fullPathList.includes(this.activeKey)
- if (needNavigate) {
- this.selectedLastPath()
- }
- // 清理已关闭标签页的组件缓存
- this.$nextTick(() => {
- this.removePageFromCache(closedPage)
- })
- },
- selectedLastPath () {
- this.activeKey = this.fullPathList[this.fullPathList.length - 1]
- },
- // content menu
- closeThat (e) {
- // 判断是否为最后一个标签页,如果是最后一个,则无法被关闭
- if (this.fullPathList.length > 1) {
- this.remove(e)
- } else {
- this.$message.info('这是最后一个标签了, 无法被关闭')
- }
- },
- closeLeft (e) {
- const currentIndex = this.fullPathList.indexOf(e)
- if (currentIndex > 0) {
- this.fullPathList.forEach((item, index) => {
- if (index < currentIndex) {
- this.remove(item)
- }
- })
- } else {
- this.$message.info('左侧没有标签')
- }
- },
- closeRight (e) {
- const currentIndex = this.fullPathList.indexOf(e)
- if (currentIndex < (this.fullPathList.length - 1)) {
- this.fullPathList.forEach((item, index) => {
- if (index > currentIndex) {
- this.remove(item)
- }
- })
- } else {
- this.$message.info('右侧没有标签')
- }
- },
- closeAll (e) {
- const currentIndex = this.fullPathList.indexOf(e)
- this.fullPathList.forEach((item, index) => {
- if (index !== currentIndex) {
- this.remove(item)
- }
- })
- },
- closeMenuClick (key, route) {
- this[key](route)
- },
- renderTabPaneMenu (e) {
- return (
- <a-menu {...{ on: { click: ({ key, item, domEvent }) => { this.closeMenuClick(key, e) } } }}>
- <a-menu-item key="closeThat">关闭当前标签</a-menu-item>
- <a-menu-item key="closeRight">关闭右侧</a-menu-item>
- <a-menu-item key="closeLeft">关闭左侧</a-menu-item>
- <a-menu-item key="closeAll">关闭全部</a-menu-item>
- </a-menu>
- )
- },
- // render
- renderTabPane (title, keyPath) {
- const menu = this.renderTabPaneMenu(keyPath)
- return (
- <a-dropdown overlay={menu} trigger={['contextmenu']}>
- <span style={{ userSelect: 'none' }}>{ title }</span>
- </a-dropdown>
- )
- },
- /**
- * 解析路由对应的组件名称(关闭页签时的默认处理,正常情况优先用 store 中 mounted 记录的真实 name)
- */
- getRouteComponentName (route) {
- if (!route) return null
- const layoutComponents = ['BasicLayout', 'RouteView', 'PageView', 'UserLayout', 'BlankLayout']
- if (route.matched && route.matched.length > 0) {
- const lastRecord = route.matched[route.matched.length - 1]
- const instance = lastRecord.instances && lastRecord.instances.default
- const instanceName = instance && instance.$options && instance.$options.name
- if (instanceName && !layoutComponents.includes(instanceName)) {
- return instanceName
- }
- const component = lastRecord.components && lastRecord.components.default
- if (component && component.name && !layoutComponents.includes(component.name)) {
- return component.name
- }
- }
- return route.name || null
- },
- /**
- * 关闭标签页时,从缓存中移除对应组件
- * 仅当没有其他打开的标签使用同一组件时才移除
- */
- removePageFromCache (closedPage) {
- if (!closedPage) return
- const fullPath = closedPage.fullPath
- // 优先用页面 mounted 时记入 store 的真实组件名(异步组件名称与路由名常常不一致)
- const componentName = this.$store.state.app.routeCacheMap[fullPath] || this.getRouteComponentName(closedPage)
- // 移除该页签的映射
- this.$store.commit('DEL_ROUTE_CACHE_MAP', fullPath)
- if (!componentName) return
- // 检查是否还有其他打开的标签页使用同一组件
- const stillInUse = this.pages.some(page => {
- return (this.$store.state.app.routeCacheMap[page.fullPath] || this.getRouteComponentName(page)) === componentName
- })
- if (!stillInUse) {
- this.$store.dispatch('DelCachedView', componentName)
- }
- }
- },
- watch: {
- '$route': function (newVal) {
- this.activeKey = newVal.fullPath
- if (this.fullPathList.indexOf(newVal.fullPath) < 0) {
- this.fullPathList.push(newVal.fullPath)
- this.pages.push(newVal)
- }
- // 页面保活的注册由全局混入在组件 mounted 时完成,无需在此手动加入
- },
- activeKey: function (newPathKey) {
- this.$router.push({ path: newPathKey })
- }
- },
- render () {
- const { onEdit, $data: { pages } } = this
- const panes = pages.map(page => {
- return (
- <a-tab-pane
- style={{ height: 0 }}
- tab={this.renderTabPane(page.meta.customTitle || page.meta.title, page.fullPath)}
- key={page.fullPath} closable={pages.length > 1}
- >
- </a-tab-pane>)
- })
- return (
- <div class="ant-pro-multi-tab">
- <div class="ant-pro-multi-tab-wrapper">
- <a-tabs
- hideAdd
- type={'editable-card'}
- v-model={this.activeKey}
- tabBarStyle={{ background: '#FFF', margin: 0, paddingLeft: '16px', paddingTop: '1px' }}
- {...{ on: { edit: onEdit } }}>
- {panes}
- </a-tabs>
- </div>
- </div>
- )
- }
- }
- </script>
|