[封装自己的ui库] radio组件基础功能的实现

460 阅读1分钟

问题

构建到了radio组件的部分了,写一篇文章记录一下构建的过程

原理

看着el-radio思考了一下原理,是要利用@input来实现了

  • 构建组件,写好样式
  • 向构建组件传递v-model,label,change等
  • 内部组件获取v-model的value,再将value双向绑定到原生input节点上
  • 隐藏原生节点
  • 判断value与label值是否相等,相等则触发自定义input的选中样式
  • 当点击自定义input选中时,触发input事件,会被外层的v-model检测到并改变最外其绑定值
  • 条件判断是否具有radio-group来分支选中类型

代码

  • 自定义radio

value ---> v-model值 label ---> 标识值 disable ---> 是否禁用

<template>
    <label ref="props" :for="label" :class="{ 'ty-radio': true, 'select_radio': select, 'disable': forbidden }">
        <div class="main">
            <span class="border">
                <span class="fill"></span>
                <input :id="label" type="radio" :value="label" style="display: none;" v-model="radioValue">
            </span>
            <div class="text">
                <slot></slot>
            </div>
        </div>
    </label>
</template>

<script>
export default {
    name: 'ty-radio',
    props: {
        // 外层的v-model值
        value: {
            type: [String, Number]
        },
        // 外层标识值
        label: {
            type: [String, Number]
        },
        disable: {
            type: Boolean,
            default: false
        },
        // 选中label时触发
        change: {
            type: Function
        }
    },

    data() {
        return {
            // 禁用按钮
            forbidden: false
        }
    },

    watch: {
        disable: {
            handler(newV) {
                if (typeof newV == 'boolean') {
                    this.forbidden = newV
                }
            }, immediate: true
        },
        // 判断是否选中, 选中则展示选中效果
        select: {
            handler(newV) {
                if (newV) this.$emit('change', this.label);
            }
        }
    },

    computed: {
    // 设置给隐藏input的值
        radioValue: {
            get() {
                // 判断是否是有分组 有分组则设置为来自分组的value , 没有则设置来自radio的value
                return this.isGroup ? this.$parent.$attrs.value : this.value
            },
            set(newValue) {
                if (this.isGroup) {
                    // 有分组则设置为来自分组的vmodel值
                    this.$parent.$emit('input', newValue)
                } else {
                    // 无分组设置为当前radio的vmodel值
                    this.$emit('input', newValue)
                }
            }
        },
        // 是否选中
        select: {
            get() {
                return this.$props.label === this.radioValue;
            }
        },
        // 是否是分组
        isGroup: {
            get() {
                return this.$parent.$attrs.value ? true : false
            }
        },
    }
}
</script>

<style lang='less' scoped>
@import '../../../css/radio.less';
</style>
  • 自定义radioGroup

只起到一个传递值的作用

<template>
    <div class="ty-radio-group">
        <slot></slot>
    </div>
</template>

<script>
export default {
    name: 'ty-radio-group',
}
</script>
  • 测试
<template>
    <div class='testRadio'>
        <p>基本使用</p>
        <ty-radio v-model="value" label="1">选项1</ty-radio>
        <ty-radio v-model="value" label="2">选项2</ty-radio>
        <ty-radio v-model="value" label="3">选项3</ty-radio>


        <p>禁用效果</p>
        <ty-radio v-model="valueA" label="a">选项1</ty-radio>
        <ty-radio v-model="valueA" label="b" :disable="true">选项2</ty-radio>
        <ty-radio v-model="valueA" label="c">选项3</ty-radio>

        <p>单选框组</p>
        <ty-radio-group v-model="address">
            <ty-radio label="China">中国</ty-radio>
            <ty-radio label="America">美国</ty-radio>
            <ty-radio label="Russia">俄罗斯</ty-radio>
            <ty-radio label="Japan" :disable="true">日本</ty-radio>
            <ty-radio label="Korea" :disable="true">韩国</ty-radio>
        </ty-radio-group>

        <p>change值变化事件</p>
        <ty-radio v-model="valueB" label="4" @change="change">选项1</ty-radio>
        <ty-radio v-model="valueB" label="5" @change="change">选项2</ty-radio>
        <ty-radio v-model="valueB" label="6" @change="change">选项3</ty-radio>
    </div>
</template>

<script>
export default {
    name: 'testRadio',
    data() {
        return {
            value: '1',
            valueA: 'c',
            valueB: '4',
            valueC: '3',
            address: 'China'
        }
    },

    methods: {
        change(n) {
            this.$tyMessage({
                message: n,
                type: 'success'
            })
        }
    }
}
</script>

<style lang='less' scoped>
.testRadio {
    padding: 20px;
    width: 100%;
    height: 100%;

    p {
        margin: 10px 0;
    }

    .ty-radio {
        margin: 10px;
    }
}
</style>

效果

QQ录屏20221105233918.gif

其他

后续发现可使用provideinject来实现group稍微方便一些