Menu.vue 7.5 KB

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