MultiTab.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. <script>
  2. import events from './events'
  3. export default {
  4. name: 'MultiTab',
  5. data () {
  6. return {
  7. fullPathList: [],
  8. tabsList: [],
  9. pages: [],
  10. activeKey: '',
  11. newTabIndex: 0
  12. }
  13. },
  14. inject: ['reloadView'],
  15. created () {
  16. // bind event
  17. events.$on('open', val => {
  18. if (!val) {
  19. throw new Error(`multi-tab: open tab ${val} err`)
  20. }
  21. this.activeKey = val
  22. }).$on('close', val => {
  23. if (!val) {
  24. this.closeThat(this.activeKey)
  25. return
  26. }
  27. this.closeThat(val)
  28. }).$on('rename', ({ key, name }) => {
  29. // console.log('rename', key, name)
  30. try {
  31. const item = this.pages.find(item => item.path === key)
  32. item.meta.customTitle = name
  33. this.$forceUpdate()
  34. } catch (e) {
  35. }
  36. })
  37. this.pages.push(this.$route)
  38. this.tabsList.push(this.$route.name)
  39. this.selectedLastPath()
  40. },
  41. methods: {
  42. onEdit (targetKey, action) {
  43. this[action](targetKey)
  44. },
  45. onChange (targetKey) {
  46. const index = this.tabsList.indexOf(targetKey)
  47. if (index >= 0) {
  48. const row = this.pages[index]
  49. if (row && row.fullPath && row.fullPath.indexOf('/list') == -1) { // 非列表页
  50. this.$store.state.app.isNewSubTab = true
  51. } else {
  52. this.$store.state.app.isNewSubTab = false
  53. }
  54. }
  55. },
  56. remove (targetKey) {
  57. this.pages = this.pages.filter(page => page.name !== targetKey)
  58. this.tabsList = this.tabsList.filter(name => name !== targetKey)
  59. // 判断当前标签是否关闭,若关闭则跳转到最后一个还存在的标签页
  60. if (!this.tabsList.includes(this.activeKey)) {
  61. this.selectedLastPath()
  62. }
  63. },
  64. selectedLastPath () {
  65. this.activeKey = this.tabsList[this.tabsList.length - 1]
  66. },
  67. // content menu
  68. refashThat (e) {
  69. this.reloadView()
  70. },
  71. closeThat (e) {
  72. // 判断是否为最后一个标签页,如果是最后一个,则无法被关闭
  73. if (this.tabsList.length > 1) {
  74. this.remove(e)
  75. } else {
  76. this.$message.info('这是最后一个标签了, 无法被关闭')
  77. }
  78. },
  79. closeLeft (e) {
  80. const currentIndex = this.tabsList.indexOf(e)
  81. if (currentIndex > 0) {
  82. this.tabsList.forEach((item, index) => {
  83. if (index < currentIndex) {
  84. this.remove(item)
  85. }
  86. })
  87. } else {
  88. this.$message.info('左侧没有标签')
  89. }
  90. },
  91. closeRight (e) {
  92. const currentIndex = this.tabsList.indexOf(e)
  93. if (currentIndex < (this.tabsList.length - 1)) {
  94. this.tabsList.forEach((item, index) => {
  95. if (index > currentIndex) {
  96. this.remove(item)
  97. }
  98. })
  99. } else {
  100. this.$message.info('右侧没有标签')
  101. }
  102. },
  103. closeAll (e) {
  104. const currentIndex = this.tabsList.indexOf(e)
  105. this.tabsList.forEach((item, index) => {
  106. if (index !== currentIndex) {
  107. this.remove(item)
  108. }
  109. })
  110. },
  111. closeMenuClick (key, route) {
  112. this[key](route)
  113. },
  114. renderTabPaneMenu (e) {
  115. // return (
  116. // <a-menu {...{ on: { click: ({ key, item, domEvent }) => { this.closeMenuClick(key, e) } } }}>
  117. // {this.activeKey == e ? (<a-menu-item key="refashThat">刷新页面</a-menu-item>) : ''}
  118. // <a-menu-item key="closeRight">关闭右侧</a-menu-item>
  119. // <a-menu-item key="closeLeft">关闭左侧</a-menu-item>
  120. // <a-menu-item key="closeAll">关闭全部</a-menu-item>
  121. // </a-menu>
  122. // )
  123. return (
  124. <a-menu {...{ on: { click: ({ key, item, domEvent }) => { this.closeMenuClick(key, e) } } }}>
  125. <a-menu-item key="closeRight">关闭右侧</a-menu-item>
  126. <a-menu-item key="closeLeft">关闭左侧</a-menu-item>
  127. <a-menu-item key="closeAll">关闭全部</a-menu-item>
  128. </a-menu>
  129. )
  130. },
  131. // render
  132. renderTabPane (title, keyPath) {
  133. const menu = this.renderTabPaneMenu(keyPath)
  134. return (
  135. <a-dropdown overlay={menu} trigger={['contextmenu']}>
  136. <span style={{ userSelect: 'none' }}>{ title }</span>
  137. </a-dropdown>
  138. )
  139. }
  140. },
  141. watch: {
  142. '$route': function (newVal, oldVal) {
  143. // 父页面(列表)进入子页面(编辑、详情)再返回至父页面时关闭子页面页签 子页面点击“返回列表”
  144. if (oldVal && oldVal.name && newVal.query.closeLastOldTab) {
  145. if (oldVal.name.indexOf('List') == -1) { // 要关闭的页面不能是列表页
  146. this.remove(oldVal.name) // 关闭子页面
  147. }
  148. }
  149. this.activeKey = newVal.name
  150. const index = this.tabsList.indexOf(newVal.name)
  151. // 解决页面地址相同参数不同时打开多个页签问题(例 详情页id或sn不同时)
  152. if (index < 0) { // 不存在
  153. this.tabsList.push(newVal.name)
  154. this.pages.push(newVal)
  155. this.$store.state.app.isNewTab = true
  156. } else { // 已存在
  157. this.pages.splice(index, 1, newVal)
  158. this.$store.state.app.isNewTab = false
  159. }
  160. },
  161. activeKey: function (newPathKey) {
  162. const row = this.pages.find(item => item.name == newPathKey)
  163. if (row.fullPath.indexOf('/list') == -1) {
  164. this.$router.push({ path: row.fullPath })
  165. } else {
  166. // this.$router.push({ path: row.fullPath, query: { closeLastOldTab: true } }) // 需求:切换页签需关掉子页面
  167. this.$router.push({ path: row.path }) // 需求:切换页签不需关掉子页面
  168. }
  169. }
  170. },
  171. render () {
  172. const { onEdit, onChange, refashThat, $data: { pages } } = this
  173. const panes = pages.map(page => {
  174. return (
  175. <a-tab-pane
  176. style={{ height: 0 }}
  177. key={page.name} closable={pages.length > 1}
  178. >
  179. <span slot="tab">
  180. {this.renderTabPane(page.meta.customTitle || page.meta.title, page.name)}
  181. {this.activeKey == 'page.name' ? (<a-icon
  182. {...{ on: { click: refashThat } }}
  183. style={{ marginLeft: '8px', fontSize: '12px' }}
  184. type="reload" />) : ''}
  185. </span>
  186. </a-tab-pane>)
  187. })
  188. return (
  189. <div class="ant-pro-mu lti-tab">
  190. <div class="ant-pro-multi-tab-wrapper multi-tab-con">
  191. <a-tabs
  192. hideAdd
  193. type={'editable-card'}
  194. v-model={this.activeKey}
  195. tabBarStyle={{ background: '#FFF', margin: 0, paddingLeft: '16px', paddingTop: '1px' }}
  196. {...{ on: { edit: onEdit, 'change': onChange } }}>
  197. {panes}
  198. </a-tabs>
  199. </div>
  200. </div>
  201. )
  202. }
  203. }
  204. </script>