如何封装一款单选组件

631 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

TIP 👉 近水楼台先得月,向阳花木易为春。宋·俞文豹《清夜录》

前言

在我们日常项目开发中,我们经常会写一些表单项,所以封装了这款表单项组件。

单选框组件

属性

1. title
  • 单选框组的标题
  • 值为字符串类型
2. options
  • 单选项目数组
  • 必填

数组中对象示例:{ value: ‘01’, label: '男', disabled:false}

  • value: 值
  • label: 显示的文字
  • disabled: 是否可用
3. value
  • 当前选中值
4. disabled
  • 是否可用
  • 值为布尔类型

示例

<template>
  <div class="radio-wrap">
    <BaseRadio title="请选择银行:" :options="radioOptions" v-model="bank" @change="doChange"></BaseRadio>
    <br><h3>选择结果为:{{bank}}</h3>
  </div>
</template>
<script>
import BaseRadio from '@/components/base/radio/index.vue'
export default {
  name: 'RadioDemo',
  components: {
    BaseRadio
  },
  data () {
    return {
      bank: '03',
      radioOptions: [
        { value: '01', label: '全部' },
        { value: '02', label: '零散' },
        { value: '03', label: '部分', disabled: true }
      ]
    }
  },
  methods: {
    doChange (val) {
      console.log('触发change事件,value = ', val)
    }
  }
}
</script>
<style lang="scss" scoped>
.radio-wrap{
  padding: 40px;
  font-size: 36px;
  line-height: 60px;
}
</style>

实现radio.vue

<!-- 单选组件 -->
<template>
  <div class="radio-list" :class="disabled ? 'disabled': ''">
    <div v-if="title" class="radio-title">{{title}}</div>
    <div class="radio-item" v-for="option in options" :key="option.value">
      <label>
        <input type="radio" :name="groupName" v-model="currentValue" :disabled="disabled || option.disabled" :value="option.value"/>
        <span class="input-box">
          <span class="input-box-circle"></span>
        </span>
        <span class="input-span">{{option.label}}</span>
      </label>
    </div>
  </div>
</template>
<script>
import { generateUUID } from '@/assets/js/utils.js'
export default {
  name: 'BaseRadio',
  props: {
    // 单选组标题
    title: String,
    /**
     * 单选项目数组
     * 数组中对象示例:{ value: ‘01’, label: '男', disabled:false}
     *  value: 值
     *  label: 显示的文字
     *  disabled: 是否可用
     **/
    options: {
      type: Array,
      required: true
    },
    // 当前选中值
    value: [String, Number],
    // 是否可用
    disabled: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      currentValue: this.value,
      groupName: generateUUID()
    }
  },
  watch: {
    value (val) {
      this.currentValue = val
    },
    currentValue (val) {
      this.$emit('input', val)
      this.$emit('change', val)
    }
  }
}
</script>
<style lang="scss" scoped px2rem="false">
$outer-border-color: $border-color-base;
$disable-bg-color: $input-disabled-bg-color;
$disable-inner-dot-color: $color-text-placeholder;
.radio-list{
  .radio-title{
    display: inline-block;
    vertical-align: middle;
    margin: 0 15px;
    color: #333;
  }
  .radio-item{
    position: relative;
    display: inline-block;
    padding: 0 30px 0 0;
    // vertical-align: middle;
    white-space: nowrap;
    &:last-of-type {
      padding-right: 0;
    }
    label {
      display: inline-block;
      align-items: center;
      position: relative;
      cursor: pointer;
      input {
        position: absolute;
        top: 50%;
        left: 0;
        width: 1em;
        height: 1em;
        transform: translate(0, -50%);
        font-size: inherit;
        opacity: 0;
        z-index: 1;
        &:checked + .input-box {
          @include primary-border-color();
          @include primary-background-color();
          &> .input-box-circle {
            width: 4px;
            height: 4px;
            opacity: 1;
          }
        }
        &:disabled {
          & + .input-box {
            border-color: $outer-border-color !important;
            background-color: $disable-bg-color !important;
            cursor: not-allowed;
            .input-box-circle {
              background-color: $disable-inner-dot-color;
            }
            & + .input-span {
              color: #999;
              cursor: not-allowed;
            }
          }
        }
      }
      .input-box {
        box-sizing: border-box;
        position: relative;
        display: inline-block;
        border: solid 1px $outer-border-color;
        border-radius: 50%;
        padding: 0;
        width: 1em;
        height: 1em;
        vertical-align: middle;
        overflow: hidden;
        user-select: none;
        margin-right: 10px;
        margin-top: -3px;
        flex: none;
        &:hover {
          @include primary-border-color();
        }
      }
      .input-box-circle {
        position: absolute;
        display: block;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 0;
        height: 0;
        background-color: #FFF;
        border-radius: 50%;
        opacity: 0;
        transition: width 0.15s ease-in, height 0.15s ease-in, margin 0.15s ease-in;
      }
      .input-span{
        display: inline-block;
        // vertical-align: middle;
        color: #666;
      }
    }
  }
  &.disabled {
    cursor: not-allowed;
    label {
      cursor: not-allowed;
    }
  }
}
</style>
// 下面注释勿删,用于根据配置文件 isMobile 配置项判断是否支持移动端,如果不支持,注释掉该样式
/* [auto html command START {isMobile=false}] */
<style lang="scss" scoped>
@media (max-width: $max-mobile-width) {
  .radio-list {
    .radio-title {
      display: block;
      margin: 0;
      padding: 0 40px;
      // background-color: #f5f5f5;
      @include primary-background-color(.03);
    }
    .radio-item {
      display: block;
      padding: 0 40px;
      label {
        display: flex;
        .input-box {
          flex: none;
        }
      }
    }
  }
}
</style>
/* [auto html command END {isMobile=false}] */

感谢评论区大佬的点拨。

希望看完的朋友可以给个赞,鼓励一下