<template> <Layout class="article-detail"> <ArticleDetailHeader ref="header" class="article-detail-header" v-if="!share" :data="articleInfo" :step="headerAnimateStep" :title-step="headerTitleAnimateStep"> <div v-if="articleInfo.articleId && !articleInfo.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" :more="false" @on-follow="onFollowAuthor"></ArticleItemHeader> <div class="title-info-rec">{{listTitle}}</div> </div> </div> </ArticleDetailHeader> <RecycleScrollReveal :size="10" ref="scroll" :class="{'preview-page': previewPage}" @scroll="onScroll" :offset="2000" :on-fetch="onFetch" :manual-init="true"> <template v-slot:eternalTop> <ClientOnly> <Download v-if="share"></Download> </ClientOnly> <ArticleDeatilLong v-if="articleInfo.sort == 2" ref="detailLong" :data="articleInfo" :scroll-top="scrollTop" :list-title="listTitle" :scroll-to="scrollTo" :share="share" :pos-id="posId" @on-show-more="onShowMore" @on-follow="onFollowAuthor"> </ArticleDeatilLong> <ArticleDeatilNote v-else ref="detailNote" :data="articleInfo" :scroll-top="scrollTop" :list-title="listTitle" :scroll-to="scrollTo" :share="share" :pos-id="posId" @on-show-more="onShowMore" @on-follow="onFollowAuthor" @on-praise="onPraise"> </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> <DownloadBottom v-if="share" :class="{'scroll-opacity': scrolling}"></DownloadBottom> <ReplaceToHome :scrolling="scrolling" class="back-to-home"></ReplaceToHome> <ArticleDetailFooter ref="footer" class="detail-fixed-footer" :style="`transform: translate3d(0, ${viewMoreArticles ? '100%' : '0'}, 0)`" v-bind="footerData" @on-comment-click="onComment"> <template v-if="articleInfo.sort != 2" v-slot:before> <div class="footer-comment"> <CommentPlaceholder ref="commentInput" :share="share" class="comment-input hover-opacity" :dest-id="articleInfo.articleId" :add-type="0" :article-id="articleInfo.articleId" :pos-id="posId" :column-type="1001" :autoUpdate="false" @on-comment="onCommentInput"> 添加评论... </CommentPlaceholder> </div> </template> <template v-slot:after> <div v-if="articleProducts.length" class="article-goods">文中商品</div> <div v-else></div> </template> </ArticleDetailFooter> <MoreActionSheet transfer ref="moreAction" @on-follow="onFollowAuthor" @on-delete="onDelete" @on-edit="onEdit"></MoreActionSheet> </Layout> </template> <script> import {get} from 'lodash'; import Share from 'plugins/share'; import {getDetailShareData} from 'utils/share-handler'; import YAS from 'utils/yas-constants'; import ArticleDetailHeader from './components/detail/article-header'; import ArticleItemHeader from './components/article/article-item-header'; import ArticleDeatilLong from './components/detail/article-long'; import ArticleDeatilNote from './components/detail/article-note'; import ArticleItem2 from './components/article/article-item2'; import ArticleDetailFooter from './components/detail/article-footer'; import Download from './components/download-top'; import DownloadBottom from './components/download-bottom'; import MoreActionSheet from './components/detail/more-action-sheet'; import {mapState as mapYohoState, mapMutations as mapYohoMutations, createNamespacedHelpers} from 'vuex'; import ReplaceToHome from 'components/replace-to-home/replace-to-home'; const {mapState, mapActions, mapMutations} = createNamespacedHelpers('article'); export default { name: 'ArticleDetailPage', data() { return { toHomeHref: '', toHomeImgSrc: '', id: 0, scrollTop: 0, scrolling: false, listTitle: '', colWidthForTwo: 370, posId: YAS.scene.newsDetail, previewPage: false, articleProducts: [], share: false }; }, async created() { this.previewPage = (this.$route.query.type === 'preview'); }, 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(); } else { this.setShareData(this.articleInfo); this.reportProductShow(this.articleInfo); } this.$yoho.safetyInterface(() => { this.SET_STATUS_BAR_COLOR({ color: this.headerAnimateStep > 55 ? 'black' : 'white' }); }); }, async serverPrefetch() { this.id = parseInt(this.$route.params.id, 10); if (this.id > 0 && !this.previewPage) { return this.fetchArticleList({ articleId: this.id, singleDetail: 'Y', thumb: true }); } return; }, mounted() { this.colWidthForTwo = Math.floor(this.$el.offsetWidth / 2); this.loadPreData(+this.$route.params.id); this.share = !this.$yoho.isApp; }, computed: { ...mapYohoState(['yoho']), ...mapState(['articleSingleDetail', 'articleStates', 'authorStates']), articleInfo() { return this.articleSingleDetail[this.id || this.$route.params.id] || {}; }, articleState() { const articleState = this.articleStates[this.articleInfo.articleId] || this.articleInfo; const authorState = this.authorStates[`${this.articleInfo.authorUid}-${this.articleInfo.authorType}`] || this.articleInfo; return Object.assign({...articleState}, {hasAttention: authorState.hasAttention}); }, headerAnimateStep() { if (this.articleInfo.sort !== 2 || this.scrollTop > this.coverHeight) { return 100; } else if (this.scrollTop > 0) { let coverHeight = get(this.$refs, 'detailLong.coverHeight', 0); let step = Math.round((this.scrollTop - 10) / (coverHeight - 90) * 100); step = Math.max(Math.min(step, 100), 0); this.SET_STATUS_BAR_COLOR({ color: step > 55 ? 'black' : 'white' }); return step; } else { this.SET_STATUS_BAR_COLOR({ color: 'white' }); return 0; } }, headerTitleAnimateStep() { if (this.articleInfo.sort === 2) { let {height, top} = get(this.$refs, 'detailLong.authorBlock', {}); let scrollTop = this.scrollTop + get(this.$refs, 'header.$el.offsetHeight', 0); if (top && height) { if (scrollTop >= top + height) { return 100; } else if (scrollTop > top) { let step = Math.round((scrollTop - top) / height * 100); return Math.max(Math.min(step, 100), 0); } } return 0; } else { return 100; } }, viewMoreArticles() { let scrollTop = this.scrollTop; if (this.$refs && this.$refs.header) { scrollTop += this.$refs.header.$el.offsetHeight; return scrollTop > get(this.$refs, `${this.articleInfo.sort === 2 ? 'detailLong' : 'detailNote'}.$el.offsetHeight`); } else { return false; } }, authorData() { return { authorName: this.articleInfo.authorName, authorUid: this.articleInfo.authorUid, authorType: this.articleInfo.authorType, authorHeadIco: this.articleInfo.authorHeadIco, authGroupId: this.articleInfo.authGroupId, hasAttention: this.articleState.hasAttention, isAuthor: this.articleInfo.isAuthor }; }, footerData() { return { favoriteCount: this.articleState.favoriteCount, praiseCount: this.articleState.praiseCount, commentCount: this.articleState.commentCount, hasFavor: this.articleState.hasFavor, hasPraise: this.articleState.hasPraise, articleId: this.articleInfo.articleId, share: this.share }; }, }, methods: { ...mapActions(['fetchArticleList', 'fetchDetailRecommendAricles']), ...mapMutations(['CHANGE_AUTHOR_FOLLOW']), ...mapYohoMutations(['SET_STATUS_BAR_COLOR']), init() { this.recommendArticles = {}; this.fetching = true; this.syncServiceArticleDetail(); }, pageVisibileEvent() { if (this.articleOnEdit) { this.articleOnEdit = false; this.syncServiceArticleDetail(true); } }, loadPreData(articleId) { if (!this.$isServer && this.$yoho.isLocal) { this.fetchArticleList({ articleId, thumb: true, local: true, singleDetail: 'Y' }); } }, onScroll({scrollTop}) { this.scrollTop = scrollTop; this.scrolling = true; this._scTimer && clearTimeout(this._scTimer); this._scTimer = setTimeout(() => { this.scrolling = false; }, 200); }, syncServiceArticleDetail(withoutList) { const articleId = parseInt(this.id, 10); return this.fetchArticleList({ articleId, fromPlatform: this.previewPage ? 'Y' : 'N', singleDetail: 'Y' }).then((res) => { if (this.$refs.scroll && !withoutList) { this.listTitle = ''; this.fetching = false; this.$refs.scroll.init(); } const article = get(res, 'data.detailList[0]', this.articleInfo); this.reportProductShow(article); this.setShareData(article); }); }, 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.articleInfo); }, 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(); }, onEdit() { this.articleOnEdit = true; }, onPraise() { this.$refs.footer.onPraise(); }, onComment() { if (this.articleInfo.sort === 2) { this.$refs.detailLong.onComment(); } else if (this.articleState.commentCount) { this.$refs.detailNote.toCommentList(); } else { this.$refs.commentInput.$el.click(); } }, onCommentInput(comment) { if (get(this.$refs, 'detailNote.onComment')) { this.$refs.detailNote.onComment(comment); } }, setShareData(article) { if (this.$yoho.isApp || !article) { return; } Share.setShareInfo(getDetailShareData(article)); }, reportArticleShow(items) { if (!items || !items.length) { return; } this._preReportArticles = this._preReportArticles || {}; let reportArticles = {}; let articles = []; items.forEach(val => { let item = val.data || {}; reportArticles[item.articleId] = 1; if (item.articleId && !this._preReportArticles[item.articleId]) { articles.push(val); } }); this._preReportArticles = reportArticles; if (articles.length) { let name = this.$yoho.isiOS ? 'iFP_ArticleList' : 'aFP_ArticleList'; this.$store.dispatch('reportYas', { params: { appop: YAS.eventName.show, param: { DATA: [ ...articles.map(a => { return { P_NAME: name, P_PARAM: this.id, I_INDEX: a.index, ARTICLE_ID: a.data.articleId, POS_ID: this.posId }; }) ], } } }); } }, reportProductShow(article) { const productList = article.productList || []; if (!productList.length) { return; } let name = this.$yoho.isiOS ? 'iFP_ArticleList' : 'aFP_ArticleList'; this.$store.dispatch('reportYas', { params: { appop: YAS.eventName.show, param: { DATA: [ ...productList.map((p, i) => { return { P_NAME: name, P_PARAM: this.id, I_INDEX: i, ARTICLE_ID: this.id, PRD_SKN: p.productSkn, ATR_TYPE: article.sort === 2 ? 1 : 2, PRD_TYPE: p.collage_activity_id ? 'collage_info' : '' }; }) ], } } }); }, toDownloadPage() { this.$links.toDownloadApp(); } }, components: { ReplaceToHome, ArticleDetailHeader, ArticleItemHeader, ArticleDeatilLong, ArticleDeatilNote, ArticleItem2, MoreActionSheet, ArticleDetailFooter, Download, DownloadBottom } }; </script> <style lang="scss" scoped> /deep/ { .article-item-header .btn-follow { color: #ce0b24; border-color: #ce0b24; &.follow { border-color: #b0b0b0; color: #b0b0b0; } } &.preview-page .recycle-scroll-reveal-main { position: relative; &:before { content: ""; position: absolute; top: 0; bottom: 0; left: 0; right: 0; z-index: 1000; } } .scroll-reveal-list { padding: 0 6px; background-color: #f0f0f0; .scroll-reveal-col { margin: 6px 0; } } .loading { display: none; } .cube-recycle-list-no-more { display: none; } } .back-to-home { bottom: 120px; } .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; } } .detail-fixed-footer { width: 100%; position: absolute !important; 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; } } .article-goods { width: 200px; color: white; font-size: 32px; line-height: 100px; font-weight: 300; background-color: #d0021b; text-align: center; } } </style>