index.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. <template>
  2. <el-scrollbar
  3. class="vab-column-bar"
  4. :class="{
  5. 'is-collapse': collapse,
  6. ['vab-column-bar-' + theme.columnStyle]: true,
  7. }"
  8. >
  9. <vab-logo style="z-index: 999" />
  10. <el-tabs v-model="tab.data" tab-position="left" @tab-click="handleTabClick">
  11. <template v-for="(item, index) in routes" :key="index + item.name">
  12. <el-tab-pane :name="item.name">
  13. <template #label>
  14. <div
  15. class="vab-column-grid"
  16. :class="{
  17. ['vab-column-grid-' + theme.columnStyle]: true,
  18. }"
  19. :title="translate(item.meta.title)"
  20. >
  21. <div>
  22. <vab-icon v-if="item.meta.icon" :icon="item.meta.icon" :is-custom-svg="item.meta.isCustomSvg" />
  23. <span v-if="translate(item.meta.title).length < 4">
  24. {{ translate(item.meta.title) }}
  25. </span>
  26. <span v-else style="font-size: var(--el-font-size-small); zoom: 0.88">
  27. {{ translate(item.meta.title) }}
  28. </span>
  29. </div>
  30. </div>
  31. </template>
  32. </el-tab-pane>
  33. </template>
  34. </el-tabs>
  35. <el-menu
  36. background-color="var(--el-menu-background-color-second)"
  37. :default-active="activeMenu.data"
  38. :default-openeds="defaultOpeneds"
  39. mode="vertical"
  40. :unique-opened="uniqueOpened"
  41. >
  42. <vab-menu v-for="item in partialRoutes" :key="item.path" :item="item" />
  43. </el-menu>
  44. <div class="float-fold">
  45. <vab-fold fold="contract-left-line" unfold="contract-right-line" />
  46. </div>
  47. </el-scrollbar>
  48. </template>
  49. <script lang="ts" setup>
  50. import { defaultOpeneds, isHashRouterMode, openFirstMenu, uniqueOpened } from '/@/config'
  51. import { translate } from '/@/i18n'
  52. import { VabRoute } from '/@/router/types'
  53. import { useRoutesStore } from '/@/store/modules/routes'
  54. import { useSettingsStore } from '/@/store/modules/settings'
  55. import { isExternal } from '/@/utils/validate'
  56. defineOptions({
  57. name: 'VabColumnBar',
  58. })
  59. const route: VabRoute = useRoute()
  60. const router = useRouter()
  61. const settingsStore = useSettingsStore()
  62. const { collapse, device, theme } = storeToRefs(settingsStore)
  63. const { foldSideBar, openSideBar } = settingsStore
  64. const routesStore = useRoutesStore()
  65. const {
  66. getTab: tab,
  67. getTabMenu: tabMenu,
  68. getActiveMenu: activeMenu,
  69. getRoutes: routes,
  70. getPartialRoutes: partialRoutes,
  71. }: any = storeToRefs(routesStore)
  72. const handleTabClick = () => {
  73. nextTick(() => {
  74. if (tabMenu.value.meta.target === '_blank') {
  75. if (route.path !== tabMenu.value.path) {
  76. isHashRouterMode ? window.open(`#${tabMenu.value.path}`) : window.open(tabMenu.value.path)
  77. router.push('/redirect')
  78. }
  79. } else if (isExternal(tabMenu.value.path)) {
  80. window.open(tabMenu.value.path)
  81. router.push('/redirect')
  82. } else if (openFirstMenu) router.push(tabMenu.value.redirect || tabMenu.value)
  83. })
  84. }
  85. nextTick(() => {
  86. if (theme.value.layout === 'column')
  87. watch(
  88. route,
  89. () => {
  90. const foldUnfold: any = document.querySelector('.left-panel .fold-unfold')
  91. const floatFold: any = document.querySelector('.float-fold')
  92. if (route.meta.noColumn && theme.value.layout === 'column') {
  93. if (device.value !== 'mobile') foldSideBar()
  94. if (foldUnfold) foldUnfold.style = 'display:none'
  95. if (floatFold) floatFold.style = 'display:none'
  96. } else {
  97. if (device.value !== 'mobile') openSideBar()
  98. if (foldUnfold) foldUnfold.style = ''
  99. if (floatFold) floatFold.style = ''
  100. }
  101. },
  102. {
  103. immediate: true,
  104. }
  105. )
  106. })
  107. </script>
  108. <style lang="scss" scoped>
  109. @mixin active {
  110. &:hover {
  111. color: var(--el-color-primary);
  112. background-color: var(--el-color-primary-light-9);
  113. i,
  114. svg {
  115. color: var(--el-color-primary);
  116. }
  117. }
  118. &.is-active {
  119. color: var(--el-color-primary);
  120. background-color: var(--el-color-primary-light-9);
  121. }
  122. }
  123. .vab-column-bar {
  124. position: fixed;
  125. top: 0;
  126. bottom: 0;
  127. left: 0;
  128. width: var(--el-left-menu-width);
  129. height: 100vh;
  130. overflow: hidden;
  131. background: var(--el-color-white);
  132. border-right: 1px solid var(--el-border-color);
  133. &-vertical,
  134. &-card,
  135. &-arrow {
  136. :deep() {
  137. .el-tabs + .el-menu {
  138. left: var(--el-left-menu-width-min);
  139. width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min));
  140. border: 0;
  141. }
  142. }
  143. }
  144. &-horizontal,
  145. &-semicircle {
  146. :deep() {
  147. .vab-logo-column {
  148. .logo {
  149. width: calc(var(--el-left-menu-width-min) * 1.4) !important;
  150. }
  151. .title {
  152. left: calc(var(--el-left-menu-width-min) * 1.4) !important;
  153. width: calc(var(--el-left-menu-width) - calc(var(--el-left-menu-width-min) * 1.4) - 1px);
  154. }
  155. }
  156. .el-tabs + .el-menu {
  157. left: calc(var(--el-left-menu-width-min) * 1.4);
  158. width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min) * 1.4);
  159. border: 0;
  160. }
  161. }
  162. }
  163. &-card {
  164. :deep() {
  165. .el-tabs {
  166. .el-tabs__item {
  167. padding: 5px !important;
  168. .vab-column-grid {
  169. width: calc(var(--el-left-menu-width-min) - 10px) !important;
  170. height: calc(var(--el-left-menu-width-min) - 10px) !important;
  171. border-radius: var(--el-border-radius-base);
  172. &:hover {
  173. background: var(--el-color-primary);
  174. }
  175. }
  176. &.is-active {
  177. background: transparent !important;
  178. .vab-column-grid {
  179. background: var(--el-color-primary);
  180. }
  181. }
  182. }
  183. }
  184. .el-tabs + .el-menu {
  185. left: calc(var(--el-left-menu-width-min) + 10px);
  186. width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min) - 20px);
  187. }
  188. .el-sub-menu .el-sub-menu__title,
  189. .el-menu-item {
  190. min-width: 180px;
  191. margin-bottom: 5px;
  192. border-radius: var(--el-border-radius-base);
  193. }
  194. }
  195. }
  196. &-arrow {
  197. :deep() {
  198. .el-tabs {
  199. .el-tabs__item {
  200. &.is-active {
  201. background: transparent !important;
  202. .vab-column-grid {
  203. background: transparent !important;
  204. &:after {
  205. position: absolute;
  206. right: -1px;
  207. width: 0;
  208. height: 0;
  209. overflow: hidden;
  210. content: '';
  211. border-color: transparent var(--el-color-white) transparent transparent;
  212. border-style: solid dashed dashed;
  213. border-width: 8px;
  214. }
  215. }
  216. }
  217. }
  218. }
  219. .el-tabs + .el-menu {
  220. left: calc(var(--el-left-menu-width-min) + 10px);
  221. width: calc(var(--el-left-menu-width) - var(--el-left-menu-width-min) - 20px);
  222. }
  223. .el-sub-menu .el-sub-menu__title,
  224. .el-menu-item {
  225. min-width: 180px;
  226. margin-bottom: 5px;
  227. border-radius: var(--el-border-radius-base);
  228. }
  229. }
  230. }
  231. &-semicircle {
  232. :deep() {
  233. .el-tabs {
  234. .el-tabs__item {
  235. &.is-active {
  236. border-top-left-radius: 99px;
  237. border-bottom-left-radius: 99px;
  238. }
  239. }
  240. }
  241. }
  242. }
  243. .vab-column-grid {
  244. display: flex;
  245. align-items: center;
  246. width: var(--el-left-menu-width-min);
  247. overflow: hidden;
  248. text-align: center;
  249. text-overflow: ellipsis;
  250. word-break: break-all;
  251. white-space: nowrap;
  252. &-vertical,
  253. &-card,
  254. &-arrow {
  255. justify-content: center;
  256. height: var(--el-left-menu-width-min);
  257. > div {
  258. svg,
  259. [class*='ri-'] {
  260. display: block;
  261. height: 20px;
  262. }
  263. }
  264. }
  265. &-horizontal,
  266. &-semicircle {
  267. justify-content: left;
  268. width: calc(var(--el-left-menu-width-min) * 1.4);
  269. height: calc(var(--el-left-menu-width-min) / 1.4);
  270. padding-left: var(--el-padding);
  271. svg,
  272. [class*='ri-'] {
  273. margin-right: 3px;
  274. }
  275. }
  276. }
  277. :deep() {
  278. * {
  279. transition: var(--el-transition);
  280. }
  281. .el-scrollbar__wrap {
  282. overflow-x: hidden;
  283. }
  284. .el-tabs {
  285. position: fixed;
  286. z-index: 9999;
  287. .el-tabs__header.is-left {
  288. margin-right: 0 !important;
  289. .el-tabs__nav-wrap.is-left {
  290. margin-right: 0 !important;
  291. background: var(--el-menu-background-color);
  292. .el-tabs__nav-scroll {
  293. height: 100%;
  294. overflow-y: auto;
  295. &::-webkit-scrollbar {
  296. width: 0;
  297. height: 0;
  298. }
  299. }
  300. }
  301. }
  302. .el-tabs__nav {
  303. height: calc(100vh - var(--el-logo-height));
  304. background: var(--el-menu-background-color);
  305. }
  306. .el-tabs__item {
  307. height: auto;
  308. padding: 0;
  309. color: var(--el-color-white);
  310. &.is-active {
  311. background: var(--el-color-primary);
  312. }
  313. }
  314. }
  315. .el-tabs__active-bar.is-left,
  316. .el-tabs--left .el-tabs__nav-wrap.is-left::after {
  317. display: none;
  318. }
  319. .el-menu {
  320. margin-top: 10px;
  321. border: 0;
  322. .el-menu-item,
  323. .el-sub-menu__title {
  324. height: var(--el-menu-item-height);
  325. overflow: hidden;
  326. line-height: var(--el-menu-item-height);
  327. text-overflow: ellipsis;
  328. white-space: nowrap;
  329. @include active;
  330. }
  331. }
  332. }
  333. &.is-collapse {
  334. :deep() {
  335. width: 0;
  336. }
  337. }
  338. }
  339. .float-fold {
  340. position: fixed;
  341. bottom: 13px;
  342. left: 14px;
  343. z-index: 9999;
  344. width: 34px;
  345. height: 34px;
  346. line-height: 34px;
  347. text-align: center;
  348. background: var(--el-color-primary-light-5);
  349. border-radius: var(--el-border-radius-base);
  350. :deep() {
  351. .fold-unfold,
  352. .ri-user-heart-line {
  353. font-size: 20px;
  354. color: var(--el-color-white);
  355. cursor: pointer;
  356. }
  357. }
  358. }
  359. </style>