index.js 6.2 KB

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