widget-icon-btn.vue 9.96 KB
<template>
  <div class="icon-btn" :class="{'btn-selected': viewOption.selected}" :style="btnStyle">
    <AuthComponent :auth="isAuth" class="click-wrap" @click="onClick"></AuthComponent>
    <i class="iconfont" :class="iconClass" :style="iconStyle">
      <div v-if="type === 'fav'" class="praise-lottie">
        <WidgetLottie ref="lottie" class="praise-lottie-wrap" :options="lottieOptions"></WidgetLottie>
      </div>
    </i>
    <p v-if="viewText" class="icon-btn-text" :style="textStyle">
      <span class="view-text">{{Number(viewText) > 0 ? viewText : ''}}</span>
      <span class="placeholder-text">{{placText}}</span>
    </p>
  </div>
</template>

<script>
import {forEach, fill} from 'lodash';
import {createNamespacedHelpers} from 'vuex';
const {mapActions} = createNamespacedHelpers('user');
const {mapActions: articleMapActions} = createNamespacedHelpers('article');

import YAS from 'utils/yas-constants';
import lottieJson from '../../statics/lottie/list-heart.json';

const classMap = {
  fav: {
    default: 'icon-heart',
    selected: 'icon-heart imgfont img-heart-fill',
    boldSelected: 'icon-heart-fill',
  },
  star: {
    default: 'icon-star',
    selected: 'icon-star imgfont img-star-fill'
  },
  share: {
    default: 'icon-share',
    boldDefault: 'icon-share-blod',
  },
  msg: {
    default: 'icon-msg'
  }
};

const defaultOption = {
  canSelect: true, // 是否支持选中
  selected: false, // 初始选中状态(不受是否支持选中控制)
  color: '#444', // btn字体颜色
  selectedColor: '#d90025', // btn选中状态字体颜色(不设置默认与非选中一致)
  iconBold: false, // icon粗体
  iconFontSize: 48, // icon字号(单位px)
  textSelectedColor: '#444', // text选中状态字体颜色(不设置默认与selectedColor一致)
  textFontSize: 20, // text字号(单位px)
  textAlign: 'top', // text位置, 默认normal(支持normal, top, bottom)
  textZoom: 0.9, // text缩放
  textAutoChange: true, // text自动增减,只支持number类型(受是否支持选中控制)
  emitName: '' // 点击触发事件名称
};

export default {
  name: 'WidgetIconBtn',
  props: {
    type: {
      type: String,
      default: 'fav'
    },
    text: {
      type: [String, Number],
      default() {
        return '';
      }
    },
    articleId: Number,
    posId: Number,
    commentId: Number,
    option: {
      type: Object,
      default() {
        return defaultOption;
      }
    },
    share: Boolean
  },
  data() {
    return {
      currentClass: {},
      viewOption: {},
      editText: null,
      lottieOptions: {
        animationData: lottieJson
      }
    };
  },
  created() {
    this.setOptions();
    this.setCurrentClass();
  },
  computed: {
    syncFnName() {
      if (this.articleId) {
        if (this.type === 'fav') {
          return 'praiseArticle';
        } else if (this.type === 'star') {
          return 'followArticle';
        }
      } else if (this.commentId && this.type === 'fav') {
        return 'praiseComment';
      }
    },
    syncReportName() {
      if (this.articleId) {
        if (this.type === 'fav') {
          return 'reportFav';
        } else if (this.type === 'star') {
          return 'reportStar';
        }
      }

      return '';
    },
    isAuth() {
      return !!this.syncFnName;
    },
    btnStyle() {
      let color = this.viewOption.color || defaultOption.color;

      return `color: ${this.viewOption.selected ? (this.viewOption.selectedColor || color) : color};`;
    },
    iconClass() {
      return this.viewOption.selected ? this.currentClass.selected : this.currentClass.default;
    },
    iconStyle() {
      return `font-size: ${this.pxToRem(this.viewOption.iconFontSize)};`;
    },
    textStyle() {
      let style = `font-size: ${this.pxToRem(this.viewOption.textFontSize)};`;

      let textAlign = this.viewOption.textAlign;

      if (['top', 'bottom'].indexOf(textAlign) >= 0) {
        style += ` vertical-align: ${textAlign};`;
      }

      let textZoom = this.viewOption.textZoom;

      if (!isNaN(Number(textZoom))) {
        style += ` transform: scale(${textZoom}, ${textZoom});`;
      }

      if (this.viewOption.textSelectedColor) {
        style += ` color: ${this.viewOption.textSelectedColor};`;
      }

      return style;
    },
    viewText() {
      return `${(this.editText === null ? this.text : this.editText)}`;
    },
    placText() {
      if (!isNaN(Number(this.text)) && this.viewText.length) {
        return fill(Array(this.viewText.length), 0).join('');
      } else {
        return `${this.text}`;
      }
    }
  },
  watch: {
    type() {
      this.setCurrentClass();
    },
    option(val, oldVal) {
      if (oldVal.iconBold !== val.iconBold) {
        this.setCurrentClass();
      }

      this.setOptions();
    }
  },
  methods: {
    ...mapActions(['followArticle', 'praiseArticle', 'praiseComment']),
    ...articleMapActions(['fetchArticleUpdate']),
    pxToRem(px) {
      const rootValue = 40;

      if (typeof px !== 'number') {
        px = parseInt(`0${px}`, 10);
      }

      if (px > 2) {
        return (px / rootValue).toFixed(2) + 'rem';
      } else {
        return px + 'px';
      }
    },
    setOptions() {
      forEach(defaultOption, (value, key) => {
        this.viewOption[key] = this.option.hasOwnProperty(key) ? this.option[key] : defaultOption[key];
      });

      this.viewOption = {...this.viewOption};
    },
    setCurrentClass() {
      let currentClass = classMap[this.type] || classMap.fav;

      currentClass = {...currentClass};
      currentClass.selected = currentClass.selected || currentClass.default;

      if (this.option.iconBold) {
        currentClass.boldDefault && (currentClass.default = currentClass.boldDefault);
        currentClass.boldSelected && (currentClass.selected = currentClass.boldSelected);
      }

      this.currentClass = currentClass;
    },
    changeBtnStatus(e) {
      this.viewOption.selected = !this.viewOption.selected;

      if (e && this.type === 'fav' && this.viewOption.selected) {
        this._lottieTimer && clearTimeout(this._lottieTimer);
        this.$refs.lottie.play();
        this._lottieTimer = setTimeout(() => {
          this.$refs.lottie.stop();
        }, 1000);
      }

      if (this.viewOption.textAutoChange) {
        if (!isNaN(Number(this.viewText))) {
          this.editText = Number(this.viewText) + (this.viewOption.selected ? 1 : -1);
        }
      }
    },
    syncService(type, data) {
      if (typeof this[type] === 'function') {
        return this[type](data);
      } else {
        return Promise.resolve({code: 404});
      }
    },
    onClick(evt) {
      if (this.share) {
        return this.$links.toDownloadApp();
      }
      if (this.syncing) {
        return;
      }

      if (this.viewOption.canSelect) {
        this.changeBtnStatus(evt);

        if (this.syncFnName) {
          const backFn = (res) => {
            this.syncing = false;

            if (res.code !== 200) {
              this.changeBtnStatus();
              this.$createToast && this.$createToast({
                txt: res.message || '服务器开小差了',
                type: 'warn',
                time: 1000
              }).show();
            } else {
              if (this.viewOption.selected && this.type === 'star') {
                this.$createToast && this.$createToast({
                  txt: '收藏成功',
                  type: 'txt',
                  time: 1000
                }).show();
              }

              if (['praiseArticle', 'followArticle'].some(s => s === this.syncFnName)) {
                this.fetchArticleUpdate({articleId: this.articleId});
              }
            }

            this[this.syncReportName] && this[this.syncReportName]();
          };

          this.syncing = true;

          this.syncService(this.syncFnName, {
            articleId: this.articleId,
            commentId: this.commentId,
            status: this.viewOption.selected
          }).then(backFn).catch(backFn);
        }
      }

      this.viewOption.emitName && this.$emit(this.viewOption.emitName, evt);
    },
    reportFav() {
      if (this.viewOption.selected) {
        this.$store.dispatch('reportYas', {
          params: {
            appop: YAS.eventName.fav,
            param: {
              POS_ID: this.posId,
              ARTICLE_ID: this.articleId
            }
          }
        });
      }
    },
    reportStar() {
      if (this.viewOption.selected) {
        this.$store.dispatch('reportYas', {
          params: {
            appop: YAS.eventName.star,
            param: {
              POS_ID: this.posId,
              ARTICLE_ID: this.articleId
            }
          }
        });
      }
    }
  },
};
</script>

<style type="scss">
  .icon-btn {
    display: inline-block;
    line-height: 1;
    vertical-align: middle;
    position: relative;

    > * {
      display: inline-block;
      vertical-align: middle;
    }

    .click-wrap {
      width: 150%;
      height: calc(100% + 30px);
      position: absolute;
      left: -25%;
      top: -15px;
      z-index: 2;
    }

    .icon-btn-text {
      position: relative;

      .view-text {
        position: absolute;
      }

      .placeholder-text {
        opacity: 0;
      }
    }

    .iconfont {
      position: relative;
      z-index: 0;
    }

    .imgfont {
      &:before {
        opacity: 0;
      }

      &:after {
        width: 100%;
        height: 100%;
        content: "";
        position: absolute;
        left: 0;
        top: 0;
        z-index: 1;
        background-size: 100%;
      }
    }

    .img-heart-fill:after {
      background-image: url('~statics/image/components/img-icon-heart-fill.png');
    }

    .img-star-fill:after {
      background-image: url('~statics/image/components/img-icon-star-fill.png');
    }

    .praise-lottie {
      position: absolute;
      width: 90px;
      height: 60px;
      overflow: hidden;
      left: calc(50% - 42px);
      top: -54px;


      .praise-lottie-wrap {
        width: 100%;
        height: 214%!important;
      }
    }
  }
</style>