前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Vue复习姿势系列之UI组件——单选框(Radio)

Vue复习姿势系列之UI组件——单选框(Radio)

作者头像
玖柒的小窝
修改2021-10-20 10:16:26
3.2K0
修改2021-10-20 10:16:26
举报
文章被收录于专栏:各类技术文章~各类技术文章~

介绍

基础组件,功能是提供一组备选项供用户选择,只能单选。

要实现的功能

属性

功能

说明

v-model/value

绑定值

禁用

禁止使用

单选框组

提供一组选项给用户,v-model绑定在父级

带有边框

样式增强,并且提供四种尺寸

按钮样式

样式增加,提供四种尺寸

事件

事件名称

说明

回调参数

change

绑定值变化时的触发事件

radio的value值

功能实现

1. 基础功能

src/packages目录下新建radio文件夹,文件夹内创建radio.vueindex.jssrc/styles目录下心新建radio.scss,并在src/styles/index.scss中引入。 我们用label标签将input[type=radio]span包裹在一起。目的是让input的鼠标事件扩散到整个label上,这样即使隐藏了input元素,也能正常使用它的功能。

代码语言:javascript
复制
// radio.vue
<template>
  <label class="my-radio" :class="{ ['my-radio-selected']: selected }">
    <input class="my-radio-input" type="radio" @click="onClick" />
    <span
      class="my-radio-icon"
      :class="{ ['my-radio-icon-selected']: selected }"
    >
    </span>
    <span class="my-radio-label">
      <slot></slot>
    </span>
  </label>
</template>

<script>
export default {
  name: 'myRadio',
  data() {
    return {
      selected: false, // 是否被选中
    }
  },
  props: {},
  methods: {
    onClick() {
      this.selected = true
      console.log('点击事件触发')
    },
  },
}
</script>
复制代码
代码语言:javascript
复制
// radio.scss
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";

@include b(radio) {
  display: inline-block;
  box-sizing: border-box;
  vertical-align: top;
  font-size: $--font-size-large;
  line-height: 20px;
  height: 20px;
  margin-right: 20px;
  cursor: pointer;
  &-input {
    display: none;
  }
  &-icon {
    box-sizing: border-box;
    border: 1px solid #ddd;
    height: 14px;
    width: 14px;
    border-radius: 50%;
    background-color: #fff;
    display: inline-block;
    position: relative;
    top: 2px;
    &:after {
      content: "";
      position: absolute;
      width: 4px;
      height: 4px;
      background-color: #fff;
      left: 50%;
      top: 50%;
      border-radius: 50%;
      transition: transform 0.2s;
      transform: translate(-50%,-50%) scale(0);
    }
    &-selected {
      background-color: $--color-primary;
      &:after {
        transform: translate(-50%,-50%) scale(1);
      }
    }
  }
  &-label {
    display: inline-block;
    margin-left: 3px;
  }
  &-selected {
    color: $--color-primary;
  }
}

复制代码

2. 基础用法

首先是实现v-modellabel两个属性 v-model语法糖拆开后实际上是v-bind:value$emit("input")两个功能组成的。 考虑到初始化时v-model有可能有值,因此watch时需要immediate设置为true,这样radio初始化后能执行一次值同步。

代码语言:javascript
复制
// radio.vue
<template>
  <label class="my-radio" :class="{ ['my-radio-selected']: selected }">
    <input class="my-radio-input" type="radio" @click="onClick" />
    <span
      class="my-radio-icon"
      :class="{ ['my-radio-icon-selected']: selected }"
    >
    </span>
    <span class="my-radio-label">
      <slot></slot>
    </span>
  </label>
</template>

<script>
export default {
  name: 'myRadio',
  data() {
    return {
      selected: false, // 是否被选中
    }
  },
  props: {
    value: {
      type: [String, Number, Boolean],
      default: ""
    },
    label: {
      type: [String, Number, Boolean],
      default: ""
    }
  },
  watch: {
    // 初始化判断是否已被选中
    value: {
      handler(newVal) {
        this.selected = this.value && this.value === this.label
      },
      immediate: true
    },
  },
  methods: {
    onClick() {
      this.selected = true
      this.$emit("input", this.label);
    },
  },
}
</script>

复制代码
image.png
image.png

3. 禁用状态

利用原生radiodisabled即可。

代码语言:javascript
复制
// button.vue 部分代码省略
<template>
  <label
    class="my-radio"
    :class="{ 'my-radio-selected': selected, 'my-radio-disabled': disabled }"
  >
    <input
      class="my-radio-input"
      type="radio"
      @click="onClick"
      :disabled="disabled"
    />
    <span
      class="my-radio-icon"
      :class="{
        'my-radio-icon-selected': selected,
        'my-radio-icon-disabled': disabled
      }"
    >
    </span>
    <span class="my-radio-label">
      <slot></slot>
    </span>
  </label>
