Menu.vue 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <script lang="tsx">
  2. import { computed, defineComponent, unref, PropType } from 'vue'
  3. import { ElMenu, ElScrollbar } from 'element-plus'
  4. import { useAppStore } from '@/store/modules/app'
  5. import { usePermissionStore } from '@/store/modules/permission'
  6. import { useRenderMenuItem } from './components/useRenderMenuItem'
  7. import { useRouter } from 'vue-router'
  8. import { isUrl } from '@/utils/is'
  9. import { useDesign } from '@/hooks/web/useDesign'
  10. const { getPrefixCls } = useDesign()
  11. const prefixCls = getPrefixCls('menu')
  12. export default defineComponent({
  13. name: 'Menu',
  14. props: {
  15. menuSelect: {
  16. type: Function as PropType<(index: string) => void>,
  17. default: undefined
  18. }
  19. },
  20. setup(props) {
  21. const appStore = useAppStore()
  22. const layout = computed(() => appStore.getLayout)
  23. const { push, currentRoute } = useRouter()
  24. const permissionStore = usePermissionStore()
  25. const menuMode = computed((): 'vertical' | 'horizontal' => {
  26. // 竖
  27. const vertical: LayoutType[] = ['classic', 'topLeft', 'cutMenu']
  28. if (vertical.includes(unref(layout))) {
  29. return 'vertical'
  30. } else {
  31. return 'horizontal'
  32. }
  33. })
  34. const routers = computed(() =>
  35. unref(layout) === 'cutMenu' ? permissionStore.getMenuTabRouters : permissionStore.getRouters
  36. )
  37. const collapse = computed(() => appStore.getCollapse)
  38. const uniqueOpened = computed(() => appStore.getUniqueOpened)
  39. const activeMenu = computed(() => {
  40. const { meta, path } = unref(currentRoute)
  41. // if set path, the sidebar will highlight the path you set
  42. if (meta.activeMenu) {
  43. return meta.activeMenu as string
  44. }
  45. return path
  46. })
  47. const menuSelect = (index: string) => {
  48. if (props.menuSelect) {
  49. props.menuSelect(index)
  50. }
  51. // 自定义事件
  52. if (isUrl(index)) {
  53. window.open(index)
  54. } else {
  55. push(index)
  56. }
  57. }
  58. const renderMenuWrap = () => {
  59. if (unref(layout) === 'top') {
  60. return renderMenu()
  61. } else {
  62. return <ElScrollbar>{renderMenu()}</ElScrollbar>
  63. }
  64. }
  65. const renderMenu = () => {
  66. return (
  67. <ElMenu
  68. defaultActive={unref(activeMenu)}
  69. mode={unref(menuMode)}
  70. collapse={
  71. unref(layout) === 'top' || unref(layout) === 'cutMenu' ? false : unref(collapse)
  72. }
  73. uniqueOpened={unref(layout) === 'top' ? false : unref(uniqueOpened)}
  74. backgroundColor="var(--left-menu-bg-color)"
  75. textColor="var(--left-menu-text-color)"
  76. activeTextColor="var(--left-menu-text-active-color)"
  77. popperClass={
  78. unref(menuMode) === 'vertical'
  79. ? `${prefixCls}-popper--vertical`
  80. : `${prefixCls}-popper--horizontal`
  81. }
  82. onSelect={menuSelect}
  83. >
  84. {{
  85. default: () => {
  86. const { renderMenuItem } = useRenderMenuItem(menuMode)
  87. return renderMenuItem(unref(routers))
  88. }
  89. }}
  90. </ElMenu>
  91. )
  92. }
  93. return () => (
  94. <div
  95. id={prefixCls}
  96. class={[
  97. `${prefixCls} ${prefixCls}__${unref(menuMode)}`,
  98. 'h-[100%] overflow-hidden flex-col bg-[var(--left-menu-bg-color)]',
  99. {
  100. 'w-[var(--left-menu-min-width)]': unref(collapse) && unref(layout) !== 'cutMenu',
  101. 'w-[var(--left-menu-max-width)]': !unref(collapse) && unref(layout) !== 'cutMenu'
  102. }
  103. ]}
  104. >
  105. {renderMenuWrap()}
  106. </div>
  107. )
  108. }
  109. })
  110. </script>
  111. <style lang="less" scoped>
  112. @prefix-cls: ~'@{adminNamespace}-menu';
  113. .@{prefix-cls} {
  114. position: relative;
  115. transition: width var(--transition-time-02);
  116. :deep(.@{elNamespace}-menu) {
  117. width: 100% !important;
  118. border-right: none;
  119. // 设置选中时子标题的颜色
  120. .is-active {
  121. & > .@{elNamespace}-sub-menu__title {
  122. color: var(--left-menu-text-active-color) !important;
  123. }
  124. }
  125. // 设置子菜单悬停的高亮和背景色
  126. .@{elNamespace}-sub-menu__title,
  127. .@{elNamespace}-menu-item {
  128. &:hover {
  129. color: var(--left-menu-text-active-color) !important;
  130. background-color: var(--left-menu-bg-color) !important;
  131. }
  132. }
  133. // 设置选中时的高亮背景和高亮颜色
  134. .@{elNamespace}-menu-item.is-active {
  135. color: var(--left-menu-text-active-color) !important;
  136. background-color: var(--left-menu-bg-active-color) !important;
  137. &:hover {
  138. background-color: var(--left-menu-bg-active-color) !important;
  139. }
  140. }
  141. .@{elNamespace}-menu-item.is-active {
  142. position: relative;
  143. }
  144. // 设置子菜单的背景颜色
  145. .@{elNamespace}-menu {
  146. .@{elNamespace}-sub-menu__title,
  147. .@{elNamespace}-menu-item:not(.is-active) {
  148. background-color: var(--left-menu-bg-light-color) !important;
  149. }
  150. }
  151. }
  152. // 折叠时的最小宽度
  153. :deep(.@{elNamespace}-menu--collapse) {
  154. width: var(--left-menu-min-width);
  155. & > .is-active,
  156. & > .is-active > .@{elNamespace}-sub-menu__title {
  157. position: relative;
  158. background-color: var(--left-menu-collapse-bg-active-color) !important;
  159. }
  160. }
  161. // 折叠动画的时候,就需要把文字给隐藏掉
  162. :deep(.horizontal-collapse-transition) {
  163. .@{prefix-cls}__title {
  164. display: none;
  165. }
  166. }
  167. // 水平菜单
  168. &__horizontal {
  169. height: calc(~'var(--top-tool-height)') !important;
  170. :deep(.@{elNamespace}-menu--horizontal) {
  171. height: calc(~'var(--top-tool-height)');
  172. border-bottom: none;
  173. // 重新设置底部高亮颜色
  174. & > .@{elNamespace}-sub-menu.is-active {
  175. .@{elNamespace}-sub-menu__title {
  176. border-bottom-color: var(--el-color-primary) !important;
  177. }
  178. }
  179. .@{elNamespace}-menu-item.is-active {
  180. position: relative;
  181. &::after {
  182. display: none !important;
  183. }
  184. }
  185. .@{prefix-cls}__title {
  186. /* stylelint-disable-next-line */
  187. max-height: calc(~'var(--top-tool-height) - 2px') !important;
  188. /* stylelint-disable-next-line */
  189. line-height: calc(~'var(--top-tool-height) - 2px');
  190. }
  191. }
  192. }
  193. }
  194. </style>
  195. <style lang="less">
  196. @prefix-cls: ~'@{adminNamespace}-menu-popper';
  197. .@{prefix-cls}--vertical,
  198. .@{prefix-cls}--horizontal {
  199. // 设置选中时子标题的颜色
  200. .is-active {
  201. & > .el-sub-menu__title {
  202. color: var(--left-menu-text-active-color) !important;
  203. }
  204. }
  205. // 设置子菜单悬停的高亮和背景色
  206. .el-sub-menu__title,
  207. .el-menu-item {
  208. &:hover {
  209. color: var(--left-menu-text-active-color) !important;
  210. background-color: var(--left-menu-bg-color) !important;
  211. }
  212. }
  213. // 设置选中时的高亮背景
  214. .el-menu-item.is-active {
  215. position: relative;
  216. background-color: var(--left-menu-bg-active-color) !important;
  217. &:hover {
  218. background-color: var(--left-menu-bg-active-color) !important;
  219. }
  220. }
  221. }
  222. @submenu-prefix-cls: ~'@{adminNamespace}-submenu-popper';
  223. .@{submenu-prefix-cls}--vertical {
  224. overflow-y: auto;
  225. max-height: 100%;
  226. }
  227. </style>