cl-radio.uvue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. <template>
  2. <view
  3. class="cl-radio"
  4. :class="[
  5. {
  6. 'cl-radio--disabled': isDisabled,
  7. 'cl-radio--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-radio__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-radio__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 } from "vue";
  45. import type { PassThroughProps } from "../../types";
  46. import { get, parseClass, parsePt } from "@/cool";
  47. import type { ClIconProps } from "../cl-icon/props";
  48. import { useForm } from "../../hooks";
  49. defineOptions({
  50. name: "cl-radio"
  51. });
  52. // 定义组件属性
  53. const props = defineProps({
  54. // 透传样式配置
  55. pt: {
  56. type: Object,
  57. default: () => ({})
  58. },
  59. modelValue: {
  60. type: null
  61. },
  62. // 选中时的图标
  63. activeIcon: {
  64. type: String,
  65. default: "checkbox-circle-line"
  66. },
  67. // 未选中时的图标
  68. inactiveIcon: {
  69. type: String,
  70. default: "checkbox-blank-circle-line"
  71. },
  72. // 是否显示图标
  73. showIcon: {
  74. type: Boolean,
  75. default: true
  76. },
  77. // 标签文本
  78. label: {
  79. type: String,
  80. default: ""
  81. },
  82. // 选项值 - 该单选框对应的值
  83. value: {
  84. type: null
  85. },
  86. // 是否禁用
  87. disabled: {
  88. type: Boolean,
  89. default: false
  90. }
  91. });
  92. // 定义组件事件
  93. const emit = defineEmits(["update:modelValue", "change"]);
  94. const slots = useSlots();
  95. // 透传样式类型定义
  96. type PassThrough = {
  97. className?: string;
  98. icon?: ClIconProps;
  99. label?: PassThroughProps;
  100. };
  101. // 解析透传样式配置
  102. const pt = computed(() => parsePt<PassThrough>(props.pt));
  103. // cl-form 上下文
  104. const { disabled } = useForm();
  105. // 是否禁用
  106. const isDisabled = computed(() => props.disabled || disabled.value);
  107. // 是否为选中状态
  108. const isChecked = computed(() => props.modelValue == props.value);
  109. // 是否显示标签
  110. const showLabel = computed(() => props.label != "" || get(slots, "default") != null);
  111. // 图标名称
  112. const iconName = computed(() => {
  113. // 选中状态
  114. if (isChecked.value) {
  115. return props.activeIcon;
  116. }
  117. // 默认状态
  118. return props.inactiveIcon;
  119. });
  120. /**
  121. * 点击事件处理函数
  122. * 在非禁用状态下切换选中状态
  123. */
  124. function onTap() {
  125. if (!isDisabled.value) {
  126. emit("update:modelValue", props.value);
  127. emit("change", props.value);
  128. }
  129. }
  130. </script>
  131. <style lang="scss" scoped>
  132. .cl-radio {
  133. @apply flex flex-row items-center;
  134. &--disabled {
  135. @apply opacity-50;
  136. }
  137. }
  138. </style>