</template>
<script>
......
props: {
  // 禁用状态
  disabled: {
    type: Boolean,
    default: false,
  },
}
......
</script>
复制代码
image.png
image.png

4. 单选组框

  1. 该功能实现方式是创建个radio-group组件将radio包裹,radio功能由父级接管。
  2. 会用组件通信中的$dispatch$broadcast,前者向上派发事件,后者向下广播事件。
  3. vue组件生命周期是由内而外的:父created -> 子created -> 子mounted -> 父mounted,父组件要在created中监听事件,不能在mounted中监听。
  4. radio-groupdisabled具体逻辑比较简单,只需根据disabled值来调整radio组件内的myDisabled属性即可。而input[type="radio"]disabled属性由myDisableddisabled共同决定。

创建radio-group组件: src/packages目录下新建radio-group文件夹,文件夹内创建radio-group.vueindex.jssrc/styles 目录下心新建radio-group.scss,并在src/styles/index.scss中引入。

代码语言:javascript
复制
// radio-group.vue
<template>
  <div class="my-radio-group">
    <slot></slot>
  </div>
</template>

<script>
export default {
  name: 'myRadioGroup',
  data() {
    return {
      options: [], // 选项
    }
  },
  props: {
    value: {
      type: [Boolean, String, Number],
      default: false,
    },
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  watch: {
    value: {
      handler(newVal) {
        this.syncValue(newVal)
      },
      immediate: true,
    },
    // 禁用状态
    disabled: {
      handler(newVal) {
        this.syncOptionsDisable(newVal)
      },
      immediate: true,
    },
  },
  created() {
    // 监听on-radio-add事件,将radio实例存到options上
    this.$on('on-radio-add', (radio) => {
      this.options.push(radio)
      this.syncValue(this.value)
      this.syncOptionsDisable(this.disabled);
    })
    // 监听on-radio-remove事件,将radio实例从options中移除
    this.$on('on-radio-remove', (radio) => {
      this.options.splice(this.options.indexOf(radio), 1)
    })
    // 监听radio的选中事件,做value值同步
    this.$on('on-radio-select', (radio) => {
      this.syncValue(radio.label)
    })
  },
  methods: {
    /**
     * @description value值同步
     * @param {Boolean/String/Number} focusVal 选中值,为radio组件的label属性
     */
    syncValue(focusVal) {
      this.$emit('input', focusVal)
      this.options.forEach((d) => {
        d.selected = d.label === focusVal
      })
    },
    /**
     * @description 设置子选项的myDisabled属性
     * @param {Boolean} disabled 是否禁用
     */
    syncOptionsDisable(disabled) {
      this.options.forEach((d) => {
        d.myDisabled = disabled
      })
    },
  },
}
</script>
复制代码

调整radio组件代码,抛出事件给radio-group。

代码语言:javascript
复制
// radio.vue 省略部分代码
......
<template>
  <label
    class="my-radio"
    :class="{ 'my-radio-selected': selected, 'my-radio-disabled': disabled || myDisabled }"
  >
    <input
      class="my-radio-input"
      type="radio"
      @click="onClick"
      :disabled="disabled || myDisabled"
    />
    <span
      class="my-radio-icon"
      :class="{
        'my-radio-icon-selected': selected,
        'my-radio-icon-disabled': disabled || myDisabled,
      }"
    >
    </span>
    <span class="my-radio-label">
      <slot></slot>
    </span>
  </label>
</template>
<script>
......
data() {
  return {
    selected: false, // 是否被选中
    myDisabled: false, // 内部的禁用属性,交由父级控制
  }
},
mounted() {
  // 通知myRadioGroup组件调用on-radio-add方法,参数为当前radio实例
  this.dispatch('myRadioGroup', 'on-radio-add', this)
},
beforeDestroy() {
  // 移除时,调用myRadioGroup组件的on-radio-remove方法
  this.dispatch('myRadioGroup', 'on-radio-remove', this)
},
methods: {
  onClick() {
    this.selected = true
    this.$emit('input', this.label)
    this.dispatch("myRadioGroup", 'on-radio-select', this)
  },
},
</script>
......
复制代码
image.png
image.png

5. 带有边框

  1. radio组件增加border属性,可以渲染带有边框的选项,此功能主要是对css的操作。
  2. border生效时,size同样生效。需开发4种尺寸,此功能也是对css的操作。
代码语言:javascript
复制
// radio.vue 省略部分代码
<template>
......
  <label
    class="my-radio"
    :class="{
      'my-radio-selected': selected,
      'my-radio-disabled': disabled || myDisabled,
      [`my-radio-${size}-border`]: border
    }"
  >
  ......
  </label>
