Vue3多选框(Checkbox)

简介: 这是一个可高度定制的多选框组件,支持多种属性设置,如复选元素数据、是否禁用、垂直排列、当前选中值、间距、展示区域宽高及全选样式控制等。提供了在线预览和示例代码,便于快速集成与自定义。

可自定义设置以下属性:

  • 复选元素数据(options),类型:Array<{label: string, value: any, disabled?: boolean}>,默认 []

  • 是否禁用所有复选框(disabled),类型:boolean,默认 false

  • 是否垂直排列(vertical),类型:boolean,默认 false

  • 当前选中的值(v-model:value),类型:Array,默认 []

  • 多个单选框之间的间距(gap),类型:number,单位px,默认 8px,垂直排列时,间距即垂直间距

  • 复选区域最大展示宽度,超出后折行(width),类型:string | number,默认 'auto'

  • 复选区域最大展示高度,超出后滚动(height),类型:string | number,默认 'auto'

  • 全选时的样式控制(indeterminate),类型:boolean,默认 false

  • 是否全选(v-model:checked),类型:boolean,默认 false

效果如下图:在线预览

①创建多选框组件Checkbox.vue

<script setup lang="ts">
import { ref, computed, watchEffect } from 'vue'
interface Option {
  label: string // 选项名
  value: any // 选项值
  disabled?: boolean // 是否禁用选项
}
interface Props {
  options?: Array<Option> // 复选元素数据
  disabled?: boolean // 是否禁用所有复选框
  vertical?: boolean // 是否垂直排列
  value?: any[] // (v-model) 当前选中的值
  gap?: number // 多个单选框之间的间距,单位 px,垂直排列时,间距即垂直间距
  width?: string | number // 复选区域最大展示宽度,超出后折行
  height?: string | number // 复选区域最大展示高度,超出后滚动
  indeterminate?: boolean // 全选时的样式控制
  checked?: boolean // (v-model) 是否全选
}
const props = withDefaults(defineProps<Props>(), {
  options: () => [],
  disabled: false,
  vertical: false,
  value: () => [],
  gap: 8,
  width: 'auto',
  height: 'auto',
  indeterminate: false,
  checked: false
})
const sum = computed(() => {
  // 选项总数
  return props.options.length
})
const maxWidth = computed(() => {
  // 选项总数
  if (typeof props.width === 'number') {
    return props.width + 'px'
  } else {
    return props.width
  }
})
const maxHeight = computed(() => {
  // 选项总数
  if (typeof props.height === 'number') {
    return props.height + 'px'
  } else {
    return props.height
  }
})
const styleObject = computed(() => {
  if (props.vertical) {
    return {
      marginBottom: props.gap + 'px'
    }
  } else {
    return {
      marginRight: props.gap + 'px'
    }
  }
})
const checkedValue = ref<any[]>([])
watchEffect(() => {
  checkedValue.value = props.value
})
const emits = defineEmits(['update:value', 'update:checked', 'change'])
function onClick(value: any) {
  if (props.value.includes(value)) {
    // 已选中
    const newVal = checkedValue.value.filter((target) => target !== value)
    emits('update:value', newVal)
    emits('change', newVal)
  } else {
    // 未选中
    const newVal = [...checkedValue.value, value]
    emits('update:value', newVal)
    emits('change', newVal)
  }
}
function onCheckAll() {
  // 全选切换
  emits('update:checked', !props.checked)
}
</script>
<template>
  <div class="m-checkbox" :style="`max-width: ${maxWidth}; max-height: ${maxHeight};`">
    <template v-if="sum">
      <div
        class="m-checkbox-wrap"
        :class="{ 'checkbox-vertical': vertical }"
        :style="sum !== index + 1 ? styleObject : ''"
        v-for="(option, index) in options"
        :key="index"
      >
        <div
          class="m-checkbox-box"
          :class="{ 'checkbox-disabled': disabled || option.disabled }"
          @click="disabled || option.disabled ? () => false : onClick(option.value)"
        >
          <span class="checkbox-box" :class="{ 'checkbox-checked': checkedValue.includes(option.value) }"></span>
          <span class="checkbox-label">
            <slot :label="option.label">{
  
  { option.label }}</slot>
          </span>
        </div>
      </div>
    </template>
    <div v-else class="m-checkbox-wrap">
      <div class="m-checkbox-box" :class="{ 'checkbox-disabled': disabled }" @click="onCheckAll">
        <span
          class="checkbox-box"
          :class="{ 'checkbox-checked': checked && !indeterminate, indeterminate: indeterminate }"
        ></span>
        <span class="checkbox-label">
          <slot>Check all</slot>
        </span>
      </div>
    </div>
  </div>
