Vue 自定义菜单条及下拉框

目录

一、背景

二、源码

三、知识点

四、效果


一、背景

项目需要,自定义菜单栏,有二级下拉菜单。点击后可跳到指定的路由页面。

二、源码

<template>
    <div class="menubar-page">
        <!-- 菜单栏 -->
        <div class="menu-bar">
            <span v-for="item of menus" :key="item.type" 
                class="menu-item" 
                :class="(activeMenu === item.type | subParentType === item.type) ? 'menu-item-active' : ''"
                @click="menuClick(item)"
                @mouseover="menuMouse($event, item, true)"
                @mouseout="menuMouse($event, item, false)">
                {{item.name}}
            </span>
        </div>
        <!-- 子菜单列表 -->
        <div :id="item.type+'Submenu'" class="sub-menu" v-for="item of submenus" :key="item.type" 
                v-show="showSubMenu && hoverMenu === item.type"
                @mouseover="submenuMouse(item, true)"
                @mouseout="submenuMouse(item, false)">
            <div v-for="subitem of item.children" :key="subitem.type" 
                class="sub-menu-item"
                @click="submenuClick(subitem, item)">
                {{subitem.name}}
            </div>
        </div>
    </div>
</template>
<script>
export default {
    data() {
        return {
            // 菜单配置
            menus: [
                {
                    name: '首页',
                    type: 'first'
                },
                {
                    name: '科普知识',
                    type: 'science'
                },
                {
                    name: '旅游资源',
                    type: 'source',
                    children: [{
                        name: '海岛介绍',
                        type: 'island'
                    },{
                        name: '沙漠路线',
                        type: 'desert'
                    }]
                },
                {
                    name: '数据管理',
                    type: 'data',
                    children: [{
                        name: '热点访问',
                        type: 'visit'
                    },{
                        name: '热点路线',
                        type: 'hot'
                    }]
                },
                {
                    name: '分析评价',
                    type: 'evaluate',
                    children: [{
                        name: '数字货币',
                        type: 'map'
                    },{
                        name: '场景布局',
                        type: 'scene'
                    }]
                }
            ],
            // 子菜单列表
            submenus: [],
            // 激活菜单
            activeMenu: 'first',
            // hover 菜单
            hoverMenu: 'first',
            // 显示子菜单
            showSubMenu: false,
            // 子菜单的父菜单index
            subParentType: ''
        }
    },
    methods: {
        // 父菜单 over 和 out
        menuMouse(e, item, show) {
            // 子菜单显示
            this.showSubMenu = show;
            // hover 显示
            this.hoverMenu = item.type;
            // 子菜单显示 left
            if(item.children) {
                let offsetLeft = e.target.offsetLeft;
                document.getElementById(item.type+'Submenu').style.left = offsetLeft + 'px';
            }
        },
        // 父菜单 click
        menuClick(item) {    
            // 当前 menu
            this.activeMenu = item.type; 
            // 路由跳转
            if(!item.children) {                
                this.$router.push({ path: item.type === 'first' ? '/' : `/${item.type}` });
            }
        },
        // 子菜单 over 和 out
        submenuMouse(subitem, show) {
            this.showSubMenu = show;
            this.hoverMenu = subitem.type;
            this.subParentType = show ? subitem.parentType : '';
            
        },
        // 子菜单 click
        submenuClick(subitem, item) {
            // 父 active 设置
            this.activeMenu = item.type;
            // 路由跳转
            this.$router.push({ path: `/${subitem.type}` });
        }
    },
    created() {// DOM渲染之前
        // 有下拉框的菜单
        for(let i = 0, len = this.menus.length; i< len; i++) {
            let item = this.menus[i];
            if(item.children) {
                // 用于鼠标over子菜单时,父菜单active效果
                item.parentType = item.type;
                this.submenus.push(item);
            }
        }
        // 获取路径
        let split = window.location.href.split('/#/');
        let pageType = split[1] ? split[1] : '';
        // active 菜单设置
        for(let i = 0, len = this.menus.length; i < len; i++) {
            let menu = this.menus[i];
            if(menu.children) {
                for(let j = 0, l = menu.children.length; j < l; j++) {
                    let submenu = menu.children[j];
                    if(pageType === submenu.type) {
                        this.activeMenu = menu.type;
                        break;
                    }
                }
            }
            else if(pageType === menu.type) {
                this.activeMenu = pageType;
                break;
            }
        }
    }
}
</script>
<style lang="stylus" scoped>
.menubar-page {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: #ccc;
}
.menu-bar{
    text-align center
    background  #003466
    height: 64px;
    .menu-item {
        display: inline-block;
        width 240px
        height: 64px;
        line-height: 64px;
        cursor: pointer;
        color: white;
        user-select:none;
        &:hover {
            background: #0147b2
        }
    }
    .menu-item-active {
        background: #0147b2
    }
}
// 拉框菜单
.sub-menu {
    position absolute
    display inline-block
    width 240px
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1)
    cursor: pointer;
    user-select:none;
    text-align center
    z-index 1000
    background white
}
.sub-menu-item {
    height 50px
    line-height 50px
    &:hover {
        color $subColor
        background rgb(231,242,255)
    }
}
</style>

