MultiTab.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <script>
  2. import events from './events'
  3. export default {
  4. name: 'MultiTab',
  5. data () {
  6. return {
  7. pagesRecordList: [], // 记录打开过的页面
  8. tabsList: [], // 已打开页签
  9. pages: [], // 已打开页面
  10. activeKey: '', // 当前页面
  11. newTabIndex: 0,
  12. maxTabNums: 10
  13. }
  14. },
  15. inject: ['reloadView'],
  16. created () {
  17. // bind event
  18. events.$on('open', val => {
  19. if (!val) {
  20. throw new Error(`multi-tab: open tab ${val} err`)
  21. }
  22. this.activeKey = val
  23. }).$on('close', val => {
  24. if (!val) {
  25. this.closeThat(this.activeKey)
  26. return
  27. }
  28. this.closeThat(val)
  29. }).$on('rename', ({ key, name }) => {
  30. // console.log('rename', key, name)
  31. try {
  32. const item = this.pages.find(item => item.path === key)
  33. item.meta.customTitle = name
  34. this.$forceUpdate()
  35. } catch (e) {
  36. }
  37. })
  38. console.log('rename')
  39. if (this.pages.length <= this.maxTabNums) {
  40. this.pages.push(this.$route)
  41. this.tabsList.push(this.$route.name)
  42. this.pagesRecordList.push(this.$route.name)
  43. this.selectedLastPath()
  44. }
  45. },
  46. methods: {
  47. onEdit (targetKey, action) {
  48. this[action](targetKey)
  49. },
  50. onChange (targetKey) {
  51. const index = this.tabsList.indexOf(targetKey)
  52. if (index >= 0) {
  53. const row = this.pages[index]
  54. if (row && row.fullPath && row.fullPath.indexOf('/list') == -1) { // 非列表页,根据当前路由路径判断是子页还是列表页
  55. this.$store.state.app.isNewSubTab = true
  56. } else {
  57. this.$store.state.app.isNewSubTab = false
  58. }
  59. }
  60. },
  61. remove (targetKey) {
  62. this.pages = this.pages.filter(page => page.name !== targetKey)
  63. this.tabsList = this.tabsList.filter(name => name !== targetKey)
  64. // 判断当前标签是否关闭,若关闭则跳转到最后一个还存在的标签页
  65. if (!this.tabsList.includes(this.activeKey)) {
  66. this.selectedLastPath()
  67. }
  68. },
  69. selectedLastPath () {
  70. this.activeKey = this.tabsList[this.tabsList.length - 1]
  71. },
  72. replaceTab (newtab, oldtab) {
  73. const index = this.pages.findIndex(item => item.name == oldtab.name)
  74. this.pages.splice(index, 1, newtab)
  75. this.tabsList.splice(index, 1, newtab.name)
  76. },
  77. // content menu
  78. refashThat (e) {
  79. this.reloadView()
  80. },
  81. closeThat (e) {
  82. // 判断是否为最后一个标签页,如果是最后一个,则无法被关闭
  83. if (this.tabsList.length > 1) {
  84. this.remove(e)
  85. } else {
  86. this.$message.info('这是最后一个标签了, 无法被关闭')
  87. }
  88. },
  89. closeLeft (e) {
  90. const currentIndex = this.tabsList.indexOf(e)
  91. if (currentIndex > 0) {
  92. this.tabsList.forEach((item, index) => {
  93. if (index < currentIndex) {
  94. this.remove(item)
  95. }
  96. })
  97. } else {
  98. this.$message.info('左侧没有标签')
  99. }
  100. },
  101. closeRight (e) {
  102. const currentIndex = this.tabsList.indexOf(e)
  103. if (currentIndex < (this.tabsList.length - 1)) {
  104. this.tabsList.forEach((item, index) => {
  105. if (index > currentIndex) {
  106. this.remove(item)
  107. }
  108. })
  109. } else {
  110. this.$message.info('右侧没有标签')
  111. }
  112. },
  113. closeAll (e) {
  114. const currentIndex = this.tabsList.indexOf(e)
  115. this.tabsList.forEach((item, index) => {
  116. if (index !== currentIndex) {
  117. this.remove(item)
  118. }
  119. })
  120. },
  121. closeMenuClick (key, route) {
  122. this[key](route)
  123. },
  124. renderTabPaneMenu (e) {
  125. // return (
  126. // <a-menu {...{ on: { click: ({ key, item, domEvent }) => { this.closeMenuClick(key, e) } } }}>
  127. // {this.activeKey == e ? (<a-menu-item key="refashThat">刷新页面</a-menu-item>) : ''}
  128. // <a-menu-item key="closeRight">关闭右侧</a-menu-item>
  129. // <a-menu-item key="closeLeft">关闭左侧</a-menu-item>
  130. // <a-menu-item key="closeAll">关闭全部</a-menu-item>
  131. // </a-menu>
  132. // )
  133. return (
  134. <a-menu {...{ on: { click: ({ key, item, domEvent }) => { this.closeMenuClick(key, e) } } }}>
  135. <a-menu-item key="closeRight">关闭右侧</a-menu-item>
  136. <a-menu-item key="closeLeft">关闭左侧</a-menu-item>
  137. <a-menu-item key="closeAll">关闭全部</a-menu-item>
  138. </a-menu>
  139. )
  140. },
  141. // render
  142. renderTabPane (title, keyPath) {
  143. const menu = this.renderTabPaneMenu(keyPath)
  144. return (
  145. <a-dropdown overlay={menu} trigger={['contextmenu']}>
  146. <span style={{ userSelect: 'none' }}>{ title }</span>
  147. </a-dropdown>
  148. )
  149. }
  150. },
  151. watch: {
  152. '$route': function (newVal, oldVal) {
  153. if (newVal.name == oldVal.name) { return }
  154. console.log(newVal, oldVal)
  155. // 解决页面地址相同参数不同时打开多个页签问题(例 详情页id或sn不同时)
  156. const index = this.tabsList.indexOf(newVal.name)
  157. // 是否时同一个父级菜单下的页面
  158. const mnlen = newVal.matched.length < 3
  159. const molen = oldVal.matched.length < 3
  160. const hasChildren = newVal.matched[mnlen ? 1 : 2].name == oldVal.matched[molen ? 1 : 2].name
  161. // console.log(newVal.matched[mnlen ? 1 : 2].name, oldVal.matched[molen ? 1 : 2].name, hasChildren)
  162. if (index < 0) { // 不存在
  163. console.log(1, '不存在')
  164. // 判断是否是新增,编辑,详情页
  165. const na = newVal.meta.title.indexOf('详情') >= 0
  166. const nb = newVal.meta.title.indexOf('新增') >= 0
  167. const nc = newVal.meta.title.indexOf('编辑') >= 0
  168. const nxt = newVal.meta.title.indexOf('下推销售单') >= 0
  169. const oa = oldVal.meta.title.indexOf('详情') >= 0
  170. const ob = oldVal.meta.title.indexOf('新增') >= 0
  171. const oc = oldVal.meta.title.indexOf('编辑') >= 0
  172. const oxt = oldVal.meta.title.indexOf('下推销售单') >= 0
  173. if ((na || nb || nc || nxt || oa || ob || oc || oxt) && hasChildren) {
  174. // 替换当前页签
  175. this.replaceTab(newVal, oldVal)
  176. // 从列表打开新增,编辑
  177. this.$store.state.app.isNewTab = na || nb || nc
  178. // 从编辑,新增返回列表,刷新列表
  179. this.$store.state.app.updateList = ob || oc
  180. this.activeKey = newVal.name
  181. } else {
  182. // 新打开的页签
  183. if (this.pages.length < this.maxTabNums) {
  184. this.tabsList.push(newVal.name)
  185. this.pages.push(newVal)
  186. this.pagesRecordList.push(newVal.name)
  187. this.$store.state.app.isNewTab = true
  188. this.$store.state.app.updateList = false
  189. this.activeKey = newVal.name
  190. } else {
  191. // this.$message.info('您当前打开窗口太多,请关闭一些,窗口不能超过' + this.maxTabNums + '个')
  192. const _this = this
  193. this.$warning({
  194. title: '提示',
  195. content: '您当前打开窗口太多,请关闭一些,窗口不能超过' + this.maxTabNums + '个',
  196. onOk () {
  197. _this.$router.go(-1)
  198. }
  199. })
  200. }
  201. }
  202. } else { // 已存在
  203. console.log(1, '已存在')
  204. this.pages.splice(index, 1, newVal)
  205. this.tabsList.splice(index, 1, newVal.name)
  206. this.$store.state.app.isNewTab = false
  207. this.activeKey = newVal.name
  208. }
  209. },
  210. activeKey: function (newPathKey) {
  211. console.log('-----' + newPathKey)
  212. const pages = this.pages.find(item => item.name == newPathKey)
  213. this.$router.push({ name: newPathKey, params: pages.params, query: pages.query })
  214. }
  215. },
  216. render () {
  217. const { onEdit, onChange, refashThat, $data: { pages, maxTabNums } } = this
  218. const tabPanel = pages.length > maxTabNums ? pages.slice(0, maxTabNums - 1) : pages
  219. const panes = tabPanel.map(page => {
  220. return (
  221. <a-tab-pane
  222. style={{ height: 0 }}
  223. key={page.name} closable={tabPanel.length > 1}
  224. >
  225. <span slot="tab">
  226. {this.renderTabPane(page.meta.customTitle || page.meta.title, page.name)}
  227. {this.activeKey == 'page.name' ? (<a-icon
  228. {...{ on: { click: refashThat } }}
  229. style={{ marginLeft: '8px', fontSize: '12px' }}
  230. type="reload" />) : ''}
  231. </span>
  232. </a-tab-pane>)
  233. })
  234. return (
  235. <div class="ant-pro-mu lti-tab">
  236. <div class="ant-pro-multi-tab-wrapper multi-tab-con">
  237. <a-tabs
  238. hideAdd
  239. type={'editable-card'}
  240. v-model={this.activeKey}
  241. tabBarStyle={{ background: '#FFF', margin: 0, paddingLeft: '16px', paddingTop: '1px' }}
  242. {...{ on: { edit: onEdit, 'change': onChange } }}>
  243. {panes}
  244. </a-tabs>
  245. </div>
  246. </div>
  247. )
  248. }
  249. }
  250. </script>