cl-checkbox.uvue 3.0 KB

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