基于Vue.js封装一个简单的select组件

2,400 阅读2分钟

事情是这样的 我们公司开发单页面应用时引用网上的组件库发现就是有时候不灵活("公司领导觉得那样样式的改成这样那样的 然后在全局改select组件的样式 发现特恶心的是其它组件的样式也改了 很火大 于是 没办法就自己写一个 ")

大概的样子

功能

template

    <template>
    <div class="select_container_nw" :style="{width:strtransition(sl_width)+'px'}">
        <div class="select_main" @click="sllistshow" :class="{select_showlist:slshow}">
            <input class="searchinput" v-model="search" @change="alldata" :placeholder="placeholder" onfocus="this.select()" v-if="filterable">
            <template v-if="!filterable">
                <span class="select_content" v-show="showPlaceholder" :style="{width:(strtransition(sl_width)-40)+'px'}">{{placeholder}}</span>
                <span class="select_content" v-show="!showPlaceholder" :style="{width:(strtransition(sl_width)-40)+'px'}">{{label}}</span>
            </template>
            <span class="select_arrow" :class="{cast_rotate:!slshow}"></span>
            <span class="select_arrow_after " :class="{cast_rotate:!slshow}" style="margin-top: 1px;"></span>
        </div>
        <transition name="el-zoom-in-top">
            <div class="select_list" v-show="slshow" :style="{'min-width':strtransition(sl_width)+'px'}">
                <div class="select_list_body" style="position: relative; overflow: scroll;margin-right: -17px;height: 100%;max-height:150px;    margin-bottom: -17px;">
                    <div :style="{position:'relative',top:'0px',left:strtransition(sl_left)+'px'}">
                        <ul class="select_list_ul" v-if="searchData.length > 0">
                            <li v-for="(item,index) in searchData" :key="index" @click.stop="slchang(item,index)" :class="{current:focusIndex==index}">{{item[defaultExpandedKeys.label]}}</li>
                        </ul>
                        <ul class="select_Nolist_ul" v-else>
                            <li>无数据</li>
                        </ul>
                    </div>
                </div>
            </div>
        </transition>
    </div>
</template>

script

<script>
export default {
    name: "selectlist",
    props: {
        sl_width: {
            //默认select长度
            type:  [String, Number, Array],
            default: "75"
        },
        sl_left: {
            //默认select长度
            type:  [String, Number, Array],
            default: "0"
        },
        opintdata: {
            //默认select数据
            type: Array,
            default() {
                return [];
            }
        },
        value: {
            //v-model值
            type: [String, Number, Array],
            default: ""
        },
        placeholder: {
            //初始显示的内容
            type: String,
            default: "请选择"
        },
        filterable: {
            //是否开启搜索功能
            type: Boolean,
            default: false
        },
        disabled: {//是否禁用当前组件
            type: Boolean,
            default: false
        },
        newlabel:{
            type: String,
            default: "请选择"
        },
        defaultExpandedKeys: {//初始绑定的键值
            type: [Array, Object],
            default() {
                return {
                    label: 'label',
                    value: 'value',
                };
            }
        }
    },
    data() {
        return {
            slshow: false,
            focusIndex: -1,
            model: this.value,
            showPlaceholder: true,
            label: "",
            search: "",
            isshowalldata: true,
        };
    },
    created() {
        this.opintchang();
    },
    mounted() {
        document.addEventListener('click', (e) => {
            if (!this.$el.contains(e.target)) this.slshow = false;
        })
    },
    watch: {
        value(val) {
            this.model = val;
            this.opintchang();
        },
        opintdata(val) {
            this.label = this.placeholder;
            this.opintchang();
        },
        search(val) {
            this.isshowalldata = false;
        },
        newlabel(val){
            this.label=val;
        }
    },
    computed: {
        searchData: function () {
            var search = this.search;
            if (this.isshowalldata && this.filterable) {
                return this.opintdata
            }
            else if (search && this.filterable) {
                return this.opintdata.filter(function (product) {
                    return Object.keys(product).some(function (key) {
                        return (
                            String(product[key])
                                .toLowerCase()
                                .indexOf(search) > -1
                        );
                    });
                });
            }
            return this.opintdata;
        }
    },
    methods: {
        alldata() {

        },
        strtransition(str) {
            //处理处进来的长度
            if (parseInt(str) != NaN) {
                return parseInt(str);
            } else {
                return 115;
            }
        },
        sllistshow(e) {
            //控制list显示不显示
            this.isshowalldata = true;
            this.slshow = !this.slshow;
        },
        slchang(item, index, isnotchange = true) {
            //list点击事件
            this.model = item[this.defaultExpandedKeys.label];
            this.label = item[this.defaultExpandedKeys.label];
            this.slshow = false;
            this.focusIndex = index;
            this.showPlaceholder = false;
            this.$emit("input", item[this.defaultExpandedKeys.value]); //触发 input 事件,并传入新值
            if (isnotchange) {
                this.search = item[this.defaultExpandedKeys.label];
            }
            this.$emit("change", item, index);
        },
        handleClose() {
            //关闭list列表
            this.slshow = false;
        },
        opintchang() {
            if (typeof this.opintdata == "object") {
                let pi = this.opintdata.filter(item => {
                    if (this.value == item[this.defaultExpandedKeys.value]) {
                        return item;
                    }
                })
                let index = 0;
                let item = {};
                if (pi.length > 0) {
                    index = this.opintdata.findIndex(item => {
                        if (this.value == item[this.defaultExpandedKeys.value]) {
                            return item;
                        }
                    })
                    item = pi[0];
                } else {
                    item = this.opintdata.length > 0 ? this.opintdata[0] : {};
                }
                if (this.opintdata.length > 0) {
                    this.slchang(item, index, false);
                    this.showPlaceholder = false;
                }
            }
        }
    }
}
</script>

