articleDetail.vue 5.38 KB
<template>
  <LayoutApp class="article-detail-container" :show-back="true" title="社区详情">
    <div class="detail-container">

      <div class="author-container">
        <ArticleAuthor />
      </div>

      <div class="source-container">
        <ArticleVideo v-if="isVideo" :videoUrl="detailInfo.videoUrl" :coverUrl="detailInfo.coverUrl"></ArticleVideo>
        <ArticleImage v-else :imageList="imageList"></ArticleImage>
      </div>

      <div class="slide-container">
        <associated-item :items="detailInfo.productList"></associated-item>
      </div>

      <div class="note-container">
        <div v-if="content" class="note-content" v-html="content"></div>
        <div class="note-date">{{detailInfo.publishTimeStr.slice(0, -3)}}</div>
        <div class="praise-wrapper">
          <div class="praise-border" @click="onPraise(detailInfo)"></div>
          <div class="praise-div">
            <div :class="detailInfo.hasPraise === 'Y' ? 'praise-icon praised-icon' : 'praise-icon'"></div>
            <div class="praise-num">{{detailInfo.praiseCount}}</div>
          </div>
        </div>
      </div>

      <div class="recommend-container">
        <div class="recommend-text">推荐阅读</div>
          <ClientOnly>
            <waterFallList
              :listData="recommendArticleList"
              :item-w="344" :gutter-w="15"
              @update-praise="onUpdatePraise"
            >
            </waterFallList>
          </ClientOnly>
      </div>

    </div>
  </LayoutApp>
</template>

<script>
import { createNamespacedHelpers } from 'vuex';

import ArticleAuthor from './components/article-author';
import ArticleVideo from './components/article-video';
import ArticleImage from './components/article-image';
import AssociatedItem from './components/associated-item';
import waterFallList from './components/waterfall';

const { mapState, mapActions } = createNamespacedHelpers('article/articleDetail');

export default {
  name: 'articleDetail',
  components: {
    ArticleAuthor,
    ArticleVideo,
    ArticleImage,
    AssociatedItem,
    waterFallList
  },
  props: {
    articleId: {
      required: true
    }
  },
  computed: {
    ...mapState(['detailInfo', 'recommendArticleList']),
    isVideo({detailInfo: {sort}}) {
      return +sort === 4;
    },
    imageList({detailInfo: {blockList = []}}) {
      return blockList.filter(content => content.templateKey === 'image').map(content => ({image_url: content.contentData}));
    },
    content({detailInfo: {blockList = [], atUserInfo = {}}}) {
      const contentBlock = blockList.find(block => block.templateKey === 'text');

      if (!contentBlock) {
        return '';
      }

      const content = contentBlock.contentData || '';

      return content.replace(/@(\d*)#|(\r\n|\n)/gm, (match, p1, p2)=> {
        if (p2) {
          return '<br/>';
        }
        return `<span style="color:#1890ff" id='${p1}'>@${atUserInfo[p1]}</span>`;
      });
    }
  },

  asyncData({store, router}) {
    const articleId = router.params.articleId;

    return store.dispatch('article/articleDetail/fetchDetailInfo', {articleId});
  },
  activated() {
    this.fetchRecommendArticle({articleId: this.articleId});
  },
  methods: {
    ...mapActions(['updatePraiseStatus', 'fetchRecommendArticle']),
    async onPraise({articleId, hasPraise}) {
      const user = await this.$sdk.getUser(true);

      if (user && user.uid) {
        this.updatePraiseStatus({
          articleId,
          status: hasPraise === 'Y' ? 1 : 0
        });
      } else {
        this.$yoho.auth();
      }
    },
    onUpdatePraise({articleId, status}) {
      this.updatePraiseStatus({
        articleId,
        status
      });
    }
  }
};
</script>

<style lang="scss" scoped>
.article-detail-container {
  /deep/ .layout-context {
    -webkit-overflow-scrolling: touch;
  }
}

.detail-container {
  background: #fff;
}

.author-container {
  width: 100%;
  height: 100px;
}

.source-container {
  width: 100%;
  height: 750px;
}

.slide-container {
  padding: 40px 0 36px 24px;
}

.note-container {
  padding: 0 24px 100px;
  background: #fff;
}

.note-header {
  font-size: 36px;
  color: #222;
  letter-spacing: 0.34px;
  line-height: 56px;
  font-weight: bold;
}

.note-content {
  font-size: 30px;
  color: #000;
  letter-spacing: 0.28px;
  line-height: 50px;
}

.note-date {
  margin-top: 20px;
  font-size: 30px;
  color: #999;
  letter-spacing: 0.28px;
}

.praise-wrapper {
  margin-top: 60px;
  width: 100%;
  height: 160px;
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.praise-border {
  width: 160px;
  height: 160px;
  border-radius: 50%;
  border: 2px solid #EEEEEE;
  position: absolute;
  top: 0;
  left: calc((100%-160px)/2);
}

.praise-div {
  display: flex;
  flex-direction: column;
  align-items: center;

  .praise-icon {
    width: 64px;
    height: 64px;
    background-image: url(~statics/image/article/praise@3x.png);
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
  }

  .praised-icon {
    background-image: url(~statics/image/article/praised@3x.png);
  }

  .praise-num {
    margin-top: 8px;
    font-size: 22px;
    color: #002B47;
    letter-spacing: 0.21px;
  }
}

.recommend-container {
  background: #F2F2F2;
}

.recommend-text {
  font-size: 32px;
  color: #222222;
  font-weight: bold;
  text-align: left;
  padding: 36px 24px 0;
}

.recommend-list {
  margin-top: 30px;
}

</style>