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 加入
实时交互,万物互联,全球实时互动云服务商领跑者!










评论