MenuItem.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <template>
  2. <div class="menu-item-wrapper" :class="[`theme-${theme}`]">
  3. <div
  4. class="menu-row"
  5. :class="[
  6. {
  7. 'is-leaf': !hasChildren,
  8. 'is-folder': hasChildren,
  9. 'is-root': level === 0,
  10. 'is-sub': level > 0
  11. },
  12. 'level-' + level
  13. ]"
  14. :style="{ paddingLeft: level * 20 + 15 + 'px' }"
  15. @click="handleClick"
  16. >
  17. <i v-if="node.icon" :class="node.icon" class="node-icon"></i>
  18. <span class="node-label">{{ node.label }}</span>
  19. <span
  20. v-if="hasChildren"
  21. class="arrow-icon"
  22. :class="{ 'is-open': isOpen }"
  23. >
  24. <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>
  25. </span>
  26. </div>
  27. <div class="menu-children" v-show="isOpen" v-if="hasChildren">
  28. <MenuItem
  29. v-for="child in node.children"
  30. :key="child.id"
  31. :node="child"
  32. :level="level + 1"
  33. :theme="theme"
  34. @node-click="passEventUp"
  35. />
  36. </div>
  37. </div>
  38. </template>
  39. <script>
  40. export default {
  41. name: 'MenuItem',
  42. props: {
  43. node: {
  44. type: Object,
  45. required: true
  46. },
  47. level: {
  48. type: Number,
  49. default: 0
  50. },
  51. // 可选值:'tech' (科技渐变) | 'dark' (极简暗色)
  52. theme: {
  53. type: String,
  54. default: 'tech'
  55. }
  56. },
  57. data() {
  58. return {
  59. isOpen: true
  60. };
  61. },
  62. computed: {
  63. hasChildren() {
  64. return this.node.children && this.node.children.length > 0;
  65. }
  66. },
  67. methods: {
  68. handleClick() {
  69. if (this.hasChildren) {
  70. this.isOpen = !this.isOpen;
  71. } else {
  72. this.$emit('node-click', this.node);
  73. }
  74. },
  75. passEventUp(nodeData) {
  76. this.$emit('node-click', nodeData);
  77. }
  78. }
  79. };
  80. </script>
  81. <style scoped>
  82. .menu-item-wrapper {
  83. color: #fff;
  84. font-size: 14px;
  85. }
  86. /* ================== 公共基础样式 ================== */
  87. .menu-row {
  88. display: flex;
  89. align-items: center;
  90. min-height: 44px;
  91. max-height: 56px;
  92. padding: 12px 20px;
  93. cursor: pointer;
  94. border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  95. background: #081734;
  96. transition: all 0.3s ease;
  97. user-select: none;
  98. }
  99. .node-icon { margin-right: 8px; font-size: 16px; }
  100. .node-label { flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  101. .arrow-icon { margin-left: 10px; display: flex; justify-content: center; align-items: center; color: #909399; transform: rotate(180deg); transition: transform 0.3s ease; }
  102. .arrow-icon.is-open { transform: rotate(0deg); }
  103. /* ================== 风格 A: 科技渐变风 (theme-tech) ================== */
  104. /* 1. 最外层父节点 (level: 0) */
  105. .theme-tech.menu-item-wrapper {
  106. background: linear-gradient( 180deg, rgba(5,20,46,0) 0%, #05142E 100%);
  107. }
  108. .theme-tech .menu-row.level-0 {
  109. font-size: 20px;
  110. line-height: 28px;
  111. background: linear-gradient( 180deg, rgba(119,161,255,0) 0%, #77A1FF 100%);
  112. border-radius: 2px 0px 0px 2px;
  113. border: 1px solid rgba(161,190,255,0.7);
  114. font-weight: bold;
  115. }
  116. /* 2. 第二层子节点 (level: 1) */
  117. .theme-tech .menu-row.level-1 {
  118. background: #0f224350;
  119. font-size: 18px;
  120. line-height: 25px;
  121. color: #ffffff;
  122. }
  123. /* 3. 包含子节点的目录加粗 */
  124. .theme-tech .menu-row.is-folder {
  125. font-weight: bold;
  126. font-size: 18px;
  127. line-height: 25px;
  128. color: #ffffff;
  129. }
  130. .theme-tech .menu-row.level-2 {
  131. font-size: 18px;
  132. line-height: 25px;
  133. color: rgba(255,255,255, 0.8);
  134. }
  135. /* 4. 叶子节点字体颜色 */
  136. .theme-tech .menu-row.is-leaf {
  137. font-size: 18px;
  138. line-height: 25px;
  139. font-weight: normal;
  140. color: #ffffff;
  141. background: rgba(8,23,51,0.9);
  142. color: rgba(255,255,255, 0.5);
  143. }
  144. /* 5. 悬浮交互 */
  145. .theme-tech .menu-row:hover {
  146. background: #3760A9;
  147. color: #ffffff;
  148. }
  149. .theme-tech .menu-row.is-leaf:hover { color: #ffffff; }
  150. /* ================== 风格 B: 极简暗色风 (theme-dark) ================== */
  151. /* 1. 主控中心标题 (顶级菜单 level: 0) */
  152. .theme-dark .menu-row.is-root {
  153. background-color: #112445;
  154. color: #ffffff;
  155. font-weight: bold;
  156. font-size: 20px;
  157. line-height: 28px;
  158. }
  159. /* 2. 其他子菜单 (level > 0) */
  160. .theme-dark .menu-row.is-sub {
  161. background-color: #0b1a37;
  162. color: #ffffff ;
  163. font-size: 18px;
  164. line-height: 25px;
  165. }
  166. /* 3. 叶子节点颜色 */
  167. .theme-dark .menu-row.is-leaf {
  168. color: #6b7280;
  169. font-weight: normal;
  170. }
  171. /* 4. 悬浮交互 */
  172. .theme-dark .menu-row:hover {
  173. background-color: rgba(0, 229, 255, 0.1);
  174. color: #00e5ff;
  175. }
  176. .theme-dark .menu-row.is-leaf:hover { color: #ffffff; }
  177. </style>