封装radio单选框

210 阅读1分钟

APP.vue

  <div id="app">
    <!-- label是radio的值value -->
    <!--🌸 希望两个单选框是一组的话,控制一个值的话,就得有v-model -->
    <hm-radio label="0" v-model="gender">男</hm-radio>
    <hm-radio label="1" v-model="gender">女</hm-radio>
    <hm-radio label="3" v-model="gender"></hm-radio>
    <hm-radio label="4" v-model="gender"></hm-radio>
  </div>
</template>

<script>
export default {
  name: 'App',
  data () {
    return {
      gender: 1,//radio.vue
    }
  }
}
</script>

<style lang="less">
  #app {
    .hm-input {
      width: 180px;
    }
  }
</style>

src/components/radio

  <!-- 🌸当前radio的label值 = 父组件传过来的value值 -->
  <label class="hm-radio" :class="{ 'is-checked': label == value }">
    <span class="hm-radio_input">
      <span class="hm-radio_inner"></span>
      <input
        type="radio"
        class="hm-radio_original"
        :value="label"
        :name="name"
        v-model="model"
      />
      <!--🌸 这里的v-model应该和value绑定,但是value是传进来的值,所以不能直接写 -->
    </span>
    <span class="hm-radio_label">
      <slot></slot>
      <template v-if="!$slots.default">{{ label }}</template>
      <!-- 🌸如果radio不传内容进来,就把label渲染进页面 -->
    </span>
  </label>
</template>

<script>
export default {
  name: 'HmRadio',
  props: {
    label: {
      type: [String, Number, Boolean],
      default: ''
    },
    value: null,
    name: {
      type: String,
      default: '',
    }
  },
  //🌸双向绑定计算属性的话,必须提供他的get和set值
  computed: {
    model: {
      get () {//获取父组件的value
        return this.value
      },
      set (value) {
        // 🌸触发父组件给当前组件注册的input事件,因为父组件有v-model,内部有value
        this.$emit('input', value)
      },
    }
  }
}
</script>

<style lang="less" scoped>
  .hm-radio {
    color: #606266;
    font-weight: 500;
    line-height: 1;
    position: relative;
    cursor: pointer;
    display: inline-block;
    white-space: nowrap;
    outline: none;
    font-size: 14px;
    margin-right: 30px;
    // -moz-user-select: none;
    // -webkit-user-select: none;
    // -ms-user-select: none;
    .hm-radio_input {
      white-space: nowrap;
      cursor: pointer;
      outline: none;
      display: inline-block;
      line-height: 1;
      position: relative;
      vertical-align: middle;
      .hm-radio_inner {
        border: 1px solid #dcdfe6;
        border-radius: 100%;
        width: 14px;
        height: 14px;
        background-color: #fff;
        position: relative;
        cursor: pointer;
        display: inline-block;
        box-sizing: border-box;
        &:after {
          width: 4px;
          height: 4px;
          border-radius: 100%;
          background-color: #fff;
          content: "";
          position: absolute;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%) scale(0);
          transition: transform 0.15s ease-in;
        }
      }
      .hm-radio_original {
        opacity: 0;
        outline: none;
        position: absolute;
        z-index: 1;
        top: 0;
        left: 0px;
        right: 0;
        bottom: 0;
        margin: 0;
      }
    }
    .hm-radio_label {
      font-size: 14px;
      padding-left: 10px;
    }
  }
  .hm-radio.is-checked {
    .hm-radio_input {
      .hm-radio_inner {
        border-color: #409eff;
        background: #409eff;
        &:after {
          transform: translate(-50%, -50%) scale(1);
        }
      }
    }
  }
  .hm-radio_label {
    color: #409eff;
  }
</style>