index.vue 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <template>
  2. <div :class="classes" @mouseleave="handleMouseleave">
  3. <div
  4. v-for="item in count"
  5. :key="item"
  6. :class="starCls(item)"
  7. @mousemove="handleMousemove(item, $event)"
  8. @click="handleClick(item)">
  9. <span class="iconfont favorite" v-if="!character && !icon">
  10. <span class="iconfont favorite-half" type="half"></span>
  11. </span>
  12. <template v-if="character">
  13. <span class="m-rate-star-first" type="half">{{ character }}</span>
  14. <span class="m-rate-star-second">{{ character }}</span>
  15. </template>
  16. <i class="iconfont other-icon" :class="['icon-'+ icon]" v-if="icon">
  17. <i class="iconfont other-icon-half" :class="['icon-'+ icon]" type="half"></i>
  18. </i>
  19. </div>
  20. <div class="m-rate-text" v-if="showText && currentValue > 0">
  21. <slot>{{currentValue}} 星</slot>
  22. </div>
  23. </div>
  24. </template>
  25. <script>
  26. export default {
  27. name: "mRate"
  28. };
  29. </script>
  30. <script setup>
  31. import { computed, reactive, toRefs, watch } from 'vue-demi';
  32. const emits = defineEmits(['update:modelValue','on-change'])
  33. const props = defineProps({
  34. count: {
  35. type: Number,
  36. default: 5
  37. },
  38. modelValue: {
  39. type: Number,
  40. default: 0
  41. },
  42. allowHalf: {
  43. type: Boolean,
  44. default: false
  45. },
  46. showText: {
  47. type: Boolean,
  48. default: false
  49. },
  50. disabled: {
  51. type: Boolean,
  52. default: false
  53. },
  54. clearable: {
  55. type: Boolean,
  56. default: false
  57. },
  58. character: {
  59. type: String,
  60. default: ''
  61. },
  62. icon: {
  63. type: String,
  64. default: ''
  65. },
  66. })
  67. const state = reactive({
  68. hoverIndex: -1,
  69. isHover: false,
  70. currentValue: props.modelValue,
  71. isHalf: props.allowHalf && props.modelValue.toString().indexOf('.') > -1,
  72. })
  73. watch(() => props.modelValue,(newVal) => {
  74. state.currentValue = newVal
  75. },{immediate:true})
  76. const classes = computed(() => {
  77. return [
  78. 'm-rate',
  79. {
  80. 'm-rate-disabled': props.disabled
  81. }
  82. ]
  83. })
  84. const starCls = (value) => {
  85. const currentIndex = state.isHover ? state.hoverIndex : state.currentValue;
  86. let full = false;
  87. let isLast = false;
  88. if (currentIndex >= value) full = true;
  89. if (state.isHover) {
  90. isLast = currentIndex === value;
  91. } else {
  92. isLast = Math.ceil(state.currentValue) === value;
  93. }
  94. return [
  95. 'm-rate-star',
  96. {
  97. 'm-rate-star-full': (!isLast && full) || (isLast && !state.isHalf),
  98. 'm-rate-star-zero': !full,
  99. 'm-rate-star-half': isLast && state.isHalf,
  100. }
  101. ]
  102. }
  103. const handleMousemove = (value, event) => {
  104. if (props.disabled) return;
  105. state.isHover = true;
  106. if (props.allowHalf) {
  107. const type = event.target.getAttribute('type') || false;
  108. state.isHalf = type === 'half';
  109. } else {
  110. state.isHalf = false;
  111. }
  112. // hoverIndex 为整
  113. state.hoverIndex = value;
  114. }
  115. const handleMouseleave = () => {
  116. if (props.disabled) return;
  117. state.isHover = false;
  118. setHalf(state.currentValue);
  119. state.hoverIndex = -1;
  120. }
  121. const setHalf = (val) => {
  122. state.isHalf = props.allowHalf && val.toString().indexOf('.') > -1;
  123. }
  124. const handleClick = (value) => {
  125. if (props.disabled) return;
  126. if (state.isHalf) value -= 0.5;
  127. if(props.clearable && Math.abs(value - state.currentValue) < 0.01) {
  128. value = 0
  129. }
  130. state.currentValue = value;
  131. emits('update:modelValue',value)
  132. emits('on-change',value)
  133. }
  134. const { currentValue } = toRefs(state);
  135. </script>
  136. <style lang="scss" scoped>
  137. @import '../../styles/components/rate.scss';
  138. </style>