menu.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import Menu from 'ant-design-vue/es/menu'
  2. import Icon from 'ant-design-vue/es/icon'
  3. const { Item, SubMenu } = Menu
  4. const IconFont = Icon.createFromIconfontCN({
  5. scriptUrl: '//at.alicdn.com/t/c/font_3266072_5m7ce94jj1.js'
  6. })
  7. export default {
  8. name: 'SMenu',
  9. props: {
  10. menu: {
  11. type: Array,
  12. required: true
  13. },
  14. theme: {
  15. type: String,
  16. required: false,
  17. default: 'dark'
  18. },
  19. mode: {
  20. type: String,
  21. required: false,
  22. default: 'inline'
  23. },
  24. collapsed: {
  25. type: Boolean,
  26. required: false,
  27. default: false
  28. }
  29. },
  30. data () {
  31. return {
  32. openKeys: [],
  33. selectedKeys: [],
  34. cachedOpenKeys: []
  35. }
  36. },
  37. computed: {
  38. rootSubmenuKeys: vm => {
  39. const keys = []
  40. vm.menu.forEach(item => keys.push(item.path))
  41. return keys
  42. }
  43. },
  44. mounted () {
  45. this.updateMenu()
  46. },
  47. watch: {
  48. collapsed (val) {
  49. if (val) {
  50. this.cachedOpenKeys = this.openKeys.concat()
  51. this.openKeys = []
  52. } else {
  53. this.openKeys = this.cachedOpenKeys
  54. }
  55. },
  56. $route: function () {
  57. this.updateMenu()
  58. }
  59. },
  60. methods: {
  61. // select menu item
  62. onOpenChange (openKeys) {
  63. // 在水平模式下时执行,并且不再执行后续
  64. if (this.mode === 'horizontal') {
  65. this.openKeys = openKeys
  66. return
  67. }
  68. // 非水平模式时
  69. const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key))
  70. if (!this.rootSubmenuKeys.includes(latestOpenKey)) {
  71. this.openKeys = openKeys
  72. } else {
  73. this.openKeys = latestOpenKey ? [latestOpenKey] : []
  74. }
  75. },
  76. updateMenu () {
  77. const routes = this.$route.matched.concat()
  78. const { hidden } = this.$route.meta
  79. if (routes.length >= 3 && hidden) {
  80. routes.pop()
  81. this.selectedKeys = [routes[routes.length - 1].path]
  82. } else {
  83. this.selectedKeys = [routes.pop().path]
  84. }
  85. const openKeys = []
  86. if (this.mode === 'inline') {
  87. routes.forEach(item => {
  88. openKeys.push(item.path)
  89. })
  90. }
  91. this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
  92. },
  93. // render
  94. renderItem (menu) {
  95. if (!menu.hidden) {
  96. return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu)
  97. }
  98. return null
  99. },
  100. renderMenuItem (menu) {
  101. const target = menu.meta.target || null
  102. const tag = target && 'a' || 'router-link'
  103. const props = { to: { name: menu.name } }
  104. const attrs = { href: menu.path, target: menu.meta.target }
  105. if (menu.children && menu.hideChildrenInMenu) {
  106. // 把有子菜单的 并且 父菜单是要隐藏子菜单的
  107. // 都给子菜单增加一个 hidden 属性
  108. // 用来给刷新页面时, selectedKeys 做控制用
  109. menu.children.forEach(item => {
  110. item.meta = Object.assign(item.meta, { hidden: true })
  111. })
  112. }
  113. return (
  114. <Item {...{ key: menu.path }}>
  115. <tag {...{ props, attrs }}>
  116. {this.renderIcon(menu.meta.icon)}
  117. <span>{menu.meta.title}</span>
  118. </tag>
  119. </Item>
  120. )
  121. },
  122. renderSubMenu (menu) {
  123. const itemArr = []
  124. if (!menu.hideChildrenInMenu) {
  125. menu.children.forEach(item => itemArr.push(this.renderItem(item)))
  126. }
  127. return (
  128. <SubMenu {...{ key: menu.path }}>
  129. <span slot="title">
  130. {this.renderIcon(menu.meta.icon)}
  131. <span>{menu.meta.title}</span>
  132. </span>
  133. {itemArr}
  134. </SubMenu>
  135. )
  136. },
  137. renderIcon (icon) {
  138. if (icon === 'none' || icon === undefined) {
  139. return null
  140. }
  141. const props = {}
  142. typeof (icon) === 'object' ? props.component = icon : props.type = icon
  143. if (icon.includes('icon-')) {
  144. return <IconFont {... { props } }/>
  145. } else {
  146. return <Icon {... { props } }/>
  147. }
  148. }
  149. },
  150. render () {
  151. const { mode, theme, menu } = this
  152. const props = {
  153. mode: mode,
  154. theme: theme,
  155. openKeys: this.openKeys
  156. }
  157. const on = {
  158. select: obj => {
  159. this.selectedKeys = obj.selectedKeys
  160. this.$emit('select', obj)
  161. },
  162. openChange: this.onOpenChange
  163. }
  164. const menuTree = menu.map(item => {
  165. if (item.hidden) {
  166. return null
  167. }
  168. return this.renderItem(item)
  169. })
  170. // {...{ props, on: on }}
  171. return (
  172. <Menu vModel={this.selectedKeys} {...{ props, on: on }}>
  173. {menuTree}
  174. </Menu>
  175. )
  176. }
  177. }