side-nav.vue 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. <style>
  2. .side-nav {
  3. width: 100%;
  4. box-sizing: border-box;
  5. padding-right: 30px;
  6. transition: opacity .5s;
  7. &:hover {
  8. opacity: 1 !important;
  9. }
  10. li {
  11. list-style: none;
  12. }
  13. ul {
  14. padding: 0;
  15. margin: 0;
  16. overflow: hidden;
  17. }
  18. > ul > .nav-item > a {
  19. margin-top: 15px;
  20. }
  21. .nav-item {
  22. a {
  23. font-size: 16px;
  24. color: #333;
  25. line-height: 40px;
  26. height: 40px;
  27. margin: 0;
  28. padding: 0;
  29. text-decoration: none;
  30. display: block;
  31. position: relative;
  32. transition: .15s ease-out;
  33. font-weight: bold;
  34. &.active {
  35. color: #409EFF;
  36. }
  37. }
  38. .nav-item {
  39. a {
  40. display: block;
  41. height: 40px;
  42. color: #666;
  43. line-height: 40px;
  44. font-size: 14px;
  45. overflow: hidden;
  46. white-space: nowrap;
  47. text-overflow: ellipsis;
  48. font-weight: normal;
  49. &:hover,
  50. &.active {
  51. color: #409EFF;
  52. }
  53. }
  54. }
  55. }
  56. .nav-group__title {
  57. font-size: 12px;
  58. color: #999;
  59. line-height: 26px;
  60. margin-top: 15px;
  61. }
  62. #code-sponsor-widget {
  63. margin: 0 0 0 -20px;
  64. }
  65. }
  66. .nav-dropdown-list {
  67. width: 120px;
  68. margin-top: -8px;
  69. li {
  70. font-size: 14px;
  71. }
  72. }
  73. </style>
  74. <template>
  75. <div
  76. class="side-nav"
  77. @mouseenter="isFade = false"
  78. :style="navStyle">
  79. <ul>
  80. <li class="nav-item" v-for="item in data">
  81. <a v-if="!item.path && !item.href" @click="expandMenu">{{item.name}}</a>
  82. <a v-if="item.href" :href="item.href" target="_blank">{{item.name}}</a>
  83. <router-link
  84. v-if="item.path"
  85. active-class="active"
  86. :to="base + item.path"
  87. exact
  88. v-text="item.title || item.name">
  89. </router-link>
  90. <ul class="pure-menu-list sub-nav" v-if="item.children">
  91. <li class="nav-item" v-for="navItem in item.children">
  92. <router-link
  93. class=""
  94. active-class="active"
  95. :to="base + navItem.path"
  96. exact
  97. v-text="navItem.title || navItem.name">
  98. </router-link>
  99. </li>
  100. </ul>
  101. <template v-if="item.groups">
  102. <div class="nav-group" v-for="group in item.groups">
  103. <div class="nav-group__title" @click="expandMenu">{{group.groupName}}</div>
  104. <ul class="pure-menu-list">
  105. <li
  106. class="nav-item"
  107. v-for="navItem in group.list"
  108. v-if="!navItem.disabled">
  109. <router-link
  110. active-class="active"
  111. :to="base + navItem.path"
  112. exact
  113. v-text="navItem.title"></router-link>
  114. </li>
  115. </ul>
  116. </div>
  117. </template>
  118. </li>
  119. </ul>
  120. <div id="code-sponsor-widget"></div>
  121. </div>
  122. </template>
  123. <script>
  124. import bus from '../bus';
  125. import compoLang from '../i18n/component.json';
  126. export default {
  127. props: {
  128. data: Array,
  129. base: {
  130. type: String,
  131. default: ''
  132. }
  133. },
  134. data() {
  135. return {
  136. highlights: [],
  137. navState: [],
  138. isSmallScreen: false,
  139. isFade: false
  140. };
  141. },
  142. watch: {
  143. '$route.path'() {
  144. this.handlePathChange();
  145. },
  146. isFade(val) {
  147. bus.$emit('navFade', val);
  148. }
  149. },
  150. computed: {
  151. navStyle() {
  152. const style = {};
  153. if (this.isSmallScreen) {
  154. style.paddingBottom = '60px';
  155. }
  156. if (this.isFade) {
  157. style.opacity = '0.5';
  158. }
  159. return style;
  160. },
  161. langConfig() {
  162. return compoLang.filter(config => config.lang === this.$route.meta.lang)[0]['nav'];
  163. }
  164. },
  165. methods: {
  166. handleResize() {
  167. this.isSmallScreen = document.documentElement.clientWidth < 768;
  168. this.handlePathChange();
  169. },
  170. handlePathChange() {
  171. if (!this.isSmallScreen) {
  172. this.expandAllMenu();
  173. return;
  174. }
  175. this.$nextTick(() => {
  176. this.hideAllMenu();
  177. let activeAnchor = this.$el.querySelector('a.active');
  178. let ul = activeAnchor.parentNode;
  179. while (ul.tagName !== 'UL') {
  180. ul = ul.parentNode;
  181. }
  182. ul.style.height = 'auto';
  183. });
  184. },
  185. hideAllMenu() {
  186. [].forEach.call(this.$el.querySelectorAll('.pure-menu-list'), ul => {
  187. ul.style.height = '0';
  188. });
  189. },
  190. expandAllMenu() {
  191. [].forEach.call(this.$el.querySelectorAll('.pure-menu-list'), ul => {
  192. ul.style.height = 'auto';
  193. });
  194. },
  195. expandMenu(event) {
  196. if (!this.isSmallScreen) return;
  197. let target = event.currentTarget;
  198. if (!target.nextElementSibling || target.nextElementSibling.tagName !== 'UL') return;
  199. this.hideAllMenu();
  200. event.currentTarget.nextElementSibling.style.height = 'auto';
  201. }
  202. },
  203. created() {
  204. bus.$on('fadeNav', () => {
  205. this.isFade = true;
  206. });
  207. },
  208. mounted() {
  209. this.handleResize();
  210. window.addEventListener('resize', this.handleResize);
  211. },
  212. beforeDestroy() {
  213. window.removeEventListener('resize', this.handleResize);
  214. }
  215. };
  216. </script>