tn-radio.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. <template>
  2. <view class="tn-radio-class tn-radio" :style="[radioStyle]">
  3. <view
  4. class="tn-radio__icon-wrap"
  5. :class="[iconClass]"
  6. :style=[iconStyle]
  7. @tap="toggle"
  8. >
  9. <view class="tn-icon-success tn-radio__icon-wrap__icon"></view>
  10. </view>
  11. <view
  12. class="tn-radio__label"
  13. :style="{
  14. fontSize: labelSize ? labelSize + 'rpx' : ''
  15. }"
  16. @tap="onClickLabel"
  17. >
  18. <slot></slot>
  19. </view>
  20. </view>
  21. </template>
  22. <script>
  23. export default {
  24. name: 'tn-radio',
  25. props: {
  26. // radio名称
  27. name: {
  28. type: [String, Number],
  29. default: ''
  30. },
  31. // 是否禁用
  32. disabled: {
  33. type: Boolean,
  34. default: false
  35. },
  36. // 禁用点击标签进行选择
  37. disabledLabel: {
  38. type: Boolean,
  39. default: false
  40. },
  41. // 选择框的形状 square 方形 circle 圆形
  42. shape: {
  43. type: String,
  44. default: ''
  45. },
  46. // 选中时的颜色
  47. activeColor: {
  48. type: String,
  49. default: ''
  50. },
  51. // 组件尺寸
  52. size: {
  53. type: Number,
  54. default: 0
  55. },
  56. // 图标大小
  57. iconSize: {
  58. type: Number,
  59. default: 0
  60. },
  61. // label字体大小
  62. labelSize: {
  63. type: Number,
  64. default: 0
  65. }
  66. },
  67. computed: {
  68. // 禁用,父组件会覆盖子组件的状态
  69. isDisabled() {
  70. return this.disabled ? this.disabled : (this.parentData.disabled ? this.parentData.disabled : false)
  71. },
  72. // 禁用label点击,父组件会覆盖子组件的状态
  73. isDisabledLabel() {
  74. return this.disabledLabel ? this.disabledLabel : (this.parentData.disabledLabel ? this.parentData.disabledLabel : false)
  75. },
  76. // 组件尺寸
  77. elSize() {
  78. return this.size ? this.size : (this.parentData.size ? this.parentData.size : 34)
  79. },
  80. // 组件选中时的颜色
  81. elActiveColor() {
  82. return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#01BEFF')
  83. },
  84. // 组件形状
  85. elShape() {
  86. return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle')
  87. },
  88. iconClass() {
  89. let clazz = ''
  90. clazz += (' tn-radio__icon-wrap--' + this.elShape)
  91. if (this.parentData.value === this.name) clazz += ' tn-radio__icon-wrap--checked'
  92. if (this.isDisabled) clazz += ' tn-radio__icon-wrap--disabled'
  93. if (this.parentData.value === this.name && this.isDisabled) clazz += ' tn-radio__icon-wrap--disabled--checked'
  94. return clazz
  95. },
  96. iconStyle() {
  97. // 当前radio的name等于parent的value才认为时选中
  98. let style = {}
  99. if (this.elActiveColor && this.parentData.value === this.name && !this.isDisabled) {
  100. style.borderColor = this.elActiveColor
  101. style.backgroundColor = this.elActiveColor
  102. }
  103. style.color = this.parentData.value === this.name ? '#FFFFFF' : 'transparent'
  104. style.width = this.elSize + 'rpx'
  105. style.height = style.width
  106. style.fontSize = (this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 20)) + 'rpx'
  107. return style
  108. },
  109. radioStyle() {
  110. let style = {}
  111. if (this.parent && this.parentData.width) {
  112. // #ifdef MP
  113. // 各家小程序因为它们特殊的编译结构,使用float布局
  114. style.float = 'left';
  115. // #endif
  116. // #ifndef MP
  117. // H5和APP使用flex布局
  118. style.flex = `0 0 ${this.parentData.width}`;
  119. // #endif
  120. }
  121. if(this.parent && this.parentData.wrap) {
  122. style.width = '100%';
  123. // #ifndef MP
  124. // H5和APP使用flex布局,将宽度设置100%,即可自动换行
  125. style.flex = '0 0 100%';
  126. // #endif
  127. }
  128. return style
  129. }
  130. },
  131. data() {
  132. return {
  133. // 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.xxx的形式
  134. // 故只能使用如此方法
  135. parentData: {
  136. value: null,
  137. disabled: null,
  138. disabledLabel: null,
  139. shape: null,
  140. activeColor: null,
  141. size: null,
  142. width: null,
  143. wrap: null,
  144. iconSize: null,
  145. }
  146. }
  147. },
  148. created() {
  149. // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
  150. this.updateParentData()
  151. this.parent.children.push(this)
  152. },
  153. methods: {
  154. updateParentData() {
  155. this.getParentData('tn-radio-group')
  156. },
  157. onClickLabel() {
  158. if (!this.isDisabledLabel && !this.isDisabled) {
  159. this.setRadioCheckedStatus()
  160. }
  161. },
  162. toggle() {
  163. if (!this.isDisabled) {
  164. this.setRadioCheckedStatus()
  165. }
  166. },
  167. emitEvent() {
  168. // tn-radio的name不等于父组件的v-model的值时(意味着未选中),才发出事件,避免多次点击触发事件
  169. if (this.parentData.value !== this.name) this.$emit('change', this.name)
  170. },
  171. // 改变选中的状态
  172. // 更改本组件的parentData.value的值为本组件的name值,同时通过父组件遍历所有的radio实例
  173. // 将本组件外的其他radio的parentData.value都设置为空
  174. setRadioCheckedStatus() {
  175. this.emitEvent()
  176. if (this.parent) {
  177. this.parent.setValue(this.name)
  178. this.parentData.value = this.name
  179. }
  180. }
  181. }
  182. }
  183. </script>
  184. <style lang="scss" scoped>
  185. .tn-radio {
  186. /* #ifndef APP-NVUE */
  187. display: inline-flex;
  188. /* #endif */
  189. align-items: center;
  190. overflow: hidden;
  191. user-select: none;
  192. line-height: 1.8;
  193. &__icon-wrap {
  194. color: $tn-font-color;
  195. display: flex;
  196. flex-direction: row;
  197. flex: none;
  198. align-items: center;
  199. justify-content: center;
  200. box-sizing: border-box;
  201. width: 42rpx;
  202. height: 42rpx;
  203. color: transparent;
  204. text-align: center;
  205. transition-property: color, border-color, background-color;
  206. font-size: 20rpx;
  207. border: 1rpx solid $tn-font-sub-color;
  208. transition-duration: 0.2s;
  209. /* #ifdef MP-TOUTIAO */
  210. // 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
  211. &__icon {
  212. line-height: 0;
  213. }
  214. /* #endif */
  215. &--circle {
  216. border-radius: 100%;
  217. }
  218. &--square {
  219. border-radius: 6rpx;
  220. }
  221. &--checked {
  222. color: #FFFFFF;
  223. background-color: $tn-main-color;
  224. border-color: $tn-main-color;
  225. }
  226. &--disabled {
  227. background-color: $tn-font-holder-color;
  228. border-color: $tn-font-sub-color;
  229. }
  230. &--disabled--checked {
  231. color: $tn-font-sub-color !important;
  232. }
  233. }
  234. &__label {
  235. word-wrap: break-word;
  236. margin-left: 10rpx;
  237. margin-right: 24rpx;
  238. color: $tn-font-color;
  239. font-size: 30rpx;
  240. &--disabled {
  241. color: $tn-font-sub-color;
  242. }
  243. }
  244. }
  245. </style>