MenuDt.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  1. <!--
  2. * @Author: your name
  3. * @Date: 2026-05-21 15:49:05
  4. * @LastEditTime: 2026-05-21 15:49:16
  5. * @LastEditors: milo-MacBook-Pro.local
  6. * @Description: In User Settings Edit
  7. * @FilePath: /performance-test/src/views/home/components/MenuDt.vue
  8. -->
  9. <template>
  10. <el-aside
  11. :width="!isCollapse ? '250px' : '100px'"
  12. :class="['asideBox', isTechBlueTheme ? 'asideBox--tech' : 'asideBox--legacy']"
  13. >
  14. <div class="homeBox"></div>
  15. <el-menu
  16. collapse-transition
  17. ref="menu"
  18. :class="['mt-3 el-menu-vertical-demo', menuThemeClass]"
  19. @open="handleOpen"
  20. @close="handleClose"
  21. :background-color="menuBackgroundColor"
  22. :text-color="menuTextColor"
  23. :active-text-color="menuActiveTextColor"
  24. :router="false"
  25. :default-active="menuDefaultActive"
  26. :collapse="isCollapse"
  27. :default-openeds="openMenus"
  28. >
  29. <template v-for="item in routerList">
  30. <!-- 有子菜单 -->
  31. <el-submenu
  32. v-if="
  33. item.children && item.children.length && item.meta?.hidden === false
  34. "
  35. :index="normalizedMenuParentIndex(item)"
  36. :popper-append-to-body="true"
  37. :popper-class="menuPopperClass"
  38. >
  39. <template slot="title">
  40. <i v-if="isElPrefix(item.iconName)" class="el-icon-menu"></i>
  41. <i v-else class="svnIcon">
  42. <svg-icon :icon-class="item.iconName" />
  43. </i>
  44. <span>{{ item.name }}</span>
  45. </template>
  46. <!-- 子菜单项 -->
  47. <el-menu-item
  48. v-for="child in item.children"
  49. v-if="child.meta?.hidden === false"
  50. :key="child.id"
  51. class="menu-dt-leaf"
  52. :class="{ 'dt-menu-leaf-active': menuLeafIsActive(child) }"
  53. :index="menuItemIndex(child)"
  54. @click="handleChangeMenuUrl(child)"
  55. >
  56. <i v-if="isElPrefix(child.iconName)" class="el-icon-menu"></i>
  57. <i v-else class="svnIcon">
  58. <svg-icon :icon-class="child.iconName" />
  59. </i>
  60. <span>{{ child.name }}</span>
  61. </el-menu-item>
  62. </el-submenu>
  63. <!-- 没有子菜单 -->
  64. <el-tooltip
  65. v-else-if="item.meta?.hidden === false"
  66. class="box-item"
  67. effect="dark"
  68. :content="item.name"
  69. placement="right-start"
  70. >
  71. <el-menu-item
  72. class="menu-dt-leaf"
  73. :class="{ 'dt-menu-leaf-active': menuLeafIsActive(item) }"
  74. :key="item.id"
  75. :index="menuItemIndex(item)"
  76. @click="handleChangeMenuUrl(item)"
  77. >
  78. <i v-if="isElPrefix(item.iconName)" class="el-icon-menu"></i>
  79. <i v-else-if="item.iconName" class="svnIcon">
  80. <svg-icon :icon-class="item.iconName" />
  81. </i>
  82. <i
  83. class="svnIcon"
  84. v-else-if="item.path === '/home/laserRangeFinder'"
  85. >
  86. <svg-icon
  87. style="width: 22px; height: 22px"
  88. icon-class="laserRangeFinder"
  89. />
  90. </i>
  91. <span>{{ item.name }}</span>
  92. </el-menu-item>
  93. </el-tooltip>
  94. </template>
  95. </el-menu>
  96. <!-- 展开/收起 -->
  97. <div class="foldBox flexCenter">
  98. <span
  99. v-show="isCollapse"
  100. class="el-icon-s-unfold icon"
  101. @click.stop="isCollapse = false"
  102. >展开</span
  103. >
  104. <el-switch
  105. v-show="!isCollapse"
  106. :value="isExpanded"
  107. @change="toggleAllMenus"
  108. />
  109. <span
  110. v-show="!isCollapse"
  111. class="el-icon-s-fold icon"
  112. @click.stop="isCollapse = true"
  113. >收起</span
  114. >
  115. </div>
  116. </el-aside>
  117. </template>
  118. <script>
  119. import { mapActions, mapState } from "vuex";
  120. export default {
  121. data() {
  122. return {
  123. isExpanded: false,
  124. openMenus: [],
  125. isCollapse: true,
  126. activeIndex: false,
  127. keyObject: {},
  128. activeTextColor: "#ffffff",
  129. /** Element UI 会先响应 default-active 变 '' 并清空内部高亮,解析失败时用最近一次有效 index 兜底,避免闪一下又消失 */
  130. staleMenuFallbackIndex: "",
  131. routerList: [
  132. {
  133. id: 1,
  134. path: "/home/cockpitManage",
  135. name: "首页",
  136. iconName: "gps",
  137. meta: { hidden: false },
  138. },
  139. ],
  140. };
  141. },
  142. created() {
  143. this.routerList = [
  144. ...this.routerList,
  145. ...this.$store.state.auth.dynamicRouter,
  146. ];
  147. },
  148. computed: {
  149. ...mapState({
  150. currentMenuIndex: (state) => state.breadStore?.currentUrl?.routeUrl,
  151. currentTheme: (state) => state.themes?.theme,
  152. }),
  153. isTechBlueTheme() {
  154. return this.currentTheme === "tech-blue";
  155. },
  156. menuThemeClass() {
  157. return this.isTechBlueTheme ? "menu-dt-dark" : "menu-dt-light";
  158. },
  159. menuBackgroundColor() {
  160. return this.isTechBlueTheme ? "#141414" : "#eff1f3";
  161. },
  162. menuTextColor() {
  163. return this.isTechBlueTheme ? "#999999" : "#606266";
  164. },
  165. menuActiveTextColor() {
  166. if (this.isTechBlueTheme) return "#ffffff";
  167. const primary = getComputedStyle(document.documentElement)
  168. .getPropertyValue("--primary-color")
  169. .trim();
  170. return primary || "#82780c";
  171. },
  172. menuPopperClass() {
  173. return this.isTechBlueTheme ? "menu-dt-dark-popup" : "";
  174. },
  175. allMenuIndexes() {
  176. return this.routerList.map((item) =>
  177. item.children && item.children.length && item.meta?.hidden === false
  178. ? this.normalizedMenuParentIndex(item)
  179. : this.menuItemIndex(item),
  180. );
  181. },
  182. /** 传给 el-menu 的 default-active:优先按路由解析,失败时若在「同一菜单页」则用 stale 占位,防止 Element 把 activeIndex 清空 */
  183. menuDefaultActive() {
  184. const direct = this.resolveMenuActiveFromRoute(this.$route);
  185. if (direct) return direct;
  186. if (
  187. this.staleMenuFallbackIndex &&
  188. this.routeMatchesMenuIndex(
  189. this.$route,
  190. this.staleMenuFallbackIndex,
  191. )
  192. ) {
  193. return this.staleMenuFallbackIndex;
  194. }
  195. return "";
  196. },
  197. },
  198. watch: {
  199. currentMenuIndex(newVal) {
  200. if (newVal && this.$refs.menu) {
  201. this.$refs.menu.close(newVal);
  202. }
  203. },
  204. $route() {
  205. const d = this.resolveMenuActiveFromRoute(this.$route);
  206. if (d) {
  207. this.staleMenuFallbackIndex = d;
  208. } else if (
  209. !this.routeMatchesMenuIndex(
  210. this.$route,
  211. this.staleMenuFallbackIndex,
  212. )
  213. ) {
  214. this.staleMenuFallbackIndex = "";
  215. }
  216. this.$nextTick(() => this.syncElMenuHighlight());
  217. },
  218. routerList() {
  219. const d = this.resolveMenuActiveFromRoute(this.$route);
  220. if (d) this.staleMenuFallbackIndex = d;
  221. this.$nextTick(() => this.syncElMenuHighlight());
  222. },
  223. },
  224. mounted() {
  225. const initial = this.resolveMenuActiveFromRoute(this.$route);
  226. if (initial) this.staleMenuFallbackIndex = initial;
  227. this.$nextTick(() => this.syncElMenuHighlight());
  228. },
  229. methods: {
  230. ...mapActions("menuTag", ["addTag"]),
  231. toggleAllMenus() {
  232. this.isExpanded = !this.isExpanded;
  233. this.openMenus = this.isExpanded
  234. ? this.routerList
  235. .filter((item) => item.children && item.children.length)
  236. .map((item) => this.normalizedMenuParentIndex(item))
  237. : [];
  238. },
  239. normalizePath(path) {
  240. if (path == null || path === "") return "";
  241. const t = String(path).trim();
  242. return t.startsWith("/") ? t : `/${t}`;
  243. },
  244. /** 后台 path 常与 vue-router resolve 不一致,用多候选路径对齐 $route.path */
  245. menuPathCandidatesForLeaf(rawPath) {
  246. const p = this.normalizePath(rawPath);
  247. const set = new Set([p]);
  248. if (!/^\/home(\/|$)/.test(p)) {
  249. set.add(`/home${p.startsWith("/") ? "" : "/"}${p.replace(/^\//, "")}`);
  250. }
  251. return [...set];
  252. },
  253. leafMatchesRoute(leaf, routePath, qid) {
  254. const pathOk = this.menuPathCandidatesForLeaf(leaf.path).includes(
  255. routePath,
  256. );
  257. if (!pathOk) return false;
  258. if (qid === undefined) return true;
  259. return String(leaf.id) === qid;
  260. },
  261. parseMenuIndexString(indexStr) {
  262. if (!indexStr || typeof indexStr !== "string") return null;
  263. const q = indexStr.indexOf("?");
  264. const pathPart = q >= 0 ? indexStr.slice(0, q) : indexStr;
  265. let id = undefined;
  266. if (q >= 0) {
  267. const params = new URLSearchParams(indexStr.slice(q + 1));
  268. if (params.has("id")) id = String(params.get("id"));
  269. }
  270. const pathname = this.normalizePath(pathPart);
  271. return { pathname, id };
  272. },
  273. /** 当前路由是否与某个已注册的菜单 index(含 path + id)一致,用于避免因 path 写法差异误清高亮 */
  274. routeMatchesMenuIndex(route, indexStr) {
  275. const parsed = this.parseMenuIndexString(indexStr);
  276. if (!parsed) return false;
  277. const qid =
  278. route.query.id !== undefined ? String(route.query.id) : undefined;
  279. const candidates = this.menuPathCandidatesForLeaf(parsed.pathname);
  280. if (!candidates.includes(route.path)) return false;
  281. // 两边的 id 都有则必须一致;若 URL 暂时没有 id(重定向瞬时等),只要 path 对上就视为仍为该菜单页
  282. if (parsed.id !== undefined && qid !== undefined) {
  283. return parsed.id === qid;
  284. }
  285. return true;
  286. },
  287. /** 必须与 el-menu-item 的 :index 字符串完全一致(含路径规范),否则高亮失效 */
  288. menuItemIndex(item) {
  289. const p = this.normalizePath(item.path);
  290. return `${p}?id=${item.id}`;
  291. },
  292. normalizedMenuParentIndex(item) {
  293. return this.normalizePath(item.path);
  294. },
  295. /** 与 menuDefaultActive 一致即选中(兜底子菜单叶子项未及时打上 is-active 时仍显示蓝底高亮) */
  296. menuLeafIsActive(item) {
  297. if (!item) return false;
  298. const idx = this.menuDefaultActive;
  299. return !!idx && idx === this.menuItemIndex(item);
  300. },
  301. getVisibleLeafMenuItems() {
  302. const leaves = [];
  303. for (const item of this.routerList) {
  304. if (item.meta?.hidden !== false) continue;
  305. const isSubmenuParent =
  306. item.children &&
  307. item.children.length &&
  308. item.meta?.hidden === false;
  309. if (isSubmenuParent) {
  310. for (const child of item.children) {
  311. if (child.meta?.hidden === false) {
  312. leaves.push(child);
  313. }
  314. }
  315. } else {
  316. leaves.push(item);
  317. }
  318. }
  319. return leaves;
  320. },
  321. resolveMenuActiveFromRoute(route) {
  322. const routePath = route.path;
  323. const qid =
  324. route.query.id !== undefined ? String(route.query.id) : undefined;
  325. const leaves = this.getVisibleLeafMenuItems();
  326. let chosen = null;
  327. const pathMatches = leaves.filter((leaf) =>
  328. this.leafMatchesRoute(leaf, routePath, qid),
  329. );
  330. if (pathMatches.length === 1) {
  331. chosen =
  332. qid !== undefined &&
  333. String(pathMatches[0].id) !== qid
  334. ? null
  335. : pathMatches[0];
  336. } else if (pathMatches.length > 1) {
  337. chosen =
  338. qid !== undefined
  339. ? pathMatches.find((leaf) => String(leaf.id) === qid) || null
  340. : null;
  341. }
  342. let index = chosen ? this.menuItemIndex(chosen) : "";
  343. /** 兜底:pathname 已对但菜单 index 仅用「无 /home 前缀」等写法注册了 items */
  344. if (!index && leaves.length && qid !== undefined) {
  345. const idLeaf = leaves.find((leaf) => String(leaf.id) === qid);
  346. if (
  347. idLeaf &&
  348. this.menuPathCandidatesForLeaf(idLeaf.path).includes(routePath)
  349. ) {
  350. index = this.menuItemIndex(idLeaf);
  351. }
  352. }
  353. return index;
  354. },
  355. syncElMenuHighlight(retriesLeft = 16) {
  356. const menu = this.$refs.menu;
  357. if (!menu) return;
  358. const idx = this.menuDefaultActive;
  359. const items = menu.items || {};
  360. const current = menu.activeIndex;
  361. if (!idx) {
  362. if (
  363. current &&
  364. items[current] &&
  365. this.routeMatchesMenuIndex(this.$route, current)
  366. ) {
  367. return;
  368. }
  369. menu.activeIndex = "";
  370. return;
  371. }
  372. if (!items[idx]) {
  373. if (retriesLeft > 0) {
  374. this.$nextTick(() =>
  375. this.syncElMenuHighlight(retriesLeft - 1),
  376. );
  377. } else if (
  378. current &&
  379. items[current] &&
  380. this.routeMatchesMenuIndex(this.$route, current)
  381. ) {
  382. return;
  383. }
  384. return;
  385. }
  386. menu.activeIndex = idx;
  387. if (idx) this.staleMenuFallbackIndex = idx;
  388. menu.initOpenedMenu();
  389. },
  390. // 修复后的跳转方法
  391. handleChangeMenuUrl(item) {
  392. const key = this.menuItemIndex(item);
  393. this.staleMenuFallbackIndex = key;
  394. const tag = { path: key, name: item.name, label: item.name };
  395. this.addTag(tag);
  396. // 用 nextTick 兼容 Firefox
  397. this.$nextTick(() => {
  398. this.$router.push(key).catch((err) => {
  399. if (err.name !== "NavigationDuplicated") {
  400. console.error("路由跳转失败:", err);
  401. }
  402. });
  403. });
  404. },
  405. isElPrefix(str) {
  406. return /^el-/.test(str);
  407. },
  408. handleOpen(key, keyPath) {
  409. this.activeIndex = false;
  410. this.keyObject = { key, keyPath };
  411. },
  412. handleClose(key, keyPath) {
  413. this.activeIndex = false;
  414. this.keyObject = { key, keyPath };
  415. },
  416. },
  417. };
  418. </script>
  419. <style lang="scss" scoped>
  420. /* 深色侧栏主题色 */
  421. $dt-bg: #141414;
  422. $dt-item-blue: #1890ff;
  423. $dt-active-bg: #1890ff;
  424. $dt-text-inactive: #999999;
  425. $dt-radius: 8px;
  426. .el-menu-vertical-demo:not(.el-menu--collapse) {
  427. width: 200px;
  428. min-height: 400px;
  429. }
  430. .stop-animation,
  431. .active-animation {
  432. font-size: 0px;
  433. }
  434. .asideBox {
  435. height: 100vh;
  436. overflow-y: scroll;
  437. position: relative;
  438. padding-bottom: 40px;
  439. padding-top: 16px;
  440. box-sizing: border-box;
  441. display: flex;
  442. align-items: flex-start;
  443. justify-content: flex-start;
  444. flex-direction: column;
  445. position: relative;
  446. &.asideBox--tech {
  447. background: $dt-bg;
  448. border-right: 1px solid #2a2a2a;
  449. border-radius: 0 16px 16px 0;
  450. .el-menu {
  451. background-color: $dt-bg !important;
  452. }
  453. .foldBox .icon {
  454. color: #a6a6a6;
  455. }
  456. }
  457. &.asideBox--legacy {
  458. background: #eff1f3;
  459. border-right: 1px solid #e4e7ed;
  460. border-radius: 0;
  461. .el-menu {
  462. background-color: #eff1f3 !important;
  463. }
  464. .foldBox .icon {
  465. color: #666666;
  466. }
  467. }
  468. .homeBox {
  469. width: 100%;
  470. padding: 0 20px;
  471. display: flex;
  472. flex-direction: column;
  473. align-items: flex-start;
  474. justify-content: flex-start;
  475. box-sizing: border-box;
  476. .logoItem {
  477. display: flex;
  478. width: 100%;
  479. cursor: pointer;
  480. box-sizing: border-box;
  481. span {
  482. line-height: 50px;
  483. margin: 0 auto;
  484. }
  485. .logoImg {
  486. img {
  487. height: 100%;
  488. width: inherit;
  489. }
  490. width: 50px;
  491. height: 50px;
  492. }
  493. }
  494. .search {
  495. display: flex;
  496. width: 100%;
  497. align-items: center;
  498. justify-content: center;
  499. .el-icon-search {
  500. margin-top: 28px;
  501. }
  502. .el-input {
  503. width: inherit;
  504. }
  505. }
  506. .frame {
  507. display: flex;
  508. justify-content: space-between;
  509. align-items: center;
  510. color: #666;
  511. width: 100%;
  512. margin-top: 20px;
  513. margin-bottom: 10px;
  514. cursor: pointer;
  515. .homeIndex {
  516. display: flex;
  517. justify-content: center;
  518. align-items: center;
  519. font-size: 18px;
  520. .home_active {
  521. margin-right: 2px;
  522. }
  523. p {
  524. color: #666;
  525. font-size: 18px;
  526. }
  527. }
  528. &:hover {
  529. p {
  530. color: #19436d;
  531. }
  532. transition: width 0.5s ease, transform 0.5s ease;
  533. transform-origin: left;
  534. }
  535. .left {
  536. left: 50%;
  537. transform: scaleX(0);
  538. }
  539. .right {
  540. left: 0;
  541. transform: scaleX(0);
  542. }
  543. }
  544. }
  545. .el-menu {
  546. margin-bottom: 10px;
  547. border: none;
  548. overflow-y: auto;
  549. width: 100% !important;
  550. height: 100%;
  551. }
  552. }
  553. ::v-deep .menu-dt-light.el-menu {
  554. .el-menu-item,
  555. .el-submenu__title {
  556. color: #606266 !important;
  557. border-radius: 4px;
  558. margin: 2px 8px;
  559. height: 44px;
  560. line-height: 44px;
  561. }
  562. .el-menu-item:not(.is-active):not(.dt-menu-leaf-active) .svnIcon,
  563. .el-submenu__title .svnIcon {
  564. color: #606266;
  565. svg {
  566. fill: #606266;
  567. }
  568. }
  569. .el-menu-item.dt-menu-leaf-active,
  570. .el-menu-item.is-active {
  571. background-color: transparent !important;
  572. color: var(--primary-color, #82780c) !important;
  573. span {
  574. color: var(--primary-color, #82780c) !important;
  575. }
  576. .el-icon-menu,
  577. .svnIcon {
  578. color: var(--primary-color, #82780c) !important;
  579. }
  580. .svnIcon svg {
  581. fill: var(--primary-color, #82780c) !important;
  582. }
  583. }
  584. .el-menu-item:hover:not(.is-active):not(.dt-menu-leaf-active),
  585. .el-submenu__title:hover {
  586. background-color: rgba(0, 0, 0, 0.04) !important;
  587. }
  588. .el-menu--inline {
  589. background-color: #eff1f3 !important;
  590. }
  591. .el-submenu .el-menu-item {
  592. padding-left: 44px !important;
  593. }
  594. }
  595. .flexCenter {
  596. display: flex;
  597. align-items: center !important;
  598. justify-content: space-between !important;
  599. width: 200px;
  600. }
  601. .foldBox {
  602. position: fixed;
  603. bottom: 10px;
  604. left: 20px;
  605. padding: 10px;
  606. .icon {
  607. cursor: pointer;
  608. color: #a6a6a6;
  609. font-size: 14px;
  610. }
  611. }
  612. ::v-deep .menu-dt-dark.el-menu {
  613. .el-menu-item,
  614. .el-submenu__title {
  615. color: $dt-text-inactive !important;
  616. border-radius: $dt-radius;
  617. margin: 4px 10px;
  618. margin-left: 10px !important;
  619. height: 44px;
  620. line-height: 44px;
  621. border: none !important;
  622. }
  623. /* 未选中:字灰、图标品牌蓝 */
  624. .el-menu-item:not(.is-active):not(.dt-menu-leaf-active) .el-icon-menu,
  625. .el-submenu__title .el-icon-menu {
  626. color: $dt-item-blue !important;
  627. }
  628. .el-menu-item:not(.is-active):not(.dt-menu-leaf-active) span,
  629. .el-submenu__title span {
  630. color: $dt-text-inactive !important;
  631. }
  632. .el-menu-item:not(.is-active):not(.dt-menu-leaf-active) .svnIcon,
  633. .el-submenu__title .svnIcon {
  634. color: $dt-item-blue;
  635. svg {
  636. fill: $dt-item-blue;
  637. }
  638. }
  639. /* Element is-active + 路由类:蓝底白字(子菜单内需盖过 theme-chalk hover 底色)*/
  640. .el-menu-item.dt-menu-leaf-active,
  641. .el-menu-item.is-active {
  642. background-color: $dt-active-bg !important;
  643. color: #fff !important;
  644. span {
  645. color: #fff !important;
  646. }
  647. .el-icon-menu,
  648. .svnIcon {
  649. color: #fff !important;
  650. }
  651. .svnIcon svg {
  652. fill: #fff !important;
  653. }
  654. }
  655. .el-submenu .el-menu--inline .el-menu-item.dt-menu-leaf-active,
  656. .el-submenu .el-menu--inline .el-menu-item.is-active {
  657. background-color: $dt-active-bg !important;
  658. }
  659. .el-menu-item.dt-menu-leaf-active:hover,
  660. .el-menu-item.dt-menu-leaf-active:focus,
  661. .el-menu-item.is-active:hover,
  662. .el-menu-item.is-active:focus {
  663. background-color: $dt-active-bg !important;
  664. color: #fff !important;
  665. }
  666. .el-menu-item:focus,
  667. .el-menu-item:hover:not(.is-active):not(.dt-menu-leaf-active) {
  668. background-color: rgba(255, 255, 255, 0.06) !important;
  669. }
  670. .el-submenu__title:hover {
  671. background-color: rgba(255, 255, 255, 0.06) !important;
  672. }
  673. /* 子菜单展开区域 */
  674. .el-menu--inline {
  675. background-color: #0d0d0d !important;
  676. }
  677. .el-submenu .el-menu-item {
  678. min-width: auto;
  679. padding-left: 44px !important;
  680. }
  681. }
  682. ::v-deep .el-submenu__title {
  683. display: flex;
  684. align-items: center;
  685. }
  686. ::v-deep .el-submenu__icon-arrow {
  687. right: 16px;
  688. color: $dt-text-inactive;
  689. }
  690. ::v-deep .svnIcon {
  691. vertical-align: middle;
  692. margin-right: 8px;
  693. width: 24px;
  694. text-align: center;
  695. font-size: 18px;
  696. display: inline-flex;
  697. align-items: center;
  698. justify-content: center;
  699. }
  700. ::-webkit-scrollbar {
  701. width: 6px;
  702. display: none;
  703. }
  704. ::-webkit-scrollbar-thumb {
  705. border-radius: 10px;
  706. background-color: #3a3a3a;
  707. cursor: pointer;
  708. }
  709. ::-webkit-scrollbar-thumb:hover {
  710. background-color: #4a4a4a;
  711. }
  712. ::-webkit-scrollbar-track-piece {
  713. background-color: #1a1a1a;
  714. border-radius: 10px;
  715. }
  716. .asideBox:hover ::-webkit-scrollbar {
  717. display: block !important;
  718. }
  719. /* 旧版侧栏左缩进由 ::v-deep 内 margin 统一控制 */
  720. </style>
  721. <!-- 收起侧栏时子菜单弹出层挂到 body,需非 scoped 样式 -->
  722. <style lang="scss">
  723. $dt-bg: #141414;
  724. $dt-item-blue: #1890ff;
  725. $dt-active-bg: #1890ff;
  726. $dt-text-inactive: #999999;
  727. $dt-radius: 8px;
  728. .menu-dt-dark-popup {
  729. .el-menu--popup {
  730. background-color: $dt-bg !important;
  731. border: 1px solid rgba(0, 170, 255, 0.25);
  732. min-width: 160px;
  733. padding: 4px 0;
  734. }
  735. .el-menu-item {
  736. color: $dt-text-inactive !important;
  737. border-radius: $dt-radius;
  738. margin: 4px 8px;
  739. height: 44px;
  740. line-height: 44px;
  741. span {
  742. color: $dt-text-inactive !important;
  743. }
  744. .el-icon-menu,
  745. .svnIcon {
  746. color: $dt-item-blue !important;
  747. }
  748. .svnIcon svg {
  749. fill: $dt-item-blue;
  750. }
  751. }
  752. .el-menu-item.is-active,
  753. .el-menu-item.dt-menu-leaf-active {
  754. background-color: $dt-active-bg !important;
  755. color: #fff !important;
  756. span {
  757. color: #fff !important;
  758. }
  759. .el-icon-menu,
  760. .svnIcon {
  761. color: #fff !important;
  762. }
  763. .svnIcon svg {
  764. fill: #fff !important;
  765. }
  766. }
  767. .el-menu-item.is-active:hover,
  768. .el-menu-item.is-active:focus,
  769. .el-menu-item.dt-menu-leaf-active:hover,
  770. .el-menu-item.dt-menu-leaf-active:focus {
  771. background-color: $dt-active-bg !important;
  772. color: #fff !important;
  773. span {
  774. color: #fff !important;
  775. }
  776. }
  777. .el-menu-item:hover:not(.is-active):not(.dt-menu-leaf-active) {
  778. background-color: rgba(255, 255, 255, 0.06) !important;
  779. }
  780. }
  781. </style>