263 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <section class="sliderNav">
 | |
|     <el-input class="searchApp" size="small" v-model="searchApp" placeholder="搜索应用" clearable
 | |
|               prefix-icon="iconfont iconSearch" @change="recordSearch"/>
 | |
|     <el-scrollbar class="ai-menu">
 | |
|       <div v-for="(item,i) in navs" :key="i">
 | |
|         <div class="rootMenu" :class="{isActive:menuPath.includes(item.name)}"
 | |
|              @click.stop="openKidMenu(item)">
 | |
|           <i class="prep-icon" :class="item.style||'iconfont iconloudongmoxing'"/>
 | |
|           <span class="menuName fill" v-text="item.label"/>
 | |
|           <el-badge type="warning" :hidden="!item.project" :value="item.project"/>
 | |
|           <i v-if="item.children" class="iconfont" :class="arrowIcon(item.showChildren)"/>
 | |
|         </div>
 | |
|         <div class="kidMenu" v-if="item.showChildren" @click.stop>
 | |
|           <div class="kidPane">
 | |
|             <div class="submenu wrap" flex v-for="menu in item.children" :key="menu.name">
 | |
|               <b v-text="menu.label" :class="{menuBtn:menu.type==1,current:menuPath.includes(menu.name)}"
 | |
|                  @click="handleSelect(menu)"/>
 | |
|               <div class="menuBtn" v-for="kid in menu.children" :key="kid.name" v-text="kid.label"
 | |
|                    @click="handleSelect(kid)"
 | |
|                    :class="{current:menuPath.includes(kid.name)}"/>
 | |
|             </div>
 | |
|           </div>
 | |
|         </div>
 | |
|       </div>
 | |
|       <div class="divider"/>
 | |
|     </el-scrollbar>
 | |
|   </section>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
| import {mapState} from "vuex";
 | |
| 
 | |
