components/radio/radio.vue

<template>
    <div class="ion-radio" :class="[modeClass,colorClass,{'radio-disabled':isDisabled}]">
        <div class="radio-icon" :class="{'radio-checked':isChecked}">
            <div class="radio-inner"></div>
        </div>
        <vm-button role="radio" @click="onPointerDownHandler($event)" :id="id"></vm-button>
    </div>
</template>
<script type="text/javascript">
  /**
   * @component Radio
   * @description
   *
   * ## 表单组件 / Radio单选框组件
   *
   * ### 注意
   *
   * 使用v-modal切换状态(数据控制), 不支持checked属性, value为纯string字段, 用于唯一标识;
   *
   * ### 其他
   *
   * - 组件支持异步操作实例化
   * - 目前组件的层级嵌套关系如下:  List -> Item -> Radio
   * - 动态设置了单个radio的禁用状态, 如果之前是选中的则取消选中, 手动点击和数据选中都会生效
   *
   * ### 组件通讯关系
   *
   * 1. 初始化时, radio组件自己的this传递给radio-group, recordRadio()
   * 2. Radio点击时, 调用List组件的onRadioChange函数, 传递自己的value
   * 3. List组件得到value触发input更新v-modal值, 之后遍历子组件Radio, 触发组件的setChecked, 传递value
   * 4. 子组件根据传入的value设置自己的状态
   *
   *
   * ### 如何引入
   * ```
   * // 引入
   * import Radio from 'vimo/lib/radio'
   * // 安装
   * Vue.component(Radio.name, Radio)
   * // 或者
   * export default{
   *   src: {
   *    Radio
   *  }
   * }
   * ```
   *
   * @props {String} [mode='ios'] - 模式
   * @props {String} [color] - 颜色
   * @props {Boolean} [disabled=false] - 单向选择, 点击且换并不对父组件传递
   *
   * @fire component:Radio#onSelect - 点按选择时触发
   * @demo #/radio
   * @usage
   * <vm-list radio-group v-model="fruits" :disabled="isListDisabled">
   *    <vm-list-header>Fruits</vm-list-header>
   *    <vm-item>
   *        <vm-label>Apple</vm-label>
   *        <vm-radio value="apple" :disabled="isAppleDisabled" @onSelect="onSelectHandler"></vm-radio>
   *    </vm-item>
   *    <vm-item>
   *        <vm-label>Banana</vm-label>
   *        <vm-radio value="banana" color="danger" @onSelect="onSelectHandler"></vm-radio>
   *    </vm-item>
   *    <vm-item>
   *        <vm-label>Cherry (secondary color)</vm-label>
   *        <vm-radio value="cherry" color="secondary" @onSelect="onSelectHandler"></vm-radio>
   *    </vm-item>
   *    <vm-item>
   *         <vm-label>Disabled</vm-label>
   *        <vm-radio value="disabled" :disabled="true" @onSelect="onSelectHandler"></vm-radio>
   *    </vm-item>
   *    <vm-item>
   *        <vm-label>Default</vm-label>
   *        <vm-radio value="default" @onSelect="onSelectHandler"></vm-radio>
   *    </vm-item>
   * </vm-list>
   *
   **/
  import { setElementClass, isTrueProperty } from '../../util/util'
  import ThemeMixins from '../../themes/theme.mixins'
  import VmButton from "../button/button.vue";

  let id = 0
  export default {
    components: {VmButton},
    name: 'vm-radio',
    mixins: [ThemeMixins],
    data () {
      return {
        isChecked: false,               // 内部 选中
        isDisabled: this.disabled,      // 内部 禁用
        itemComponent: null,            // item组件实例
        radioGroupComponent: null,      // list(radioGroup)组件实例
        isInit: false,                  // 初始化状态
        id: `rb-${id++}`                // id
      }
    },
    props: {
      // 固定值
      value: String,
      disabled: Boolean,
    },
    watch: {
      disabled (val) {
        this.setDisabled(isTrueProperty(val))
      }
    },
    mounted () {
      this.init()
    },
    methods: {
      /**
       * 设置当前radio的禁用状态
       **/
      setDisabled (isDisabled) {
        this.setChecked(null)
        this.isDisabled = isDisabled
        this.itemComponent && setElementClass(this.itemComponent.$el, 'item-radio-disabled', isDisabled)
      },

      /**
       * 设置当前的radio的选中状态
       **/
      setChecked (checked) {
        let isChecked = (checked === this.value) && !this.isDisabled
        if (this.isChecked !== isChecked) {
          this.isChecked = isChecked
          this.isInit && this.isChecked && this.$emit('onSelect', this.value)
          this.itemComponent && setElementClass(this.itemComponent.$el, 'item-checkbox-checked', this.isChecked)
        }
      },

      /**
       * 当radio点击时
       **/
      onPointerDownHandler ($event) {
        $event.preventDefault()
        $event.stopPropagation()
        !this.isDisabled && this.radioGroupComponent && this.radioGroupComponent.onRadioChange(this.value)
      },

      /**
       * init
       **/
      init () {
        // 找到外部item实例
        if (this.$parent.$options.name.toLowerCase() === 'vm-item') {
          this.itemComponent = this.$parent
          setElementClass(this.itemComponent.$el, 'item-radio', true)
        }

        // 找到外部List实例
        if (this.$parent.$parent.$options._componentTag.toLowerCase() === 'vm-list') {
          let node = this.$parent.$parent
          if (node.radioGroup) {
            this.radioGroupComponent = node
            this.radioGroupComponent.recordRadio(this)
          }
          console.assert(this.radioGroupComponent, 'Radio组件需要在List组件中加上`radio-group`属性才能正常使用v-model指令!')
        }

        // 初始化禁用状态
        this.setDisabled(this.disabled)

        this.isInit = true
      }
    }
  }
</script>
<style lang="scss">
    @import "radio.ios";
    @import "radio.md";
</style>