1
web 技术支持| 基于 vue3 实现自己的组件库第三章:Checkbox 组件
 作者:anyRTC开发者
- 2022 年 8 月 10 日 上海
 本文字数:11531 字
阅读完需:约 38 分钟
大家好今天的内容是基于vue3实现自己的组件库系列第二章,本文默认你会安装和创建 vue3 项目,如果不会请参考vue官网;
Checkbox.vue Template
<template>    <div :class='["v-checkbox", { border },     { disabled: proxyDisabled || disabled || computedDisabled },     { medium: border && (size || proxySize) === "medium" },    { small: border && (size || proxySize) === "small" },    { mini: border && (size || proxySize) === "mini" },    { "v-radio-checked": computedChecked && !computedDisabled }]'     @click.stop='handleChecked'>        <input type="checkbox" :checked='computedChecked' :disabled='disabled' :name='name || modelValue || proxyValue' :value='label'>        <span :class='["v-radio-input", { checked: computedChecked }, { indeterminate }]'></span>        <span :class='["v-radio-label", { active: computedChecked }]'>            <slot></slot>        </span>    </div></template>
复制代码
 Checkbox.vue Script
<script>import { inject, computed } from 'vue';import VairCheckout from '@/types/checkout';export default {    name: 'checkbox',    props: VairCheckout,    setup (props, ctx) {        const proxyValue = inject('proxyValue');        const proxyDisabled = inject('proxyDisabled');        const proxySize = inject('proxySize');        const proxyMin = inject('proxyMin');        const proxyMax = inject('proxyMax');        const update = inject('update');
        const handleChecked = () => {            if (!props.disabled && !computedDisabled.value) {                if (proxyValue) {                    ctx.emit('change', props.label);                    update(props.label);                } else {                    const value = props.modelValue? false : true;                    ctx.emit('update:modelValue', value);                    ctx.emit('change', value);                }            }        };
        const computedDisabled = computed(() => {            var bool = false;            if (proxyMax || proxyMin) {                if (proxyMax && proxyValue.value.length >= proxyMax.value) {                    if (!computedChecked.value) {                        bool = true;                    }                } else if (proxyMin && proxyValue.value.length <= proxyMin.value) {                    if (computedChecked.value) {                        bool = true;                    }                }            }            return bool;        });
        const computedChecked = computed(() => {            if (proxyValue) { // 如果是被 checkbox-group 组件包裹着的话就判断数组中有没有符合的值                return proxyValue.value.find(item => item === props.label);            } else {                return props.modelValue;            }        });
        return {            handleChecked,            proxyDisabled,            computedChecked,            computedDisabled,            proxyValue,            proxySize        }    }}</script>
复制代码
 Checkbox.js
const VairCheckout = {    modelValue: null, // 绑定值    name: String, // 原生 name 属性    label: [Number, String, Boolean], // Checkbox 的 value    size: String, // Radio 的尺寸,仅在 border 为真时有效    indeterminate: { // 设置 indeterminate 状态,只负责样式控制        type: Boolean,        default: () => {            return false        }    },    border: { // 是否显示边框        type: Boolean,        default: () => {            return false        }    },    disabled: { // 是否禁用        type: Boolean,        default: () => {            return false        }    },};
// Event    // change (label)
export default VairCheckout;
复制代码
 Checkbox.vue Style
<style lang='less' scoped>@import url('../../assets/css/animation.css');.v-checkbox {    display: flex;    align-items: center;    cursor: pointer;    transition: .3s;    margin-right: 30px;    &:last-child {        margin-right: 0;    }    input {        display: none;    }    .v-radio-input {        border: 1px solid#dcdfe6;        width: 14px;        height: 14px;        position: relative;        box-sizing: border-box;        border-radius: 2px;        transition: .3s;        &:after {            box-sizing: content-box;            content: "";            border: 1px solid #fff;            border-left: 0;            border-top: 0;            height: 7px;            left: 4px;            position: absolute;            top: 1px;            transform: rotate(45deg) scaleY(0);            width: 3px;            transition: transform .15s ease-in .05s;            transform-origin: center;        }    }    .indeterminate {        background-color: #409eff;        border-color: #409eff;        position: relative;        &:after {            content: "";            position: absolute;            display: block;            background-color: #fff;            height: 2px;            transform: scale(.5);            left: 0;            right: 0;            width: 10px;            transition: width 0s;            top: 4px;        }    }    .v-radio-label {        color: #606266;        font-weight: 500;        margin-left: 10px;        font-size: 14px;        transition: .3s;        white-space: nowrap;        user-select: none;    }    .active {        color: #409eff;    }    .checked {        background-color: #409eff;        border-color: #409eff;        &:after {            transform: rotate(45deg) scaleY(1);        }    }    &:hover {        border-color: #409eff;        .v-radio-label {            color: #409eff;        }        .v-radio-input {            border-color: #409eff;        }    }}.border {    border: 1px solid #dcdfe6;    border-radius: 4px;    padding: 12px 14px;}.medium {    padding: 10px 10px;}.small {    padding: 8px 8px;}.mini {    padding: 6px 6px;}.v-radio-checked {    border-color: #409eff;}.disabled {    border-color: #dcdfe6;    .v-radio-input {        background-color: #F5F7FA;        border: 1px solid#dcdfe6;    }    .v-radio-label {        color: #c0c4cc;    }    .active {        color: #c0c4cc;    }    .checked {        background-color: #f2f6fc;        border-color: #dcdfe6;        &:after {            transform: rotate(45deg) scaleY(1);            border-color: #c0c4cc;        }    }    &:hover {        border-color: #dcdfe6;        .v-radio-label {            color: #c0c4cc;        }        .v-radio-input {            border-color: #c0c4cc;        }    }    cursor: not-allowed;}</style>
复制代码
 Checkbox-group.vue Template
<template>    <div class="v-radio-group">        <slot></slot>    </div></template>
复制代码
 Checkbox-group.vue Script
<script>import { provide, ref, watchEffect } from 'vue';export default {    name: 'checkbox-group',    props: {        modelValue: Array, // 绑定值        size: String, // 单选框组尺寸,仅对按钮形式的 Radio 或带有边框的 Radio 有效        min: Number, // 可被勾选的 checkbox 的最小数量        max: Number, // 可被勾选的 checkbox 的最大数量        textColor: { // 按钮形式的 Radio 激活时的文本颜色            type: String,            default: () => {                return '#FFFFFF';            }        },        fill: { // 按钮形式的 Radio 激活时的填充色            type: String,            default: () => {                return '#409EFF';            }        },        disabled: { // 是否禁用            type: Boolean,            default: () => {                return false            }        },    },    setup (props, ctx) {        const proxyValue = ref(props.modelValue);        const proxyDisabled = ref(props.disabled);        const proxyFill = ref(props.fill);        const proxyTextColor = ref(props.textColor);        const proxySize = ref(props.size);        const proxyMin = ref(props.min);        const proxyMax = ref(props.max);
        watchEffect(() => {            if (!Array.isArray(props.modelValue)) {                throw new TypeError(`v-model wants to receive an array, but received a ${typeof props.modelValue}`);            }            proxySize.value = props.size;            proxyTextColor.value = props.textColor;            proxyFill.value = props.fill;            proxyMin.value = props.min;            proxyMax.value = props.max;            proxyDisabled.value = props.disabled;            proxyValue.value = props.modelValue;        });
        const update = (value) => {            const bool = props.modelValue.find(item => item === value);            var list = [];            if (bool) {                props.modelValue.forEach(item => {                    if (item !== value) {                        list.push(item);                    }                });            } else {                list = [].concat(props.modelValue, [value]);            }            ctx.emit('update:modelValue', list);            ctx.emit('change', list);        };
        provide('proxyValue', proxyValue);        provide('proxyFill', proxyFill);        provide('proxyDisabled', proxyDisabled);        provide('proxyTextColor', proxyTextColor);        provide('proxySize', proxySize);        provide('proxyMax', proxyMax);        provide('proxyMin', proxyMin);        provide('update', update);        provide('border', true);    }}</script>
复制代码
 Checkbox-group.vue Style
<style lang='less' scoped>.v-radio-group {    display: flex;}</style>
复制代码
 Checkbox-button.vue Template
<template>    <div     :class='["v-radio-button",     { disabled: proxyDisabled || disabled || computedDisabled },     { medium: proxySize === "medium" },    { small: proxySize === "small" },    { mini: proxySize === "mini" },    { disabledChecked: computedChecked && (proxyDisabled || disabled || computedDisabled) },     { "v-radio-button-checked": computedChecked && !computedDisabled}, { border }]'     :style='{         backgroundColor: (computedChecked && !proxyDisabled && !disabled && !computedDisabled)? proxyFill : "",         color: (computedChecked && !proxyDisabled && !disabled && !computedDisabled)? proxyTextColor : ""    }'    @click.stop='handleChecked'>        <input type="checkbox" :checked='computedChecked' :disabled='disabled' :name='name || modelValue || proxyValue' :value='label'>        <span        :style='{ color: (computedChecked && !proxyDisabled && !disabled && !computedDisabled)? proxyTextColor : "" }'        ><slot></slot></span>    </div></template>
复制代码
 Checkbox-button.vue Script
<script>import { computed, inject } from 'vue';export default {    name: 'checkbox-button',    props: {        name: String, // 原生 name 属性        label: [Number, String, Boolean], // Radio 的 value        disabled: { // 是否禁用            type: Boolean,            default: () => {                return false            }        },    },    setup (props, ctx) {        const proxyValue = inject('proxyValue');        const proxyDisabled = inject('proxyDisabled');        const proxySize = inject('proxySize');        const proxyFill = inject('proxyFill');        const proxyTextColor = inject('proxyTextColor');        const proxyMin = inject('proxyMin');        const proxyMax = inject('proxyMax');        const update = inject('update');        const border = inject('border');
        const handleChecked = () => {            if (!computedChecked.value && !computedDisabled.value) {                ctx.emit('change', props.label);            }            if (!props.disabled && !(proxyDisabled && proxyDisabled.value) && !computedDisabled.value) {                update(props.label);            }        };
        const computedDisabled = computed(() => {            var bool = false;            if (proxyMax || proxyMin) {                if (proxyMax && proxyValue.value.length >= proxyMax.value) {                    if (!computedChecked.value) {                        bool = true;                    }                } else if (proxyMin && proxyValue.value.length <= proxyMin.value) {                    if (computedChecked.value) {                        bool = true;                    }                }            }            return bool;        });
        const computedChecked = computed(() => {            return proxyValue.value.find(item => item === props.label);        });
        return {            handleChecked,            proxyValue,            computedChecked,            proxyDisabled,            proxySize,            border,            proxyFill,            proxyTextColor,            computedDisabled        }    }}</script>
复制代码
 Checkbox-button.vue Style
<style lang='less' scoped>.v-radio-button {    display: inline-block;    padding: 12px 18px;    border-radius: 4px;    border: 1px solid #dcdfe6;    cursor: pointer;    transition: .3s;    input {        display: none;    }    span {        color: #606266;        font-weight: 500;        font-size: 14px;        white-space: nowrap;        transition: .3s;        user-select: none;    }    &:hover {        span {            color: #409eff;        }    }}.v-radio-button-checked {    background-color: #409eff;    span {        color: #fff;    }    &:hover {        span {            color: #fff;        }    }}.medium {    padding: 10px 16px;}.small {    padding: 8px 14px;}.mini {    padding: 6px 12px;}.disabled {    border-color: #dcdfe6;    background-color: #fff;    cursor: not-allowed;    span {        color: #c0c4cc;    }    &:hover {        span {            color: #c0c4cc;        }    }}.disabledChecked {    background-color: #f2f6fc;    span {        color: #c0c4cc;    }    &:hover {        span {            color: #c0c4cc;        }    }}.border {    border-radius: 0;    border-right: none;    &:first-child {        border-radius: 4px 0 0 4px;    }    &:last-child {        border-right: 1px solid #dcdfe6;        border-radius: 0 4px 4px 0;    }}</style>
复制代码
 index.js 出口文件中引入组件
// Checkbox 单选框import Checkbox from './components/Checkbox/Checkbox.vue';import CheckboxGroup from './components/Checkbox/components/Checkbox-group.vue';import CheckboxButton from './components/Checkbox/components/Checkbox-button.vue';
const Vair = function(Vue) {    // Checkbox 单选框    Vue.component(`v-${Checkbox.name}`, Checkbox);    Vue.component(`v-${CheckboxGroup.name}`, CheckboxGroup);    Vue.component(`v-${CheckboxButton.name}`, CheckboxButton);}
export default Vair;复制代码
复制代码
 使用组件
在main.js中引入
import { createApp } from 'vue'; import App from './App.vue'; import Vair from './libs/vair/index.js'; const app = createApp(App); app.use(Vair).mount('#app');
复制代码
 App.vue 中调用
<template>    <div class='checkbox'>        <div class='box'>            <p>基础用法</p>            <div class='son'>                <v-checkbox v-model="checked" @change='change'>上海</v-checkbox>            </div>        </div>
        <div class='box'>            <p>禁用状态</p>            <div class='son'>                <v-checkbox v-model="checked1" @change='change' disabled>上海</v-checkbox>                <v-checkbox v-model="checked2" @change='change' disabled>北京</v-checkbox>            </div>        </div>
        <div class='box'>            <p>多选框组</p>            <div class='son radio-group'>                <v-checkbox-group v-model="checked3" @change='change' class='radio-group'>                    <v-checkbox @change='change' label='上海'>上海</v-checkbox>                    <v-checkbox @change='change' label='北京'>北京</v-checkbox>                    <v-checkbox @change='change' label='广州'>广州</v-checkbox>                    <v-checkbox @change='change' label='深圳'>深圳</v-checkbox>                    <v-checkbox @change='change' label='耶路撒冷'>耶路撒冷</v-checkbox>                </v-checkbox-group>            </div>        </div>
        <div class='box'>            <p>indeterminate 状态</p>            <div class='son radio-group' style='display: block'>                <v-checkbox @change='handleCheckAllChange' :indeterminate='indeterminate' v-model="checkAll">全选</v-checkbox>                <div style="margin: 15px 0;"></div>                <v-checkbox-group v-model="checked4" @change='handleCheckedCitiesChange' class='radio-group'>                    <v-checkbox v-for='city in data' :key='city' :label='city' @change='change'>{{ city }}</v-checkbox>                </v-checkbox-group>            </div>        </div>
        <div class='box'>            <p>可选项目数量的限制</p>            <div class='son radio-group' style='display: block'>                <v-checkbox-group v-model="checked5" @change='change' :min='min' :max='max' class='radio-group'>                    <v-checkbox v-for='city in data1' :key='city' :label='city' @change='change'>{{ city }}</v-checkbox>                </v-checkbox-group>            </div>        </div>                <div class='box'>            <p>按钮样式</p>            <div class='son radio-group'>                <v-checkbox-group v-model="checked6" @change='change' class='radio-group'>                    <v-checkbox-button @change='change' label='上海'>上海</v-checkbox-button>                    <v-checkbox-button @change='change' label='北京'>北京</v-checkbox-button>                    <v-checkbox-button @change='change' label='广州'>广州</v-checkbox-button>                    <v-checkbox-button @change='change' label='深圳'>深圳</v-checkbox-button>                    <v-checkbox-button @change='change' label='耶路撒冷'>耶路撒冷</v-checkbox-button>                </v-checkbox-group>            </div>                        <div class='son radio-group'>                <v-checkbox-group v-model="checked8" @change='change' size='medium' class='radio-group'>                    <v-checkbox-button @change='change' label='上海'>上海</v-checkbox-button>                    <v-checkbox-button @change='change' label='北京'>北京</v-checkbox-button>                    <v-checkbox-button @change='change' label='广州'>广州</v-checkbox-button>                    <v-checkbox-button @change='change' label='深圳'>深圳</v-checkbox-button>                    <v-checkbox-button @change='change' label='耶路撒冷'>耶路撒冷</v-checkbox-button>                </v-checkbox-group>            </div>
            <div class='son radio-group'>                <v-checkbox-group v-model="checked9" @change='change' size='small' class='radio-group'>                    <v-checkbox-button @change='change' label='上海'>上海</v-checkbox-button>                    <v-checkbox-button @change='change' label='北京'>北京</v-checkbox-button>                    <v-checkbox-button @change='change' label='广州'>广州</v-checkbox-button>                    <v-checkbox-button @change='change' label='深圳'>深圳</v-checkbox-button>                    <v-checkbox-button @change='change' label='耶路撒冷'>耶路撒冷</v-checkbox-button>                </v-checkbox-group>            </div>
            <div class='son radio-group'>                <v-checkbox-group v-model="checked10" disabled @change='change' size='mini' class='radio-group'>                    <v-checkbox-button @change='change' label='上海'>上海</v-checkbox-button>                    <v-checkbox-button @change='change' label='北京'>北京</v-checkbox-button>                    <v-checkbox-button @change='change' label='广州'>广州</v-checkbox-button>                    <v-checkbox-button @change='change' label='深圳'>深圳</v-checkbox-button>                    <v-checkbox-button @change='change' label='耶路撒冷'>耶路撒冷</v-checkbox-button>                </v-checkbox-group>            </div>        </div>
        <div class='box'>            <p>带边框</p>            <div class='son'>                <v-checkbox v-model="checked7" @change='change' border>选项一</v-checkbox>                <v-checkbox v-model="checked7" @change='change' border>选项二</v-checkbox>            </div>
            <div class='son'>                <v-checkbox v-model="checked7" @change='change' size='medium' border>选项一</v-checkbox>                <v-checkbox v-model="checked7" @change='change' size='medium' border>选项二</v-checkbox>            </div>
            <div class='son'>                <v-checkbox v-model="checked7" @change='change' size='small' border>选项一</v-checkbox>                <v-checkbox v-model="checked7" @change='change' size='small' border>选项二</v-checkbox>            </div>
            <div class='son'>                <v-checkbox v-model="checked7" @change='change' size='mini' border>选项一</v-checkbox>                <v-checkbox v-model="checked7" @change='change' size='mini' border>选项二</v-checkbox>            </div>        </div>    </div></template>
<script>import { ref } from 'vue';export default {    setup () {        const min = ref(1);        const max = ref(3);        const checked = ref(true);        const checked1 = ref(false);        const checked2 = ref(true);        const checked3 = ref(['上海', '北京']);        const data = ref(['上海', '北京', '广州', '深圳']);        const checked4 = ref(['上海', '北京']);        const checkAll = ref(false);        const indeterminate = ref(true);        const data1 = ref(['上海', '北京', '广州', '深圳']);        const checked5 = ref(['上海', '北京']);        const checked6 = ref(['上海']);        const checked8 = ref(['上海']);        const checked9 = ref(['上海']);        const checked10 = ref(['上海']);        const checked7 = ref(true);        const change = (label) => {            console.log(label)        };
        const handleCheckAllChange = (val) => {            checked4.value = val? data.value : [];            indeterminate.value = false;        };                const handleCheckedCitiesChange = (value) => {            let checkedCount = value.length;            checkAll.value = checkedCount === data.value.length;            indeterminate.value = checkedCount > 0 && checkedCount < data.value.length;        };
        return {            checked,            checked1,            checked2,            checked3,            checked4,            checkAll,            data,            data1,            checked5,            checked6,            checked7,            checked8,            checked9,            checked10,            indeterminate,            handleCheckAllChange,            handleCheckedCitiesChange,            change,            min,            max        }    }}</script>
<style lang='less' scoped>.checkbox {    .box {        margin-bottom: 50px;        p {            margin-bottom: 20px;            font-size: 14px;        }        .son {            width: 200px;            display: flex;            margin-bottom: 10px;            justify-content: space-between;                   }        .radio-group {            margin-bottom: 10px;            width: 300px;        }    }}</style>
复制代码
 
 划线
评论
复制
发布于: 刚刚阅读数: 3
版权声明: 本文为 InfoQ 作者【anyRTC开发者】的原创文章。
原文链接:【http://xie.infoq.cn/article/25abef3d84e7154cfbb1595c7】。文章转载请联系作者。
anyRTC开发者
关注
实时交互,万物互联! 2020.08.10 加入
实时交互,万物互联,全球实时互动云服务商领跑者!










    
评论