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 |
|
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) {
|
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 |
|
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>
|