cl-radio.uvue 2.8 KB

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