</template>
<script>
// 工具函数,用于判断传入的值是否符合条件
import { oneOf } from '../../utils/assist';
export default {
  props: {
     ......
     // 是否绘制边框
    border: {
      type: Boolean,
      default: false,
    },
    // 尺寸
    size: {
      validator(value) {
        return oneOf(value, ['large', 'medium', 'small', 'mini'])
      },
      type: String,
      default: 'medium',
    },
    ......
  }
}
</script>
复制代码
代码语言:javascript
复制
// radio.scss 省略部分代码
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";

@include b(radio) {
  ......
  // size和border相关样式
  &-large-border {
    height: 40px;
    padding: 8px 8px 12px 8px;
    border: 1px solid $--border-color;
    border-radius: 4px;
  }
  &-medium-border {
    height: 36px;
    padding: 6px 8px 10px 8px;
    border: 1px solid $--border-color;
    border-radius: 4px;
  }
  &-small-border {
    height: 32px;
    padding: 4px 8px 8px 8px;
    border: 1px solid $--border-color;
    border-radius: 4px;
    font-size: $--font-size-medium;
  }
  &-mini-border {
    height: 28px;
    padding: 2px 8px 6px 8px;
    border: 1px solid $--border-color;
    border-radius: 4px;
    font-size: $--font-size-medium;
  }
}
复制代码
image.png
image.png

6. 按钮样式

  1. 将radio渲染成按钮样式,也是对css的操作。
  2. button属性设置给radio-group,由父级接管该功能。
代码语言:javascript
复制
// radio.vue 省略部分代码
<template>
  <label
    class="my-radio"
    :class="{
      'my-radio-selected': selected,
      'my-radio-disabled': disabled || myDisabled,
      [`my-radio-${size}-border`]: border,
      [`my-radio-${size}-button`]: button,
      'my-radio-selected-button': selected && button,
    }"
  >
    <input
      class="my-radio-input"
      type="radio"
      @click="onClick"
      :disabled="disabled || myDisabled"
    />
    <span
      class="my-radio-icon"
      :class="{
        'my-radio-icon-selected': selected,
        'my-radio-icon-disabled': disabled || myDisabled,
        'my-radio-icon-button': button
      }"
    >
    </span>
    <span class="my-radio-label">
      <slot></slot>
    </span>
  </label>
</template>
<script>
......
data() {
  return {
    button: false, //按钮样式,由父级控制
  }
}
</script>
复制代码
代码语言:javascript
复制
// radio-group.vue 省略部分代码
<script>
......
props: {
  ......
  // 是否启用按钮样式
  button: {
    type: Boolean,
    default: false
  }
},
watch: {
  ......
  // 是否使用按钮样式
   button: {
    handler(newVal) {
      this.syncOptionsButtonStyle(newVal)
    },
    immediate: true,
  }
},
created() {
  // 监听on-radio-add事件,将radio实例存到options上
  this.$on('on-radio-add', (radio) => {
    ......
    this.syncOptionsButtonStyle(this.button);
  })
},
methods: {
  /**
   * @description 设置子选项的button属性,用以控制按钮样式
   * @param {Boolean} value 是否设置
   */
  syncOptionsButtonStyle(value) {
    this.options.forEach((d) => {
      d.button = value
    })
  }
}
</script>
复制代码
代码语言:javascript
复制
// radio.scss 省略部分代码
@charset "UTF-8";
@import "common/var";
@import "mixins/mixins";

@include b(radio) {
  ......
  // 按钮样式相关样式
  &-large-button {
    float: left;
    background-color: #fff;
    height: 40px;
    line-height: 38px;
    padding: 0 15px 0 12px;
    margin: 0;
    border: 1px solid $--border-color;
  }
  &-medium-button {
    float: left;
    background-color: #fff;
    height: 36px;
    line-height: 34px;
    padding: 0 15px 0 12px;
    margin: 0;
    border: 1px solid $--border-color;
  }
  &-small-button {
    float: left;
    background-color: #fff;
    height: 32px;
    line-height: 30px;
    padding: 0 15px 0 12px;
    margin: 0;
    border: 1px solid $--border-color;
    font-size: $--font-size-medium;
  }
  &-mini-button {
    float: left;
    background-color: #fff;
    height: 28px;
    line-height: 26px;
    padding: 0 15px 0 12px;
    margin: 0;
    border: 1px solid $--border-color;
    font-size: $--font-size-medium;
  }
}
复制代码
image.png
image.png

结语

以上便是单选框组件的部分功能开发过程,单选框组功能中我们创建了新的组件radio-group作为父级,运用组件通讯中的广播与派发机制来协调父子之间的相互调用,以此完成v-modeldisabled功能的实现。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 要实现的功能
  • 功能实现
    • 1. 基础功能
      • 2. 基础用法
        • 3. 禁用状态
          • 4. 单选组框
            • 5. 带有边框
              • 6. 按钮样式
              • 结语
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档