scss

<style lang="scss" type="text/css" scoped>
$slbordercolor: #dfe4ed;
$fontcolor: #5a5e66;

.disabled {
  pointer-events: none;
  cursor: default;
  filter: alpha(opacity=50); /*IE滤镜,透明度50%*/
  -moz-opacity: 0.5; /*Firefox私有,透明度50%*/
  opacity: 0.5; /*其他,透明度50%*/
}

.select_container_nw {
  /* margin-right: 10px; */
  font-size: 14px;
  position: relative;
  font-size: 13px;
  font-family: "Helvetica Neue", arial, sans-serif;
  font-weight: 300;
  letter-spacing: 1px;
  display: inline-block;
  background-color: white;
}

.select_main {
  position: relative;
  border-radius: 15px;
  border: 1px solid #adadad;
  line-height: 20px;
  cursor: pointer;
}

.select_main:hover {
  border-color: #409eff;
}

.select_circle {
  position: absolute;
  width: 16px;
  border-radius: 50%;
  height: 16px;
  right: 6px;
  top: 50%;
  transform: translate3d(0, -50%, 0);
  background-color: #ecf6fe;
}

.select_showlist {
  border-color: #409eff;
}

.select_content {
  color: #49a8f6;
  display: block;
  padding: 0px 10px 0px 7px;
  overflow: hidden;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
  white-space: nowrap;
}

.select_content::after {
  content: " ";
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 40px;
  border-radius: 0 2px 2px 0;
}

.select_input {
  padding-left: 10px;
  padding-right: 32px;
  font-size: 13px;
  overflow: hidden;
  outline: none;
  height: 25px;
  border: none;
  border-bottom: 1px solid $slbordercolor;
  z-index: 1;
}

.search_svg {
  position: absolute;
  top: 2px;
  right: 3px;
  z-index: 2;
}

.select_arrow,
.select_arrow_after {
  content: " ";
  position: absolute;
  right: 9px;
  top: 42%;
  border: 4px solid transparent;
  border-top: 4px solid #2988fc;
  z-index: 1;
  -webkit-transform-origin: 50% 20%;
  -moz-transform-origin: 50% 20%;
  -ms-transform-origin: 50% 20%;
  transform-origin: 50% 20%;
  transition: all 150ms ease-in-out;
  cursor: pointer;
}

.select_arrow_after {
  cursor: pointer;
  margin-top: -1px;
}

.select_list {
  position: absolute;
  left: 0px;
  top: 100%;
  border: 1px solid $slbordercolor;
  margin: 2px 0 0 0;
  overflow: hidden;
  transform-origin: center top 0px;
  border-radius: 2px;
  background-color: #fff;
  outline: none;
  z-index: 2020;
}

.select_list > .select_list_body ul {
  list-style: none;
  padding: 10px 0;
  margin: 0;
  box-sizing: border-box;
  position: relative;
  display: block;
}

.select_list > .select_list_body ul li {
  font-size: 14px;
  padding: 0 20px;
  position: relative;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: black;
  height: 20px;
  line-height: 20px;
  box-sizing: border-box;
}

.select_list_ul li {
  cursor: pointer;
}

.select_list_ul li:hover {
  background-color: #f5f7fa;
  color: $fontcolor;
}

.current {
  background-color: #f5f7fa;
  color: #409eff !important;
  font-weight: 700;
}

.cast_rotate {
  transform-origin: 50% 20%;
  -webkit-transform: rotate(180deg);
  -moz-transform: rotate(180deg);
  -ms-transform: rotate(180deg);
  transform: rotate(180deg);
}

.select_list_body {
  max-height: 200px;
  overflow: hidden;
}

.list_current {
  display: none;
}
.searchinput {
  border: none;
  width: 74%;
  height: 93%;
  outline: none;
  text-align: center;
  margin: 0 0 0 7px;
  &:focus {
    border: none;
  }
}
.no_result {
  display: none;
  text-align: center;
  color: $slbordercolor;
}

.list_open {
  display: block;
}

.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}

.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style> 

基本上就是这样的了 原理我代码写的很乱