article-note.vue 6.44 KB
<template>
  <div class="article-detail-notes">
    <ArticleItemHeader v-if="share" :share="share" :data="authorData" :lazy="lazy" :more="false"
                       @on-follow="onFollowAuthor"></ArticleItemHeader>
    <LayoutHeader v-else ref="header" style="visibility: hidden;"></LayoutHeader>

    <div v-if="data.empty" class="article-empty">
      {{data.emptyTip || '文章不存在或文章被删除'}}
    </div>
    <div v-else class="article-context">
      <VideoPlayer ref="video" v-if="+data.sort === 4" class="note-video" :source="data.videoUrl" :cover="data.coverUrl"
                   :width="data.width" :height="data.height"></VideoPlayer>
      <ArticleItemSlide v-else :thumb="thumb" :share="share" :data="slideData" :slide-index="slideIndex" :lazy="lazy"
                        @on-praise="onPraise" @change="onChangeSlide"></ArticleItemSlide>
      <ProductGroup name="ArticleDetail" :article-id="data.articleId" :pos-id="0" :index="0" :thumb="thumb"
                    v-if="productListData.length" :share="share" :data="productListData" :lazy="lazy"></ProductGroup>

      <div class="context-title">{{data.articleTitle}}</div>

      <ArticleDetailIntro :data="introData" :pos-id="posId"></ArticleDetailIntro>
      <ArticleItemTopics :data="topicsData" :share="share"></ArticleItemTopics>

      <div class="publish-time">
        <span>{{publishTime}}</span>
        <div v-if="data.articleId && !share" class="more-wrap">
          <i class="iconfont icon-more1" @click="onMore"></i>
        </div>
      </div>

      <ArticleDetailCommentList ref="commentList" v-if="data.articleId"
                                :article-id="data.articleId"
                                :share="share"
                                :comment-count="articleState.commentCount"
                                :has-comment="data.hasComment"
                                :authorUid="data.authorUid"
                                :authorName="data.authorName">
      </ArticleDetailCommentList>

    </div>

    <div v-if="listTitle" class="rec-article-title">
      <LayoutTitle>{{listTitle}}</LayoutTitle>
    </div>
  </div>
</template>

<script>
import { get } from 'lodash';
import ArticleDetailCommentList from './comment-list';
import ArticleItemHeader from '../article/article-item-header';
import ArticleItemSlide from '../article/article-item-slide';
import ArticleItemTopics from '../article/article-item-topics';
import ArticleDetailIntro from './article-intro';
import dayjs from 'utils/day';
import { createNamespacedHelpers } from 'vuex';

const { mapState } = createNamespacedHelpers('article');

export default {
  name: 'ArticleDetailNote',
  props: {
    data: {
      type: Object,
      default() {
        return {};
      }
    },
    listTitle: String,
    scrollTop: Number,
    scrollTo: Function,
    share: Boolean,
    posId: Number,
  },
  data() {
    return {
      slideIndex: 1,
      commentList: [],
      commentPage: 1,
      commentFetching: false,
      showCommentMore: false,
      isRegister: false
    };
  },
  mounted() {
    this.$bus.$once('beforeFinishPage', () => {
      this.$refs.video && this.$refs.video.pausePlayer();
    });
  },
  computed: {
    ...mapState(['articleStates', 'authorStates']),
    articleState() {
      const articleState = this.articleStates[this.data.articleId] || this.data;
      const authorState = this.authorStates[`${this.data.authorUid}-${this.data.authorType}`] || this.data;

      return Object.assign({ ...articleState }, { hasAttention: authorState.hasAttention });
    },
    thumb() {
      return !!this.data.thumb;
    },
    authorData() {
      return {
        authorName: this.data.authorName,
        authorUid: this.data.authorUid,
        authorType: this.data.authorType,
        authorHeadIco: this.data.authorHeadIco,
        authGroupId: this.data.authGroupId,
        hasAttention: this.articleState.hasAttention,
        isAuthor: this.data.isAuthor
      };
    },
    slideData() {
      return {
        blockList: get(this.data, 'blockList', []).filter(block => block.templateKey === 'image'),
        articleId: this.data.articleId,
      };
    },
    productListData() {
      return this.data.productList || [];
    },
    introData() {
      let intro;

      if (this.data.sort === 4) {
        intro = this.data.content;
      } else {
        intro = get(get(this.data, 'blockList', []).filter(block => block.templateKey === 'text'), '[0].contentData', '');
      }
      return {
        authorType: this.data.authorType,
        authorUid: this.data.authorUid,
        articleTitle: this.data.articleTitle,
        intro,
        atUserInfo: this.data.atUserInfo ?? {},
        maxLines: 10
      };
    },
    topicsData() {
      return {
        topicList: this.data.topicList,
        articleType: this.data.articleType
      };
    },
    publishTime() {
      return this.data.publishTime ? dayjs(this.data.publishTime).fromNow() : '';
    },
    lazy() {
      return this.data.lazy;
    }
  },
  methods: {
    onFollowAuthor(follow) {
      this.$emit('on-follow', this.data, follow);
    },
    onPraise() {
      this.$emit('on-praise');
    },
    onChangeSlide({ index }) {
      this.slideIndex = index;
    },
    onComment({ comment }) {
      this.$refs.commentList && this.$refs.commentList.addComment(comment);
    },
    toCommentList() {
      if (this.$refs.commentList && this.scrollTo) {
        this.scrollTo({ scrollTop: this.$refs.commentList.$el.offsetTop - get(this.$refs, 'header.$el.offsetHeight', 0) });
      }
    },
    onMore() {
      this.$emit('on-show-more');
    }
  },
  components: {
    ArticleItemHeader,
    ArticleItemSlide,
    ArticleItemTopics,
    ArticleDetailIntro,
    ArticleDetailCommentList,
  }
};
</script>

<style lang="scss" scoped>
.article-empty {
  height: 900px;
  color: #ccc;
  font-size: 28px;
  background-color: #f0f0f0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.context-title {
  font-size: 36px;
  color: #222;
  line-height: 56px;
  margin-top: 10px;
  margin-bottom: 16px;
  letter-spacing: 0.68px;
  font-weight: bolder;
  padding: 0 30px;
}

.publish-time {
  font-size: 24px;
  color: #b0b0b0;
  padding: 20px 30px;
  display: flex;
  align-items: center;
  justify-content: space-between;

  .more-wrap {
    display: flex;
    align-items: center;
    color: #444;

    .iconfont {
      font-size: 44px;
    }
  }
}

.note-video {
  height: 750px;
}

.rec-article-title {
  padding: 14px 0 4px;
  border-top: 1px solid #f0f0f0;
}
</style>