index.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. import baseComponent from '../helpers/baseComponent'
  2. import classNames from '../helpers/classNames'
  3. import eventsMixin from '../helpers/eventsMixin'
  4. baseComponent({
  5. behaviors: [eventsMixin()],
  6. relations: {
  7. '../field/index': {
  8. type: 'ancestor',
  9. },
  10. },
  11. properties: {
  12. prefixCls: {
  13. type: String,
  14. value: 'wux-rater',
  15. },
  16. max: {
  17. type: Number,
  18. value: 5,
  19. observer() {
  20. this.setValue(this.data.inputValue)
  21. },
  22. },
  23. icon: {
  24. type: String,
  25. value: '',
  26. },
  27. star: {
  28. type: String,
  29. value: '★',
  30. },
  31. defaultValue: {
  32. type: Number,
  33. value: 0,
  34. },
  35. value: {
  36. type: Number,
  37. value: 0,
  38. observer(newVal) {
  39. if (this.data.controlled) {
  40. this.setValue(newVal)
  41. }
  42. },
  43. },
  44. activeColor: {
  45. type: String,
  46. value: '#ffc900',
  47. },
  48. margin: {
  49. type: Number,
  50. value: 2,
  51. },
  52. fontSize: {
  53. type: Number,
  54. value: 25,
  55. },
  56. disabled: {
  57. type: Boolean,
  58. value: false,
  59. },
  60. allowHalf: {
  61. type: Boolean,
  62. value: false,
  63. },
  64. allowClear: {
  65. type: Boolean,
  66. value: false,
  67. },
  68. allowTouchMove: {
  69. type: Boolean,
  70. value: false,
  71. },
  72. controlled: {
  73. type: Boolean,
  74. value: false,
  75. },
  76. },
  77. data: {
  78. inputValue: 0,
  79. },
  80. computed: {
  81. classes: ['prefixCls, disabled', function(prefixCls, disabled) {
  82. const wrap = classNames(prefixCls, {
  83. [`${prefixCls}--disabled`]: disabled,
  84. })
  85. const star = `${prefixCls}__star`
  86. const box = `${prefixCls}__box`
  87. const inner = `${prefixCls}__inner`
  88. const outer = `${prefixCls}__outer`
  89. const icon = `${prefixCls}__icon`
  90. return {
  91. wrap,
  92. star,
  93. box,
  94. inner,
  95. outer,
  96. icon,
  97. }
  98. }],
  99. },
  100. observers: {
  101. ['inputValue, max, activeColor'](inputValue, max, activeColor) {
  102. const stars = [...new Array(max)].map((_, i) => i)
  103. const colors = stars.reduce((a, _, i) => ([...a, i <= inputValue - 1 ? activeColor : '#ccc']), [])
  104. const _val = inputValue.toString().split('.')
  105. const sliceValue = _val.length === 1 ? [_val[0], 0] : _val
  106. this.setData({
  107. stars,
  108. colors,
  109. cutIndex: sliceValue[0] * 1,
  110. cutPercent: sliceValue[1] * 10,
  111. })
  112. },
  113. },
  114. methods: {
  115. updated(inputValue) {
  116. if (this.hasFieldDecorator) return
  117. if (this.data.inputValue !== inputValue) {
  118. this.setData({ inputValue })
  119. }
  120. },
  121. setValue(value) {
  122. const { max } = this.data
  123. const inputValue = value <= 0 ? 0 : value > max ? max : value
  124. this.updated(inputValue)
  125. },
  126. updateHalfStarValue(index, x, cb) {
  127. const { prefixCls } = this.data
  128. const query = wx.createSelectorQuery().in(this)
  129. query.selectAll(`.${prefixCls}__star`).boundingClientRect((rects) => {
  130. if (rects.filter((n) => !n).length) return
  131. const { left, width } = rects[index]
  132. const has = (x - left) < width / 2
  133. const value = has ? index + .5 : index + 1
  134. cb.call(this, value, index)
  135. }).exec()
  136. },
  137. onTap(e) {
  138. const { index } = e.currentTarget.dataset
  139. const { inputValue, disabled, allowHalf, allowClear } = this.data
  140. // 判断是否禁用
  141. if (!disabled) {
  142. // 判断是否支持选中半星
  143. if (!allowHalf) {
  144. const value = index + 1
  145. const isReset = allowClear && value === inputValue
  146. this.onChange(isReset ? 0 : value, index)
  147. } else {
  148. this.updateHalfStarValue(index, e.detail.x, (value, index) => {
  149. const isReset = allowClear && value === inputValue
  150. this.onChange(isReset ? 0 : value, index)
  151. })
  152. }
  153. }
  154. },
  155. onChange(value, index) {
  156. if (!this.data.controlled) {
  157. this.setValue(value)
  158. }
  159. this.triggerEvent('change', { value, index })
  160. },
  161. onTouchMove(e) {
  162. const { disabled, allowHalf, allowTouchMove } = this.data
  163. if (!disabled && allowTouchMove) {
  164. const x = e.changedTouches[0].pageX
  165. const { prefixCls } = this.data
  166. const query = wx.createSelectorQuery().in(this)
  167. query.selectAll(`.${prefixCls}__star`).boundingClientRect((rects) => {
  168. if (rects.filter((n) => !n).length) return
  169. const { left, width } = rects[0]
  170. const maxWidth = rects.map((n) => n.width).reduce((a, b) => a + b)
  171. const diff = x - left
  172. let value = Math.ceil(diff / width)
  173. // 判断是否在组件宽度范围内
  174. if (diff > 0 && diff < maxWidth) {
  175. const index = value - 1
  176. if (allowHalf) {
  177. const star = rects[index]
  178. const has = (x - star.left) < star.width / 2
  179. value = has ? value - .5 : value
  180. }
  181. this.onChange(value, index)
  182. }
  183. }).exec()
  184. }
  185. },
  186. },
  187. attached() {
  188. const { defaultValue, value, controlled } = this.data
  189. const inputValue = controlled ? value : defaultValue
  190. this.setValue(inputValue)
  191. },
  192. })