2025-07-21 16:47:04 +08:00
|
|
|
<template>
|
|
|
|
|
<view
|
|
|
|
|
class="cl-checkbox"
|
|
|
|
|
:class="[
|
|
|
|
|
{
|
2025-08-06 16:30:49 +08:00
|
|
|
'cl-checkbox--disabled': isDisabled,
|
2025-07-21 16:47:04 +08:00
|
|
|
'cl-checkbox--checked': isChecked
|
|
|
|
|
},
|
|
|
|
|
pt.className
|
|
|
|
|
]"
|
|
|
|
|
@tap="onTap"
|
|
|
|
|
>
|
|
|
|
|
<cl-icon
|
|
|
|
|
v-if="showIcon"
|
|
|
|
|
:name="iconName"
|
|
|
|
|
:size="pt.icon?.size ?? 40"
|
|
|
|
|
:pt="{
|
|
|
|
|
className: parseClass([
|
|
|
|
|
'cl-checkbox__icon mr-1',
|
|
|
|
|
{
|
|
|
|
|
'!text-primary-500': isChecked
|
|
|
|
|
},
|
|
|
|
|
pt.icon?.className
|
|
|
|
|
])
|
|
|
|
|
}"
|
|
|
|
|
></cl-icon>
|
|
|
|
|
|
|
|
|
|
<cl-text
|
|
|
|
|
:pt="{
|
|
|
|
|
className: parseClass([
|
|
|
|
|
'cl-checkbox__label',
|
|
|
|
|
{
|
|
|
|
|
'!text-primary-500': isChecked
|
|
|
|
|
},
|
|
|
|
|
pt.label?.className
|
|
|
|
|
])
|
|
|
|
|
}"
|
|
|
|
|
v-if="showLabel"
|
|
|
|
|
>
|
|
|
|
|
<slot>{{ label }}</slot>
|
|
|
|
|
</cl-text>
|
|
|
|
|
</view>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
import { computed, useSlots, type PropType } from "vue";
|
|
|
|
|
import type { PassThroughProps } from "../../types";
|
|
|
|
|
import { get, parseClass, parsePt, pull } from "@/cool";
|
|
|
|
|
import type { ClIconProps } from "../cl-icon/props";
|
2025-08-06 16:30:49 +08:00
|
|
|
import { useForm } from "../../hooks";
|
2025-07-21 16:47:04 +08:00
|
|
|
|
|
|
|
|
defineOptions({
|
|
|
|
|
name: "cl-checkbox"
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 定义组件属性
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
// 透传样式配置
|
|
|
|
|
pt: {
|
|
|
|
|
type: Object,
|
|
|
|
|
default: () => ({})
|
|
|
|
|
},
|
|
|
|
|
// 绑定值 - 当前选中的值
|
|
|
|
|
modelValue: {
|
|
|
|
|
type: [Array, Boolean] as PropType<any[] | boolean>,
|
|
|
|
|
default: () => []
|
|
|
|
|
},
|
|
|
|
|
// 标签文本
|
|
|
|
|
label: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: ""
|
|
|
|
|
},
|
|
|
|
|
// 选项值 - 该单选框对应的值
|
|
|
|
|
value: {
|
|
|
|
|
type: null
|
|
|
|
|
},
|
|
|
|
|
// 是否禁用
|
|
|
|
|
disabled: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: false
|
|
|
|
|
},
|
|
|
|
|
// 选中时的图标
|
|
|
|
|
activeIcon: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: "checkbox-line"
|
|
|
|
|
},
|
|
|
|
|
// 未选中时的图标
|
|
|
|
|
inactiveIcon: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: "checkbox-blank-line"
|
|
|
|
|
},
|
|
|
|
|
// 是否显示图标
|
|
|
|
|
showIcon: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: true
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 定义组件事件
|
|
|
|
|
const emit = defineEmits(["update:modelValue", "change"]);
|
|
|
|
|
|
|
|
|
|
const slots = useSlots();
|
2025-08-06 16:30:49 +08:00
|
|
|
const { disabled } = useForm();
|
|
|
|
|
|
|
|
|
|
// 是否禁用
|
|
|
|
|
const isDisabled = computed(() => props.disabled || disabled.value);
|
2025-07-21 16:47:04 +08:00
|
|
|
|
|
|
|
|
// 透传样式类型定义
|
|
|
|
|
type PassThrough = {
|
|
|
|
|
className?: string;
|
|
|
|
|
icon?: ClIconProps;
|
|
|
|
|
label?: PassThroughProps;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 解析透传样式配置
|
|
|
|
|
const pt = computed(() => parsePt<PassThrough>(props.pt));
|
|
|
|
|
|
|
|
|
|
// 是否为选中状态
|
|
|
|
|
const isChecked = computed(() => {
|
|
|
|
|
if (Array.isArray(props.modelValue)) {
|
|
|
|
|
return props.modelValue.includes(props.value!);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof props.modelValue == "boolean") {
|
|
|
|
|
return props.modelValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// 是否显示标签
|
|
|
|
|
const showLabel = computed(() => props.label != "" || get(slots, "default") != null);
|
|
|
|
|
|
|
|
|
|
// 图标名称
|
|
|
|
|
const iconName = computed(() => {
|
|
|
|
|
// 选中状态
|
|
|
|
|
if (isChecked.value) {
|
|
|
|
|
return props.activeIcon;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 默认状态
|
|
|
|
|
return props.inactiveIcon;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 点击事件处理函数
|
|
|
|
|
* 在非禁用状态下切换选中状态
|
|
|
|
|
*/
|
|
|
|
|
function onTap() {
|
2025-08-06 16:30:49 +08:00
|
|
|
if (!isDisabled.value) {
|
2025-07-21 16:47:04 +08:00
|
|
|
let val = props.modelValue;
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
|
if (isChecked.value) {
|
|
|
|
|
val = pull(val, props.value!);
|
|
|
|
|
} else {
|
|
|
|
|
val.push(props.value!);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
val = !val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
emit("update:modelValue", val);
|
|
|
|
|
emit("change", val);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.cl-checkbox {
|
|
|
|
|
@apply flex flex-row items-center;
|
|
|
|
|
|
|
|
|
|
&--disabled {
|
2025-08-06 16:30:49 +08:00
|
|
|
@apply opacity-50;
|
2025-07-21 16:47:04 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|