研发图纸文件管理系统-前端项目
chenghongxing
2020-09-30 313af63f3348c9deb7a716e1836be6b26ca477d9
feat: add function of refreshing page; :star: #132
新增:刷新页面的功能;
5个文件已修改
151 ■■■■■ 已修改文件
src/components/menu/Contextmenu.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/tabs/TabsHead.vue 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/tabs/TabsView.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/tabs/i18n.js 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/plugins/tabs-page-plugin.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/menu/Contextmenu.vue
@@ -33,6 +33,7 @@
      left: 0,
      top: 0,
      target: null,
      meta: null,
      selectedKeys: []
    }
  },
@@ -45,14 +46,12 @@
    }
  },
  created () {
    const clickHandler = () => this.closeMenu()
    const contextMenuHandler = e => this.setPosition(e)
    window.addEventListener('click', clickHandler)
    window.addEventListener('contextmenu', contextMenuHandler)
    this.$emit('hook:beforeDestroy', () => {
      window.removeEventListener('click', clickHandler)
      window.removeEventListener('contextmenu', contextMenuHandler)
    })
    window.addEventListener('click', this.closeMenu)
    window.addEventListener('contextmenu', this.setPosition)
  },
  beforeDestroy() {
    window.removeEventListener('click', this.closeMenu)
    window.removeEventListener('contextmenu', this.setPosition)
  },
  methods: {
    closeMenu () {
@@ -62,9 +61,10 @@
      this.left = e.clientX
      this.top = e.clientY
      this.target = e.target
      this.meta = e.meta
    },
    handleClick ({ key }) {
      this.$emit('select', key, this.target)
      this.$emit('select', key, this.target, this.meta)
      this.closeMenu()
    }
  }
src/layouts/tabs/TabsHead.vue
@@ -5,9 +5,6 @@
        :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
@@ -18,7 +15,11 @@
        />
      </a-tooltip>
      <a-tab-pane v-for="page in pageList" :key="page.fullPath">
        <span slot="tab" :pagekey="page.fullPath">{{pageName(page)}}</span>
        <div slot="tab" class="tab" @contextmenu="e => onContextmenu(page.fullPath, e)">
          <a-icon v-if="page.fullPath === active || page.loading" @click="onRefresh(page)" class="icon-sync" :type="page.loading ? 'loading' : 'sync'" />
          <span @click="onTabClick(page.fullPath)" >{{pageName(page)}}</span>
          <a-icon @click="onClose(page.fullPath)" class="icon-close" type="close"/>
        </div>
      </a-tab-pane>
    </a-tabs>
    <div v-if="affixed" class="virtual-tabs"></div>
@@ -58,11 +59,6 @@
      }
    },
    inject:['adminLayout'],
    watch: {
      'adminLayout.collapsed': (val) => {
        console.log(val)
      }
    },
    created() {
      this.affixed = this.fixedTabs
    },
