cl-checkbox.uvue 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <template>
  2. <view
  3. class="cl-checkbox"
  4. :class="[
  5. {
  6. 'cl-checkbox--disabled': isDisabled,
  7. 'cl-checkbox--checked': isChecked
  8. },
  9. pt.className
  10. ]"
  11. @tap="onTap"
  12. >
  13. <cl-icon
  14. v-if="showIcon"
  15. :name="iconName"
  16. :size="pt.icon?.size ?? 20"
  17. :pt="{
  18. className: parseClass([
  19. 'cl-checkbox__icon mr-1',
  20. {
  21. 'text-primary-500': isChecked
  22. },
  23. pt.icon?.className
  24. ])
  25. }"
  26. ></cl-icon>
  27. <cl-text
  28. :size="pt.label?.size"
  29. :color="pt.label?.color"
  30. :pt="{
  31. className: parseClass([
  32. 'cl-checkbox__label',
  33. {
  34. 'text-primary-500': isChecked
  35. },
  36. pt.label?.className
  37. ])
  38. }"
  39. v-if="showLabel"
  40. >
  41. <slot>{{ label }}</slot>
  42. </cl-text>
  43. </view>
  44. </template>
  45. <script lang="ts" setup>
  46. import { computed, useSlots, type PropType } from "vue";
  47. import { get, parseClass, parsePt, pull } from "@/.cool";
  48. import type { ClIconProps } from "../cl-icon/props";
  49. import { useForm } from "../../hooks";
  50. import type { ClTextProps } from "../cl-text/props";
  51. defineOptions({
  52. name: "cl-checkbox"
  53. });
  54. // 定义组件属性
  55. const props = defineProps({
  56. // 透传样式配置
  57. pt: {
  58. type: Object,
  59. default: () => ({})
  60. },
  61. // 绑定值 - 当前选中的值
  62. modelValue: {
  63. type: [Array, Boolean] as PropType<any[] | boolean>,
  64. default: () => []
  65. },
  66. // 标签文本
  67. label: {
  68. type: String,
  69. default: ""
  70. },
  71. // 选项值 - 该单选框对应的值
  72. value: {
  73. type: null
  74. },
  75. // 是否禁用
  76. disabled: {
  77. type: Boolean,
  78. default: false
  79. },
  80. // 选中时的图标
  81. activeIcon: {
  82. type: String,
  83. default: "checkbox-line"
  84. },
  85. // 未选中时的图标
  86. inactiveIcon: {
  87. type: String,
  88. default: "checkbox-blank-line"
  89. },
  90. // 是否显示图标
  91. showIcon: {
  92. type: Boolean,
  93. default: true
  94. }
  95. });
  96. // 定义组件事件
  97. const emit = defineEmits(["update:modelValue", "change"]);
  98. const slots = useSlots();
  99. const { disabled } = useForm();
  100. // 是否禁用
  101. const isDisabled = computed(() => props.disabled || disabled.value);
  102. // 透传样式类型定义
  103. type PassThrough = {
  104. className?: string;
  105. icon?: ClIconProps;
  106. label?: ClTextProps;
  107. };
  108. // 解析透传样式配置
  109. const pt = computed(() => parsePt<PassThrough>(props.pt));
  110. // 是否为选中状态
  111. const isChecked = computed(() => {
  112. if (Array.isArray(props.modelValue)) {
  113. return props.modelValue.includes(props.value!);
  114. }
  115. if (typeof props.modelValue == "boolean") {
  116. return props.modelValue;
  117. }
  118. return false;
  119. });
  120. // 是否显示标签
  121. const showLabel = computed(() => props.label != "" || get(slots, "default") != null);
  122. // 图标名称
  123. const iconName = computed(() => {
  124. // 选中状态
  125. if (isChecked.value) {
  126. return props.activeIcon;
  127. }
  128. // 默认状态
  129. return props.inactiveIcon;
  130. });
  131. /**
  132. * 点击事件处理函数
  133. * 在非禁用状态下切换选中状态
  134. */
  135. function onTap() {
  136. if (!isDisabled.value) {
  137. let val = props.modelValue;
  138. if (Array.isArray(val)) {
  139. if (isChecked.value) {
  140. val = pull(val, props.value!);
  141. } else {
  142. val.push(props.value!);
  143. }
  144. } else {
  145. val = !val;
  146. }
  147. emit("update:modelValue", val);
  148. emit("change", val);
  149. }
  150. }
  151. </script>
  152. <style lang="scss" scoped>
  153. .cl-checkbox {
  154. @apply flex flex-row items-center;
  155. &--disabled {
  156. @apply opacity-50;
  157. }
  158. }
  159. </style>