<template>
  <div>
    <div ref="m-star" class="m-star">
      <div
        class="m-star__icon"
        :style="{ 'margin-right': margin + 'px' }"
        v-for="(star, index) in stars"
        :key="index"
        @touchstart.stop="touchstart"
        @touchmove.stop="touchmove"
        @mousedown.stop="mousedown"
        @mousemove.stop="mousemove"
        @mouseleave="mouseleave"
      >
        <div
          class="star"
          :style="{ width: size + 'px', height: size + 'px' }"
        />
        <div :style="{ width: star.activeWitch }" class="m-star__icon-on">
          <div
            class="star-fill"
            :style="{ width: size + 'px', height: size + 'px' }"
          />
        </div>
      </div>
      <div
        v-show="showLabel"
        :style="{ lineHeight: size + 'px', color: '#aeaeae', fontSize: '12px' }"
      >
        {{ label }}
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "UniRate",
  props: {
    isFill: {
      // 星星的类型，是否镂空
      type: [Boolean, String],
      default: true,
    },
    color: {
      // 星星未选中的颜色
      type: String,
      default: "#ececec",
    },
    activeColor: {
      // 星星选中状态颜色
      type: String,
      default: "#ffca3e",
    },
    size: {
      // 星星的大小
      type: [Number, String],
      default: 20,
    },
    value: {
      // 当前评分
      type: [Number, String],
      default: 1,
    },
    max: {
      // 最大评分
      type: [Number, String],
      default: 5,
    },
    margin: {
      // 星星的间距
      type: [Number, String],
      default: 8,
    },
    readonly: {
      // 是否只读
      type: [Boolean, String],
      default: false,
    },
    allowHalf: {
      // 是否显示半星
      type: [Boolean, String],
      default: false,
    },
    touchable: {
      // 是否支持滑动手势
      type: [Boolean, String],
      default: true,
    },
    showLabel: Boolean,
  },
  data() {
    return {
      valueSync: "",
      userMouseFristMove: true,
      userRated: false,
      userLastRate: 1,
    };
  },
  watch: {
    value(newVal) {
      this.valueSync = Number(newVal);
    },
  },
  computed: {
    stars() {
      const value = this.valueSync ? this.valueSync : 0;
      const starList = [];
      const floorValue = Math.floor(value);
      const ceilValue = Math.ceil(value);
      for (let i = 0; i < this.max; i++) {
        if (floorValue > i) {
          starList.push({
            activeWitch: "100%",
          });
        } else if (ceilValue - 1 === i) {
          starList.push({
            activeWitch: (value - floorValue) * 100 + "%",
          });
        } else {
          starList.push({
            activeWitch: "0",
          });
        }
      }
      return starList;
    },
    label() {
      let per = this.valueSync / this.max;
      let label = "";
      if (per <= 0.2) {
        label = "非常差";
      } else if (per <= 0.4) {
        label = "差";
      } else if (per <= 0.6) {
        label = "一般";
      } else if (per <= 0.8) {
        label = "好";
      } else if (per <= 1) {
        label = "非常好";
      }

      return label;
    },
  },
  created() {
    this.valueSync = Number(this.value);
    this._rateBoxLeft = 0;
    this._oldValue = null;
  },
  mounted() {
    setTimeout(() => {
      this._getSize();
    }, 100);
  },
  methods: {
    touchstart(e) {
      if (this.readonly) return;
      const { clientX, screenX } = e.changedTouches[0];
      // TODO 做一下兼容，只有 Nvue 下才有 screenX，其他平台式 clientX
      this._getRateCount(clientX || screenX);
    },
    touchmove(e) {
      if (this.readonly || !this.touchable) return;
      const { clientX, screenX } = e.changedTouches[0];
      this._getRateCount(clientX || screenX);
    },

    mousedown(e) {
      if (this.readonly) return;
      const { clientX } = e;
      this.userLastRate = this.valueSync;
      this._getRateCount(clientX);
      this.userRated = true;
    },
    mousemove(e) {
      if (this.userRated) return;
      if (this.userMouseFristMove) {
        console.log("---mousemove----", this.valueSync);
        this.userLastRate = this.valueSync;
        this.userMouseFristMove = false;
      }
      if (this.readonly || !this.touchable) return;
      const { clientX } = e;
      this._getRateCount(clientX);
    },
    mouseleave(e) {
      if (this.readonly || !this.touchable) return;
      if (this.userRated) {
        this.userRated = false;
        return;
      }
      this.valueSync = this.userLastRate;
    },

    /**
     * 获取星星个数
     */
    _getRateCount(clientX) {
      const size = Number(this.size);
      if (size === NaN) {
        return new Error("size 属性只能设置为数字");
      }
      const rateMoveRange = clientX - this._rateBoxLeft;
      let index = parseInt(rateMoveRange / (size + this.margin));
      index = index < 0 ? 0 : index;
      index = index > this.max ? this.max : index;
      const range = parseInt(rateMoveRange - (size + this.margin) * index);
      let value = 0;
      if (this._oldValue === index) return;
      this._oldValue = index;
      if (this.allowHalf) {
        if (range > size / 2) {
          value = index + 1;
        } else {
          value = index + 0.5;
        }
      } else {
        value = index + 1;
      }

      value = Math.max(0.5, Math.min(value, this.max));
      this.valueSync = value;
      this._onChange();
    },

    /**
     * 触发动态修改
     */
    _onChange() {
      this.$emit("input", this.valueSync);
      this.$emit("change", {
        value: this.valueSync,
      });
    },
    /**
     * 获取星星距离屏幕左侧距离
     */
    _getSize() {
      if(this.$refs["m-star"]){
        this._rateBoxLeft = this.$refs["m-star"].getBoundingClientRect().left;
      }
    },
  },
};
</script>

<style lang="less" scoped>
.m-star {
  display: flex;
  line-height: 1;
  font-size: 0;
  flex-direction: row;
  cursor: pointer;
}

.m-star {
  .star {
    background: url("~@/assets/img/star.png") no-repeat;
    background-size: 100%;
  }
  .star-fill {
    background: url("~@/assets/img/star-fill.png") no-repeat;
    background-size: 100%;
  }
}

.m-star__icon {
  position: relative;
  line-height: 1;
  font-size: 0;
}

.m-star__icon-on {
  overflow: hidden;
  position: absolute;
  top: 0;
  left: 0;
  line-height: 1;
  text-align: left;
}
</style>