@@ -84,16 +80,19 @@
          this.affixed = false
        }
      },
      onChange(key) {
      onTabClick(key) {
        if (this.active !== key) {
        this.$emit('change', key)
      },
      onEdit(key, action) {
        if (action === 'remove') {
          this.$emit('close', key)
        }
      },
      onContextmenu(e) {
        this.$emit('contextmenu', e)
      onClose(key) {
        this.$emit('close', key)
      },
      onRefresh(page) {
        this.$emit('refresh', page.fullPath, page)
      },
      onContextmenu(pageKey, e) {
        this.$emit('contextmenu', pageKey, e)
      },
      pageName(page) {
        return this.$t(getI18nKey(page.keyPath))
@@ -103,6 +102,28 @@
</script>
<style scoped lang="less">
  .tab{
    margin: 0 -16px;
    padding: 0 16px;
    font-size: 14px;
    user-select: none;
    .icon-close{
      font-size: 12px;
      margin-left: 6px;
      margin-right: -4px;
      color: @text-color-second;
      &:hover{
        color: @text-color;
      }
    }
    .icon-sync{
      margin-left: -4px;
      color: @primary-4;
      &:hover{
        color: @primary-color;
      }
    }
  }
  .tabs-head{
    margin: 0 auto;
    &.head.fixed{
src/layouts/tabs/TabsView.vue
@@ -7,12 +7,13 @@
        :page-list="pageList"
        @change="changePage"
        @close="remove"
        @refresh="refresh"
        @contextmenu="onContextmenu"
    />
    <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">
        <a-keep-alive v-if="multiPage" v-model="clearCaches">
          <router-view ref="tabContent" :key="$route.fullPath" />
          <router-view v-if="!refreshing" ref="tabContent" :key="$route.fullPath" />
        </a-keep-alive>
        <router-view v-else />
      </page-toggle-transition>
@@ -38,7 +39,8 @@
      clearCaches: [],
      pageList: [],
      activePage: '',
      menuVisible: false
      menuVisible: false,
      refreshing: false
    }
  },
  computed: {
@@ -47,7 +49,8 @@
      return [
        { key: '1', icon: 'vertical-right', text: this.$t('closeLeft') },
        { key: '2', icon: 'vertical-left', text: this.$t('closeRight') },
        { key: '3', icon: 'close', text: this.$t('closeOthers') }
        { key: '3', icon: 'close', text: this.$t('closeOthers') },
        { key: '4', icon: 'sync', text: this.$t('refresh') },
      ]
    },
    tabsOffset() {
@@ -106,9 +109,6 @@
      this.activePage = key
      this.$router.push(key)
    },
    editPage (key, action) {
      this[action](key) // remove
    },
    remove (key, next) {
      if (this.pageList.length === 1) {
        return this.$message.warning(this.$t('warn'))
@@ -124,19 +124,30 @@
        this.$router.push(this.activePage)
      }
    },
    onContextmenu (e) {
      const pageKey = getPageKey(e.target)
    refresh (key, page) {
      page = page || this.pageList.find(item => item.fullPath === key)
      page.loading = true
      this.clearCache(page)
      if (key === this.activePage) {
        this.reloadContent(() => page.loading = false)
      } else {
        // 其实刷新很快,加这个延迟纯粹为了 loading 状态多展示一会儿,让用户感知刷新这一过程
        setTimeout(() => page.loading = false, 500)
      }
    },
    onContextmenu(pageKey, e) {
      if (pageKey) {
        e.preventDefault()
        e.meta = pageKey
        this.menuVisible = true
      }
    },
    onMenuSelect (key, target) {
      let pageKey = getPageKey(target)
    onMenuSelect (key, target, pageKey) {
      switch (key) {
        case '1': this.closeLeft(pageKey); break
        case '2': this.closeRight(pageKey); break
        case '3': this.closeOthers(pageKey); break
        case '4': this.refresh(pageKey); break
        default: break
      }
    },
@@ -172,6 +183,22 @@
        this.$router.push(this.activePage)
      }
    },
    clearCache(page) {
      page._init_ = false
      this.clearCaches = [page.cachedKey]
    },
    reloadContent(onLoaded) {
      this.refreshing = true
      setTimeout(() => {
        this.refreshing = false
        this.$nextTick(() => {
          this.setCachedKey(this.$route)
          if (typeof onLoaded === 'function') {
            onLoaded.apply(this, [])
          }
        })
      }, 200)
    },
    pageName(page) {
      return this.$t(getI18nKey(page.keyPath))
    },
@@ -180,6 +207,7 @@
     */
    addListener() {
      window.addEventListener('page:close', this.closePageListener)
      window.addEventListener('page:refresh', this.refreshPageListener)
      window.addEventListener('unload', this.unloadListener)
    },
    /**
@@ -187,6 +215,7 @@
     */
    removeListener() {
      window.removeEventListener('page:close', this.closePageListener)
      window.removeEventListener('page:refresh', this.refreshPageListener)
      window.removeEventListener('unload', this.unloadListener)
    },
    /**
@@ -199,6 +228,14 @@
      this.remove(closePath, nextRoute)
    },
    /**
     * 页面刷新事件监听
     * @param event 页签关闭事件
     */
    refreshPageListener(event) {
      const {pageKey} = event.detail
      this.refresh(pageKey)
    },
    /**
     * 页面 unload 事件监听器,添加页签到 session 缓存,用于刷新时保留页签
     */
    unloadListener() {
@@ -206,7 +243,7 @@
      sessionStorage.setItem(process.env.VUE_APP_TBAS_KEY, JSON.stringify(tabs))
    },
    createPage(route) {
      return {keyPath: route.matched[route.matched.length - 1].path, fullPath: route.fullPath}
      return {keyPath: route.matched[route.matched.length - 1].path, fullPath: route.fullPath, loading: false}
    },
    /**
     * 设置页面缓存的key
@@ -239,20 +276,6 @@
    },
    ...mapMutations('setting', ['correctPageMinHeight'])
  }
}
/**
 * 由于ant-design-vue组件库的TabPane组件暂不支持自定义监听器,无法直接获取到右键target所在标签页的 pagekey 。故增加此方法用于
 * 查询右键target所在标签页的标识 pagekey ,以用于自定义右键菜单的事件处理。
 * 注:TabPane组件支持自定义监听器后可去除该方法并重构 ‘自定义右键菜单的事件处理’
 * @param target 查询开始目标
 * @param depth 查询层级深度 (查找层级最多不超过3层,超过3层深度直接返回 null)
 * @returns {String}
 */
function getPageKey (target, depth = 0) {
  if (depth > 2 || !target) {
    return null
  }
  return target.getAttribute('pagekey') || getPageKey(target.firstElementChild, ++depth)
}
</script>
src/layouts/tabs/i18n.js
@@ -4,18 +4,21 @@
      closeLeft: '关闭左侧',
      closeRight: '关闭右侧',
      closeOthers: '关闭其它',
      refresh: '刷新页面',
      warn: '这是最后一页,不能再关闭了',
    },
    HK: {
      closeLeft: '關閉左側',
      closeRight: '關閉右側',
      closeOthers: '關閉其它',
      refresh: '刷新頁面',
      warn: '這是最後一頁,不能再關閉了',
    },
    US: {
      closeLeft: 'close left',
      closeRight: 'close right',
      closeOthers: 'close others',
      refresh: 'refresh the page',
      warn: 'This is the last page, you can\'t close it',
    },
  }
src/plugins/tabs-page-plugin.js
@@ -5,6 +5,10 @@
        $closePage(closeRoute, nextRoute) {
          const event = new CustomEvent('page:close', {detail:{closeRoute, nextRoute}})
          window.dispatchEvent(event)
        },
        $refreshPage(pageKey) {
          const event = new CustomEvent('page:refresh', {detail:{pageKey}})
          window.dispatchEvent(event)
        }
      }
    })