index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. <template>
  2. <div class="le-tabs-menu-wrap">
  3. <div class="tabs-menu">
  4. <el-tabs v-model="tabsMenuValue" :class="tabsModeClass" type="card" @tab-click="tabClick" @tab-remove="tabRemove">
  5. <el-tab-pane v-for="item in tabsMenuList" :key="item.path" :label="item.fullPath" :name="item.path" :item="item" :closable="!item.meta.affix">
  6. <template #label>
  7. <!--@click="tabClick2(item)"-->
  8. <div class="le-tabs__item" @contextmenu.prevent="openDropMenu(item, $event)">
  9. <PickerIcon v-if="tabsIcon && item.meta?.icon" class="tabs-icon" :icon-class="item.meta.icon" />
  10. {{ generateTitle(item.title) }}
  11. </div>
  12. </template>
  13. </el-tab-pane>
  14. </el-tabs>
  15. <ul v-show="dropVisible" ref="dropdownMenu" :style="`left: ${dropLeft}px; top: ${dropTop}px`" class="local-contextmenu el-dropdown-menu">
  16. <li class="el-dropdown-menu__item" @click="contentMaximizeChange">
  17. <le-icon icon-class="icon-fullscreen"></le-icon>
  18. <span>{{ $t('le.tabs.opts.contentMax') }}</span>
  19. </li>
  20. <li class="el-dropdown-menu__item" @click="refreshSelectedTag(selectedTag)">
  21. <le-icon icon-class="icon-refresh"></le-icon>
  22. <span>{{ $t('le.refresh') }}</span>
  23. </li>
  24. <li role="separator" class="el-dropdown-menu__item--divided"></li>
  25. <li class="el-dropdown-menu__item" @click="closeOtherTags">
  26. <le-icon icon-class="icon-close_other" style="transform: rotate(90deg)"></le-icon>
  27. <span>{{ $t('le.tabs.opts.closeOther') }}</span>
  28. </li>
  29. <li v-if="!isFirstView" class="el-dropdown-menu__item" @click="closeSideTags('left')">
  30. <le-icon icon-class="icon-close_left"></le-icon>
  31. <span>{{ $t('le.tabs.opts.closeOther') }}</span>
  32. </li>
  33. <li v-if="!isLastView" class="el-dropdown-menu__item" @click="closeSideTags('right')">
  34. <le-icon icon-class="icon-close_right"></le-icon>
  35. <span>{{ $t('le.tabs.opts.closeRight') }}</span>
  36. </li>
  37. <li class="el-dropdown-menu__item" @click="closeAllTags">
  38. <le-icon icon-class="icon-close_all"></le-icon>
  39. <span>{{ $t('le.tabs.opts.closeAll') }}</span>
  40. </li>
  41. </ul>
  42. <el-dropdown popper-class="le-tabs-fast-dropdown-popper" trigger="hover" @visible-change="fastDropVisibleChange">
  43. <div class="fast-drop-wrap">
  44. <span class="fast-drop-button"><i class="box box-t"></i><i class="box box-b"></i></span>
  45. <!-- <i :class="'iconfont icon-xiala'"></i>-->
  46. </div>
  47. <template #dropdown>
  48. <el-dropdown-menu>
  49. <el-dropdown-item @click="contentMaximizeChange">
  50. <le-icon icon-class="icon-fullscreen"></le-icon>
  51. <span>{{ $t('le.tabs.opts.contentMax') }}</span>
  52. </el-dropdown-item>
  53. <el-dropdown-item @click="refreshSelectedTag(selectedTag)">
  54. <le-icon icon-class="icon-refresh"></le-icon>
  55. <span>{{ $t('le.refresh') }}</span>
  56. </el-dropdown-item>
  57. <el-dropdown-item divided @click="closeOtherTags">
  58. <le-icon icon-class="icon-close_other" style="transform: rotate(90deg)"></le-icon>
  59. <span>{{ $t('le.tabs.opts.closeOther') }}</span>
  60. </el-dropdown-item>
  61. <el-dropdown-item v-if="!isFirstView" @click="closeSideTags('left')">
  62. <le-icon icon-class="icon-close_left"></le-icon>
  63. <span>{{ $t('le.tabs.opts.closeOther') }}</span>
  64. </el-dropdown-item>
  65. <el-dropdown-item v-if="!isLastView" @click="closeSideTags('right')">
  66. <le-icon icon-class="icon-close_right"></le-icon>
  67. <span>{{ $t('le.tabs.opts.closeRight') }}</span>
  68. </el-dropdown-item>
  69. <el-dropdown-item @click="closeAllTags">
  70. <le-icon icon-class="icon-close_all"></le-icon>
  71. <span>{{ $t('le.tabs.opts.closeAll') }}</span>
  72. </el-dropdown-item>
  73. </el-dropdown-menu>
  74. </template>
  75. </el-dropdown>
  76. </div>
  77. </div>
  78. </template>
  79. <script setup lang="ts">
  80. import Sortable from 'sortablejs'
  81. import { generateTitle } from '@/utils/i18n'
  82. import { ref, computed, watch, onMounted, getCurrentInstance, ComponentInternalInstance, nextTick } from 'vue'
  83. import { useRoute, useRouter } from 'vue-router'
  84. import PickerIcon from '@/components/IconPicker/PickerIcon.vue'
  85. import { TabsPaneContext, TabPaneName } from 'element-plus'
  86. // import { AppRouteRecordRaw } from '@/router/types'
  87. import useStore from '@/store'
  88. import { TagView } from '@/types'
  89. const { proxy } = getCurrentInstance() as ComponentInternalInstance // 获取当前组件实例
  90. const route = useRoute()
  91. const router = useRouter()
  92. const { tagsView, setting, permission } = useStore()
  93. const tabsMenuValue = ref(route.fullPath)
  94. const tabsMenuList = computed(() => tagsView.visitedViews)
  95. const tabsIcon = computed(() => setting.tabsIcon)
  96. const tabsModeClass = computed(() => {
  97. return `tabs-${setting.tabsMode}`
  98. })
  99. const selectedTag = ref<any>({})
  100. const dropVisible = ref(false)
  101. const dropLeft = ref(0)
  102. const dropTop = ref(0)
  103. const isAffix = (tag: TagView) => {
  104. return tag.meta && tag.meta.affix
  105. }
  106. // todo TagView
  107. const isActive = (tag: TagView) => {
  108. return tag.path === route.path
  109. }
  110. const isFirstView = computed(() => {
  111. try {
  112. const cur_path = selectedTag.value.path as string
  113. const curIdx = tabsMenuList.value.findIndex(v => v.path === cur_path)
  114. // console.error(cur_path, 'cur curIdx', curIdx)
  115. return tabsMenuList.value.slice(0, curIdx).every(v => {
  116. return v.meta?.affix
  117. })
  118. } catch (err) {
  119. return false
  120. }
  121. })
  122. const isLastView = computed(() => {
  123. try {
  124. const cur_path = selectedTag.value.path as string
  125. return cur_path === tabsMenuList.value[tabsMenuList.value.length - 1].path
  126. } catch (err) {
  127. return false
  128. }
  129. })
  130. // delAllViews
  131. const addTab = () => {
  132. if (route.name) {
  133. tagsView.addView(route)
  134. tabsMenuValue.value = route.path
  135. }
  136. }
  137. onMounted(() => {
  138. tabsDrop()
  139. initTabs()
  140. addTab()
  141. })
  142. watch(route, () => {
  143. addTab()
  144. // console.error(route, 'cur route')
  145. })
  146. // 多页签初始化
  147. const initTabs = () => {
  148. // 初始化: 将固定的 tabs(多页签)
  149. // const affixTabs = []
  150. permission.showMenuList.forEach(route => {
  151. const meta = route.meta || {}
  152. // affix 固定钉子
  153. if (meta?.affix && !meta.hidden) {
  154. const affixItem = {
  155. fullPath: route.path,
  156. path: route.path,
  157. name: route.name,
  158. meta: route.meta
  159. }
  160. tagsView.addView(affixItem)
  161. }
  162. })
  163. }
  164. // 标签 拖拽排序
  165. const tabsDrop = () => {
  166. Sortable.create(document.querySelector('.el-tabs__nav') as HTMLElement, {
  167. // draggable: '.el-tabs__item',
  168. draggable: '.el-tabs__item.is-closable', // .is-closable 表示可以关闭(非affix)
  169. animation: 200,
  170. onEnd({ newIndex, oldIndex }) {
  171. const tabsList = [...tagsView.visitedViews]
  172. const currRow = tabsList.splice(oldIndex as number, 1)[0]
  173. tabsList.splice(newIndex as number, 0, currRow)
  174. tagsView.setViews(tabsList)
  175. },
  176. onMove(event: any) {
  177. // console.log(event.related, 'onMove', event.relatedRect)
  178. return event.related.className.indexOf('is-closable') !== -1
  179. }
  180. })
  181. }
  182. // window.router = router
  183. // Tab Click
  184. const tabClick = (tabItem: TabsPaneContext) => {
  185. const fullPath = tabItem.props.label as string
  186. router.push(fullPath)
  187. }
  188. /*const tabClick2 = (tabItem: any) => {
  189. console.error(tabItem, 'tabClick2 tabItem')
  190. // const fullPath = tabItem.props.name as string
  191. const routeParams = {
  192. path: tabItem.path,
  193. fullPath: tabItem.fullPath,
  194. query: tabItem.query
  195. }
  196. router.push(routeParams)
  197. }*/
  198. // Remove Tab
  199. const tabRemove = (path: TabPaneName) => {
  200. const curTab = tabsMenuList.value.find(v => v.path === path)
  201. tagsView.delView(curTab).then((res: any) => {
  202. // if (isActive(curTab)) {
  203. if (path === route.path) {
  204. toLastView(res.visitedViews, curTab)
  205. }
  206. })
  207. }
  208. function toLastView(visitedViews: TagView[], view?: any) {
  209. const latestView = visitedViews.slice(-1)[0]
  210. if (latestView && latestView.fullPath) {
  211. router.push(latestView.fullPath)
  212. } else {
  213. // now the default is to redirect to the home page if there is no tags-view,
  214. // you can adjust it according to your needs.
  215. if (view.name === 'dashboard') {
  216. // to reload home page
  217. router.replace({ path: '/redirect' + view.fullPath })
  218. } else {
  219. router.push('/')
  220. }
  221. }
  222. }
  223. function openDropMenu(tag: TagView, e: MouseEvent) {
  224. // console.error(tag, e, 'test openDropMenu')
  225. const menuMinWidth = 107
  226. const el_rect = proxy?.$el.getBoundingClientRect()
  227. const offsetWidth = proxy?.$el.offsetWidth // container width
  228. const maxLeft = offsetWidth - menuMinWidth // left boundary
  229. const l = e.clientX - el_rect.left + 15 // 15: margin right
  230. const t = e.clientY - el_rect.top + 8 // 8: margin top
  231. if (l > maxLeft) {
  232. dropLeft.value = maxLeft
  233. } else {
  234. dropLeft.value = l
  235. }
  236. dropTop.value = t
  237. dropVisible.value = true
  238. selectedTag.value = tag
  239. }
  240. const fastDropVisibleChange = (visible: boolean) => {
  241. if (visible) {
  242. const activeTag = tabsMenuList.value.find(v => v.fullPath === route.fullPath)
  243. if (activeTag) {
  244. selectedTag.value = activeTag
  245. }
  246. dropVisible.value = false
  247. }
  248. }
  249. watch(dropVisible, value => {
  250. if (value) {
  251. document.body.addEventListener('click', closeDrop)
  252. } else {
  253. document.body.removeEventListener('click', closeDrop)
  254. }
  255. })
  256. const closeDrop = () => {
  257. dropVisible.value = false
  258. }
  259. // 刷新
  260. function refreshSelectedTag(view: TagView) {
  261. tagsView.delCachedView(view)
  262. const { fullPath } = view
  263. nextTick(() => {
  264. router.replace({ path: '/redirect' + fullPath }).catch(err => {
  265. console.warn(err)
  266. })
  267. })
  268. }
  269. // 关闭其他
  270. function closeOtherTags() {
  271. tagsView.delOtherViews(selectedTag.value).then(() => {
  272. // moveToCurrentTag()
  273. const remain = tagsView.visitedViews
  274. const hasTab = remain.some(v => v.path === route.path)
  275. if (!hasTab) {
  276. toLastView(tagsView.visitedViews, selectedTag.value)
  277. // const activePath = remain[remain.length - 1].fullPath
  278. // router.push(activePath)
  279. }
  280. })
  281. }
  282. function closeSideTags(side: 'left' | 'right' = 'left') {
  283. tagsView.closeSideTags(selectedTag.value, side).then((res: any) => {
  284. // console.error(res.visitedViews, 'visitedViews')
  285. if (!res.visitedViews.find((item: any) => item.path === route.path)) {
  286. toLastView(res.visitedViews)
  287. }
  288. })
  289. }
  290. function closeAllTags() {
  291. tagsView.delAllViews().then((res: any) => {
  292. // console.error(res.visitedViews, 'visitedViews')
  293. if (!res.visitedViews.find((item: any) => item.path === route.path)) {
  294. toLastView(res.visitedViews)
  295. }
  296. })
  297. }
  298. const contentMaximizeChange = () => {
  299. setting.contentMaximize = !setting.contentMaximize
  300. }
  301. </script>
  302. <style lang="scss">
  303. $transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1), border 0s, color 0.1s, font-size 0s;
  304. .#{$prefix}tabs-menu-wrap {
  305. background-color: var(--el-bg-color);
  306. .sortable-ghost {
  307. box-shadow: inset 1px 1px 5px 2px rgba(0, 0, 0, 0.15);
  308. transition: 0.18s ease;
  309. }
  310. .sortable-chosen {
  311. //box-shadow: 1px 1px 5px 2px rgba(0,0,0,.15);
  312. box-shadow: inset 0 0 6px -2px rgba(0, 0, 0, 0.15);
  313. }
  314. .tabs-menu {
  315. position: relative;
  316. width: 100%;
  317. .el-dropdown {
  318. position: absolute;
  319. top: 0;
  320. right: 0;
  321. bottom: 0;
  322. z-index: 1;
  323. /*.fast-drop-button {
  324. display: flex;
  325. align-items: center;
  326. justify-content: center;
  327. width: 43px;
  328. cursor: pointer;
  329. border-left: 1px solid var(--el-border-color-light);
  330. transition: all 0.3s;
  331. &:hover {
  332. background-color: var(--el-color-info-light-9);
  333. }
  334. .iconfont {
  335. font-size: 12.5px;
  336. }
  337. }*/
  338. }
  339. .el-tabs {
  340. .el-tabs__header {
  341. box-sizing: border-box;
  342. height: 40px;
  343. padding: 0 10px;
  344. //padding: 0;
  345. margin: 0;
  346. border-bottom: 0;
  347. //box-shadow: 0 1px 4px -1px var(--el-border-color-light);
  348. box-shadow: 0 -1px 0 2px var(--el-border-color-light);
  349. z-index: 1;
  350. /*.el-tabs__nav-next,
  351. .el-tabs__nav-prev {
  352. height: 40px;
  353. }*/
  354. .el-tabs__nav-wrap {
  355. position: absolute;
  356. width: calc(100% - 50px);
  357. .el-tabs__nav {
  358. display: flex;
  359. border: none;
  360. .el-tabs__item {
  361. display: flex;
  362. align-items: center;
  363. justify-content: center;
  364. color: var(--el-text-color-regular);
  365. padding: 0 20px;
  366. //padding: 0 !important;
  367. border: 0;
  368. &:hover {
  369. color: var(--el-color-primary);
  370. }
  371. .le-tabs__item {
  372. height: 100%;
  373. display: inline-flex;
  374. align-items: center;
  375. /* &:nth-child(2):not(.is-active).is-closable:hover {
  376. padding-left: 0;
  377. }*/
  378. //padding: 0 20px;
  379. //margin: 0 -20px;
  380. }
  381. /*
  382. .is-icon-close {
  383. //right: -2px;
  384. right: 18px;
  385. //margin-left: -20px;
  386. }*/
  387. .tabs-icon {
  388. margin: 1.5px 4px 0 0;
  389. font-size: 15px;
  390. }
  391. .is-icon-close {
  392. margin-top: 1px;
  393. }
  394. .is-icon-close {
  395. &:hover {
  396. background-color: var(--el-color-primary-light-3);
  397. }
  398. }
  399. &.is-active {
  400. color: var(--el-color-primary) !important;
  401. background: var(--el-color-primary-light-9);
  402. .is-icon-close {
  403. &:hover {
  404. background-color: var(--el-color-primary);
  405. }
  406. }
  407. }
  408. }
  409. }
  410. }
  411. }
  412. // 风格1 谷歌风格
  413. &.tabs-chrome {
  414. .el-tabs__header {
  415. /*.el-tabs__nav-next,
  416. .el-tabs__nav-prev {
  417. line-height: 52px;
  418. }*/
  419. .el-tabs__nav-wrap {
  420. .el-tabs__nav {
  421. .el-tabs__item {
  422. margin-left: -12px;
  423. margin-top: 6px;
  424. //height: 36px;
  425. height: calc(var(--el-tabs-header-height) - 6px);
  426. mask: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANoAAAAkCAYAAADvhSSyAAAACXBIWXMAAAsTAAALEwEAmpwYAAABbUlEQVR4nO3d4U3CUBSG4beJA9QNcAJhAtnAOoGOwCaOoBvoCDgBOIHdQDbAH7cJMaHtxcJB6/skJ6Thptw/H+cSktOCn5kCc+AWKJtraaxqYA28A8/N9cktgE9ga1n/tBYcqDhw/QtQHfoh0gitgdkpbrzg/N8klvWbKruz5Xa0CbAi/R6TtDMjdbdOF5k3qzBk0j4VRwzazbC9SKN1nbMo9+j4QTo+SvpuA1z2LcoN2nbYXqRR681R7tFR0gAGTQpg0KQABk0KYNCkAAZNCmDQpAAGTQpg0KQABk0KYNCkAAZNCmDQpAAGTQpg0KQABk0KYNCkAAZNGq4kjTRolRM0x31L3Sb0TMLKCZqTiaVu9/QErW+oyAQHp0p9NqRBqnXbgq6glcATdjQpRw1ctb3ZFrQSeAQejr8fabRegbvcxXPScfHcDxCwrL9YK/Y0qILdgwSnpNHfHhWl4ZbAW/O6LEgplHRC/mEtBfgClkhxraFbr7gAAAAASUVORK5CYII=);
  427. //mask-size: 100% 100%;
  428. mask-size: 100% calc(100% + 1px);
  429. mask-repeat: no-repeat;
  430. //mask-position: bottom;
  431. /*border-bottom: 0;
  432. &::before {
  433. display: none;
  434. }*/
  435. &:first-child {
  436. margin-left: 0;
  437. }
  438. &:hover {
  439. z-index: 1;
  440. background: var(--el-border-color-light);
  441. color: var(--el-color-primary-light-3);
  442. }
  443. &.is-active {
  444. background: var(--el-color-primary-light-9);
  445. .is-icon-close {
  446. &:hover {
  447. background-color: var(--el-color-primary);
  448. }
  449. }
  450. }
  451. }
  452. }
  453. }
  454. }
  455. }
  456. // 风格2 卡片风格
  457. &.tabs-card {
  458. .el-tabs__header {
  459. .el-tabs__nav-wrap {
  460. .el-tabs__nav {
  461. .el-tabs__item {
  462. border: 1px solid var(--el-border-color);
  463. border-radius: 4px;
  464. margin-left: 4px;
  465. margin-top: 6px;
  466. //height: 36px;
  467. height: calc(var(--el-tabs-header-height) - 12px);
  468. padding: 0 4px;
  469. &:first-child {
  470. margin-left: 0;
  471. }
  472. &:hover {
  473. color: var(--el-color-primary);
  474. border-color: var(--el-color-primary-light-7);
  475. }
  476. /*.le-tabs__item {
  477. padding: 0 4px;
  478. }*/
  479. &.is-active {
  480. background-color: var(--el-color-primary-light-9);
  481. color: var(--el-color-primary);
  482. border-color: var(--el-color-primary-light-5);
  483. .is-icon-close {
  484. &:hover {
  485. background-color: var(--el-color-primary);
  486. }
  487. }
  488. }
  489. }
  490. }
  491. }
  492. }
  493. }
  494. // 风格3 矩形
  495. &.tabs-rectangle {
  496. .el-tabs__header {
  497. .el-tabs__nav-wrap {
  498. .el-tabs__nav {
  499. .el-tabs__item {
  500. //margin-left: 2px;
  501. transition: $transition;
  502. &:first-child {
  503. margin-left: 0;
  504. }
  505. &::before {
  506. position: absolute;
  507. bottom: 0;
  508. left: 0;
  509. width: 0;
  510. height: 0;
  511. content: '';
  512. //border-bottom: 2px solid var(--el-color-primary-light-5);
  513. border-bottom: 2px solid var(--el-color-primary);
  514. transition: $transition;
  515. }
  516. &:hover {
  517. //background: var(--el-color-primary-light-9);
  518. background: var(--el-border-color-light);
  519. //color: var(--el-color-primary-light-3);
  520. &::before {
  521. width: 100%;
  522. transition: $transition;
  523. }
  524. }
  525. &.is-active {
  526. background: var(--el-color-primary-light-9);
  527. color: var(--el-color-primary);
  528. &::before {
  529. width: 100%;
  530. border-bottom-color: var(--el-color-primary);
  531. }
  532. .is-icon-close {
  533. &:hover {
  534. background-color: var(--el-color-primary);
  535. }
  536. }
  537. }
  538. }
  539. }
  540. }
  541. }
  542. }
  543. }
  544. }
  545. .local-contextmenu {
  546. //--el-dropdown-menu-box-shadow: var(--el-box-shadow-light);
  547. //box-shadow: var(--el-dropdown-menu-box-shadow);
  548. box-shadow: var(--el-box-shadow-light);
  549. position: absolute;
  550. //position: fixed;
  551. top: 0;
  552. left: 0;
  553. z-index: 10;
  554. .el-dropdown-menu__item {
  555. //padding: 4px 12px;
  556. /*.le-icon {
  557. margin-right: 5px;
  558. }*/
  559. &:hover {
  560. color: var(--el-color-primary);
  561. background-color: var(--el-color-primary-light-9);
  562. }
  563. }
  564. .el-dropdown-menu__item {
  565. //padding: 4px 12px;
  566. .le-icon {
  567. margin-right: 5px;
  568. }
  569. }
  570. .el-dropdown-menu__item--divided {
  571. margin: 2px 0;
  572. }
  573. }
  574. .fast-drop-wrap {
  575. position: relative;
  576. width: 40px;
  577. height: 40px;
  578. display: flex;
  579. align-items: center;
  580. justify-content: center;
  581. &:hover,
  582. &[aria-expanded='true'] {
  583. .fast-drop-button {
  584. transform: rotate(90deg);
  585. .box-t:before {
  586. transform: rotate(45deg);
  587. }
  588. .box:before,
  589. .box:after {
  590. background: var(--el-color-primary);
  591. }
  592. }
  593. }
  594. }
  595. .fast-drop-button {
  596. display: inline-block;
  597. color: var(--el-text-color-regular);
  598. cursor: pointer;
  599. transition: transform 0.3s ease-out;
  600. .box {
  601. position: relative;
  602. display: block;
  603. width: 14px;
  604. height: 8px;
  605. &::before {
  606. position: absolute;
  607. top: 2px;
  608. left: 0;
  609. width: 6px;
  610. height: 6px;
  611. content: '';
  612. background: var(--el-text-color-regular);
  613. }
  614. &::after {
  615. position: absolute;
  616. top: 2px;
  617. left: 8px;
  618. width: 6px;
  619. height: 6px;
  620. content: '';
  621. background: var(--el-text-color-regular);
  622. }
  623. }
  624. .box-t::before {
  625. transition: transform 0.3s ease-out 0.3s;
  626. }
  627. }
  628. }
  629. .le-tabs-fast-dropdown-popper {
  630. .el-dropdown-menu__item {
  631. //padding: 4px 12px;
  632. .le-icon {
  633. margin-right: 5px;
  634. }
  635. }
  636. .el-dropdown-menu__item--divided {
  637. margin: 2px 0;
  638. }
  639. }
  640. </style>