article-item-intro.vue 7.78 KB
<template>
  <div class="article-item-intro">
    <div ref="intro" v-if="intro" class="intro" :class="introClass" :style="introStyle" @click="onExpanding">
      <p class="pre-wrap">{{intro}}</p>
      <div ref="introPool" class="intro-pool pre-wrap" v-if="!data.introHeight && isEllipsis">{{trimIntro}}</div>
      <span ref="expand" class="expand" v-if="showExpandTxt">…<b>继续阅读</b></span>
      <div class="collapse" v-if="showCollapseTxt">收起</div>
    </div>
    <div class="topics">
      <WidgetTopic
        :share="share"
        :topic="topic.topicName"
        @click.native="onTopic(topic)"
        v-for="topic in data.topicList"
        :key="topic.topicId">
      </WidgetTopic>
    </div>
    <div class="widgets">
      <div class="share">
        <WidgetShare :share="share" @click.native="onShare"></WidgetShare>
      </div>
      <div class="opts">
        <WidgetFav ref="favIcon" :class="invisibleClass" :share="share" :num="data.praiseCount" :article-id="data.articleId" :option="praiseOption" :pos-id="posId"></WidgetFav>
        <WidgetLike :class="invisibleClass" :share="share" :num="data.favoriteCount" :article-id="data.articleId" :option="favoriteOption" :pos-id="posId"></WidgetLike>
        <WidgetComment :class="invisibleClass" :share="share" :num="data.commentCount" @click.native="onShowComment"></WidgetComment>
      </div>
    </div>
  </div>
</template>

<script>
import {get, forEach, trim} from 'lodash';
import {createNamespacedHelpers} from 'vuex';
import YAS from 'utils/yas-constants';
const {mapMutations} = createNamespacedHelpers('article');

export default {
  name: 'ArticleItemIntro',
  props: {
    data: {
      type: Object,
      default() {
        return {};
      }
    },
    type: String,
    share: Boolean,
    thumb: Boolean,
    posId: Number
  },
  data() {
    return {
      showIntro: '',
      animateing: true,
      canRetract: false
    };
  },
  computed: {
    invisibleClass() {
      return {
        invisible: !this.data.hasFavor && !this.data.hasPraise
      };
    },
    showExpandTxt() {
      return (!this.data.isExpand && this.animateing && this.isEllipsis) || this.data.articleType === 2;
    },
    showCollapseTxt() {
      return this.data.isExpand && this.animateing && this.isEllipsis && this.canRetract;
    },
    isEllipsis() {
      let line = 0;
      let textArr = [];

      forEach(this.trimIntro, (val) => {
        let isEnter = /(\r\n)|(\n)/.test(val);

        textArr[line] = textArr[line] || '';
        textArr[line] += val;

        if (textArr[line].length >= 24 || isEnter) {
          line++;
        }

        if (line > 5) {
          textArr[5] = textArr[5].substring(0, Math.min(21, textArr[5].length));
          return false;
        }
      });

      this.showIntro = trim(textArr.join(''));
      textArr.length = 0;

      return line > 5;
    },
    intro() {
      if (this.data.isExpand || !this.isEllipsis) {
        return this.trimIntro;
      } else {
        return this.showIntro;
      }
    },
    trimIntro() {
      return trim(this.data.intro);
    },
    introClass() {
      return {
        'intro-expand': this.data.isExpand,
        'no-more': !this.isEllipsis,
        'intro-will-change': !this.animateing
      };
    },
    introStyle() {
      let introHeight;

      if (this.data.isExpand) {
        introHeight = this.data.introHeight;
      } else {
        introHeight = this.data.introCollapseHeight;
      }

      return {
        height: introHeight ? `${introHeight}px` : void 0
      };
    },
    favoriteOption() {
      return {
        selected: this.data.hasFavor === 'Y'
      };
    },
    praiseOption() {
      return {
        selected: this.data.hasPraise === 'Y'
      };
    }
  },
  mounted() {
    this.init();
  },
  destroyed() {
    this.$refs.intro && this.$refs.intro.removeEventListener('transitionend', this.onExpand.bind(this));
  },
  methods: {
    ...mapMutations(['CHANGE_ARTICLE_LIST_INTRO_HEIGHT', 'CHANGE_ARTICLE_LIST_INTRO']),
    init() {
      if (this.data.introHeight === 0 && this.isEllipsis) {
        this.CHANGE_ARTICLE_LIST_INTRO_HEIGHT({
          articleId: this.data.articleId,
          introHeight: get(this.$refs, 'introPool.scrollHeight', 0) + 20,
          introCollapseHeight: get(this.$refs, 'intro.offsetHeight', 0),
          type: this.type
        });
      }
      this.$refs.intro && this.$refs.intro.addEventListener('transitionend', this.onExpand.bind(this));
    },
    onTopic({topicId, topicName}) {
      if (this.share) {
        return this.$links.toDownloadApp();
      }
      if (this.data.articleType === 5) {
        return;
      }

      this.$router.push({
        name: 'topic',
        params: {
          topicId,
          topicName
        }
      });

      this.reportLabel(topicId);
    },
    onShare() {
      this.$yoho.share({
        title: `@${this.data.authorName} 在有货社区上发了一篇笔记,快点开看看!`,
        imgUrl: this.data.shareImage,
        link: `${location.origin}/grass/article/share/${this.data.articleId}`,
        desc: this.data.intro,
        hideType: ['7', '8', '9']
      });
    },
    onExpanding() {
      if (this.data.articleType === 2) {
        return this.$emit('on-show-guang');
      }

      if (!this.isEllipsis || (this.data.isExpand && !this.canRetract)) {
        return;
      }

      if (!this.canRetract) {
        this.canRetract = this.data.introHeight - 21 > get(this.$refs, 'expand.offsetHeight', 0) * 10;
      }

      const isExpand = !this.data.isExpand;

      this.$emit('on-expanding');

      this.animateing = false;
      this.CHANGE_ARTICLE_LIST_INTRO({
        articleId: this.data.articleId,
        isExpand,
        type: this.type
      });
    },
    onExpand() {
      this.animateing = true;
      this.$nextTick(() => {
        this.$emit('on-expand');
      });
    },
    onShowComment() {
      this.$emit('on-show-comment', {
        commentCount: this.data.commentCount
      });
    },
    onPraise() {
      if (this.data.hasPraise !== 'Y') {
        this.$refs.favIcon.click();
      }
    },
    reportLabel(id) {
      this.$store.dispatch('reportYas', {
        params: {
          appop: YAS.eventName.labelClick,
          param: {
            LABEL_ID: id,
            POS_ID: this.posId
          }
        }
      });
    }
  }
};
</script>

