tn-checkbox.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <template>
  2. <view class="tn-checkbox-class tn-checkbox" :style="[checkboxStyle]">
  3. <view
  4. class="tn-checkbox__icon-wrap"
  5. :class="[iconClass]"
  6. :style="[iconStyle]"
  7. @tap="toggle"
  8. >
  9. <view class="tn-checkbox__icon-wrap__icon" :class="[`tn-icon-${iconName}`]"></view>
  10. </view>
  11. <view
  12. class="tn-checkbox__label"
  13. :class="[labelClass]"
  14. :style="{
  15. fontSize: labelSize ? labelSize + 'rpx' : ''
  16. }"
  17. @tap="onClickLabel"
  18. >
  19. <slot></slot>
  20. </view>
  21. </view>
  22. </template>
  23. <script>
  24. export default {
  25. name: 'tn-checkbox',
  26. props: {
  27. // checkbox名称
  28. name: {
  29. type: [String, Number],
  30. default: ''
  31. },
  32. // 是否为选中状态
  33. value: {
  34. type: Boolean,
  35. default: false
  36. },
  37. // 禁用选择
  38. disabled: {
  39. type: Boolean,
  40. default: false
  41. },
  42. // 禁用点击标签进行选择
  43. disabledLabel: {
  44. type: Boolean,
  45. default: false
  46. },
  47. // 选择框的形状 square 方形 circle 圆形
  48. shape: {
  49. type: String,
  50. default: ''
  51. },
  52. // 选中时的颜色
  53. activeColor: {
  54. type: String,
  55. default: ''
  56. },
  57. // 组件大小
  58. size: {
  59. type: Number,
  60. default: 0
  61. },
  62. // 图标名称
  63. iconName: {
  64. type: String,
  65. default: 'success'
  66. },
  67. // 图标大小
  68. iconSize: {
  69. type: Number,
  70. default: 0
  71. },
  72. // label的字体大小
  73. labelSize: {
  74. type: Number,
  75. default: 0
  76. }
  77. },
  78. computed: {
  79. // 是否禁用选中,父组件的禁用会覆盖当前的禁用状态
  80. isDisabled() {
  81. return this.disabled ? this.disabled : (this.parent ? this.parentData.disabled : false)
  82. },
  83. // 是否禁用点击label选中,父组件的禁用会覆盖当前的禁用状态
  84. isDisabledLabel() {
  85. return this.disabledLabel ? this.disabledLabel : (this.parent ? this.parentData.disabledLabel : false)
  86. },
  87. // 尺寸
  88. checkboxSize() {
  89. return this.size ? this.size : (this.parent ? this.parentData.size : 34)
  90. },
  91. // 激活时的颜色
  92. elAvtiveColor() {
  93. return this.activeColor ? this.activeColor : (this.parent ? this.parentData.activeColor : '#01BEFF')
  94. },
  95. // 形状
  96. elShape() {
  97. return this.shape ? this.shape : (this.parent ? this.parentData.shape : 'square')
  98. },
  99. iconClass() {
  100. let clazz = ''
  101. clazz += (' tn-checkbox__icon-wrap--' + this.elShape)
  102. if (this.checkValue) clazz += ' tn-checkbox__icon-wrap--checked'
  103. if (this.isDisabled) clazz += ' tn-checkbox__icon-wrap--disabled'
  104. if (this.value && this.isDisabled) clazz += ' tn-checkbox__icon-wrap--disabled--checked'
  105. return clazz
  106. },
  107. iconStyle() {
  108. let style = {}
  109. // 判断是否用户手动禁用和传递的值
  110. if (this.elAvtiveColor && this.checkValue && !this.isDisabled) {
  111. style.borderColor = this.elAvtiveColor
  112. style.backgroundColor = this.elAvtiveColor
  113. }
  114. // checkbox内部的勾选图标,如果选中状态,为白色,否则为透明色即可
  115. style.color = this.checkValue ? '#FFFFFF' : 'transparent'
  116. style.width = this.checkboxSize + 'rpx'
  117. style.height = style.width
  118. style.fontSize = (this.iconSize ? this.iconSize : (this.parent ? this.parentData.iconSize : 20)) + 'rpx'
  119. return style
  120. },
  121. checkboxStyle() {
  122. let style = {}
  123. if (this.parent && this.parentData.width) {
  124. // #ifdef MP
  125. // 各家小程序因为它们特殊的编译结构,使用float布局
  126. style.float = 'left';
  127. // #endif
  128. // #ifndef MP
  129. // H5和APP使用flex布局
  130. style.flex = `0 0 ${this.parentData.width}`;
  131. // #endif
  132. }
  133. if(this.parent && this.parentData.wrap) {
  134. style.width = '100%';
  135. // #ifndef MP
  136. // H5和APP使用flex布局,将宽度设置100%,即可自动换行
  137. style.flex = '0 0 100%';
  138. // #endif
  139. }
  140. return style
  141. },
  142. labelClass() {
  143. let clazz = ''
  144. if (this.isDisabled) {
  145. clazz += ' tn-checkbox__label--disabled'
  146. }
  147. return clazz
  148. }
  149. },
  150. data() {
  151. return {
  152. // 当前checkbox的value值
  153. checkValue: false,
  154. parentData: {
  155. value: null,
  156. max: null,
  157. disabled: null,
  158. disabledLabel: null,
  159. shape: null,
  160. activeColor: null,
  161. size: null,
  162. width: null,
  163. wrap: null,
  164. iconSize: null
  165. }
  166. }
  167. },
  168. watch: {
  169. value(val) {
  170. this.checkValue = val
  171. }
  172. },
  173. created() {
  174. // 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环应用
  175. // this.parent = this.$t.$parent.call(this, 'tn-checkbox-group')
  176. // // 如果存在u-checkbox-group,将本组件的this塞进父组件的children中
  177. // this.parent && this.parent.children.push(this)
  178. // // 初始化父组件的value值
  179. // this.parent && this.parent.emitEvent()
  180. this.updateParentData()
  181. this.parent && this.parent.children.push(this)
  182. },
  183. methods: {
  184. updateCheckValue() {
  185. // 更新当前checkbox的选中状态
  186. this.checkValue = (this.parent && this.parentData.value.includes(this.name)) || this.value === true
  187. if (this.parent) {
  188. if (this.value && !this.parentData.value.includes(this.name)) {
  189. this.parentData.value.push(this.name)
  190. this.parent.initValue(this.parentData.value)
  191. }
  192. }
  193. },
  194. updateParentData() {
  195. this.getParentData('tn-checkbox-group')
  196. this.updateCheckValue()
  197. },
  198. onClickLabel() {
  199. if (!this.isDisabled && !this.isDisabledLabel) {
  200. this.setValue()
  201. }
  202. },
  203. toggle() {
  204. if (!this.isDisabled) {
  205. this.setValue()
  206. }
  207. },
  208. emitEvent() {
  209. this.$emit('change', {
  210. name: this.name,
  211. value: !this.checkValue
  212. })
  213. if (this.parent) {
  214. this.checkValue = !this.checkValue
  215. // 执行父组件tn-checkbox-group的事件方法
  216. // 等待下一个周期再执行,因为this.$emit('input')作用于父组件,再反馈到子组件内部,需要时间
  217. setTimeout(() => {
  218. if(this.parent.emitEvent) this.parent.emitEvent();
  219. }, 80)
  220. }
  221. },
  222. // 设置input的值,通过v-modal绑定组件的值
  223. setValue() {
  224. // 判断是否为可选项组
  225. if (this.parent) {
  226. // 反转状态
  227. if (this.checkValue === true) {
  228. this.emitEvent()
  229. // this.$emit('input', !this.checkValue)
  230. } else {
  231. // 超出最大可选项,弹出提示
  232. if (this.parentData.value.length >= this.parentData.max) {
  233. return this.$t.message.toast(`最多可选${this.parent.max}项`)
  234. }
  235. // 如果原来为未选中状态,需要选中的数量少于父组件中设置的max值,才可以选中
  236. this.emitEvent();
  237. // this.$emit('input', !this.checkValue);
  238. }
  239. } else {
  240. // 只有一个可选项
  241. this.emitEvent()
  242. this.$emit('input', !this.checkValue)
  243. }
  244. }
  245. }
  246. }
  247. </script>
  248. <style lang="scss" scoped>
  249. .tn-checkbox {
  250. /* #ifndef APP-NVUE */
  251. display: inline-flex;
  252. /* #endif */
  253. align-items: center;
  254. overflow: hidden;
  255. user-select: none;
  256. line-height: 1.8;
  257. &__icon-wrap {
  258. color: $tn-font-color;
  259. flex: none;
  260. display: flex;
  261. flex-direction: row;
  262. align-items: center;
  263. justify-content: center;
  264. box-sizing: border-box;
  265. width: 42rpx;
  266. height: 42rpx;
  267. color: transparent;
  268. text-align: center;
  269. transition-property: color, border-color, background-color;
  270. border: 1px solid $tn-font-sub-color;
  271. transition-duration: 0.2s;
  272. /* #ifdef MP-TOUTIAO */
  273. // 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
  274. &__icon {
  275. line-height: 0;
  276. }
  277. /* #endif */
  278. &--circle {
  279. border-radius: 100%;
  280. }
  281. &--square {
  282. border-radius: 6rpx;
  283. }
  284. &--checked {
  285. color: #FFFFFF;
  286. background-color: $tn-main-color;
  287. border-color: $tn-main-color;
  288. }
  289. &--disabled {
  290. background-color: $tn-font-holder-color;
  291. border-color: $tn-font-sub-color;
  292. }
  293. &--disabled--checked {
  294. color: $tn-font-sub-color !important;
  295. }
  296. }
  297. &__label {
  298. word-wrap: break-word;
  299. margin-left: 10rpx;
  300. margin-right: 24rpx;
  301. color: $tn-font-color;
  302. font-size: 30rpx;
  303. &--disabled {
  304. color: $tn-font-sub-color;
  305. }
  306. }
  307. }
  308. </style>