</template>
<style lang="less" scoped>
.m-checkbox {
  display: inline-block;
  color: rgba(0, 0, 0, 0.88);
  font-size: 14px;
  line-height: 1;
  overflow: auto;
  .m-checkbox-wrap {
    display: inline-block;
    .m-checkbox-box {
      height: 100%;
      display: inline-flex; // 设置为flex布局后,所有的子元素都会变成行内块元素
      align-items: flex-start;
      cursor: pointer;
      &:hover {
        .checkbox-box {
          border-color: @themeColor;
        }
      }
      .checkbox-box {
        position: relative;
        /*
          如果所有项目的flex-shrink属性都为1,当空间不足时,都将等比例缩小
          如果一个项目的flex-shrink属性为0,其他项目都为1,则空间不足时,前者不缩小。
        */
        flex-shrink: 0; // 默认 1.即空间不足时,项目将缩小
        margin-top: 3px;
        width: 16px;
        height: 16px;
        background: transparent;
        border: 1px solid #d9d9d9;
        border-radius: 4px;
        transition: all 0.3s;
        &::after {
          box-sizing: border-box;
          position: absolute;
          top: 50%;
          inset-inline-start: 21.5%;
          display: table;
          width: 5.7142857142857135px;
          height: 9.142857142857142px;
          border: 2px solid #fff;
          border-top: 0;
          border-inline-start: 0;
          transform: rotate(45deg) scale(0) translate(-50%, -50%);
          opacity: 0;
          content: '';
          transition:
            all 0.1s cubic-bezier(0.71, -0.46, 0.88, 0.6),
            opacity 0.1s;
        }
      }
      .checkbox-checked {
        background-color: @themeColor;
        border-color: @themeColor;
        &::after {
          opacity: 1;
          transform: rotate(45deg) scale(1) translate(-50%, -50%);
          transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
        }
      }
      .indeterminate {
        &::after {
          top: 50%;
          left: 50%;
          width: 8px;
          height: 8px;
          background-color: @themeColor;
          border: 0;
          transform: translate(-50%, -50%) scale(1);
          opacity: 1;
        }
      }
      .checkbox-label {
        word-break: break-all;
        padding: 0 8px;
        font-size: 14px;
        line-height: 22px;
      }
    }
    .checkbox-disabled {
      color: rgba(0, 0, 0, 0.25);
      cursor: not-allowed;
      &:hover {
        .checkbox-box {
          border-color: #d9d9d9;
        }
      }
      .checkbox-box {
        border-color: #d9d9d9;
        background-color: rgba(0, 0, 0, 0.04);
        &::after {
          border-color: rgba(0, 0, 0, 0.25);
          animation-name: none;
        }
      }
    }
  }
  .vertical {
    display: block;
  }
}
</style>

②在要使用的页面引入:

<script setup lang="ts">
import Checkbox from './Checkbox.vue'
import { ref, watch, watchEffect, computed } from 'vue'

const options = ref([
      {
        label: '北京市',
        value: 1
      },
      {
        label: '上海市',
        value: 2,
        disabled: true
      },
      {
        label: '纽约市',
        value: 3
      },
      {
        label: '旧金山',
        value: 4
      },
      {
        label: '布宜诺斯艾利斯',
        value: 5
      },
      {
        label: '伊斯坦布尔',
        value: 6
      },
      {
        label: '拜占庭',
        value: 7
      },
      {
        label: '君士坦丁堡',
        value: 8
      }
    ])

const value = ref([2]) // 多选框v-model
watchEffect(() => {
  console.log('value:', value.value)
})
function onChange (value: any[]) {
  console.log('change:', value)
}

