Files
MrleeAdmin/frontend/app/uni_modules/uview-pro/components/u-checkbox/u-checkbox.vue
T

364 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="u-checkbox" :style="$u.toStyle(checkboxStyle, customStyle)" :class="customClass">
<view class="u-checkbox__icon-wrap" @tap="toggle" :class="iconClass" :style="$u.toStyle(iconStyle)">
<u-icon name="checkbox-mark" :size="checkboxIconSize" :color="iconColor" />
</view>
<view
class="u-checkbox__label"
@tap="onClickLabel"
:style="{
fontSize: labelFontSize
}"
>
<slot>
{{ label }}
</slot>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'u-checkbox',
options: {
addGlobalClass: true,
// #ifndef MP-TOUTIAO
virtualHost: true,
// #endif
styleIsolation: 'shared'
}
};
</script>
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { $u, useChildren } from '../..';
import { CheckboxProps } from './types';
import type { SizeType } from '../../types/global';
import uIcon from '../u-icon/u-icon.vue';
/**
* checkbox 复选框
* @description 该组件需要搭配checkboxGroup组件使用,以便用户进行操作时,获得当前复选框组的选中情况。
* @tutorial https://uviewpro.cn/zh/components/checkbox.html
* @property {String Number} icon-size 图标大小,单位rpx(默认20)
* @property {String Number} label-size label字体大小,单位rpx(默认28)
* @property {String Number} name checkbox组件的标示符
* @property {String} shape 形状,见官网说明(默认circle)
* @property {Boolean} disabled 是否禁用
* @property {Boolean} label-disabled 是否禁止点击文本操作checkbox
* @property {String} active-color 选中时的颜色,如设置CheckboxGroup的active-color将失效
* @event {Function} change 某个checkbox状态发生变化时触发,回调为一个对象
* @example <u-checkbox v-model="checked" :disabled="false">天涯</u-checkbox>
*/
const props = defineProps(CheckboxProps);
const emit = defineEmits(['change', 'update:modelValue']);
// checkbox 是否选中,true/false
const checkedValue = ref(props.modelValue);
// 使用子组件Hook
const { parentExposed } = useChildren('u-checkbox', 'u-checkbox-group');
const { parentExposed: formExposed } = useChildren('u-checkbox', 'u-form');
// checkbox 的value值,id
const checkboxValue = computed(() => {
if (props.value !== '') return props.value;
return props.name;
});
watch(
() => props.modelValue,
(newVal: boolean) => {
checkedValue.value = newVal;
}
);
watch(
() => checkedValue.value,
(newVal: boolean) => {
emit('update:modelValue', newVal);
},
{ immediate: true }
);
// 根据 size 定义不同的配置
const sizeConfig = {
small: {
size: 28,
fontSize: 24,
iconSize: 16
},
default: {
size: 34,
fontSize: 28,
iconSize: 20
},
large: {
size: 40,
fontSize: 32,
iconSize: 24
}
};
// 获取实际使用的 size 值(优先级:props.size > u-form.size)
const actualSize = computed(() => {
// 优先使用 props 的 size 属性
if (props.size !== '') {
return String(props.size);
}
// 次优先:使用 u-checkbox-group 的 size 属性
if (parentExposed.value?.props?.size) {
return String(parentExposed.value.props.size);
}
// 最后:使用 u-form 的 size 属性(u-form 的 size 只支持预设值)
if (formExposed.value?.props?.size) {
return String(formExposed.value.props.size);
}
// 默认值
return 'default';
});
// 判断实际使用的 size 是否在预设配置中
const isInSizeConfig = computed(() => actualSize.value in sizeConfig);
// 获取预设 size(用于查找 sizeConfig 配置,如图标大小、高度等)
const presetSize = computed(() => {
return (isInSizeConfig.value ? actualSize.value : 'default') as SizeType;
});
// 获取当前尺寸配置
const currentSizeConfig = computed(() => sizeConfig[presetSize.value]);
// 是否禁用,如果父组件u-checkbox-group禁用的话,将会忽略子组件的配置
const isDisabled = computed(() => {
return props.disabled !== '' ? props.disabled : (parentExposed.value?.props?.disabled ?? false);
});
// 是否禁用label点击
const isLabelDisabled = computed(() => {
return props.labelDisabled !== '' ? props.labelDisabled : (parentExposed.value?.props?.labelDisabled ?? false);
});
// 组件尺寸,对应size的值,默认值为34rpx
const checkboxSize = computed(() => currentSizeConfig.value.size);
// 组件的勾选图标的尺寸,默认20rpx
const checkboxIconSize = computed(() => {
if (props.iconSize) {
return props.iconSize;
}
if (parentExposed.value?.props?.iconSize) {
return parentExposed.value?.props?.iconSize;
}
if (isInSizeConfig.value) {
return currentSizeConfig.value.iconSize;
}
return 20;
});
// label字体大小,默认28rpx
const labelFontSize = computed(() => {
if (isInSizeConfig.value) {
return $u.addUnit(currentSizeConfig.value.fontSize);
}
return $u.addUnit(props.labelSize);
});
// 组件选中激活时的颜色
const elActiveColor = computed(() => {
return props.activeColor ? props.activeColor : (parentExposed.value?.props?.activeColor ?? 'primary');
});
// 组件的形状
const elShape = computed(() => {
return props.shape ? props.shape : (parentExposed.value?.props?.shape ?? 'square');
});
// 图标样式
const iconStyle = computed(() => {
let style: Record<string, string> = {};
if (elActiveColor.value && checkedValue.value && !isDisabled.value) {
style.borderColor = elActiveColor.value;
style.backgroundColor = elActiveColor.value;
}
style.width = $u.addUnit(checkboxSize.value);
style.height = $u.addUnit(checkboxSize.value);
return style;
});
// checkbox内部的勾选图标,如果选中状态,为白色,否则为透明色即可
const iconColor = computed(() => {
return checkedValue.value ? 'var(--u-white-color)' : 'transparent';
});
const iconClass = computed(() => {
let classes: string[] = [];
classes.push('u-checkbox__icon-wrap--' + elShape.value);
if (checkedValue.value == true) classes.push('u-checkbox__icon-wrap--checked');
if (isDisabled.value) classes.push('u-checkbox__icon-wrap--disabled');
if (checkedValue.value && isDisabled.value) classes.push('u-checkbox__icon-wrap--disabled--checked');
return classes.join(' ');
});
const checkboxStyle = computed(() => {
let style: Record<string, string> = {};
if (parentExposed.value?.props?.width) {
style.width = parentExposed.value.props.width;
// #ifdef MP
style.float = 'left';
// #endif
// #ifndef MP
style.flex = `0 0 ${parentExposed.value.props.width}`;
// #endif
}
if (parentExposed.value?.props?.wrap) {
style.width = '100%';
// #ifndef MP
style.flex = '0 0 100%';
// #endif
}
return style;
});
/**
* 点击label
*/
function onClickLabel() {
if (!isLabelDisabled.value && !isDisabled.value) {
setValue();
}
}
/**
* 点击icon
*/
function toggle() {
if (!isDisabled.value) {
setValue();
}
}
/**
* 触发change事件
*/
function emitEvent() {
emit('change', !checkedValue.value);
// 通知父组件状态变化
setTimeout(() => {
if (parentExposed.value?.emitEvent) {
parentExposed.value.emitEvent();
}
}, 80);
}
/**
* 设置通过v-model绑定的组件的值
*/
function setValue() {
// 判断是否超过了可选的最大数量
if (checkedValue.value == true) {
emitEvent();
checkedValue.value = false;
} else {
if (parentExposed?.value?.validateSelection && !parentExposed?.value?.validateSelection()) {
return;
}
emitEvent();
checkedValue.value = true;
}
}
// 设置组件的modelValue值
function setChecked(data: any) {
if (!isDisabled.value) {
const needEmit = checkedValue.value !== data.checked;
checkedValue.value = data.checked;
if (needEmit) {
emitEvent();
}
}
}
defineExpose({
isChecked: computed(() => checkedValue.value),
label: props.label,
name: props.name,
value: checkboxValue.value,
setValue,
emitEvent,
props,
setChecked
});
</script>
<style lang="scss" scoped>
@import '../../libs/css/style.components.scss';
.u-checkbox {
/* #ifndef APP-NVUE */
display: inline-flex;
/* #endif */
align-items: center;
overflow: hidden;
user-select: none;
line-height: 1.8;
&__icon-wrap {
color: $u-content-color;
flex: none;
display: -webkit-flex;
@include vue-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 42rpx;
height: 42rpx;
color: transparent;
text-align: center;
transition-property: color, border-color, background-color;
font-size: 20px;
border: 1px solid var(--u-border-color);
transition-duration: 0.2s;
/* #ifdef MP-TOUTIAO */
// 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
line-height: 0;
/* #endif */
&--circle {
border-radius: 100%;
}
&--square {
border-radius: 6rpx;
}
&--checked {
color: var(--u-white-color);
background-color: $u-type-primary;
border-color: $u-type-primary;
}
&--disabled {
background-color: var(--u-bg-gray-light);
border-color: var(--u-border-color);
}
&--disabled--checked {
color: var(--u-bg-gray-light) !important;
}
}
&__label {
word-wrap: break-word;
margin-left: 10rpx;
margin-right: 24rpx;
color: $u-content-color;
font-size: 30rpx;
&--disabled {
color: var(--u-bg-gray-light);
}
}
}
</style>