index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. <template>
  2. <div
  3. class="vab-fall-bar"
  4. :class="{
  5. 'is-collapse': collapse,
  6. }"
  7. >
  8. <vab-logo style="z-index: 999" />
  9. <fall-menu :data="handleRoutes">
  10. <template #level1="{ slotScope }">
  11. <a :title="translate(slotScope.meta.title)" @click="handleLink(slotScope)">
  12. <vab-icon :icon="slotScope.meta && slotScope.meta.icon" />
  13. <span>{{ translate(slotScope.meta.title) }}</span>
  14. <vab-icon v-if="slotScope.children" class="fall-icon-right" icon="arrow-right-s-line" />
  15. </a>
  16. </template>
  17. <template #level2="{ slotScope }">
  18. <span style="cursor: pointer" @click="handleLink(slotScope)">
  19. <vab-icon :icon="slotScope.meta && slotScope.meta.icon" />
  20. <span>{{ translate(slotScope.meta.title) }}</span>
  21. </span>
  22. </template>
  23. <template #level3="{ slotScope }">
  24. <a v-for="(level3, index) in slotScope" :key="index" :href="level3.url" @click="handleLink(level3)">
  25. - {{ translate(level3.meta.title) }}
  26. <el-tag v-if="level3.meta && level3.meta.badge" effect="dark" size="small" type="danger">
  27. {{ level3.meta.badge }}
  28. </el-tag>
  29. <span v-if="level3.meta && level3.meta.dot" class="vab-dot vab-dot-error">
  30. <span></span>
  31. </span>
  32. </a>
  33. </template>
  34. </fall-menu>
  35. </div>
  36. </template>
  37. <script lang="ts" setup>
  38. /**
  39. * @author:zxwk-sundan
  40. * @description:瀑布菜单最多支持到三级菜单,不支持左侧点击选中,且非element-plus官方组件,生产环境请谨慎使用
  41. */
  42. import { FallMenu } from '@opentiny/vue'
  43. import { isHashRouterMode } from '/@/config'
  44. import { translate } from '/@/i18n'
  45. import { useRoutesStore } from '/@/store/modules/routes'
  46. import { useSettingsStore } from '/@/store/modules/settings'
  47. import { isExternal } from '/@/utils/validate'
  48. defineOptions({
  49. name: 'VabFallBar',
  50. })
  51. const settingsStore = useSettingsStore()
  52. const routesStore = useRoutesStore()
  53. const { getRoutes: routes } = storeToRefs(routesStore)
  54. const route = useRoute()
  55. const router = useRouter()
  56. const { device, collapse } = storeToRefs(settingsStore)
  57. const { foldSideBar } = settingsStore
  58. const { enter, exit } = useFullscreen()
  59. const mousePosition = ref({ x: 0, y: 0 })
  60. const handleRoutes = computed(() =>
  61. routes.value.flatMap((route: any) => (route.meta.levelHidden && route.children ? [...route.children] : route))
  62. )
  63. const handleLink = (slotScope: any) => {
  64. nextTick(() => {
  65. const routePath = slotScope.path
  66. const target = slotScope.meta.target
  67. const fullscreen = slotScope.meta.fullscreen
  68. if (target === '_blank') {
  69. if (isExternal(routePath)) {
  70. window.open(routePath)
  71. router.push('/redirect')
  72. } else if (route.path !== routePath) isHashRouterMode ? window.open(`#${routePath}`) : window.open(routePath)
  73. router.push('/redirect')
  74. } else {
  75. if (isExternal(routePath)) window.location.href = routePath
  76. else if (route.path === routePath) {
  77. $pub('reload-router-view')
  78. } else {
  79. if (device.value === 'mobile') foldSideBar()
  80. if (slotScope.children) router.push(slotScope.redirect)
  81. else router.push(slotScope.path)
  82. }
  83. }
  84. setTimeout(() => {
  85. if (fullscreen) enter()
  86. else exit()
  87. }, 1000)
  88. })
  89. }
  90. useEventListener('mousemove', (e: MouseEvent) => {
  91. mousePosition.value = {
  92. x: e.clientX,
  93. y: e.clientY,
  94. }
  95. if ((mousePosition.value.x < 265 && !collapse.value) || (mousePosition.value.x < 65 && collapse.value)) {
  96. const element: any = document.querySelector('.vab-fall-bar .tiny-fall-menu__box')
  97. const base = 60
  98. const intervalSize = 48
  99. for (let i = 0; i < 10; i++) {
  100. const lowerBound = base + i * intervalSize
  101. const upperBound = lowerBound + intervalSize
  102. if (mousePosition.value.y > lowerBound && mousePosition.value.y < upperBound) {
  103. mousePosition.value.y = lowerBound - 60
  104. break
  105. }
  106. }
  107. element.style.top = `${mousePosition.value.y}px`
  108. }
  109. })
  110. </script>
  111. <style lang="scss" scoped>
  112. .vab-fall-bar {
  113. position: fixed;
  114. top: 0;
  115. bottom: 0;
  116. left: 0;
  117. z-index: var(--el-z-index);
  118. width: calc(var(--el-left-menu-width) - 1px);
  119. background: var(--el-menu-background-color);
  120. border-right: 1px solid var(--el-border-color);
  121. .fall-icon-right {
  122. float: right;
  123. }
  124. :deep() {
  125. .tiny-fall-menu {
  126. --ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
  127. --ti-fall-menu-bg-color-hover: var(--el-color-primary);
  128. --ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
  129. --ti-fall-menu-box-text-color: var(--el-color-white);
  130. --ti-fall-menu-slot-text-color: var(--el-color-white);
  131. --ti-common-font-size-base: var(--el-font-size-base);
  132. --ti-fall-menu-title-font-size: var(--el-font-size-base);
  133. --ti-fall-menu-box-width: 560px;
  134. &__nav {
  135. height: calc(var(--vh, 1vh) * 100);
  136. }
  137. &__wrap {
  138. padding: 0;
  139. background: var(--ti-fall-menu-bg-color-normal);
  140. }
  141. &__subnav {
  142. .icon-slot-left,
  143. .icon-slot-right {
  144. opacity: 0;
  145. }
  146. }
  147. &__list {
  148. right: 0 !important;
  149. left: 0 !important;
  150. min-width: 100%;
  151. li {
  152. display: block;
  153. float: none;
  154. width: 100%;
  155. a {
  156. display: block;
  157. width: calc(100% - 20px);
  158. margin: 0 10px 0px 10px !important;
  159. text-align: left;
  160. border-radius: var(--el-border-radius-base);
  161. [class*='ri-'] {
  162. margin-left: 1.5px;
  163. }
  164. [class*='ri-'] + span {
  165. padding-left: 3px;
  166. }
  167. }
  168. }
  169. .fall-hide {
  170. opacity: 1;
  171. }
  172. }
  173. &__box {
  174. top: 5px;
  175. left: var(--el-left-menu-width);
  176. min-width: var(--ti-fall-menu-box-width);
  177. padding: var(--el-padding);
  178. overflow-y: auto;
  179. border: 0;
  180. border-radius: var(--el-border-radius-base);
  181. box-shadow: none;
  182. transition:
  183. all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1),
  184. top 0.1s !important;
  185. .cont {
  186. padding: 0;
  187. }
  188. .sublist {
  189. li {
  190. h3.mcate-item-hd {
  191. color: var(--ti-fall-menu-box-title-text-color);
  192. [class*='ri-'] + span {
  193. padding-left: 3px;
  194. }
  195. &:hover {
  196. color: var(--el-color-primary) !important;
  197. }
  198. }
  199. p.mcate-item-bd {
  200. a {
  201. font-size: 13px;
  202. color: var(--ti-fall-menu-box-text-color);
  203. &:hover {
  204. color: var(--el-color-primary) !important;
  205. }
  206. .el-tag {
  207. height: 16px;
  208. padding: 0 5px 0 5px;
  209. }
  210. }
  211. }
  212. }
  213. }
  214. }
  215. }
  216. }
  217. &.is-collapse {
  218. width: 65px;
  219. :deep() {
  220. .tiny-fall-menu {
  221. &__list {
  222. li {
  223. a {
  224. [class*='ri-'] + span {
  225. display: none;
  226. }
  227. }
  228. }
  229. .fall-hide {
  230. opacity: 1;
  231. }
  232. }
  233. &__box {
  234. left: calc(var(--el-left-menu-width-min) + 2px);
  235. padding-right: 0;
  236. }
  237. }
  238. }
  239. }
  240. }
  241. </style>
  242. <style lang="scss">
  243. .vab-theme-technology {
  244. .tiny-fall-menu {
  245. --ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
  246. --ti-fall-menu-bg-color-hover: var(--el-color-primary);
  247. --ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
  248. --ti-fall-menu-box-text-color: #fff !important;
  249. --ti-fall-menu-slot-text-color: var(--el-color-white);
  250. &__list {
  251. a {
  252. color: #fff;
  253. }
  254. }
  255. &__box {
  256. border: 1px solid var(--el-border-color) !important;
  257. .sublist {
  258. li {
  259. h3.mcate-item-hd {
  260. color: var(--ti-fall-menu-box-title-text-color);
  261. }
  262. p.mcate-item-bd {
  263. a {
  264. color: var(--ti-fall-menu-box-text-color);
  265. }
  266. }
  267. }
  268. }
  269. }
  270. }
  271. }
  272. .vab-theme-plain {
  273. .tiny-fall-menu {
  274. --ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
  275. --ti-fall-menu-bg-color-hover: var(--el-color-primary);
  276. --ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
  277. --ti-fall-menu-box-text-color: var(--el-color-grey) !important;
  278. --ti-fall-menu-slot-text-color: var(--el-color-grey) !important;
  279. &__box {
  280. border: 1px solid var(--el-border-color) !important;
  281. .sublist {
  282. li {
  283. h3.mcate-item-hd {
  284. color: #515a6e !important;
  285. }
  286. p.mcate-item-bd {
  287. a {
  288. color: var(--ti-fall-menu-box-text-color);
  289. }
  290. }
  291. }
  292. }
  293. }
  294. &__list {
  295. a:hover {
  296. color: #fff !important;
  297. }
  298. }
  299. }
  300. }
  301. .dark {
  302. .tiny-fall-menu {
  303. --ti-fall-menu-bg-color-normal: var(--el-menu-background-color);
  304. --ti-fall-menu-bg-color-hover: var(--el-color-primary);
  305. --ti-fall-menu-slot-bg-color: var(--el-menu-background-color);
  306. --ti-fall-menu-box-text-color: var(--el-color-grey) !important;
  307. --ti-fall-menu-slot-text-color: var(--el-color-grey) !important;
  308. &__box {
  309. border: 1px solid var(--el-border-color) !important;
  310. .sublist {
  311. li {
  312. h3.mcate-item-hd {
  313. color: #fff !important;
  314. }
  315. p.mcate-item-bd {
  316. a {
  317. color: var(--ti-fall-menu-box-text-color);
  318. }
  319. }
  320. }
  321. }
  322. }
  323. &__list {
  324. a:hover {
  325. color: #fff !important;
  326. }
  327. }
  328. }
  329. }
  330. </style>