<style lang="scss" scoped>
.article-item-intro {
  width: 100%;
  padding: 0 30px;
  margin-top: 20px;

  .intro {
    font-size: 28px;
    color: #4a4a4a;
    letter-spacing: 0.06PX;
    overflow: hidden;
    position: relative;
    line-height: 48px;
    box-sizing: content-box;
    transition: height 250ms cubic-bezier(0.165, 0.84, 0.44, 1);

    .pre-wrap {
      white-space: pre-wrap;
      word-wrap: break-word;
      display: inline;
    }

    &.intro-will-change {
      will-change: height;
    }

    &.intro-expand {
      // overflow-y: auto;
      -webkit-line-clamp: initial;
      text-overflow: initial;
    }

    &.no-more {
      height: auto;
    }
  }

  .intro-pool {
    position: absolute;
    width: 100%;
    top: -1000px;
    visibility: hidden;
  }

  .collapse {
    width: 100%;
    text-align: right;
    font-size: 28px;
    color: #000;
    font-weight: bold;
    position: absolute;
    right: 0;
    bottom: 0;
  }

  .expand {
    font-size: 28px;
    color: #000;
    line-height: 20px;

    &.collapse {
      position: absolute;
      right: 14px;
      bottom: 0;
      height: 28px;
    }

    > b {
      font-weight: bold;
    }
  }
}

.topics {
  width: 100%;
  margin-top: 32px;
  line-height: 60px;

  & /deep/ .topic-wrap {
    margin-right: 10px;
  }
}

.widgets {
  width: 100%;
  display: flex;
  margin-top: 40px;

  .topic {
    text-align: left;
    width: 200px;
  }

  .opts {
    text-align: right;
    flex: 1;

    & /deep/ .icon-btn {
      margin-left: 44px;
      text-align: right;
    }
  }
}
</style>