const checkAll = ref(false) // 全选v-model
const indeterminate = computed(() => { // 全选样式控制
  if (value.value.length > 0 && value.value.length < options.value.length) {
    return true
  } else {
    return false
  }
})
watch(checkAll, (to) => {
  console.log('checkAll:', to)
  if (to) {
    value.value = options.value.map(option => option.value)
  } else {
    value.value = []
  }
})
</script>
<template>
  <div>
    <h2 class="mb10">Checkbox 多选框基本使用</h2>
    <Checkbox
      :options="options"
      @change="onChange"
      v-model:value="value" />
    <h2 class="mt30 mb10">实现全选效果</h2>
    <Checkbox
      class="mb10"
      :indeterminate="indeterminate"
      v-model:checked="checkAll">
      Check All
    </Checkbox>
    <br/>
    <Checkbox
      :options="options"
      @change="onChange"
      v-model:value="value" />
    <h2 class="mt30 mb10">垂直排列</h2>
    <Checkbox
      :options="options"
      vertical
      @change="onChange"
      v-model:value="value" />
    <h2 class="mt30 mb10">自定义展示区域宽高</h2>
    <Checkbox
      :options="options"
      vertical
      :width="120"
      :height="160"
      @change="onChange"
      v-model:value="value" />
  </div>
</template>
相关文章
|
6月前
|
JavaScript 前端开发 安全
Vue 3
Vue 3以组合式API、Proxy响应式系统和全面TypeScript支持,重构前端开发范式。性能优化与生态协同并进,兼顾易用性与工程化,引领Web开发迈向高效、可维护的新纪元。(238字)
911 139
|
11月前
|
缓存 JavaScript PHP
斩获开发者口碑!SnowAdmin:基于 Vue3 的高颜值后台管理系统,3 步极速上手!
SnowAdmin 是一款基于 Vue3/TypeScript/Arco Design 的开源后台管理框架,以“清新优雅、开箱即用”为核心设计理念。提供角色权限精细化管理、多主题与暗黑模式切换、动态路由与页面缓存等功能,支持代码规范自动化校验及丰富组件库。通过模块化设计与前沿技术栈(Vite5/Pinia),显著提升开发效率,适合团队协作与长期维护。项目地址:[GitHub](https://github.com/WANG-Fan0912/SnowAdmin)。
1273 5
|
6月前
|
缓存 JavaScript 算法
Vue 3性能优化
Vue 3 通过 Proxy 和编译优化提升性能,但仍需遵循最佳实践。合理使用 v-if、key、computed,避免深度监听,利用懒加载与虚拟列表,结合打包优化,方可充分发挥其性能优势。(239字)
503 1
|
7月前
|
开发工具 iOS开发 MacOS
基于Vite7.1+Vue3+Pinia3+ArcoDesign网页版webos后台模板
最新版研发vite7+vue3.5+pinia3+arco-design仿macos/windows风格网页版OS系统Vite-Vue3-WebOS。
811 11
|
6月前
|
JavaScript 安全
vue3使用ts传参教程
Vue 3结合TypeScript实现组件传参,提升类型安全与开发效率。涵盖Props、Emits、v-model双向绑定及useAttrs透传属性,建议明确声明类型,保障代码质量。
569 0
|
8月前
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
867 1
|
8月前
|
缓存 JavaScript UED
除了循环引用,Vue3还有哪些常见的性能优化技巧?
除了循环引用,Vue3还有哪些常见的性能优化技巧?
461 0
|
9月前
|
JavaScript
vue3循环引用自已实现
当渲染大量数据列表时,使用虚拟列表只渲染可视区域的内容,显著减少 DOM 节点数量。
212 0
|
11月前
|
JavaScript API 容器
Vue 3 中的 nextTick 使用详解与实战案例
Vue 3 中的 nextTick 使用详解与实战案例 在 Vue 3 的日常开发中,我们经常需要在数据变化后等待 DOM 更新完成再执行某些操作。此时,nextTick 就成了一个不可或缺的工具。本文将介绍 nextTick 的基本用法,并通过三个实战案例,展示它在表单验证、弹窗动画、自动聚焦等场景中的实际应用。
1089 17
|
11月前
|
JavaScript 前端开发 API
Vue 2 与 Vue 3 的区别:深度对比与迁移指南
Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架,在过去的几年里,Vue 2 一直是前端开发中的重要工具。而 Vue 3 作为其升级版本,带来了许多显著的改进和新特性。在本文中,我们将深入比较 Vue 2 和 Vue 3 的主要区别,帮助开发者更好地理解这两个版本之间的变化,并提供迁移建议。 1. Vue 3 的新特性概述 Vue 3 引入了许多新特性,使得开发体验更加流畅、灵活。以下是 Vue 3 的一些关键改进: 1.1 Composition API Composition API 是 Vue 3 的核心新特性之一。它改变了 Vue 组件的代码结构,使得逻辑组
2262 0