選框(Checkbox))
可自定義設(shè)置以下屬性復(fù)選框選項(xiàng)數(shù)據(jù)options類型 Option[]默認(rèn) []是否禁用disabled類型boolean默認(rèn) false是否垂直排列vertical類型boolean默認(rèn) false當(dāng)前選中的值v-model:value類型(string | number)[]默認(rèn) []多個(gè)復(fù)選框之間的間距gap類型number | number[]單位px默認(rèn) 8垂直排列時(shí)為垂直間距數(shù)組間距用于水平排列折行時(shí)[水平間距, 垂直間距]全選時(shí)的樣式控制indeterminate類型boolean默認(rèn) false當(dāng)前是否選中v-model:checked類型boolean默認(rèn) false效果如下圖在線預(yù)覽①創(chuàng)建多選框組件Checkbox.vue其中引入使用了以下工具函數(shù)監(jiān)聽插槽存在 useSlotsExist獲取依賴注入 useInjectscript setup langts import { ref, computed, watchEffect, nextTick } from vue import { useSlotsExist, useInject } from components/utils export interface Option { label: string // 選項(xiàng)名 value: string | number // 選項(xiàng)值 disabled?: boolean // 是否禁用選項(xiàng) } export interface Props { options?: Option[] // 復(fù)選框選項(xiàng)數(shù)據(jù) disabled?: boolean // 是否禁用 vertical?: boolean // 是否垂直排列 value?: (string | number)[] // (v-model) 當(dāng)前選中的值配合 options 使用 gap?: number | number[] // 多個(gè)復(fù)選框之間的間距垂直排列時(shí)為垂直間距單位 px數(shù)組間距用于水平排列折行時(shí)[水平間距, 垂直間距] indeterminate?: boolean // 全選時(shí)的樣式控制 checked?: boolean // (v-model) 當(dāng)前是否選中 } const props withDefaults(definePropsProps(), { options: () [], disabled: false, vertical: false, value: () [], gap: 8, indeterminate: false, checked: false }) const checkboxChecked refboolean(false) // v-model:checked 是否選中標(biāo)志 const optionsCheckedValue ref(string | number)[]([]) // v-model:value 已選中的選項(xiàng)值 const wave refboolean(false) // 使用 v-model:checked 時(shí)的復(fù)選框動(dòng)畫選中效果標(biāo)志 const waveOptionsValue ref(string | number)[]([]) // 使用 v-model:value 時(shí)的復(fù)選框動(dòng)畫選中效果標(biāo)志 const { colorPalettes } useInject(Checkbox) // 主題色注入 const emits defineEmits([update:value, update:checked, change]) const slotsExist useSlotsExist([default]) // 選項(xiàng)總數(shù) const optionsAmount computed(() { return props.options.length }) const gapValue computed(() { if (!props.vertical Array.isArray(props.gap)) { return ${props.gap[1]}px ${props.gap[0]}px } return ${props.gap}px }) watchEffect(() { checkboxChecked.value props.checked }) watchEffect(() { optionsCheckedValue.value props.value }) function checkDisabled(disabled: boolean | undefined): boolean { if (disabled undefined) { return props.disabled } else { return disabled } } function onChecked(): void { startWave() checkboxChecked.value !checkboxChecked.value emits(update:checked, checkboxChecked.value) emits(change, checkboxChecked.value) } function onClick(value: string | number): void { startOptionWave(value) if (optionsCheckedValue.value.includes(value)) { // 已選中 const newVal optionsCheckedValue.value.filter((target: string | number) target ! value) optionsCheckedValue.value newVal emits(update:value, newVal) emits(change, newVal) } else { // 未選中 const newVal [...optionsCheckedValue.value, value] optionsCheckedValue.value newVal emits(update:value, newVal) emits(change, newVal) } } function startWave(): void { if (wave.value) { wave.value false nextTick(() { wave.value true }) } else { wave.value true } } function onWaveEnd(): void { wave.value false } function startOptionWave(value: string | number): void { if (waveOptionsValue.value.includes(value)) { waveOptionsValue.value waveOptionsValue.value.filter((optionValue: string | number) optionValue ! value) nextTick(() { waveOptionsValue.value.push(value) }) } else { waveOptionsValue.value.push(value) } } function onWaveOptionEnd(value: string | number): void { waveOptionsValue.value waveOptionsValue.value.filter((optionValue: string | number) optionValue ! value) } /script template div v-ifoptionsAmount classcheckbox-wrap :class{ checkbox-vertical: vertical } :style--checkbox-gap: ${gapValue}; --checkbox-primary-color: ${colorPalettes[5]}; v-bind$attrs div classcheckbox-container :class{ checkbox-disabled: checkDisabled(option.disabled) } v-for(option, index) in options :keyindex clickcheckDisabled(option.disabled) ? () false : onClick(option.value) span classcheckbox-box :class{ checkbox-checked: optionsCheckedValue.includes(option.value) } span v-if!checkDisabled(option.disabled) classcheckbox-wave :class{ wave-active: waveOptionsValue.includes(option.value) } animationendonWaveOptionEnd(option.value) /span /span span classcheckbox-label slot :optionoption :labeloption.label :indexindex{{ option.label }}/slot /span /div /div div v-else classcheckbox-container :class{ checkbox-disabled: disabled } :style--checkbox-primary-color: ${colorPalettes[5]}; clickdisabled ? () false : onChecked() v-bind$attrs span classcheckbox-box :class{ checkbox-checked: checkboxChecked !indeterminate, checkbox-indeterminate: indeterminate } span v-if!disabled classcheckbox-wave :class{ wave-active: wave } animationendonWaveEnd/span /span span v-ifslotsExist.default classcheckbox-label slot/slot /span /div /template style langless scoped .checkbox-wrap { display: inline-flex; flex-wrap: wrap; gap: var(--checkbox-gap); } .checkbox-vertical { flex-direction: column; flex-wrap: nowrap; } .checkbox-container { display: inline-flex; align-items: baseline; cursor: pointer; color: rgba(0, 0, 0, 0.88); font-size: 14px; line-height: 1.5714285714285714; :not(.checkbox-disabled):hover { .checkbox-box { border-color: var(--checkbox-primary-color); } } .checkbox-box { /* 如果所有項(xiàng)目的flex-shrink屬性都為1當(dāng)空間不足時(shí)都將等比例縮小 如果一個(gè)項(xiàng)目的flex-shrink屬性為0其他項(xiàng)目都為1則空間不足時(shí)前者不縮小。 */ flex-shrink: 0; // 默認(rèn) 1.即空間不足時(shí)項(xiàng)目將縮小 align-self: center; position: relative; width: 16px; height: 16px; background: transparent; border: 1px solid #d9d9d9; border-radius: 4px; transition: all 0.3s; ::after { position: absolute; top: 50%; left: 21.5%; display: table; width: 5.7142857142857135px; height: 9.142857142857142px; border: 2px solid #fff; border-top: 0; border-left: 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-wave { position: absolute; pointer-events: none; top: 0; right: 0; bottom: 0; left: 0; animation-iteration-count: 1; animation-duration: 0.6s; animation-timing-function: cubic-bezier(0, 0, 0.2, 1), cubic-bezier(0, 0, 0.2, 1); border-radius: inherit; } .wave-active { z-index: 1; animation-name: waveSpread, waveOpacity; keyframes waveSpread { from { box-shadow: 0 0 0.5px 0 var(--checkbox-primary-color); } to { box-shadow: 0 0 0.5px 5px var(--checkbox-primary-color); } } keyframes waveOpacity { from { opacity: 0.6; } to { opacity: 0; } } } } .checkbox-checked { background-color: var(--checkbox-primary-color); border-color: var(--checkbox-primary-color); ::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; } } .checkbox-indeterminate { ::after { top: 50%; left: 50%; width: 8px; height: 8px; background-color: var(--checkbox-primary-color); border: 0; transform: translate(-50%, -50%) scale(1); opacity: 1; } } .checkbox-label { word-break: break-all; padding: 0 8px; } } .checkbox-disabled { color: rgba(0, 0, 0, 0.25); cursor: not-allowed; .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; } } } /style②在要使用的頁面引入其中引入使用了以下組件Vue3間距SpaceVue3彈性布局FlexVue3柵格GridVue3滑動(dòng)輸入條SliderVue3顏色選擇器ColorPickerscript setup langts import Checkbox from ./Checkbox.vue import { ref, watch, watchEffect } from vue import type { CheckboxOption } from vue-amazing-ui const options refCheckboxOption[]([ { label: 北京市, value: 1 }, { label: 紐約市, value: 2 }, { label: 布宜諾斯艾利斯, value: 3 }, { label: 伊斯坦布爾, value: 4 }, { label: 拜占庭, value: 5 }, { label: 君士坦丁堡, value: 6 } ]) const optionsDisabled refCheckboxOption[]([ { label: 北京市, value: 1 }, { label: 紐約市, value: 2, disabled: true }, { label: 布宜諾斯艾利斯, value: 3 }, { label: 伊斯坦布爾, value: 4 }, { label: 拜占庭, value: 5 }, { label: 君士坦丁堡, value: 6 } ]) const checked refboolean(false) const selectedOptions ref(string | number)[]([2]) watchEffect(() { console.log(checked, checked.value) }) watchEffect(() { console.log(selectedOptions, selectedOptions.value) }) function onChange(value: boolean | (string | number)[]) { console.log(change, value) } const checkAll refboolean(false) // 是否全選 const indeterminate refboolean(false) // 全選樣式控制 watch( selectedOptions, (to: any) { indeterminate.value 0 to.length to.length options.value.length checkAll.value to.length options.value.length }, { immediate: true } ) const horizontalGap ref(16) const verticalGap ref(8) function onCheckAllChange(checked: boolean) { if (checked) { selectedOptions.value options.value.map((option) option.value) } else { selectedOptions.value [] } } /script template div h1{{ $route.name }} {{ $route.meta.title }}/h1 h2 classmt30 mb10基本使用/h2 Checkbox v-model:checkedchecked changeonChangeCheckbox/Checkbox h2 classmt30 mb10選項(xiàng)列表/h2 Checkbox :optionsoptions v-model:valueselectedOptions changeonChange / h2 classmt30 mb10禁用/h2 Checkbox :optionsoptions v-model:valueselectedOptions disabled / h2 classmt30 mb10禁用選項(xiàng)/h2 Checkbox :optionsoptionsDisabled v-model:valueselectedOptions / h2 classmt30 mb10全選/h2 Space vertical Checkbox :indeterminateindeterminate v-model:checkedcheckAll changeonCheckAllChange Check All/Checkbox Checkbox :optionsoptions v-model:valueselectedOptions / /Space h2 classmt30 mb10垂直排列/h2 Checkbox :optionsoptions vertical v-model:valueselectedOptions / h2 classmt30 mb10自定義選項(xiàng)名/h2 Checkbox :optionsoptions v-model:valueselectedOptions template #default{ option, label, index } span v-ifindex 1 stylecolor: #ff6900{{ label }}/span span v-ifindex 3 stylecolor: #1677ff{{ option.label }}/span /template /Checkbox h2 classmt30 mb10自定義間距/h2 Flex vertical Row :gutter24 Col :span12 Flex gapsmall vertical horizontal gap: Slider v-model:valuehorizontalGap / /Flex /Col Col :span12 Flex gapsmall vertical vertical gap: Slider v-model:valueverticalGap / /Flex /Col /Row Checkbox :gap[horizontalGap, verticalGap] :optionsoptions v-model:valueselectedOptions / /Flex /div /template