index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <template>
  2. <div class="aside-menu--group">
  3. <div class="aside-menu--abbr-group">
  4. <div class="aside-menu--first-group">
  5. <div
  6. class="aside-menu--first-item"
  7. :class="{
  8. 'active': String(active.apex) === first._compute._key
  9. }"
  10. v-for="(first, index) in filterMenus"
  11. :key="first._compute._key"
  12. @click="onSelectApexMenu(first)"
  13. @mouseenter="onMouseEnterMenu(first, index)"
  14. @mouseleave="onMouseLeaveMenu($event, first)"
  15. >
  16. <JyIcon :name="first.icon" classPrefix=""></JyIcon>
  17. <span v-html="first.label"></span>
  18. </div>
  19. </div>
  20. <div class="aside-menu--toggle-group">
  21. <div
  22. class="aside-menu--toggle-item"
  23. :class="{
  24. active: item.key === type
  25. }"
  26. v-for="(item, i) in menuTypes"
  27. :key="i"
  28. @click="onSwitchMenuType(item)"
  29. v-html="item.label"
  30. >
  31. </div>
  32. </div>
  33. </div>
  34. <div class="aside-menu--open-group" v-show="filterSubMenus.length">
  35. <el-menu
  36. :default-openeds="defaultOpenSubMenus"
  37. >
  38. <el-submenu
  39. v-for="group in newFilterSubMenus"
  40. :key="group._compute._uniqueIndexes[1]"
  41. :index="group._compute._uniqueIndexes[0] + '-' + group._compute._uniqueIndexes[1]"
  42. >
  43. <template slot="title">
  44. <JyIcon :name="group.icon" classPrefix=""></JyIcon>
  45. <span class="group-title ellipsis" v-html="group.label"></span>
  46. </template>
  47. <el-menu-item-group v-if="group.child">
  48. <template slot="title"></template>
  49. <el-menu-item
  50. v-for="menu in group.child"
  51. :class="{
  52. active: activeMenu._compute._key === menu._compute._key
  53. }"
  54. :key="menu._compute._key"
  55. :index="menu._compute._key"
  56. @click="onSelectSubMenu(menu)"
  57. >
  58. <span v-html="menu.label"></span>
  59. </el-menu-item>
  60. </el-menu-item-group>
  61. </el-submenu>
  62. </el-menu>
  63. </div>
  64. <div class="aside-menu--open-group aside-menu--float-group" v-show="showFloatMenus" @mouseleave="showFloatMenus = false">
  65. <el-menu :default-openeds="openedSubMenu">
  66. <el-submenu
  67. v-for="group in floatMenusList"
  68. :key="group._compute._uniqueIndexes[1]"
  69. :index="group._compute._uniqueIndexes[0] + '-' + group._compute._uniqueIndexes[1]"
  70. >
  71. <template slot="title">
  72. <JyIcon :name="group.icon" classPrefix=""></JyIcon>
  73. <span class="group-title ellipsis" v-html="group.label"></span>
  74. </template>
  75. <el-menu-item-group v-if="group.child">
  76. <template slot="title"></template>
  77. <el-menu-item
  78. v-for="menu in group.child"
  79. :class="{
  80. active: activeMenu._compute._key === menu._compute._key
  81. }"
  82. :key="menu._compute._key"
  83. :index="menu._compute._key"
  84. @click="onSelectSubMenu(menu)"
  85. >
  86. <span v-html="menu.label"></span>
  87. </el-menu-item>
  88. </el-menu-item-group>
  89. </el-submenu>
  90. </el-menu>
  91. </div>
  92. </div>
  93. </template>
  94. <script>
  95. import { mapActions, mapGetters, mapMutations, mapState } from 'vuex'
  96. export default {
  97. name: 'aside-menu',
  98. props: {
  99. beforeSelect: {
  100. type: Function,
  101. default: () => true
  102. }
  103. },
  104. computed: {
  105. ...mapState('work-bench/menu', [
  106. 'type',
  107. 'active'
  108. ]),
  109. ...mapGetters('work-bench/menu', [
  110. 'filterMenus',
  111. 'menuTypes',
  112. 'filterSubMenus',
  113. 'defaultOpenSubMenus',
  114. 'activeMenu'
  115. ]),
  116. // 过滤三级分类为空的二级分类
  117. newFilterSubMenus () {
  118. return this.filterSubMenus.filter(v => v.child && v.child.length > 0)
  119. }
  120. },
  121. data () {
  122. return {
  123. showFloatMenus: false,
  124. floatMenusList: [],
  125. openedSubMenu: [] // 鼠标悬浮默认打开的子菜单索引(index)列表
  126. }
  127. },
  128. methods: {
  129. ...mapMutations('work-bench/menu', [
  130. 'setActiveOfType'
  131. ]),
  132. ...mapActions('work-bench/menu', [
  133. 'setMenuType'
  134. ]),
  135. checkSkipSetActive ({ appType = '', openType = '' }) {
  136. return appType === 'outer' || openType === '_blank'
  137. },
  138. /**
  139. * 附属菜单选中事件回调
  140. * @param menu
  141. * @param sIndex - 附属菜单索引
  142. * @param gIndex - 分组索引
  143. */
  144. async onSelectSubMenu (menu) {
  145. const canNext = await this.beforeSelect(menu)
  146. if (!canNext) {
  147. return
  148. }
  149. if (!menu?._compute) {
  150. menu._compute = {
  151. _uniqueIndexes: [this.activeMenu._compute._uniqueIndexes[0], 0, 0]
  152. }
  153. }
  154. const skipSetActive = this.checkSkipSetActive(menu)
  155. if (skipSetActive) {
  156. this.openMenuLink(menu)
  157. } else {
  158. this.setActiveOfType({
  159. type: 'apex',
  160. index: menu._compute._uniqueIndexes[0]
  161. })
  162. this.setActiveOfType({
  163. type: 'group',
  164. index: menu._compute._uniqueIndexes[1]
  165. })
  166. this.setActiveOfType({
  167. type: 'sub',
  168. index: menu._compute._uniqueIndexes[2]
  169. })
  170. this.openMenuLink()
  171. }
  172. },
  173. /**
  174. * 顶级菜单选中事件回调
  175. * @param menu - 菜单
  176. * @param i - 索引
  177. */
  178. async onSelectApexMenu (menu) {
  179. const canNext = await this.beforeSelect(menu)
  180. if (!canNext) {
  181. return
  182. }
  183. const skipSetActive = this.checkSkipSetActive(menu)
  184. if (!skipSetActive) {
  185. // 记录当前一级菜单索引
  186. this.setActiveOfType({
  187. type: 'apex',
  188. index: menu._compute._uniqueIndexes[0]
  189. })
  190. }
  191. // 判断是否需要打开二级菜单
  192. if (menu?.child?.length) {
  193. this.onSelectSubMenu(menu.child[0].child[0])
  194. } else {
  195. this.openMenuLink(menu)
  196. }
  197. this.showFloatMenus = false
  198. },
  199. /**
  200. * 切换 全部/可用 服务状态
  201. * @param item
  202. */
  203. onSwitchMenuType (item) {
  204. this.$BRACE.$emit({
  205. fKey: 'setMenuType',
  206. spareFn: (item, next) => next(item)
  207. }, item, this.setMenuType.bind(this))
  208. },
  209. /**
  210. * 打开菜单链接
  211. * @param menu
  212. */
  213. openMenuLink (menu) {
  214. this.$emit('open', menu || this.activeMenu)
  215. },
  216. onMouseEnterMenu (menu, index) {
  217. const noChild = !menu.child || (menu.child && menu.child.length === 0)
  218. const isActive = index === this.active?.apex
  219. if (noChild || isActive) return this.showFloatMenus = false
  220. this.floatMenusList = menu.child.filter(v => v.child.length > 0)
  221. const arr = []
  222. menu.child.forEach(v => {
  223. arr.push(v._compute._key)
  224. })
  225. this.openedSubMenu = arr
  226. this.showFloatMenus = true
  227. },
  228. onMouseLeaveMenu (e, menu) {
  229. const className = e.relatedTarget?.className
  230. if (className && (className === 'el-menu' || className.indexOf('aside-menu--') > -1)) return
  231. this.showFloatMenus = false
  232. this.floatMenusList = []
  233. }
  234. }
  235. }
  236. </script>
  237. <style scoped lang="scss">
  238. $aside-menu--color: #ffffff;
  239. $aside-menu--color-main: #2CB7CA;
  240. $aside-menu--color-secondary: #25A3B5;
  241. $aside-menu--color-gray: rgba(255, 255, 255, 0.7000);
  242. $aside-menu--color-black: #2C2E33;
  243. $aside-menu--text-color: #1D1D1D;
  244. $aside-menu--text-secondary-color: #686868;
  245. $aside-menu--apex-width: 60px;
  246. $aside-menu--apex-bg: $aside-menu--color-black;
  247. .aside-menu-- {
  248. &group {
  249. height: 100%;
  250. display: flex;
  251. flex-direction: row;
  252. }
  253. &abbr- {
  254. &group {
  255. display: flex;
  256. flex-direction: column;
  257. align-items: center;
  258. justify-content: space-between;
  259. width: $aside-menu--apex-width;
  260. padding: 8px 0;
  261. box-sizing: border-box;
  262. background: $aside-menu--apex-bg;
  263. font-size: 14px;
  264. font-weight: 400;
  265. }
  266. &item {
  267. display: flex;
  268. flex-direction: column;
  269. align-items: center;
  270. justify-content: center;
  271. }
  272. }
  273. &toggle- {
  274. &group {
  275. color: $aside-menu--color-gray;
  276. line-height: 16px;
  277. margin-bottom: 16px;
  278. }
  279. &item {
  280. padding: 8px 14px;
  281. text-align: center;
  282. cursor: pointer;
  283. &.active {
  284. color: $aside-menu--color-main;
  285. }
  286. }
  287. }
  288. &first- {
  289. &group {
  290. width: 100%;
  291. color: $aside-menu--color-gray;
  292. line-height: 22px;
  293. .iconfont {
  294. font-size: 18px;
  295. }
  296. }
  297. &item {
  298. min-height: 60px;
  299. display: flex;
  300. flex-direction: column;
  301. align-items: center;
  302. justify-content: center;
  303. cursor: pointer;
  304. &.active, &:hover {
  305. background: $aside-menu--color-secondary;
  306. color: $aside-menu--color;
  307. }
  308. }
  309. }
  310. &open- {
  311. &group {
  312. width: 152px;
  313. height: 100%;
  314. background-color: $aside-menu--color;
  315. overflow-y: scroll;
  316. .iconfont {
  317. font-size: 16px;
  318. margin-right: 8px;
  319. color: $aside-menu--text-color;
  320. }
  321. .group-title {
  322. display: inline-block;
  323. max-width: 5em;
  324. }
  325. ::v-deep {
  326. .el-menu {
  327. border: none;
  328. padding: 8px;
  329. box-sizing: border-box;
  330. background-color: inherit;
  331. &.el-menu--inline {
  332. padding: unset;
  333. }
  334. }
  335. .el-submenu__title {
  336. height: auto;
  337. font-size: 14px;
  338. font-weight: 400;
  339. color: $aside-menu--text-color;
  340. line-height: 22px;
  341. margin-top: 4px;
  342. padding: 8px;
  343. padding-left: 8px !important;
  344. }
  345. .el-menu-item {
  346. min-width: unset;
  347. height: auto;
  348. font-size: 12px;
  349. font-weight: 400;
  350. color: $aside-menu--text-secondary-color;
  351. line-height: 18px;
  352. padding: 8px;
  353. padding-left: 32px !important;
  354. white-space: normal;
  355. transition: unset;
  356. &.active,
  357. &:hover {
  358. color: $aside-menu--color-main;
  359. }
  360. }
  361. .el-submenu__title:focus,
  362. .el-menu-item:focus {
  363. background-color: inherit;
  364. }
  365. .el-submenu__title:hover,
  366. .el-submenu__title.active,
  367. .el-menu-item.active,
  368. .el-menu-item:hover {
  369. background: #EAF8FA;
  370. border-radius: 4px;
  371. }
  372. .el-submenu__title .el-submenu__icon-arrow {
  373. margin-top: -8px;
  374. right: 8px;
  375. font-family: "iconfont" !important;
  376. font-size: 16px;
  377. color: #8F9399;
  378. &::before {
  379. content: "\e656";
  380. }
  381. }
  382. .el-menu-item-group__title {
  383. display: none;
  384. }
  385. }
  386. }
  387. }
  388. &float-{
  389. &group{
  390. position: fixed;
  391. width: 152px;
  392. height: calc(100vh - 64px);
  393. left: 60px;
  394. top: 64px;
  395. background: #fff;
  396. overflow-y: scroll;
  397. z-index: 10;
  398. box-shadow: 2px 2px 16px 0 rgba(0, 0, 0, 0.08);
  399. }
  400. }
  401. }
  402. </style>