封装单选组件

77 阅读2分钟

如何封装一款单选组件

前提

本次封装使用less预处理语言

1.属性

1.1 title

  • 选框组的标题
  • 值为字符串类型

1.2 options

  • 单选项目数组
  • 必填

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

  • value: 值
  • label: 显示的文字
  • disabled: 是否可用

1.3 value

  • 当前选中值

1.4 disabled

  • 是否可用
  • 值为布尔类型

2.实例

2.1 示例

<script setup>
	import mRadio from '@/components/mBtns/mRadio.vue';

	const options = [
		{ value: '01', label: '张三', disabled: false },
		{ value: '02', label: '李四', disabled: false },
		{ value: '03', label: '王五', disabled: true },
	]

</script>

<template>
	<mRadio
		title="测试单选框"
		:options="options"
		value="01"
	>

	</mRadio>
</template>

2.2 实现 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="newValue" :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 setup>
    const props = defineProps({
        title: {
            type: String,
            default: ''
        },
        options: {
            type: Array,
            default: () => []
        },
        value: {
            type: [ String, Number ],
            default: '' | 0
        },
        disabled: {
            type: Boolean,
            default: false
        }
    })

    const emits = defineEmits(['input', 'change'])

    const newValue = computed({
      get () {
        return props.value
      },
      set (val) {
        emits('input', val)
        emits('change', val)
      }
    })

    const groupName = generateUUID()

  function generateUUID() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
  }
  </script>

  <style lang="less" scoped>
    @selected-box-bg: #1967F2;
    @selected-box-color: #161E30;

    @select-box-bg: #FFF;
    @select-box-color: #B8B8B8;

    @prohibit-box-color: #8D8D8D;

    @border-color: #D7D9DC;
    
  .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 {
              background-color: @selected-box-bg;
              .input-box-circle {
                width: 10px;
                height: 10px;
                opacity: 1;
              }
              & + .input-span {
                  color:  @selected-box-color;
              }
            }

          }
          &:disabled {
            & + .input-box {
              background-color: @prohibit-box-color;
              cursor: not-allowed;
              .input-box-circle {
                width: 10px;
                height: 10px;
                opacity: 1;
                background-color: @select-box-bg;
              }
              & + .input-span {
                color: @prohibit-box-color;
                cursor: not-allowed;
              }
            }
          }
        }
        .input-box {
          box-sizing: border-box;
          position: relative;
          display: inline-block;
          border: solid 1px @border-color;
          border-radius: 50%;
          padding: 0;
          width: 20px;
          height: 20px;
          vertical-align: middle;
          overflow: hidden;
          user-select: none;
          margin-right: 10px;
          margin-top: -3px;
          flex: none;
        }
        .input-box-circle {
          position: absolute;
          display: block;
          top: 50%;
          left: 50%;
          transform: translate(-50%, -50%);
          width: 0;
          height: 0;
          background-color: @select-box-bg;
          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;
          color: @select-box-color;
        }
      }
    }
    &.disabled {
      cursor: not-allowed;
      label {
        cursor: not-allowed;
      }
    }
  }
  </style>

最后

感谢掘金大佬青莲使者的文章