article-note.vue 8.38 KB
<template>
  <div class="article-detail-notes">
    <ArticleDetailHeader ref="header" class="article-detail-header" :data="data" :step="100" :title-step="100">
      <div v-if="data.articleId && !data.empty" class="title-main">
        <div class="title-info" :style="`transform: translate3d(0, ${viewMoreArticles ? '-50%' : '0'}, 0)`">
          <ArticleItemHeader class="title-info-author" :share="share" :data="authorData" :lazy="lazy" :more="false" @on-follow="onFollowAuthor"></ArticleItemHeader>
          <div class="title-info-rec">{{listTitle}}</div>
        </div>
      </div>
    </ArticleDetailHeader>

    <ArticleItemHeader v-if="share" :share="share" :data="authorData" :lazy="lazy" :more="false" @on-follow="onFollowAuthor"></ArticleItemHeader>
    <LayoutHeader v-else style="visibility: hidden;"></LayoutHeader>

    <div v-if="data.empty" class="article-empty">
      {{data.emptyTip || '文章不存在或文章被删除'}}
    </div>
    <div v-else class="article-context">
      <ArticleItemSlide :thumb="thumb" :share="share" :data="slideData" :slide-index="slideIndex" :lazy="lazy" @on-praise="onPraise" @change="onChangeSlide"></ArticleItemSlide>
      <ProductGroup :article-id="data.articleId" :pos-id="0" :index="0" :thumb="thumb" v-if="productListData.length" :share="share" :data="productListData" :lazy="lazy"></ProductGroup>

      <ArticleDetailIntro :data="introData"></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 && !thumb" :article-id="data.articleId" :share="share" :comment-count="articleState.commentCount"></ArticleDetailCommentList>
    </div>

    <div v-if="listTitle" class="rec-article-title">
      <LayoutTitle>{{listTitle}}</LayoutTitle>
    </div>
    <ArticleDetailFooter v-show="!data.thumb" ref="footer" class="detail-fixed-footer" :class="{'disable': data.empty}" :style="`transform: translate3d(0, ${viewMoreArticles ? '100%' : '0'}, 0)`" v-bind="footerData" @on-comment-click="toCommentList">
      <template v-slot:before>
        <div class="footer-comment">
          <CommentPlaceholder
            ref="commentInput"
            :share="share"
            class="comment-input hover-opacity"
            :dest-id="data.articleId"
            :add-type="0"
            :article-id="data.articleId"
            :pos-id="posId"
            :column-type="1001"
            :autoUpdate="false"
            @on-comment="onComment">
            添加评论...
          </CommentPlaceholder>
        </div>
      </template>
      <template v-slot:after>
        <div></div>
      </template>
    </ArticleDetailFooter>
  </div>
</template>

<script>
import {get} from 'lodash';
import ArticleDetailHeader from './article-header';
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 ArticleDetailFooter from './article-footer';
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
    };
  },
  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;
    },
    viewMoreArticles() {
      let scrollTop = this.scrollTop;

      if (this.$refs && this.$refs.header) {
        scrollTop += this.$refs.header.$el.offsetHeight;
        return scrollTop > this.$el.offsetHeight;
      } else {
        return false;
      }
    },
    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 blockList = get(this.data, 'blockList', []);

      return {
        intro: get(blockList.filter(block => block.templateKey === 'text'), '[0].contentData', ''),
        maxLines: 10
      };
    },
    topicsData() {
      return {
        topicList: this.data.topicList,
        articleType: this.data.articleType
      };
    },
    footerData() {
      return {
        favoriteCount: this.articleState.favoriteCount,
        praiseCount: this.articleState.praiseCount,
        commentCount: this.articleState.commentCount,
        hasFavor: this.articleState.hasFavor,
        hasPraise: this.articleState.hasPraise,
        articleId: this.data.articleId,
        share: this.share
      };
    },
    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.$refs.footer.onPraise();
    },
    onChangeSlide({index}) {
      this.slideIndex = index;
    },
    onComment({comment}) {
      this.$refs.commentList && this.$refs.commentList.addComment(comment);
    },
    toCommentList() {
      if (this.articleState.commentCount) {
        if (this.$refs.commentList && this.scrollTo) {
          this.scrollTo({scrollTop: this.$refs.commentList.$el.offsetTop - this.$refs.header.$el.offsetHeight});
        }
      } else {
        this.$refs.commentInput.$el.click();
      }
    },
    onMore() {
      this.$emit('on-show-more');
    }
  },
  components: {
    ArticleDetailHeader,
    ArticleItemHeader,
    ArticleItemSlide,
    ArticleItemTopics,
    ArticleDetailIntro,
    ArticleDetailCommentList,
    ArticleDetailFooter
  }
};
</script>

<style lang="scss" scoped>
.title-main {
  height: 100%;
  color: #444;
  overflow: hidden;

  .title-info {
    height: 200%;
    transition: all 200ms;

    > * {
      height: 50%;
      background: none;
    }

    .title-info-rec {
      font-size: 32px;
      line-height: 1.2;
      display: flex;
      justify-content: center;
      align-items: center;
    }
  }

  /deep/ .avatar {
    padding-left: 0;
  }

  /deep/ .opts {
    padding-right: 10px;
  }
}

.article-empty {
  height: 900px;
  color: #ccc;
  font-size: 28px;
  background-color: #f0f0f0;
  display: flex;
  justify-content: center;
  align-items: center;
}

.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;
    }
  }
}

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

.article-detail-notes .detail-fixed-footer {
  width: 100%;
  position: fixed;
  bottom: 0;
  z-index: 10;
  transition: all 300ms;

  .footer-comment {
    width: 312px;
    margin-left: 30px;
    display: flex;
    justify-content: center;
    align-items: center;

    .comment-input {
      width: 100%;
      height: 68px;
      line-height: 68px;
      font-size: 28px;
      color: #b0b0b0;
      background: #f0f0f0;
      padding: 0 20px;
      border-radius: 35px;
      box-sizing: border-box;

    }
  }
}
</style>