通过自定义组件学习Vue系列(五)【下拉多选框】(附源码)

文章展示了如何在Vue中创建一个自定义组件——XSelect,用于实现多选下拉功能。组件使用了CSS布局如flex和绝对定位,以及v-model双向绑定和onchange事件来处理用户选择。同时,文章详细解释了子组件如何通过v-model向父组件传递选中值,并提供了完整的组件代码和父组件使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果图

用途:一般用于系统管理中多选角色

涉及关键知识点:

1、css样式:flex布局,postion之absolute,relative,画三角形
2、input checkbox类型的 v-model 双向绑定 onchange事件
3、数组过滤及循环遍历
4、子组件向父组件传值

样式类代码

 
/* 多选显示区 */
.ui-xselect .content{
    min-height: 30px;
    border-radius: 2px;
    border: 1px solid rgba(0, 0, 0, .3);

}

/* 选中区 */
.ui-xselect .content .selected {
    width: calc(100% - 30px);
}

/* 选中区单项 */
.ui-xselect .content .selected .item {
    border-radius: 2px;
    border: 1px solid rgba(0, 0, 0, .3);
}

/* 待选列表 */
.ui-xselect .list {
    background-color: #fff;
    position: absolute;
    border: 1px solid rgba(0, 0, 0, .3);
    left:0;
    top:0;
    right: 0;   
    height: 100px;
    width: 300px;
    z-index: 99;
}

/* 待选列表行高 */
.ui-xselect .list .item {
    line-height: 24px;
}

/* 选中框大小 */
.list input {
    height: 16px;
    width:  16px;
    border: 1px solid rgba(0, 0, 0, .3);
}

组件代码解析
<template>
    <div class = "ui-xselect flex flex-col">
        <div class="content font-s flex flex-center-cz padding-left-m padding-right-m flex-space-between ">
            <div class="selected flex flex-wrap padding-bottom-s ">
                <div v-for="item in selectData" class="item margin-right-m margin-top-s padding-left-s padding-right-s flex flex-center-cz">
                    <div>{{item.text}}</div>
                    <img src="@/assets/images/guanbi.png" class="logo-18 pointer" @click="close(item.value)"/>
                </div>
            </div>
            <div class="menu-down margin-top-s pointer" @click="select"></div>
        </div>
        <div v-if="show" class="position-relative" @click="listClick">
            <div class="list flex flex-col font-s ">
                <div class="flex flex-center-cz item cannotselect" v-for="item in data" >
                    <input @change="inputChange" :value="item.value" type="checkbox" v-model="checks">{{item.text}}</div>
            </div>
        </div>
    </div>
</template>

<script>
     export default {
        name:"XSelect",
        props:{
            data:{},
            values:{}
        },
        watch:{
            values:{
                handler(newValue) {
                    console.log("newValue", newValue);
                    this.checks = newValue;
                    this.selectData = [];
                    newValue.forEach(item => {
                        let find = this.data.filter(b=>b.value == item);
                        if (find.length > 0) {
                            this.selectData.push(find[0]);
                        }
                    });
                },
                immediate: true,
                deep: true   //深度监听
            }
        },
        data() {
            return {
                checks:[],
                selectData:[],
                show: false,
            }
        },
        methods:{
            close(value) {
                this.selectData = this.selectData.filter(item=>item.value != value);//数组过滤得到一个匹配的一个新数组
                this.checks = this.checks.filter(item=>item != value);//数组过滤得到一个匹配的一个新数组
                this.$emit("change", this.checks);//触发父组件 change事件
            },
            inputChange() {
                console.log("change", this.checks)
                this.selectData = [];
                this.checks.forEach(item => {
                    let find = this.data.filter(b=>b.value == item);//数组过滤得到一个匹配的一个新数组
                    if (find.length > 0) {
                        this.selectData.push(find[0]);//往数组中添加一项
                    }
                });  
                this.$emit("change", this.checks);//子组件向父组件传值           
            },
            listClick(event) {
                event.stopPropagation();//阻止冒泡,防止触发下层点击事件
            },
            select(event) {
                event.stopPropagation();//阻止冒泡,防止触发下层点击事件
                document.addEventListener("click", this.closeList, false);// 添加监听点击事件
                this.show = !this.show;
            },
            closeList() {
                document.removeEventListener("click", this.closeList, false);//关闭监听点击事件
                this.show = false;
            }

        }
     }
</script>

父组件代码
<template>
    <div class="body">
      <div class="table">
        <div class="filter font-bold">组件库(五) ComboBox下拉多选组件</div>
        <div class="margin-top-l margin-left-l flex flex-col ">
          <x-select class="x-select-demo" :data="selectData" :values="values" @change="valueChange"></x-select>
          <div class="margin-top-l">选中值为{{lastVal}}</div>
        </div>
      </div>
    </div>
</template>

<script>
/*
       名称:组件库(五) ComboBox下拉多选组件
       作者:唐赢   
       时间:2023-4-6
*/

  import XSelect from '@/components/xSelect/XSelect'
  export default {
    name: 'XSelectDemo',
    components: {
        XSelect
    },
    data () {
      return {
        selectData:[
            {"value":"1", "text": "管理员"},
            {"value":"2", "text": "销售顾问"},
            {"value":"3", "text": "服务顾问"},
        ],
        values:["1","2"],
        lastVal:["1","2"]
      }
    },
    methods: {
        valueChange(values) {
            this.lastVal = values;
        }
    }
  }
</script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是唐赢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值