| export default {
 | |
|   name: "sliderNav",
 | |
|   data() {
 | |
|     return {
 | |
|       searchApp: "",
 | |
|     }
 | |
|   },
 | |
|   computed: {
 | |
|     ...mapState(['user', 'apps']),
 | |
|     navs() {
 | |
|       let reg = new RegExp(`.*${this.searchApp?.replace(/-/g,'')||''}.*`, 'gi')
 | |
|       return (this.apps || []).filter(e => !this.searchApp || reg?.test(e.name) || reg?.test(e.label)).map(e => {
 | |
|         if (/\/project\//.test(e.path)) {
 | |
|           e.project = e.path.replace(/.*project\/([^\/]+)\/.+/, '$1')
 | |
|         } else if (/\/core\//.test(e.path)) {
 | |
|           e.project = "core"
 | |
|         }
 | |
|         return e
 | |
|       })
 | |
|     },
 | |
|     isConsoleRoute() {
 | |
|       return this.$route.name == "工作台"
 | |
|     },
 | |
|     menuPath() {
 | |
|       let paths = [], current = this.apps?.find(e => e.name == this.$route.name)
 | |
|       const findParent = name => {
 | |
|         let menu = this.apps?.find(e => e.name == name)
 | |
|         if (menu) {
 | |
|           paths.push(menu.name)
 | |
|           if (!!menu.parentId) findParent(menu.parentId)
 | |
|         }
 | |
|       }
 | |
|       if (current) {
 | |
|         findParent(current.name)
 | |
|       }
 | |
|       return paths
 | |
|     }
 | |
|   },
 | |
|   methods: {
 | |
|     openKidMenu(parent) {
 | |
|       if (parent.children) {
 | |
|         parent.showChildren = !parent.showChildren
 | |
|       } else {
 | |
|         this.handleSelect(parent)
 | |
|       }
 | |
|     },
 | |
|     handleSelect(item) {
 | |
|       if (!item.path) return
 | |
|       if (item.name == this.$route.name) {
 | |
|         //避免同一路由跳转的BUG vue-router官方BUG
 | |
|       } else {
 | |
|         let {name, path} = item
 | |
|         if (/\?app=/.test(path)) {
 | |
|           this.goto({name, query: {app: path.replace(/.+\?app=/, '')}})
 | |
|         } else if (/\?moduleId=/.test(path)) {
 | |
|           this.goto({name, query: {moduleId: path.replace(/.+\?moduleId=/, '')}})
 | |
|         } else {
 | |
|           this.goto({name})
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|     goto(item) {
 | |
|       this.$router.push(item)
 | |
|     },
 | |
|     subMenuIcon(flag) {
 | |
|       return flag ? 'iconfont iconArrow_Down' : "iconfont iconArrow_Right"
 | |
|     },
 | |
|     arrowIcon(v) {
 | |
|       return v ? "iconArrow_Down" : "iconArrow_Right"
 | |
|     },
 | |
|     recordSearch() {
 | |
|       localStorage.setItem("searchApp", this.searchApp)
 | |
|     },
 | |
|   },
 | |
|   created() {
 | |
|     this.searchApp = localStorage.getItem("searchApp") || ""
 | |
|   }
 | |
| }
 | |
| </script>
 | |
| <style lang="scss" scoped>
 | |
| .sliderNav {
 | |
|   min-width: 200px;
 | |
|   height: 100%;
 | |
|   transition: width .1s;
 | |
|   display: flex;
 | |
|   justify-content: space-between;
 | |
|   flex-direction: column;
 | |
|   border-right: 1px solid #e5e5e5;
 | |
|   flex-shrink: 0;
 | |
|   box-sizing: border-box;
 | |
|   background: #EFF1F4;
 | |
|   color: #222;
 | |
|   position: relative;
 | |
| 
 | |
|   .kidMenu {
 | |
|     padding: 0 16px;
 | |
|     background: #EFF1F4;
 | |
| 
 | |
|     .rootName {
 | |
|       font-size: 20px;
 | |
|       color: #333;
 | |
|       cursor: default;
 | |
|     }
 | |
| 
 | |
|     .kidPane {
 | |
|       font-size: 13px;
 | |
| 
 | |
|       .submenu {
 | |
|         margin-top: 8px;
 | |
|         width: 100%;
 | |
|         color: #aaa;
 | |
| 
 | |
|         & > b {
 | |
|           width: 100%;
 | |
|           line-height: 28px;
 | |
|         }
 | |
| 
 | |
|         & > * {
 | |
|           cursor: default;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       .menuBtn {
 | |
|         display: block;
 | |
|         width: 50%;
 | |
|         cursor: pointer;
 | |
|         line-height: 32px;
 | |
|         color: #333;
 | |
|         flex-shrink: 0;
 | |
| 
 | |
|         &:hover {
 | |
|           color: #26f;
 | |
|         }
 | |
| 
 | |
|         &.current {
 | |
|           color: #26f;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .rootMenu {
 | |
|     padding: 0 16px;
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     height: 44px;
 | |
|     cursor: pointer;
 | |
|     box-shadow: 0px -1px 0px 0px #D8DCE3 inset, 0px 1px 0px 0px #FFF inset, -1px 0px 0px 0px #E5E5E5 inset;
 | |
|     gap: 8px;
 | |
|     font-size: 13px;
 | |
| 
 | |
|     .iconfont {
 | |
|       color: #89B;
 | |
|       font-size: 20px;
 | |
|     }
 | |
| 
 | |
|     &.isActive {
 | |
|       color: #26f;
 | |
| 
 | |
|       .iconfont {
 | |
|         color: #26f !important;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     &:hover {
 | |
|       color: #26f;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ::v-deep .ai-menu {
 | |
|     padding-left: 0;
 | |
|     flex: 1;
 | |
|     min-height: 0;
 | |
| 
 | |
|     .el-scrollbar__wrap {
 | |
|       overflow-x: auto;
 | |
|     }
 | |
| 
 | |
|     &::-webkit-scrollbar {
 | |
|       display: none;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ::v-deep .searchApp {
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     height: 44px;
 | |
|     padding: 0 16px;
 | |
|     box-shadow: 0px -1px 0px 0px #E5E5E5 inset;
 | |
| 
 | |
|     .el-input__inner {
 | |
|       border: none;
 | |
|       background: inherit;
 | |
|       padding: 0 28px;
 | |
|     }
 | |
| 
 | |
|     .el-input__prefix {
 | |
|       left: 16px;
 | |
| 
 | |
|       .iconSearch {
 | |
|         font-size: 20px;
 | |
|         width: fit-content;
 | |
|         color: #89B;
 | |
|         line-height: 44px;
 | |
|       }
 | |
| 
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   .divider {
 | |
|     color: #aaa;
 | |
|     border-top: 1px solid #ddd;
 | |
|     position: relative;
 | |
|     font-size: 12px;
 | |
|     margin: 16px 16px 32px;
 | |
| 
 | |
|     &:before {
 | |
|       content: "到达底部";
 | |
|       position: absolute;
 | |
|       top: 50%;
 | |
|       left: 50%;
 | |
|       transform: translate(-50%, -50%);
 | |
|       padding: 0 16px;
 | |
|       background: #EFF1F4;
 | |
|       white-space: nowrap;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| </style>
 |