三、知识点

生命周期 created:

1、联动子菜单和父菜单的 mouse over 监听效果。

2、配置中,type 与 页面路由名一致。可根据当前href的路由,初始化active效果

四、效果

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现一个自定义多选可搜索下拉框,可以使用Vue框架结合element-ui组件库来实现。下面是一个简单的实现方式: 首先,安装element-ui组件库: ``` npm install element-ui ``` 然后,在需要使用的组件中引入element-ui: ```javascript import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) ``` 接下来,创建一个多选可搜索下拉框组件,代码如下: ```html <template> <el-select v-model="selectedItems" multiple filterable remote :remote-method="remoteMethod" :loading="loading" :disabled="disabled" :placeholder="placeholder" clearable @change="handleChange"> <el-option v-for="(item, index) in options" :key="index" :label="item.label" :value="item.value"> </el-option> </el-select> </template> <script> export default { data() { return { selectedItems: [], options: [], loading: false, disabled: false, placeholder: '请选择', remoteUrl: '', remoteParams: {}, } }, methods: { remoteMethod(query) { if (query !== '') { this.loading = true // 发送请求获取数据 axios.get(this.remoteUrl, { params: { query, ...this.remoteParams } }).then(res => { this.options = res.data this.loading = false }).catch(() => { this.loading = false }) } else { this.options = [] } }, handleChange(val) { this.$emit('change', val) } }, props: { value: { type: Array, default: () => [] } }, watch: { value: { handler(newVal) { this.selectedItems = newVal }, immediate: true } } } </script> ``` 这个组件使用了element-ui中的`el-select`和`el-option`组件,通过设置`multiple`属性来实现多选。设置`filterable`属性来让下拉框可搜索,通过设置`remote`属性来实现远程搜索。 在组件中,通过`remoteMethod`方法来发送请求获取需要显示的选项,将获取到的选项保存在`options`数据中,同时设置`loading`状态来显示加载中状态。`handleChange`方法用于监听选项的变化,通过`$emit`方法触发`change`事件来向父组件传递选中的值。组件中还定义了一些props和data,用于接收外部传入的数据。 最后,在使用该组件的父组件中,可以通过以下方式来使用: ```html <template> <div> <custom-select v-model="selectedItems" :remote-url="remoteUrl"></custom-select> </div> </template> <script> import CustomSelect from './CustomSelect.vue' export default { components: { CustomSelect }, data() { return { selectedItems: [], remoteUrl: 'http://example.com/api/search' } }, methods: { handleChange(val) { console.log(val) } } } </script> ``` 在父组件中,可以通过v-model指令来绑定选中的值,通过设置`remote-url`属性来指定远程搜索的API地址。同时,可以监听`change`事件来获取选中的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值