element-ui Rate組件源碼分析整理筆記(十三)


Rate組件源碼比較簡單,有添加部分注釋

main.vue

<template>
    <!--valuenow當前的評分 valuetext當前顯示的文本-->
  <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>
      <!--showText是否顯示輔助文字,若為真,則會從 texts 數組中選取當前分數對應的文字內容;showScore是否顯示當前分數,show-score 和 show-text 不能同時為真-->
    <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 Migrating from 'element-ui/src/mixins/migrating';

  export default {
    name: 'ElRate',

    mixins: [Migrating],
    //provider/inject:簡單的來說就是在父組件中通過provider來提供變量,然后在子組件中通過inject來注入變量。
    inject: {
      elForm: {
        default: ''
      }
    },

    data() {
      return {
        pointerAtLeftHalf: true,
        currentValue: this.value,
        hoverIndex: -1
      };
    },

    props: {
      value: {
        type: Number,
        default: 0
      },
      lowThreshold: { //低分和中等分數的界限值,值本身被划分在低分中
        type: Number,
        default: 2
      },
      highThreshold: { //高分和中等分數的界限值,值本身被划分在高分中
        type: Number,
        default: 4
      },
      max: { //最大分值
        type: Number,
        default: 5
      },
      colors: { //icon 的顏色數組,共有 3 個元素,為 3 個分段所對應的顏色
        type: Array,
        default() {
          return ['#F7BA2A', '#F7BA2A', '#F7BA2A'];
        }
      },
      voidColor: { //未選中 icon 的顏色
        type: String,
        default: '#C6D1DE'
      },
      disabledVoidColor: { //只讀時未選中 icon 的顏色
        type: String,
        default: '#EFF2F7'
      },
      iconClasses: { //icon 的類名數組,共有 3 個元素,為 3 個分段所對應的類名
        type: Array,
        default() {
          return ['el-icon-star-on', 'el-icon-star-on', 'el-icon-star-on'];
        }
      },
      voidIconClass: { //未選中 icon 的類名
        type: String,
        default: 'el-icon-star-off'
      },
      disabledVoidIconClass: { //只讀時未選中 icon 的類名
        type: String,
        default: 'el-icon-star-on'
      },
      disabled: { //是否為只讀
        type: Boolean,
        default: false
      },
      allowHalf: { //是否允許半選
        type: Boolean,
        default: false
      },
      showText: { //是否顯示輔助文字,若為真,則會從 texts 數組中選取當前分數對應的文字內容
        type: Boolean,
        default: false
      },
      showScore: { //是否顯示當前分數,show-score 和 show-text 不能同時為真
        type: Boolean,
        default: false
      },
      textColor: { //輔助文字的顏色
        type: String,
        default: '#1f2d3d'
      },
      texts: {  //輔助文字數組
        type: Array,
        default() {
          return ['極差', '失望', '一般', '滿意', '驚喜'];
        }
      },
      scoreTemplate: {  //分數顯示模板
        type: String,
        default: '{value}'
      }
    },

    computed: {
      text() {
        let result = '';
        //如果顯示當前分數
        if (this.showScore) {
          //如果當前是只讀狀態,就顯示v-model綁定的值,否則根據用戶的評分顯示值
          result = this.scoreTemplate.replace(/\{\s*value\s*\}/, this.rateDisabled
            ? this.value
            : this.currentValue);
        } else if (this.showText) { //如果顯示輔助文字,則根據用戶設置評分currentValue來顯示texts數組中的文字
          result = this.texts[Math.ceil(this.currentValue) - 1];
        }
        return result;
      },
      //高亮半星時添加的樣式,顏色以及寬度
      decimalStyle() {
        let width = '';
        if (this.rateDisabled) {
          //這里判斷value的值是否含有小數,有小數的則這里寬度為50%,為整數的話為0%
          width = `${ this.valueDecimal < 50 ? 0 : 50 }%`;
        }
        if (this.allowHalf) {
          width = '50%';
        }
        return {
          color: this.activeColor,
          width
        };
      },

      valueDecimal() {
        return this.value * 100 - Math.floor(this.value) * 100;
      },

      decimalIconClass() {
        return this.getValueFromMap(this.value, this.classMap);
      },

      voidClass() {
        return this.rateDisabled ? this.classMap.disabledVoidClass : this.classMap.voidClass;
      },
      //根據currentValue的分所在的等級,返回對應的類名
      activeClass() {
        return this.getValueFromMap(this.currentValue, this.classMap);
      },

      colorMap() {
        return {
          lowColor: this.colors[0],
          mediumColor: this.colors[1],
          highColor: this.colors[2],
          voidColor: this.voidColor,
          disabledVoidColor: this.disabledVoidColor
        };
      },
      //根據currentValue的分所在的等級,返回對應的顏色
      activeColor() {
        return this.getValueFromMap(this.currentValue, this.colorMap);
      },
      //這里主要是判斷該星是選中還是未選中,分別加入選中和未選中icon類名
      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;
      },

      classMap() {
        return {
          lowClass: this.iconClasses[0],
          mediumClass: this.iconClasses[1],
          highClass: this.iconClasses[2],
          voidClass: this.voidIconClass,
          disabledVoidClass: this.disabledVoidIconClass
        };
      },

      rateDisabled() {
        //是否為只讀,或者父組件el-form中disabled的屬性值,disabled是否禁用該表單內的所有組件。若設置為 true,則表單內組件上的 disabled 屬性不再生效
        return this.disabled || (this.elForm || {}).disabled;
      }
    },

    watch: {
      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.'
          }
        };
      },
      //判斷當前value在屬於低分、中等分、高分中的哪個,根據不同等級返回不同的類名或者顏色
      getValueFromMap(value, map) {
        let result = '';
        if (value <= this.lowThreshold) {
          result = map.lowColor || map.lowClass;
        } else if (value >= this.highThreshold) {
          result = map.highColor || map.highClass;
        } else {
          result = map.mediumColor || map.mediumClass;
        }
        return result;
      },

      showDecimalIcon(item) {
        //如果當前value包含小數,並且item - 1 < this.value <item, showWhenDisabled為true
        let showWhenDisabled = this.rateDisabled && this.valueDecimal > 0 && item - 1 < this.value && item > this.value;
        //這里主要也是判斷是否當前星是否應顯示半星
        let showWhenAllowHalf = this.allowHalf &&
          this.pointerAtLeftHalf &&
          item - 0.5 <= this.currentValue &&
          item > this.currentValue;
        return showWhenDisabled || showWhenAllowHalf;
      },
      //返回當前星圖標的顏色
      getIconStyle(item) {
        //voidColor的值是根據是否只讀來判斷返回disabled-void-color或者void-color
        const voidColor = this.rateDisabled ? this.colorMap.disabledVoidColor : this.colorMap.voidColor;
        return {
          //判斷當前星是顯示高亮的顏色還是未選中時的顏色
          color: item <= this.currentValue ? this.activeColor : voidColor
        };
      },
       //點擊時設置值
      selectValue(value) {
        if (this.rateDisabled) {
          return;
        }
        //當可以顯示半星時,這塊傳遞的值為currentValue(鼠標移上去時會計算是否超過一半)
        if (this.allowHalf && this.pointerAtLeftHalf) {
          this.$emit('input', this.currentValue);
          this.$emit('change', this.currentValue);
        } else { //當不顯示半星直接返回value
          this.$emit('input', value);
          this.$emit('change', value);
        }
      },
      //當按鍵按下時所調用的方法
      handleKey(e) {
        //如果組件被禁用則按鍵事件無效
        if (this.rateDisabled) {
          return;
        }
        let currentValue = this.currentValue;
        const keyCode = e.keyCode;
        //當按上下左右鍵的時候,允許半選則在currentValue加或減0.5,不允許則加或減1
        if (keyCode === 38 || keyCode === 39) { // up / right
          if (this.allowHalf) {
            currentValue += 0.5;
          } else {
            currentValue += 1;
          }
          e.stopPropagation();
          e.preventDefault();
        } else if (keyCode === 37 || keyCode === 40) { // left /down
          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;
        //將currentValue通過input傳遞給子組件綁定的v-model值,觸發change事件,子組件可以change在獲取改變后的值
        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;
          //鼠標移動到包裹星星圖標的span標簽時,獲取到顯示星的icon標簽
          if (hasClass(target, 'el-rate__item')) {
            target = target.querySelector('.el-rate__icon');
          }
          if (hasClass(target, 'el-rate__decimal')) {
            target = target.parentNode;
          }
          //根據鼠標移到一顆星的左邊一半以內的位置,則減去當前值的0.5,否則就是等於當前值
          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) {
          //如果當前的value是小數,pointerAtLeftHalf為true,如果是整數則為false,這里主要是為了點擊時用來判斷傳哪個值
          this.pointerAtLeftHalf = this.value !== Math.floor(this.value);
        }
        //鼠標移上去時currentValue會改變,移走時currentValue等於之前的value
        this.currentValue = this.value;
        this.hoverIndex = -1;
      }
    },

    created() {
      if (!this.value) {
        this.$emit('input', 0);
      }
    }
  };
</script>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM