widget-icon-btn.vue 7.79 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"></i>
    <p v-if="viewText" class="icon-btn-text" :style="textStyle">
      <span class="view-text">{{Number(viewText) ? 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 cookie from 'yoho-cookie';

const classMap = {
  fav: {
    default: 'icon-zan',
    selected: 'icon-zan-fill',
    boldDefault: 'icon-zan-bold'
  },
  star: {
    default: 'icon-star',
    selected: 'icon-star-fill'
  },
  share: {
    default: 'icon-share'
  },
  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,
    commentId: Number,
    option: {
      type: Object,
      default() {
        return defaultOption;
      }
    },
    share: Boolean
  },
  data() {
    return {
      currentClass: {},
      viewOption: {},
      actionClass: '',
      editText: null
    };
  },
  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';
      }
    },
    isAuth() {
      return !!this.syncFnName;
    },
    btnStyle() {
      let color = this.viewOption.color || defaultOption.color;

      return `color: ${this.viewOption.selected ? (this.viewOption.selectedColor || color) : color};`;
    },
    iconClass() {
      if (this.actionClass) {
        return this.actionClass;
      }

      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 (Number(textZoom) !== NaN) {
        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.selected !== val.selected) {
        this.actionClass = '';
      }

      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() {
      this.viewOption.selected = !this.viewOption.selected;

      if (this.viewOption.textAutoChange) {
        if (!isNaN(Number(this.viewText))) {
          this.editText = Number(this.viewText) + (this.viewOption.selected ? 1 : -1);
        }
      }

      this.actionClass = this.viewOption.selected ? this.currentClass.selected : this.currentClass.default;
    },
    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();

        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 (['praiseArticle', 'followArticle'].some(s => s === this.syncFnName)) {
                this.fetchArticleUpdate({articleId: this.articleId});
              }
            }
          };

          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);
    }
  },
};
</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: 1;
    }

    .icon-btn-text {
      position: relative;

      .view-text {
        position: absolute;
      }

      .placeholder-text {
        opacity: 0;
      }
    }
  }
</style>