su-radio.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. <template>
  2. <view
  3. class="ui-radio ss-flex ss-col-center"
  4. @tap="onRaido"
  5. :class="[{ disabled: disabled }, { img: src }, ui]"
  6. :style="[customStyle]"
  7. >
  8. <slot name="leftLabel"></slot>
  9. <view
  10. v-if="!none"
  11. class="ui-radio-input"
  12. :class="[isChecked ? 'cur ' + bg : unbg, src ? 'radius' : 'round']"
  13. ></view>
  14. <image class="ui-radio-img radius" v-if="src" :src="src" mode="aspectFill"></image>
  15. <view class="ui-radio-content" v-else>
  16. <slot>
  17. <view class="ui-label-text" :style="[labelStyle]">{{ label }}</view>
  18. </slot>
  19. </view>
  20. <view
  21. v-if="ui.includes('card')"
  22. class="ui-radio-bg round"
  23. :class="[isChecked ? 'cur ' + bg : '']"
  24. ></view>
  25. </view>
  26. </template>
  27. <script setup>
  28. /**
  29. * 单选 - radio
  30. *
  31. *
  32. * property {Object} customStyle - 自定义样式
  33. * property {String} ui - radio样式Class
  34. * property {String} modelValue - 绑定值
  35. * property {Boolean} disabled - 是否禁用
  36. * property {String} bg - 选中时背景Class
  37. * property {String} unbg - 未选中时背景Class
  38. * property {String} src - 图片选中radio
  39. * property {String} label - label文本
  40. * property {Boolean} none - 是否隐藏raido按钮
  41. *
  42. * @slot default - 自定义label样式
  43. * @event {Function} change - change事件
  44. *
  45. */
  46. import { computed, reactive, watchPostEffect, getCurrentInstance } from 'vue';
  47. const vm = getCurrentInstance();
  48. // 组件数据
  49. const state = reactive({
  50. currentValue: false,
  51. });
  52. // 定义事件
  53. const emits = defineEmits(['change', 'update:modelValue']);
  54. // 接收参数
  55. const props = defineProps({
  56. customStyle: {
  57. type: Object,
  58. default: () => ({}),
  59. },
  60. ui: {
  61. type: String,
  62. default: 'check', //check line
  63. },
  64. modelValue: {
  65. type: [String, Number, Boolean],
  66. default: false,
  67. },
  68. disabled: {
  69. type: Boolean,
  70. default: false,
  71. },
  72. bg: {
  73. type: String,
  74. default: 'ui-BG-Main',
  75. },
  76. unbg: {
  77. type: String,
  78. default: 'borderss',
  79. },
  80. src: {
  81. type: String,
  82. default: '',
  83. },
  84. label: {
  85. type: String,
  86. default: '',
  87. },
  88. labelStyle: {
  89. type: Object,
  90. default: () => ({}),
  91. },
  92. none: {
  93. type: Boolean,
  94. default: false,
  95. },
  96. });
  97. watchPostEffect(() => {
  98. state.currentValue = props.modelValue;
  99. emits('update:modelValue', state.currentValue);
  100. });
  101. // 是否选中
  102. const isChecked = computed(() => state.currentValue);
  103. // 点击
  104. const onRaido = () => {
  105. if (props.disabled) return;
  106. state.currentValue = !state.currentValue;
  107. emits('update:modelValue', state.currentValue);
  108. emits('change', {
  109. label: props.label,
  110. value: state.currentValue,
  111. });
  112. };
  113. </script>
  114. <style lang="scss" scoped>
  115. .ui-radio {
  116. display: flex;
  117. align-items: center;
  118. margin: 0 0.5em 0 0;
  119. height: 18px;
  120. .ui-radio-input {
  121. margin: 0 0.5em 0 0;
  122. display: inline-block;
  123. width: 18px;
  124. height: 18px;
  125. vertical-align: middle;
  126. line-height: 18px;
  127. &::before {
  128. content: '';
  129. position: absolute;
  130. width: 0;
  131. height: 0;
  132. background-color: currentColor;
  133. border-radius: 18px;
  134. @include position-center;
  135. }
  136. }
  137. .ui-radio-input.cur {
  138. position: relative;
  139. &::before {
  140. width: 10px;
  141. height: 10px;
  142. transition: $transition-base;
  143. }
  144. }
  145. &:last-child {
  146. margin: 0 0.14286em;
  147. }
  148. &.check {
  149. .ui-radio-input {
  150. &::before {
  151. font-family: 'colorui';
  152. content: '\e69f';
  153. width: 18px;
  154. height: 18px;
  155. font-size: 0;
  156. background-color: transparent;
  157. }
  158. }
  159. .ui-radio-input.cur {
  160. &::before {
  161. width: 18px;
  162. height: 18px;
  163. font-size: 1em;
  164. transform: scale(0.8);
  165. text-align: center;
  166. line-height: 18px;
  167. }
  168. }
  169. }
  170. &.line {
  171. .ui-radio-input.cur {
  172. &::before {
  173. width: calc(100% - 2px);
  174. height: calc(100% - 2px);
  175. background-color: var(--ui-BG);
  176. }
  177. &::after {
  178. content: '';
  179. position: absolute;
  180. width: 10px;
  181. height: 10px;
  182. background-color: inherit;
  183. border-radius: 50%;
  184. @include position-center;
  185. }
  186. }
  187. }
  188. &.lg {
  189. .ui-radio-input {
  190. font-size: 18px;
  191. }
  192. }
  193. &.img {
  194. position: relative;
  195. margin: 0 0.28572em 0;
  196. .ui-radio-input {
  197. width: 42px;
  198. height: 42px;
  199. border-radius: 0px;
  200. position: absolute;
  201. margin: 0;
  202. left: -1px;
  203. top: -1px;
  204. &::before {
  205. width: 40px;
  206. height: 40px;
  207. border-radius: $radius;
  208. }
  209. &.cur {
  210. width: 44px;
  211. height: 44px;
  212. top: -2px;
  213. left: -2px;
  214. border-radius: 7px !important;
  215. opacity: 0.8;
  216. }
  217. }
  218. .ui-radio-img {
  219. width: 40px;
  220. height: 40px;
  221. display: block;
  222. overflow: hidden;
  223. border-radius: 10px;
  224. }
  225. }
  226. &.card {
  227. display: flex;
  228. margin: 30rpx;
  229. padding: 30rpx;
  230. position: relative;
  231. border-radius: $radius !important;
  232. flex-direction: row-reverse;
  233. justify-content: space-between;
  234. .ui-radio-bg {
  235. content: '';
  236. position: absolute;
  237. width: 200%;
  238. height: 200%;
  239. transform: scale(0.5);
  240. border-radius: #{$radius * 2} !important;
  241. z-index: 0;
  242. left: 0;
  243. top: 0;
  244. transform-origin: 0 0;
  245. background-color: var(--ui-BG);
  246. }
  247. .ui-radio-input {
  248. position: relative;
  249. z-index: 1;
  250. margin-right: 0;
  251. }
  252. .ui-radio-bg::after {
  253. content: '';
  254. position: absolute;
  255. width: calc(200% - 16px);
  256. height: calc(200% - 16px);
  257. transform: scale(0.5);
  258. transform-origin: 0 0;
  259. background-color: var(--ui-BG) !important;
  260. left: 4px;
  261. top: 4px;
  262. border-radius: #{$radius * 2 + 8} !important;
  263. z-index: 0;
  264. }
  265. .ui-radio-content {
  266. position: relative;
  267. z-index: 1;
  268. display: flex;
  269. align-items: center;
  270. flex: 1;
  271. }
  272. }
  273. }
  274. </style>