app-layout.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. <template>
  2. <!-- BEGIN MAIN CONTAINER -->
  3. <!-- <div>112</div> -->
  4. <div class="relative">
  5. <!-- sidebar menu overlay -->
  6. <div class="fixed inset-0 bg-[black]/60 z-50 lg:hidden" :class="{ hidden: !store.state.sidebar }" @click="toggleSidebar"></div>
  7. <!-- screen loader -->
  8. <div
  9. v-show="store.state.isShowMainLoader"
  10. class="screen_loader fixed inset-0 bg-[#fafafa] dark:bg-[#060818] z-[60] grid place-content-center animate__animated"
  11. >
  12. <svg width="64" height="64" viewBox="0 0 135 135" xmlns="http://www.w3.org/2000/svg" fill="#4361ee">
  13. <path
  14. d="M67.447 58c5.523 0 10-4.477 10-10s-4.477-10-10-10-10 4.477-10 10 4.477 10 10 10zm9.448 9.447c0 5.523 4.477 10 10 10 5.522 0 10-4.477 10-10s-4.478-10-10-10c-5.523 0-10 4.477-10 10zm-9.448 9.448c-5.523 0-10 4.477-10 10 0 5.522 4.477 10 10 10s10-4.478 10-10c0-5.523-4.477-10-10-10zM58 67.447c0-5.523-4.477-10-10-10s-10 4.477-10 10 4.477 10 10 10 10-4.477 10-10z"
  15. >
  16. <animateTransform attributeName="transform" type="rotate" from="0 67 67" to="-360 67 67" dur="2.5s" repeatCount="indefinite" />
  17. </path>
  18. <path
  19. d="M28.19 40.31c6.627 0 12-5.374 12-12 0-6.628-5.373-12-12-12-6.628 0-12 5.372-12 12 0 6.626 5.372 12 12 12zm30.72-19.825c4.686 4.687 12.284 4.687 16.97 0 4.686-4.686 4.686-12.284 0-16.97-4.686-4.687-12.284-4.687-16.97 0-4.687 4.686-4.687 12.284 0 16.97zm35.74 7.705c0 6.627 5.37 12 12 12 6.626 0 12-5.373 12-12 0-6.628-5.374-12-12-12-6.63 0-12 5.372-12 12zm19.822 30.72c-4.686 4.686-4.686 12.284 0 16.97 4.687 4.686 12.285 4.686 16.97 0 4.687-4.686 4.687-12.284 0-16.97-4.685-4.687-12.283-4.687-16.97 0zm-7.704 35.74c-6.627 0-12 5.37-12 12 0 6.626 5.373 12 12 12s12-5.374 12-12c0-6.63-5.373-12-12-12zm-30.72 19.822c-4.686-4.686-12.284-4.686-16.97 0-4.686 4.687-4.686 12.285 0 16.97 4.686 4.687 12.284 4.687 16.97 0 4.687-4.685 4.687-12.283 0-16.97zm-35.74-7.704c0-6.627-5.372-12-12-12-6.626 0-12 5.373-12 12s5.374 12 12 12c6.628 0 12-5.373 12-12zm-19.823-30.72c4.687-4.686 4.687-12.284 0-16.97-4.686-4.686-12.284-4.686-16.97 0-4.687 4.686-4.687 12.284 0 16.97 4.686 4.687 12.284 4.687 16.97 0z"
  20. >
  21. <animateTransform attributeName="transform" type="rotate" from="0 67 67" to="360 67 67" dur="8s" repeatCount="indefinite" />
  22. </path>
  23. </svg>
  24. </div>
  25. <div class="fixed bottom-6 ltr:right-6 rtl:left-6 z-50">
  26. <template v-if="showTopButton">
  27. <button
  28. type="button"
  29. class="btn btn-outline-primary rounded-full p-2 animate-pulse bg-[#fafafa] dark:bg-[#060818] dark:hover:bg-primary"
  30. @click="goToTop"
  31. >
  32. <svg width="24" height="24" class="h-4 w-4" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  33. <path
  34. opacity="0.5"
  35. fill-rule="evenodd"
  36. clip-rule="evenodd"
  37. d="M12 20.75C12.4142 20.75 12.75 20.4142 12.75 20L12.75 10.75L11.25 10.75L11.25 20C11.25 20.4142 11.5858 20.75 12 20.75Z"
  38. fill="currentColor"
  39. />
  40. <path
  41. d="M6.00002 10.75C5.69667 10.75 5.4232 10.5673 5.30711 10.287C5.19103 10.0068 5.25519 9.68417 5.46969 9.46967L11.4697 3.46967C11.6103 3.32902 11.8011 3.25 12 3.25C12.1989 3.25 12.3897 3.32902 12.5304 3.46967L18.5304 9.46967C18.7449 9.68417 18.809 10.0068 18.6929 10.287C18.5768 10.5673 18.3034 10.75 18 10.75L6.00002 10.75Z"
  42. fill="currentColor"
  43. />
  44. </svg>
  45. </button>
  46. </template>
  47. </div>
  48. <!-- BEGIN APP SETTING LAUNCHER -->
  49. <!-- <Setting /> -->
  50. <!-- END APP SETTING LAUNCHER -->
  51. <div class="main-container text-black dark:text-white-dark min-h-screen" :class="[store.state.navbar]">
  52. <!-- BEGIN SIDEBAR -->
  53. <Sidebar />
  54. <!-- END SIDEBAR -->
  55. <div class="main-content flex flex-col min-h-screen">
  56. <!-- BEGIN TOP NAVBAR -->
  57. <Header />
  58. <!-- END TOP NAVBAR -->
  59. <!-- BEGIN CONTENT AREA -->
  60. <div class="p-4 animation" :class="sidebarShow ? 'ml-56' : 'ml-0'">
  61. <!-- -->
  62. <!--侧边栏 -->
  63. <div :class="{ 'dark text-white-dark': store.state.semidark }">
  64. <!-- z-50 top-32 :class="sidebarShow ? '!left-0' : ''"-->
  65. <nav
  66. class="sidebar fixed min-h-screen h-full bottom-0 w-[228px] shadow-[5px_0_25px_0_rgba(94,92,154,0.1)] transition-all duration-300"
  67. :class="sidebarShow ? '!left-0' : ''"
  68. style="top: 105px"
  69. >
  70. <!-- mt-5 -->
  71. <div class="bg-white dark:bg-[#0e1726] h-full">
  72. <div class="flex justify-between items-center px-2 py-1"></div>
  73. <perfect-scrollbar
  74. v-if="superMenu == 'super'"
  75. :options="{
  76. swipeEasing: true,
  77. wheelPropagation: false,
  78. }"
  79. class="h-[calc(100vh-80px)] relative"
  80. >
  81. <ul class="relative font-semibold space-y-0.5 p-4 py-0">
  82. <li class="menu nav-item">
  83. <button
  84. type="button"
  85. class="nav-link group w-full"
  86. :class="{ active: activeDropdown === 'package' }"
  87. @click="activeDropdown === 'package' ? (activeDropdown = null) : (activeDropdown = 'package')"
  88. >
  89. <div class="flex items-center">
  90. <icon-menu-users class="group-hover:!text-primary shrink-0" />
  91. <span class="ltr:pl-3 rtl:pr-3 text-black dark:text-[#506690] dark:group-hover:text-white-dark">{{ $t('package') }}</span>
  92. </div>
  93. <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'package' }">
  94. <icon-caret-down />
  95. </div>
  96. </button>
  97. <vue-collapsible :isOpen="activeDropdown === 'package'">
  98. <ul class="sub-menu text-gray-500">
  99. <li>
  100. <router-link to="/package/packageList" @click="toggleMobileMenu">{{ $t('packageList') }}</router-link>
  101. </li>
  102. <li>
  103. <router-link to="/package/packagePermissions" @click="toggleMobileMenu">{{ $t('packagePermissions') }}</router-link>
  104. </li>
  105. </ul>
  106. </vue-collapsible>
  107. <vue-collapsible :isOpen="activeDropdown === 'package'">
  108. <ul class="sub-menu text-gray-500"></ul>
  109. </vue-collapsible>
  110. </li>
  111. <li class="menu nav-item">
  112. <button
  113. type="button"
  114. class="nav-link group w-full"
  115. :class="{ active: activeDropdown === 'templateManage' }"
  116. @click="activeDropdown === 'templateManage' ? (activeDropdown = null) : (activeDropdown = 'templateManage')"
  117. >
  118. <div class="flex items-center">
  119. <icon-menu-users class="group-hover:!text-primary shrink-0" />
  120. <span class="ltr:pl-3 rtl:pr-3 text-black dark:text-[#506690] dark:group-hover:text-white-dark">{{ $t('templateManage') }}</span>
  121. </div>
  122. <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'templateManage' }">
  123. <icon-caret-down />
  124. </div>
  125. </button>
  126. <vue-collapsible :isOpen="activeDropdown === 'templateManage'">
  127. <ul class="sub-menu text-gray-500">
  128. <li>
  129. <router-link to="/templateManage/smsTemplate" @click="toggleMobileMenu">{{ $t('smsTemplate') }}</router-link>
  130. </li>
  131. <li>
  132. <router-link to="/templateManage/emailtemplate" @click="toggleMobileMenu">{{ $t('emailtemplate') }}</router-link>
  133. </li>
  134. <li>
  135. <router-link to="/templateManage/accountTemplate" @click="toggleMobileMenu">{{ $t('accountTemplate') }}</router-link>
  136. </li>
  137. <li>
  138. <router-link to="/templateManage/systemMessage" @click="toggleMobileMenu">{{ $t('systemMessage') }}</router-link>
  139. </li>
  140. </ul>
  141. </vue-collapsible>
  142. <vue-collapsible :isOpen="activeDropdown === 'package'">
  143. <ul class="sub-menu text-gray-500"></ul>
  144. </vue-collapsible>
  145. </li>
  146. <li class="menu nav-item">
  147. <button
  148. type="button"
  149. class="nav-link group w-full"
  150. :class="{ active: activeDropdown === 'users' }"
  151. @click="activeDropdown === 'users' ? (activeDropdown = null) : (activeDropdown = 'users')"
  152. >
  153. <div class="flex items-center">
  154. <icon-menu-users class="group-hover:!text-primary shrink-0" />
  155. <span class="ltr:pl-3 rtl:pr-3 text-black dark:text-[#506690] dark:group-hover:text-white-dark">{{ $t('setting') }}</span>
  156. </div>
  157. <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'users' }">
  158. <icon-caret-down />
  159. </div>
  160. </button>
  161. <vue-collapsible :isOpen="activeDropdown === 'users'">
  162. <ul class="sub-menu text-gray-500">
  163. <li>
  164. <router-link to="/setting/companyList" @click="toggleMobileMenu">{{ $t('companyList') }}</router-link>
  165. </li>
  166. <li>
  167. <router-link to="/setting/dicData" @click="toggleMobileMenu">{{ $t('dicData') }}</router-link>
  168. </li>
  169. <li>
  170. <router-link to="/setting/menuManage" @click="toggleMobileMenu">{{ $t('menuManage') }}</router-link>
  171. </li>
  172. <li>
  173. <router-link to="/setting/express" @click="toggleMobileMenu">{{ $t('express') }}</router-link>
  174. </li>
  175. <!-- express -->
  176. </ul>
  177. </vue-collapsible>
  178. <vue-collapsible :isOpen="activeDropdown === 'users'">
  179. <ul class="sub-menu text-gray-500"></ul>
  180. </vue-collapsible>
  181. </li>
  182. </ul>
  183. </perfect-scrollbar>
  184. <perfect-scrollbar
  185. v-if="superMenu == 'compan'"
  186. :options="{
  187. swipeEasing: true,
  188. wheelPropagation: false,
  189. }"
  190. class="h-[calc(100vh-80px)] relative"
  191. >
  192. <ul class="relative font-semibold space-y-0.5">
  193. <li class="menu nav-item">
  194. <button
  195. type="button"
  196. class="nav-link group w-full !pl-2"
  197. :class="{ active: activeDropdown === 'dashboard' }"
  198. @click="activeDropdown === 'dashboard' ? (activeDropdown = null) : (activeDropdown = 'dashboard')"
  199. >
  200. <div class="flex items-center">
  201. <i class="pg-icon w-5 h-5"></i>
  202. <span class="ltr:pl-2 rtl:pr-3 text-black dark:text-[#506690] dark:group-hover:text-white-dark">
  203. {{ $t('selfBuilt') }}
  204. </span>
  205. </div>
  206. </button>
  207. <vue-collapsible :isOpen="true">
  208. <ul class="sub-menu text-gray-500">
  209. <li>
  210. <router-link to="/stock/warehouseList" @click="toggleMobileMenu">{{ $t('warehouseList') }}</router-link>
  211. </li>
  212. <li>
  213. <router-link to="/stock/warehouseLocation" @click="toggleMobileMenu">{{ $t('warehouseLocation') }}</router-link>
  214. </li>
  215. <li>
  216. <router-link to="/stock/goodsLend" @click="toggleMobileMenu">{{ $t('goodsLend') }}</router-link>
  217. </li>
  218. <li>
  219. <router-link to="/stock/goodsTransfer" @click="toggleMobileMenu">{{ $t('goodsTransfer') }}</router-link>
  220. </li>
  221. <li>
  222. <router-link to="/stock/goodsExchange" @click="toggleMobileMenu">{{ $t('goodsExchange') }}</router-link>
  223. </li>
  224. <li>
  225. <router-link to="/stock/goodsOutBound" @click="toggleMobileMenu">{{ $t('goodsOutBound') }}</router-link>
  226. </li>
  227. <li>
  228. <router-link to="/stock/goodsInventory" @click="toggleMobileMenu">{{ $t('goodsInventory') }}</router-link>
  229. </li>
  230. <li>
  231. <router-link to="/stock/goodsInventoryLoss" @click="toggleMobileMenu">{{ $t('goodsInventoryLoss') }}</router-link>
  232. </li>
  233. <li>
  234. <router-link to="/stock/goodsUnderstock" @click="toggleMobileMenu">{{ $t('goodsUnderstock') }}</router-link>
  235. </li>
  236. </ul>
  237. </vue-collapsible>
  238. </li>
  239. <div style="margin-left: -16px; border-bottom: 1px solid #ebebeb; width: 260px"></div>
  240. <li class="menu nav-item">
  241. <button
  242. type="button"
  243. class="nav-link group w-full !pl-2"
  244. :class="{ active: activeDropdown === 'overseas' }"
  245. @click="activeDropdown === 'overseas' ? (activeDropdown = null) : (activeDropdown = 'overseas')"
  246. >
  247. <div class="flex items-center">
  248. <i class="pg-icon w-5 h-5"></i>
  249. <span class="ltr:pl-3 rtl:pr-3 text-black dark:text-[#506690] dark:group-hover:text-white-dark">{{ $t('overseasWarehouse') }}</span>
  250. </div>
  251. <div :class="{ 'rtl:rotate-90 -rotate-90': activeDropdown == 'overseas' }"></div>
  252. </button>
  253. <vue-collapsible :isOpen="true">
  254. <ul class="sub-menu text-gray-500">
  255. <li>
  256. <router-link to="/stock/overseasWarehouseAuth" @click="toggleMobileMenu">{{ $t('overseasWarehouseAuth') }}</router-link>
  257. </li>
  258. <li>
  259. <router-link to="/stock/overseasList" @click="toggleMobileMenu">{{ $t('warehouseList') }}</router-link>
  260. </li>
  261. </ul>
  262. </vue-collapsible>
  263. <vue-collapsible :isOpen="activeDropdown === 'overseas'">
  264. <ul class="sub-menu text-gray-500"></ul>
  265. </vue-collapsible>
  266. </li>
  267. </ul>
  268. </perfect-scrollbar>
  269. <!-- <el-scrollbar wrap-class="scrollbar-wrapper">
  270. <el-menu mode="vertical" :default-active="$route.path" :collapse="isCollapse">
  271. <sidebar-item v-for="route in permissionRouters" :key="route.name" :item="route" :base-path="route.path"></sidebar-item>
  272. </el-menu>
  273. </el-scrollbar> -->
  274. </div>
  275. <span id="sidebarSwitch" title="Shortcut:S" :style="sidebarShow ? '' : 'left:-12px;'">
  276. <span class="w" @click="contract" :style="sidebarShow ? '' : 'width:16px;'">
  277. <b class="caret caret-left" v-if="sidebarShow"></b>
  278. <b class="caret caret-right" v-else></b>
  279. </span>
  280. </span>
  281. </nav>
  282. </div>
  283. <div class="tags-view-container tabs-box">
  284. <!-- 标签页 -->
  285. <!-- tabs-box --><!-- :class="'active'" -->
  286. <scroll-pane class="tags-view-wrapper" ref="scrollPane">
  287. <router-link
  288. ref="tag"
  289. class="tags-view-item"
  290. v-for="(item, index) in toPathList"
  291. :key="index"
  292. :to="item.path"
  293. :class="item.path == toPath ? 'active' : ''"
  294. >
  295. <div :class="item.path !== '/dashboard' ? 'flex' : ''" class="justify-around">
  296. <span>{{ t(item.name) }}</span>
  297. <span class="ml-1 mt-0.5" v-if="item.path !== '/dashboard'" @click.prevent.stop="removeTag(index)">
  298. <el-icon>
  299. <Close />
  300. </el-icon>
  301. </span>
  302. </div>
  303. </router-link>
  304. </scroll-pane>
  305. <!-- 第二种格式 -->
  306. <!-- <el-breadcrumb separator="/">
  307. <el-breadcrumb-item :to="{ path: '/' }" >{{$t('home')}}</el-breadcrumb-item>
  308. <el-breadcrumb-item :to="{path:'/stock/warehouseList'}">{{$t('warehouseList')}}</el-breadcrumb-item>
  309. <el-breadcrumb-item :to="{path:'/order/orderList'}" :active="true">{{$t('order')}}</el-breadcrumb-item>
  310. </el-breadcrumb> -->
  311. </div>
  312. <!-- 页面主体部分 -->
  313. <router-view></router-view>
  314. </div>
  315. <!-- END CONTENT AREA -->
  316. <!-- BEGIN FOOTER -->
  317. <!-- END FOOTER -->
  318. </div>
  319. <Footer />
  320. </div>
  321. </div>
  322. </template>
  323. <script setup lang="ts">
  324. import { ref, onMounted, computed, watch } from 'vue';
  325. //import Sidebar from '@/components/sidebar/index.vue'
  326. import VueCollapsible from 'vue-height-collapsible/vue3';
  327. import Sidebar from '@/components/layout/Sidebar.vue';
  328. import Header from '@/components/layout/Header.vue';
  329. import Footer from '@/components/layout/Footer.vue';
  330. import Setting from '@/components/ThemeCustomizer.vue';
  331. import appSetting from '@/app-setting';
  332. import IconMenuUsers from '@/components/icon/menu/icon-menu-users.vue';
  333. import IconCaretDown from '@/components/icon/icon-caret-down.vue';
  334. import IconCaretsDown from '@/components/icon/icon-carets-down.vue';
  335. import IconMenuDashboard from '@/components/icon/menu/icon-menu-dashboard.vue';
  336. import ScrollPane from '../components/ScrollPane.vue';
  337. import { ArrowRight, Close } from '@element-plus/icons-vue';
  338. import store from '@/stores';
  339. import { useRoute, useRouter } from 'vue-router'; // 引入 useRoute
  340. import { useI18n } from 'vue-i18n';
  341. import { Delete, Edit, Search, Share, Upload, CircleClose } from '@element-plus/icons-vue';
  342. const { t } = useI18n();
  343. /* 获取菜单数据 */
  344. const permissionRouters = computed(() => store.getters.permission_routers);
  345. const sidebar = computed(() => store.getters.sidebar);
  346. const isCollapse = computed(() => !sidebar.value.opened);
  347. const route = useRoute(); // 获取当前路由
  348. const router = useRouter();
  349. const showTopButton = ref(false);
  350. const activeDropdown: any = ref('');
  351. const superMenu = computed(() => store.state.superMenu);
  352. const toggleMobileMenu = () => {
  353. if (window.innerWidth < 1024) {
  354. store.dispatch('toggleSidebar');
  355. }
  356. };
  357. const toggleSidebars = () => {
  358. store.dispatch('toggleSidebar');
  359. };
  360. onMounted(() => {
  361. toPathList.value.push({ name: t('home'), path: '/dashboard' });
  362. window.onscroll = () => {
  363. if (document.body.scrollTop > 50 || document.documentElement.scrollTop > 50) {
  364. showTopButton.value = true;
  365. } else {
  366. showTopButton.value = false;
  367. }
  368. };
  369. const eleanimation: any = document.querySelector('.animation');
  370. eleanimation.addEventListener('animationend', function () {
  371. appSetting.changeAnimation('remove');
  372. });
  373. store.dispatch('toggleMainLoader');
  374. /* store.toggleMainLoader(); */
  375. const selector = document.querySelector('.sidebar ul a[href="' + window.location.pathname + '"]');
  376. if (selector) {
  377. selector.classList.add('active');
  378. const ul: any = selector.closest('ul.sub-menu');
  379. if (ul) {
  380. let ele: any = ul.closest('li.menu').querySelectorAll('.nav-link') || [];
  381. if (ele.length) {
  382. ele = ele[0];
  383. setTimeout(() => {
  384. ele.click();
  385. });
  386. }
  387. }
  388. }
  389. });
  390. const goToTop = () => {
  391. document.body.scrollTop = 0;
  392. document.documentElement.scrollTop = 0;
  393. };
  394. const toggleSidebar = () => {
  395. store.dispatch('toggleSidebar');
  396. };
  397. const sidebarShow = ref(true);
  398. /* 收缩 */
  399. const contract = () => {
  400. sidebarShow.value = !sidebarShow.value;
  401. };
  402. /* 展开 */
  403. const expand = () => {
  404. sidebarShow.value = true;
  405. };
  406. console.log(store.state.permission);
  407. /* 监听 */
  408. const toPath = ref('');
  409. const toPathList = ref<{ name: any; path: string }[]>([]);
  410. watch(route, (to) => {
  411. console.log(to.path);
  412. const secondSlashIndex = to.path.indexOf('/', to.path.indexOf('/') + 1);
  413. const valueBeforeSecondSlash = to.path.substring(0, secondSlashIndex);
  414. console.log(valueBeforeSecondSlash);
  415. toPath.value = to.path;
  416. //如果路径不存在则添加路径
  417. if (!toPathList.value.some((item) => item.path === to.path)) {
  418. toPathList.value.push({ name: to.name || null, path: to.path });
  419. // Limit to last 10 entries
  420. if (toPathList.value.length > 10) toPathList.value.shift();
  421. }
  422. });
  423. /* 获取页面 */
  424. const visitedViews = computed(() => store.state.tagsView.visitedViews);
  425. const lastTenVisitedViews = computed(() => {
  426. const views = store.state.tagsView.visitedViews;
  427. console.log(views);
  428. return views.slice(Math.max(views.length - 10, 0)); // 获取最后十条数据
  429. });
  430. /* 删除标签 */
  431. const removeTag = (index: number) => {
  432. if (toPathList.value.length > 1) {
  433. const tagToRemove = toPathList.value[index]; // 获取要删除的标签
  434. toPathList.value.splice(index, 1); // 删除标签
  435. // 确定要导航到的上一个标签
  436. const previousTag = toPathList.value[index] || toPathList.value[index - 1]; // 获取上一个标签或保持在当前标签
  437. // 检查当前路由是否与要删除的标签相同
  438. if (previousTag) {
  439. router.push(previousTag.path); // 导航到上一个标签
  440. }
  441. }
  442. // 保证最后一个标签为首页
  443. if (toPathList.value.length === 1 && toPathList.value[0].path !== '/dashboard') {
  444. toPathList.value.push({ name: t('home'), path: '/dashboard' });
  445. }
  446. };
  447. /* 侧边栏List */
  448. const sidebarList = ref([]);
  449. </script>
  450. <style>
  451. #sidebarSwitch {
  452. position: fixed;
  453. left: 213px;
  454. top: 43%;
  455. z-index: 5;
  456. display: inline-block;
  457. width: 30px;
  458. height: 90px;
  459. cursor: pointer;
  460. text-align: center;
  461. }
  462. #sidebarSwitch .w {
  463. display: inline-block;
  464. width: 10px;
  465. text-align: center;
  466. height: 40px;
  467. background: #fff;
  468. margin: 23px 0 0;
  469. border: 1px solid #ddd;
  470. border-radius: 5px;
  471. }
  472. #sidebarSwitch .w .caret {
  473. margin: 17px 0 0;
  474. }
  475. .caret {
  476. display: inline-block;
  477. transform: translate(0, -2px) rotate(-45deg);
  478. width: 5px;
  479. height: 5px;
  480. border-left: 1px solid #999;
  481. border-bottom: 1px solid #999;
  482. border-top: 0;
  483. border-right: 0;
  484. content: '';
  485. }
  486. .caret-left {
  487. transform: translate(1px, 0) rotate(45deg);
  488. }
  489. .caret-right {
  490. transform: translate(-1px, 0) rotate(-135deg);
  491. }
  492. .pg-icon {
  493. background: url('../assets/images/icon.png') 0 0 no-repeat;
  494. display: inline-block;
  495. }
  496. .sidebar ul.sub-menu li a::before {
  497. height: 0 !important;
  498. }
  499. .tags-view-container {
  500. /* background: rgb(250, 250, 250); */
  501. width: 100%;
  502. /* box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04); */
  503. display: flex;
  504. align-items: center;
  505. /* justify-content: space-between; */
  506. /* border-bottom: 1px solid #d8dce5; */
  507. /* margin-left: 20px;
  508. margin-top: 5px; */
  509. }
  510. .tags-view-container.tabs-box {
  511. min-width: 1260px;
  512. }
  513. .tags-view-container .dropdown-btn {
  514. margin: 0 20px;
  515. width: 20px;
  516. height: 30px;
  517. font-size: 20px;
  518. display: flex;
  519. align-items: center;
  520. justify-content: center;
  521. /* border-radius: 50%; */
  522. /* background: rgba(215,215,215, 1); */
  523. cursor: pointer;
  524. }
  525. .tags-view-container .dropdown-btn:hover {
  526. filter: brightness(1.1);
  527. transform: rotate(180deg);
  528. transition: 0.15s;
  529. }
  530. .tags-view-container .dropdown-btn .el-icon-arrow-down {
  531. font-size: 16px;
  532. font-weight: 600;
  533. color: #fff;
  534. }
  535. .tags-view-container .tags-view-wrapper {
  536. width: calc(100% - 120px);
  537. height: 40px;
  538. }
  539. .tags-view-container .tags-view-wrapper .tags-view-item {
  540. display: inline-block;
  541. position: relative;
  542. height: 36px;
  543. line-height: 36px;
  544. color: #495060;
  545. padding: 0 8px;
  546. margin-right: 9px;
  547. margin-top: 4px;
  548. background-color: #f5f7fa;
  549. border-radius: 7px 7px 0 0;
  550. }
  551. .tags-view-container .tags-view-wrapper .tags-view-item:first-of-type {
  552. /* margin-left: 15px; */
  553. }
  554. .tags-view-container .tags-view-wrapper .tags-view-item.active {
  555. background-color: #fff;
  556. /* color: #fff; */
  557. /* border-color: #409EFF; */
  558. color: #409eff;
  559. }
  560. .tags-view-container .tags-view-wrapper .tags-view-item.active::before {
  561. /* content: ''; */
  562. /* background: #fff; */
  563. /* display: inline-block; */
  564. /* width: 8px; */
  565. /* height: 8px; */
  566. /* border-radius: 50%; */
  567. /* position: relative; */
  568. /* margin-right: 2px; */
  569. }
  570. .tags-view-container .contextmenu {
  571. margin: 0;
  572. background: #fff;
  573. z-index: 999;
  574. position: absolute;
  575. list-style-type: none;
  576. padding: 5px 0;
  577. border-radius: 4px;
  578. font-size: 12px;
  579. font-weight: 400;
  580. color: #333;
  581. box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
  582. }
  583. .tags-view-container .contextmenu li {
  584. margin: 0;
  585. padding: 7px 16px;
  586. cursor: pointer;
  587. }
  588. .tags-view-container .contextmenu li:hover {
  589. background: #eee;
  590. }
  591. .tags-view-container .tags-view-item {
  592. padding: 0 10px;
  593. min-width: 80px;
  594. border-radius: 4px;
  595. text-align: center;
  596. user-select: none;
  597. }
  598. .tags-view-container .tags-view-item .el-icon-close {
  599. width: 16px;
  600. height: 16px;
  601. border-radius: 50%;
  602. text-align: center;
  603. transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
  604. transform-origin: 100% 50%;
  605. }
  606. .tags-view-container .tags-view-item .el-icon-close:before {
  607. transform: scale(0.6);
  608. display: inline-block;
  609. }
  610. .tags-view-container .tags-view-item .el-icon-close:hover {
  611. background-color: #b4bccc;
  612. color: #fff;
  613. }
  614. </style>