Vue自定义单选按钮和复选框组件

“ 做好自己就好了呀 ”

问题

前段时间接到一个任务,需要做一个自定义的单选和多选按钮,但是之前都是用现成的UI框架,都不用自己写样式,直接一套就是干!但是进公司后,发现部门不适用外部UI框架,好吧,那我就自己写呗!

构思

我想到的办法是运用原生的radio和CheckBox,再通过样式覆盖的方式。

兼容性

由于在写组件时使用了flex布局,so 低版本浏览器不支持的哈,具体有关flex的知识可以学习一下阮大佬的博客:Flex 布局教程:语法篇

废话不多说上代码

<!-- Checkbox -->
<template>
  <div class="check-boxs"
       :class="{'column':isColumn}">
    <div v-for="(item,index) in items"
         :key=index
         class="check-box"
         :class="{'width100':isColumn}"
         @click="checkBox(index)"
         v-show="!item.isShow">
      <span :class="{'checkbox-icon':true,'checkbox-input':item.isChecked,'opacity':opacity&&item.isChecked}"
            :style="item.isChecked?propsStyle:''">
        <i :class="{'checkbox-on':item.isChecked}"></i>
      </span>
      <input type="checkbox"
             :value="item.value">
      <span v-html="item.name"></span>
    </div>
  </div>
</template>

<script>
export default {
  name: "checkbox",
  data() {
    return {
      propsStyle: {
        border: "1px solid " + this.color,
        background: this.color
      }
    };
  },
  props: {
    /**
      items实例:[{name:'选项一',isChecked:false}],
      name:可以是html,
      需要显示和隐藏的加上isShow字段,true为隐藏,如下:
      [{name:'选项一',isChecked:false,isShow:false}]
     */
    items: {
      type: Array,
      default: function() {
        return [];
      }
    },
    // 已选框的颜色透明度
    opacity: {
      type: Boolean,
      default: false
    },
    // flex布局纵轴方向
    isColumn: {
      type: Boolean,
      default: false
    },
    // 最大选项数
    limit: {
      type: Number,
      default: 2
    },
    // 已选项框的背景色
    color: {
      type: String,
      default: "#65aef7"
    }
  },
  watch: {
    color(newval, oldval) {
      if (newval != oldval) {
      }
    }
  },

  methods: {
    checkBox(index) {
      let count = 0;
      for (let i = 0; i < this.items.length; i++) {
        if (this.items[i].isChecked == true) {
          count++;
        }
      }
      if (count >= this.limit && !this.items[index].isChecked) {
        this.$emit("canNotAdd", false);
        return;
      }
      // 将所选项的value值传出去,并勾选
      /* 也可将这句代码放在父组件调用这个事件的地方,可以使得复选框智能在特定条件下被选中*/
      this.$set(this.items[index], "isChecked", !this.items[index].isChecked);
      // this.items[index].isChecked = !this.items[index].isChecked;
      this.$emit("checkBox", index);
    }
  }
};
</script>

<style scoped>
.check-boxs {
  display: flex;
  display: -webkit-flex;
  justify-content: space-between;
  align-items: center;
  margin: 5px 0;
  color: #545454;
  font-size: 12px;
}
.column {
  flex-direction: column;
  justify-content: flex-start;
}
.width100 {
  width: 100%;
  display: flex;
  display: -webkit-flex;
  justify-content: space-between;
  height: 24px;
  align-items: flex-start;
  padding-right: 40px;
  box-sizing: border-box;
}
.check-box {
  position: relative;
  cursor: pointer;
}

.check-box input {
  vertical-align: middle;
  margin-top: -2px;
  margin-bottom: 1px;
  /* 前面三行代码是为了让checkbox按钮与文字对齐 */
  width: 12px;
  height: 12px;
  appearance: none; /*清楚默认样式*/
  -webkit-appearance: none;
  opacity: 0;
  outline: none;
  cursor: pointer;
  /* 注意不能设置为display:none*/
}
/* 选项框的样式修改 */
.checkbox-icon {
  width: 12px;
  height: 12px;
  position: absolute;
  top: 1px;
  z-index: 0;
  border-radius: 2px;
  background-color: #fff;
  border: 1px solid #c9c9c9;
  box-shadow: 0px 2px 5px 0px rgba(0, 0, 0, 0.15);
  cursor: pointer;
}
.checkbox-input {
  border: 1px solid #65aef7;
  background-color: #65aef7;
}
.opacity {
  opacity: 0.5;
}
.checkbox-on {
  position: absolute;
  left: 4px;
  top: 1px;
  width: 3px;
  height: 7px;
  border-right: 2px solid #fff;
  border-bottom: 2px solid #fff;
  transform: rotate(45deg);
}
</style>
<template>
  <div class="radio-container">
    <div v-for="(item,index) in items"
         :key=index
         @click="radioOn(index)"
         class="single">
      <span class="outer"><i class="inner"
           v-if="item.isChecked"
           :style="item.isChecked?propsStyle:''"></i></span>
      <input type="radio"
             :name="item.name"
             :id="item.value"
             :value="item.value"
             v-model="radio">
      <span v-html="item.name"></span>
    </div>
  </div>
</template>

<script>
export default {
  name: "Radio",
  data() {
    return {
      radio: 0,
      propsStyle: {
        background: this.color
      }
    };
  },
  /*itemsRadio: [
{ name: "杭州", value: 0, isChecked: true },
{ name: "上海", value: 1, isChecked: false },
{ name: "北京", value: 2, isChecked: false }
]*/
  props: {
    items: {
      type: Array,
      default: function() {
        return [];
      }
    },
    color: {
      type: String,
      default: "#65aef7"
    }
  },
  methods: {
    radioOn(index) {
      this.items.forEach(key => {
        key.isChecked = false;
      });
      this.items[index].isChecked = true;
      this.$emit("radioOn", index);
    }
  }
};
</script>

<style scoped>
.radio-container {
  display: flex;
  display: -webkit-flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
  cursor: pointer;
}
.single {
  position: relative;
  display: flex;
  display: -webkit-flex;
  align-items: center;
  cursor: pointer;
}
input {
  width: 12px;
  height: 12px;
  appearance: none; /*清楚默认样式*/
  -webkit-appearance: none;
  opacity: 0;
  outline: none;
  cursor: pointer;
  vertical-align: middle;
  margin-top: -2px;
  margin-bottom: 1px;
}
.outer {
  height: 12px;
  width: 12px;
  display: flex;
  display: -webkit-flex;
  justify-content: center;
  align-items: center;
  border: 1px solid #ccc;
  border-radius: 999px;
  position: absolute;
  top: 1px;
  z-index: 0;
}
.inner {
  background-color: green;
  width: 6px;
  height: 6px;
  border-radius: 999px;
}
</style>

以上两个均以组件形式调用哈,只要在父组件中传入相应数据就可以看到效果哦

代码若有瑕疵,请指出,轻喷。

    原文作者:Booleen
    原文地址: https://segmentfault.com/a/1190000018876094
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