element rate源码
<template> <div class="el-rate" @keydown="handleKey" role="slider" :aria-valuenow="currentValue" :aria-valuetext="text" aria-valuemin="0" :aria-valuemax="max" tabindex="0"> <span v-for="(item, key) in max" class="el-rate__item" @mousemove="setCurrentValue(item, $event)" @mouseleave="resetCurrentValue" @click="selectValue(item)" :style="{ cursor: rateDisabled ? 'auto' : 'pointer' }" :key="key"> <i :class="[classes[item - 1], { 'hover': hoverIndex === item }]" class="el-rate__icon" :style="getIconStyle(item)"> <i v-if="showDecimalIcon(item)" :class="decimalIconClass" :style="decimalStyle" class="el-rate__decimal"> </i> </i> </span> <span v-if="showText || showScore" class="el-rate__text" :style="{ color: textColor }">{{ text }}</span> </div> </template> <script> import { hasClass } from 'element-ui/src/utils/dom'; import { isObject } from 'element-ui/src/utils/types'; import Migrating from 'element-ui/src/mixins/migrating'; export default { name: 'ElRate', mixins: [Migrating], inject: { elForm: { default: '' } }, data() { return { // 鼠标在目标元素一半的左侧 pointerAtLeftHalf: true, // 当前值 currentValue: this.value, hoverIndex: -1 }; }, props: { // 绑定值 number — 0 value: { type: Number, default: 0 }, // 低分和中等分数的界限值,值本身被划分在低分中 number — 2 lowThreshold: { type: Number, default: 2 }, // 高分和中等分数的界限值,值本身被划分在高分中 highThreshold: { type: Number, default: 4 }, // 最大分值 number — 5 max: { type: Number, default: 5 }, // icon 的颜色。若传入数组,共有 3 个元素,为 3 个分段所对应的颜色;若传入对象,可自定义分段,键名为分段的界限值, // 键值为对应的颜色 array/object — ['#F7BA2A', '#F7BA2A', '#F7BA2A'] colors: { type: [Array, Object], default() { return ['#F7BA2A', '#F7BA2A', '#F7BA2A']; } }, // 未选中 icon 的颜色 string — #C6D1DE voidColor: { type: String, default: '#C6D1DE' }, // 只读时未选中 icon 的颜色 string — #EFF2F7 disabledVoidColor: { type: String, default: '#EFF2F7' }, // icon 的类名。若传入数组,共有 3 个元素,为 3 个分段所对应的类名;若传入对象,可自定义分段,键名为分段的界限值,键值为对应的类名 // array/object — ['el-icon-star-on', 'el-icon-star-on','el-icon-star-on'] iconClasses: { type: [Array, Object], default() { return ['el-icon-star-on', 'el-icon-star-on', 'el-icon-star-on']; } }, // 未选中 icon 的类名 string — el-icon-star-off voidIconClass: { type: String, default: 'el-icon-star-off' }, // 只读时未选中 icon 的类名 string — el-icon-star-on disabledVoidIconClass: { type: String, default: 'el-icon-star-on' }, // 是否为只读 disabled: { type: Boolean, default: false }, // 是否允许半选 allowHalf: { type: Boolean, default: false }, // 是否显示辅助文字,若为真,则会从 texts 数组中选取当前分数对应的文字内容 showText: { type: Boolean, default: false }, // 是否显示当前分数,show-score 和 show-text 不能同时为真 showScore: { type: Boolean, default: false }, // 辅助文字的颜色 textColor: { type: String, default: '#1f2d3d' }, // 辅助文字数组 array — ['极差', '失望', '一般', '满意', '惊喜'] texts: { type: Array, default() { return ['极差', '失望', '一般', '满意', '惊喜']; } }, // 分数显示模板 scoreTemplate: { type: String, default: '{value}' } }, computed: { // 文本 text() { let result = ''; if (this.showScore) { result = this.scoreTemplate.replace(/\{\s*value\s*\}/, this.rateDisabled ? this.value : this.currentValue); } else if (this.showText) { result = this.texts[Math.ceil(this.currentValue) - 1]; } return result; }, // 样式 decimalStyle() { let width = ''; if (this.rateDisabled) { // 差值 width = `${ this.valueDecimal }%`; } else if (this.allowHalf) { // 半星 width = '50%'; } return { color: this.activeColor, width }; }, // 差值 valueDecimal() { return this.value * 100 - Math.floor(this.value) * 100; }, // 字典 classMap() { return Array.isArray(this.iconClasses) ? { [this.lowThreshold]: this.iconClasses[0], [this.highThreshold]: { value: this.iconClasses[1], excluded: true }, [this.max]: this.iconClasses[2] } : this.iconClasses; }, // 根据值获取类名 decimalIconClass() { return this.getValueFromMap(this.value, this.classMap); }, // 未选中的类名 voidClass() { return this.rateDisabled ? this.disabledVoidIconClass : this.voidIconClass; }, // 获取当前值的类名 activeClass() { return this.getValueFromMap(this.currentValue, this.classMap); }, // 颜色字典 colorMap() { return Array.isArray(this.colors) ? { [this.lowThreshold]: this.colors[0], [this.highThreshold]: { value: this.colors[1], excluded: true }, [this.max]: this.colors[2] } : this.colors; }, // 当前的颜色 activeColor() { return this.getValueFromMap(this.currentValue, this.colorMap); }, classes() { let result = []; let i = 0; let threshold = this.currentValue; if (this.allowHalf && this.currentValue !== Math.floor(this.currentValue)) { threshold--; } for (; i < threshold; i++) { result.push(this.activeClass); } for (; i < this.max; i++) { result.push(this.voidClass); } return result; }, // 是否只读 rateDisabled() { return this.disabled || (this.elForm || {}).disabled; } }, watch: { // 监听value变化赋值给currentValue value(val) { this.currentValue = val; this.pointerAtLeftHalf = this.value !== Math.floor(this.value); } }, methods: { getMigratingConfig() { return { props: { 'text-template': 'text-template is renamed to score-template.' } }; }, // 根据字典获取类名 getValueFromMap(value, map) { const matchedKeys = Object.keys(map) .filter(key => { const val = map[key]; const excluded = isObject(val) ? val.excluded : false; return excluded ? value < key : value <= key; }) .sort((a, b) => a - b); const matchedValue = map[matchedKeys[0]]; return isObject(matchedValue) ? matchedValue.value : (matchedValue || ''); }, // 显示icon showDecimalIcon(item) { // 禁用是否显示 let showWhenDisabled = this.rateDisabled && this.valueDecimal > 0 && item - 1 < this.value && item > this.value; /* istanbul ignore next */ let showWhenAllowHalf = this.allowHalf && this.pointerAtLeftHalf && item - 0.5 <= this.currentValue && item > this.currentValue; return showWhenDisabled || showWhenAllowHalf; }, // 获取每个item的颜色 getIconStyle(item) { const voidColor = this.rateDisabled ? this.disabledVoidColor : this.voidColor; return { color: item <= this.currentValue ? this.activeColor : voidColor }; }, // 设置值 selectValue(value) { if (this.rateDisabled) { return; } if (this.allowHalf && this.pointerAtLeftHalf) { this.$emit('input', this.currentValue); this.$emit('change', this.currentValue); } else { this.$emit('input', value); this.$emit('change', value); } }, // 键盘事件 handleKey(e) { // 只读返回 if (this.rateDisabled) { return; } let currentValue = this.currentValue; const keyCode = e.keyCode; // 上箭头和右箭头 if (keyCode === 38 || keyCode === 39) { // 允许半星增加0.5 if (this.allowHalf) { currentValue += 0.5; } else { // 否则增加1 currentValue += 1; } e.stopPropagation(); e.preventDefault(); // 左箭头和下箭头,同理 } else if (keyCode === 37 || keyCode === 40) { if (this.allowHalf) { currentValue -= 0.5; } else { currentValue -= 1; } e.stopPropagation(); e.preventDefault(); } currentValue = currentValue < 0 ? 0 : currentValue; currentValue = currentValue > this.max ? this.max : currentValue; // 向外抛出chang和input事件,并传当前值 this.$emit('input', currentValue); this.$emit('change', currentValue); }, // 设置当前值 setCurrentValue(value, event) { // 只读则返回 if (this.rateDisabled) { return; } /* istanbul ignore if */ // 允许半星 if (this.allowHalf) { let target = event.target; if (hasClass(target, 'el-rate__item')) { target = target.querySelector('.el-rate__icon'); } if (hasClass(target, 'el-rate__decimal')) { target = target.parentNode; } // 鼠标在左半星减去半星,否则不变 this.pointerAtLeftHalf = event.offsetX * 2 <= target.clientWidth; this.currentValue = this.pointerAtLeftHalf ? value - 0.5 : value; } else { // 直接赋值 this.currentValue = value; } this.hoverIndex = value; }, // 重置当前值 resetCurrentValue() { // 只读则返回 if (this.rateDisabled) { return; } // 允许半星 if (this.allowHalf) { this.pointerAtLeftHalf = this.value !== Math.floor(this.value); } this.currentValue = this.value; this.hoverIndex = -1; } }, // 初始化 created() { if (!this.value) { this.$emit('input', 0); } } }; </script>