写点什么

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
用户头像

实时交互,万物互联! 2020.08.10 加入

实时交互,万物互联,全球实时互动云服务商领跑者!

评论

发布
暂无评论
web技术支持| 基于vue3实现自己的组件库第三章:Checkbox组件_Vue_anyRTC开发者_InfoQ写作社区