UNPKG

8.84 kBPlain TextView Raw
1<template>
2 <div
3 class="el-rate"
4 @keydown="handleKey"
5 role="slider"
6 :aria-valuenow="currentValue"
7 :aria-valuetext="text"
8 aria-valuemin="0"
9 :aria-valuemax="max"
10 tabindex="0">
11 <span
12 v-for="(item, key) in max"
13 class="el-rate__item"
14 @mousemove="setCurrentValue(item, $event)"
15 @mouseleave="resetCurrentValue"
16 @click="selectValue(item)"
17 :style="{ cursor: rateDisabled ? 'auto' : 'pointer' }"
18 :key="key">
19 <i
20 :class="[classes[item - 1], { 'hover': hoverIndex === item }]"
21 class="el-rate__icon"
22 :style="getIconStyle(item)">
23 <i
24 v-if="showDecimalIcon(item)"
25 :class="decimalIconClass"
26 :style="decimalStyle"
27 class="el-rate__decimal">
28 </i>
29 </i>
30 </span>
31 <span v-if="showText || showScore" class="el-rate__text" :style="{ color: textColor }">{{ text }}</span>
32 </div>
33</template>
34
35<script>
36 import { hasClass } from 'element-ui/src/utils/dom';
37 import Migrating from 'element-ui/src/mixins/migrating';
38
39 export default {
40 name: 'ElRate',
41
42 mixins: [Migrating],
43
44 inject: {
45 elForm: {
46 default: ''
47 }
48 },
49
50 data() {
51 return {
52 pointerAtLeftHalf: true,
53 currentValue: this.value,
54 hoverIndex: -1
55 };
56 },
57
58 props: {
59 value: {
60 type: Number,
61 default: 0
62 },
63 lowThreshold: {
64 type: Number,
65 default: 2
66 },
67 highThreshold: {
68 type: Number,
69 default: 4
70 },
71 max: {
72 type: Number,
73 default: 5
74 },
75 colors: {
76 type: Array,
77 default() {
78 return ['#F7BA2A', '#F7BA2A', '#F7BA2A'];
79 }
80 },
81 voidColor: {
82 type: String,
83 default: '#C6D1DE'
84 },
85 disabledVoidColor: {
86 type: String,
87 default: '#EFF2F7'
88 },
89 iconClasses: {
90 type: Array,
91 default() {
92 return ['el-icon-star-on', 'el-icon-star-on', 'el-icon-star-on'];
93 }
94 },
95 voidIconClass: {
96 type: String,
97 default: 'el-icon-star-off'
98 },
99 disabledVoidIconClass: {
100 type: String,
101 default: 'el-icon-star-on'
102 },
103 disabled: {
104 type: Boolean,
105 default: false
106 },
107 allowHalf: {
108 type: Boolean,
109 default: false
110 },
111 showText: {
112 type: Boolean,
113 default: false
114 },
115 showScore: {
116 type: Boolean,
117 default: false
118 },
119 textColor: {
120 type: String,
121 default: '#1f2d3d'
122 },
123 texts: {
124 type: Array,
125 default() {
126 return ['极差', '失望', '一般', '满意', '惊喜'];
127 }
128 },
129 scoreTemplate: {
130 type: String,
131 default: '{value}'
132 }
133 },
134
135 computed: {
136 text() {
137 let result = '';
138 if (this.showScore) {
139 result = this.scoreTemplate.replace(/\{\s*value\s*\}/, this.rateDisabled
140 ? this.value
141 : this.currentValue);
142 } else if (this.showText) {
143 result = this.texts[Math.ceil(this.currentValue) - 1];
144 }
145 return result;
146 },
147
148 decimalStyle() {
149 let width = '';
150 if (this.rateDisabled) {
151 width = `${ this.valueDecimal < 50 ? 0 : 50 }%`;
152 }
153 if (this.allowHalf) {
154 width = '50%';
155 }
156 return {
157 color: this.activeColor,
158 width
159 };
160 },
161
162 valueDecimal() {
163 return this.value * 100 - Math.floor(this.value) * 100;
164 },
165
166 decimalIconClass() {
167 return this.getValueFromMap(this.value, this.classMap);
168 },
169
170 voidClass() {
171 return this.rateDisabled ? this.classMap.disabledVoidClass : this.classMap.voidClass;
172 },
173
174 activeClass() {
175 return this.getValueFromMap(this.currentValue, this.classMap);
176 },
177
178 colorMap() {
179 return {
180 lowColor: this.colors[0],
181 mediumColor: this.colors[1],
182 highColor: this.colors[2],
183 voidColor: this.voidColor,
184 disabledVoidColor: this.disabledVoidColor
185 };
186 },
187
188 activeColor() {
189 return this.getValueFromMap(this.currentValue, this.colorMap);
190 },
191
192 classes() {
193 let result = [];
194 let i = 0;
195 let threshold = this.currentValue;
196 if (this.allowHalf && this.currentValue !== Math.floor(this.currentValue)) {
197 threshold--;
198 }
199 for (; i < threshold; i++) {
200 result.push(this.activeClass);
201 }
202 for (; i < this.max; i++) {
203 result.push(this.voidClass);
204 }
205 return result;
206 },
207
208 classMap() {
209 return {
210 lowClass: this.iconClasses[0],
211 mediumClass: this.iconClasses[1],
212 highClass: this.iconClasses[2],
213 voidClass: this.voidIconClass,
214 disabledVoidClass: this.disabledVoidIconClass
215 };
216 },
217
218 rateDisabled() {
219 return this.disabled || (this.elForm || {}).disabled;
220 }
221 },
222
223 watch: {
224 value(val) {
225 this.currentValue = val;
226 this.pointerAtLeftHalf = this.value !== Math.floor(this.value);
227 }
228 },
229
230 methods: {
231 getMigratingConfig() {
232 return {
233 props: {
234 'text-template': 'text-template is renamed to score-template.'
235 }
236 };
237 },
238
239 getValueFromMap(value, map) {
240 let result = '';
241 if (value <= this.lowThreshold) {
242 result = map.lowColor || map.lowClass;
243 } else if (value >= this.highThreshold) {
244 result = map.highColor || map.highClass;
245 } else {
246 result = map.mediumColor || map.mediumClass;
247 }
248 return result;
249 },
250
251 showDecimalIcon(item) {
252 let showWhenDisabled = this.rateDisabled && this.valueDecimal > 0 && item - 1 < this.value && item > this.value;
253 /* istanbul ignore next */
254 let showWhenAllowHalf = this.allowHalf &&
255 this.pointerAtLeftHalf &&
256 item - 0.5 <= this.currentValue &&
257 item > this.currentValue;
258 return showWhenDisabled || showWhenAllowHalf;
259 },
260
261 getIconStyle(item) {
262 const voidColor = this.rateDisabled ? this.colorMap.disabledVoidColor : this.colorMap.voidColor;
263 return {
264 color: item <= this.currentValue ? this.activeColor : voidColor
265 };
266 },
267
268 selectValue(value) {
269 if (this.rateDisabled) {
270 return;
271 }
272 if (this.allowHalf && this.pointerAtLeftHalf) {
273 this.$emit('input', this.currentValue);
274 this.$emit('change', this.currentValue);
275 } else {
276 this.$emit('input', value);
277 this.$emit('change', value);
278 }
279 },
280
281 handleKey(e) {
282 if (this.rateDisabled) {
283 return;
284 }
285 let currentValue = this.currentValue;
286 const keyCode = e.keyCode;
287 if (keyCode === 38 || keyCode === 39) { // left / down
288 if (this.allowHalf) {
289 currentValue += 0.5;
290 } else {
291 currentValue += 1;
292 }
293 e.stopPropagation();
294 e.preventDefault();
295 } else if (keyCode === 37 || keyCode === 40) {
296 if (this.allowHalf) {
297 currentValue -= 0.5;
298 } else {
299 currentValue -= 1;
300 }
301 e.stopPropagation();
302 e.preventDefault();
303 }
304 currentValue = currentValue < 0 ? 0 : currentValue;
305 currentValue = currentValue > this.max ? this.max : currentValue;
306
307 this.$emit('input', currentValue);
308 this.$emit('change', currentValue);
309 },
310
311 setCurrentValue(value, event) {
312 if (this.rateDisabled) {
313 return;
314 }
315 /* istanbul ignore if */
316 if (this.allowHalf) {
317 let target = event.target;
318 if (hasClass(target, 'el-rate__item')) {
319 target = target.querySelector('.el-rate__icon');
320 }
321 if (hasClass(target, 'el-rate__decimal')) {
322 target = target.parentNode;
323 }
324 this.pointerAtLeftHalf = event.offsetX * 2 <= target.clientWidth;
325 this.currentValue = this.pointerAtLeftHalf ? value - 0.5 : value;
326 } else {
327 this.currentValue = value;
328 }
329 this.hoverIndex = value;
330 },
331
332 resetCurrentValue() {
333 if (this.rateDisabled) {
334 return;
335 }
336 if (this.allowHalf) {
337 this.pointerAtLeftHalf = this.value !== Math.floor(this.value);
338 }
339 this.currentValue = this.value;
340 this.hoverIndex = -1;
341 }
342 },
343
344 created() {
345 if (!this.value) {
346 this.$emit('input', 0);
347 }
348 }
349 };
350</script>