article-detail.vue 5.74 KB
<template>
  <Layout class="article-detail">
    <RecycleScrollReveal :size="10" ref="scroll" @scroll="onScroll" :offset="2000" :on-fetch="onFetch" :manual-init="true">
      <template v-slot:eternalTop>
        <ArticleDeatilLong v-if="articleInfo.sort == 2" ref="detailLong" :data="articleInfo" :scroll-top="scrollTop" :list-title="listTitle" :scroll-to="scrollTo" @on-show-more="onShowMore" @on-follow="onFollowAuthor"></ArticleDeatilLong>
        <ArticleDeatilNote v-else ref="detailNote" :data="articleInfo" :scroll-top="scrollTop" :list-title="listTitle" :scroll-to="scrollTo" @on-show-more="onShowMore" @on-follow="onFollowAuthor"></ArticleDeatilNote>
      </template>
      <template class="article-item" #item="{ data }">
        <ArticleItem2
          type="topic"
          :index="data.index"
          :data="data.data"
          :width="colWidthForTwo"
          :share="share"
          :article-id="data.data.articleId"
          :pos-id="posId">
          <template v-if="data.data.dataType == 2">
            <ArticleResource :data="data.data"></ArticleResource>
          </template>
        </ArticleItem2>
      </template>
    </RecycleScrollReveal>

    <MoreActionSheet transfer ref="moreAction" @on-follow="onFollowAuthor" @on-delete="onDelete"></MoreActionSheet>
  </Layout>
</template>

<script>
import {get} from 'lodash';

import ArticleDeatilLong from './components/detail/article-long';
import ArticleDeatilNote from './components/detail/article-note';
import ArticleItem2 from './components/article/article-item2';
import MoreActionSheet from './components/detail/more-action-sheet';
import {mapState as mapYohoState, createNamespacedHelpers} from 'vuex';
const {mapState, mapActions, mapMutations} = createNamespacedHelpers('article');

export default {
  name: 'ArticleDetailPage',
  data() {
    return {
      id: 0,
      scrollTop: 0,
      scrolling: false,
      share: false,
      article: {},
      listTitle: '',
      colWidthForTwo: 370,
      posId: 0
    };
  },
  activated() {
    if (this.scrollTop && this.yoho.direction === 'back') {
      this.$refs.scroll.$el.scrollTop = this.scrollTop;
    }

    if (+this.$route.params.id !== this.id) {
      this.id = +this.$route.params.id;
      this.init();
    }
  },
  async serverPrefetch() {
    this.id = parseInt(this.$route.params.id, 10);

    if (this.id > 0) {
      return this.fetchArticleList({
        articleId: this.id,
        singleDetail: 'Y',
        thumb: true
      });
    }

    return;
  },
  mounted() {
    this.colWidthForTwo = Math.floor(this.$el.offsetWidth / 2);
  },
  computed: {
    ...mapYohoState(['yoho']),
    ...mapState(['articleSingleDetail']),
    articleInfo() {
      if (this.articleSingleDetail.thumb) {
        return this.articleSingleDetail;
      } else {
        return this.article;
      }
    }
  },
  methods: {
    ...mapActions(['fetchArticleList', 'fetchDetailRecommendAricles']),
    ...mapMutations(['CHANGE_AUTHOR_FOLLOW']),
    init() {
      this.recommendArticles = {};
      this.syncServiceArticleDetail();
    },
    onScroll({scrollTop}) {
      this.scrollTop = scrollTop;

      this.scrolling = true;
      this._scTimer && clearTimeout(this._scTimer);
      this._scTimer = setTimeout(() => {
        this.scrolling = false;
      }, 400);
    },
    syncServiceArticleDetail() {
      const articleId = parseInt(this.id, 10);

      return this.fetchArticleList({
        articleId,
        singleDetail: 'Y'
      }).then(res => {
        const emptyData = {
          articleId,
          empty: true
        };

        let article = {};

        if (res.code === 200) {
          article = get(res, 'data.detailList[0]', {...emptyData});
        } else if (res.code === 403) {
          article = {...emptyData};
        } else {
          article = {...emptyData, emptyTip: res.message};
        }

        this.article = article;

        if (this.$refs.scroll) {
          this.listTitle = '';

          this.$nextTick(() => {
            this.$refs.scroll.init();
          });
        }
      });
    },
    async onFetch() {
      if (!this.id || this.fetching) {
        return [];
      }

      // 推荐文章接口一次性提供,不支持分页
      if (this.recommendArticles[this.id]) {
        return false;
      }

      this.fetching = true;

      let list = [];
      const result = await this.fetchDetailRecommendAricles({
        articleId: this.id
      });

      this.fetching = false;
      this.recommendArticles[this.id] = true;

      if (result.code === 200) {
        list = get(result, 'data', []);

        if (!list || !list.length) {
          list = false;
        } else {
          this.listTitle = '推荐阅读';
        }
      } else {
        this.$createToast && this.$createToast({
          txt: result.message || '服务器开小差了',
          type: 'warn',
          time: 1000
        }).show();
      }

      return list;
    },
    scrollTo({scrollTop}) {
      this.$refs.scroll.$el.scrollTop = scrollTop;
      this.scrollTop = scrollTop;
    },
    onShowMore() {
      this.$refs.moreAction.show(this.article);
    },
    onFollowAuthor(data, follow) {
      this.CHANGE_AUTHOR_FOLLOW({authorUid: data.authorUid, authorType: data.authorType, follow});
    },
    onDelete() {
      let $detail = {$refs: {}};

      this.$refs.detailLong && ($detail = this.$refs.detailLong);
      this.$refs.detailNote && ($detail = this.$refs.detailNote);

      $detail.$refs.header && $detail.$refs.header.onBack();
    }
  },
  components: {
    ArticleDeatilLong,
    ArticleDeatilNote,
    ArticleItem2,
    MoreActionSheet
  }
};
</script>

<style lang="scss" scoped>
/deep/ .scroll-reveal-list {
  padding: 0 6px;
  background-color: #f0f0f0;

  .scroll-reveal-col {
    margin: 6px 0;
  }
}
/deep/ .loading {
  display: none;
}
</style>