Authored by 杨延青

Merge branch 'feature/detailHeader' into 'release/6.9.6'

Feature/detail header



See merge request !98
... ... @@ -668,7 +668,9 @@ const yoho = {
if (this.isYohoBuy && window.yohoInterface) {
window.yohoInterface.triggerEvent(success || nullFun, fail || nullFun, {
method: 'go.showshareaction',
arguments: args
arguments: Object.assign({
isHidePromotion: 1
}, args || {})
});
} else {
// tip(tipInfo);
... ...
<template>
<Layout class="article-detail">
<ArticleDetailHeader ref="header" class="article-detail-header" :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>
<ArticleDeatilLong
... ... @@ -24,7 +32,8 @@
:share="share"
:pos-id="posId"
@on-show-more="onShowMore"
@on-follow="onFollowAuthor">
@on-follow="onFollowAuthor"
@on-praise="onPraise">
</ArticleDeatilNote>
</template>
<template class="article-item" #item="{ data }">
... ... @@ -43,6 +52,30 @@
</template>
</RecycleScrollReveal>
<ArticleDetailFooter ref="footer" v-show="!articleInfo.thumb" 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>
... ... @@ -51,11 +84,14 @@
import {get} from 'lodash';
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 MoreActionSheet from './components/detail/more-action-sheet';
import {mapState as mapYohoState, createNamespacedHelpers} from 'vuex';
import {mapState as mapYohoState, mapMutations as mapYohoMutations, createNamespacedHelpers} from 'vuex';
const {mapState, mapActions, mapMutations} = createNamespacedHelpers('article');
export default {
... ... @@ -72,7 +108,8 @@ export default {
listTitle: '',
colWidthForTwo: 370,
posId: YAS.scene.newsDetail,
previewPage: false
previewPage: false,
articleProducts: []
};
},
created() {
... ... @@ -109,14 +146,94 @@ export default {
},
computed: {
...mapYohoState(['yoho']),
...mapState(['articleSingleDetail']),
...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;
... ... @@ -125,7 +242,7 @@ export default {
pageVisibileEvent() {
if (this.articleOnEdit) {
this.articleOnEdit = false;
this.syncServiceArticleDetail();
this.syncServiceArticleDetail(true);
}
},
loadPreData(articleId) {
... ... @@ -147,7 +264,7 @@ export default {
this.scrolling = false;
}, 200);
},
syncServiceArticleDetail() {
syncServiceArticleDetail(withoutList) {
const articleId = parseInt(this.id, 10);
return this.fetchArticleList({
... ... @@ -155,7 +272,7 @@ export default {
fromPlatform: this.previewPage ? 'Y' : 'N',
singleDetail: 'Y'
}).then((res) => {
if (this.$refs.scroll) {
if (this.$refs.scroll && !withoutList) {
this.listTitle = '';
this.fetching = false;
this.$refs.scroll.init();
... ... @@ -228,6 +345,23 @@ export default {
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);
}
},
reportArticleShow(items) {
if (!items || !items.length) {
return;
... ... @@ -305,10 +439,13 @@ export default {
}
},
components: {
ArticleDetailHeader,
ArticleItemHeader,
ArticleDeatilLong,
ArticleDeatilNote,
ArticleItem2,
MoreActionSheet
MoreActionSheet,
ArticleDetailFooter
}
};
</script>
... ... @@ -356,4 +493,75 @@ export default {
}
}
.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>
... ...
<template>
<div class="fixed-header" :style="headerStyle">
<LayoutHeader ref="header" theme="transparent">
<div class="fixed-header">
<LayoutHeader ref="header" theme="transparent" :style="headerStyle">
<template>
<div ref="titleBlock" class="title-block" :style="`transform: translate3d(0, ${blockTranslateY}, 0)`">
<slot></slot>
... ... @@ -91,7 +91,7 @@ export default {
<style scoped>
.fixed-header {
position: fixed;
position: absolute;
width: 100%;
z-index: 10;
transition: all 100ms;
... ...
<template>
<div class="article-detail-long">
<ArticleDetailHeader ref="header" class="article-detail-header" :data="data" :step="headerAnimateStep" :title-step="headerTitleAnimateStep">
<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="showMoreOpt" @on-follow="onFollowAuthor"></ArticleItemHeader>
<div class="title-info-rec">{{listTitle}}</div>
</div>
</div>
</ArticleDetailHeader>
<div class="header-cover"></div>
<ArticleItemSlideImage ref="coverFigure" class="cover-figure" :data="coverImage" :thumb-size="coverSize" :style="`transform: translate3d(0, ${coverTranslateY}px, 0)`">
</ArticleItemSlideImage>
... ... @@ -29,12 +21,6 @@
<LayoutTitle v-if="listTitle" class="rec-article-title">{{listTitle}}</LayoutTitle>
</div>
<ArticleDetailFooter v-show="!data.thumb" class="detail-fixed-footer" :style="`transform: translate3d(0, ${viewMoreArticles ? '100%' : '0'}, 0)`" v-bind="footerData" @on-comment-click="onComment">
<template v-slot:after>
<div v-if="articleProducts.length" class="article-goods">文中商品</div>
<div v-else></div>
</template>
</ArticleDetailFooter>
<YohoActionSheet transfer v-if="showCommentAction" ref="commentAction" :full="true">
<Comment ref="comment"
... ... @@ -84,7 +70,6 @@ export default {
recomendProduct: [],
showCommentAction: false,
showCommentActioning: false,
articleProducts: []
};
},
mounted() {
... ... @@ -137,55 +122,6 @@ export default {
return 0;
}
},
headerAnimateStep() {
if (this.scrollTop > this.coverHeight) {
return 100;
} else if (this.scrollTop > 0) {
let step = Math.round((this.scrollTop - 10) / (this.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() {
let {height, top} = this.authorBlock;
let scrollTop = this.scrollTop;
if (this.$refs && this.$refs.header) {
scrollTop += this.$refs.header.$el.offsetHeight;
}
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;
},
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,
... ... @@ -197,17 +133,6 @@ export default {
isAuthor: this.data.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.data.articleId,
share: this.share
};
},
lazy() {
return this.data.lazy;
}
... ... @@ -271,38 +196,6 @@ export default {
opacity: 1!important;
}
.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;
}
}
.cover-img {
width: 100%;
display: block;
... ... @@ -411,22 +304,4 @@ export default {
.rec-article-title {
margin-top: -6px;
}
.article-detail-long .detail-fixed-footer {
width: 100%;
position: fixed;
bottom: 0;
z-index: 10;
transition: all 300ms;
.article-goods {
width: 200px;
color: white;
font-size: 32px;
line-height: 100px;
font-weight: 300;
background-color: #d0021b;
text-align: center;
}
}
</style>
... ...
<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>
<LayoutHeader v-else ref="header" style="visibility: hidden;"></LayoutHeader>
<div v-if="data.empty" class="article-empty">
{{data.emptyTip || '文章不存在或文章被删除'}}
... ... @@ -34,39 +25,15 @@
<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';
... ... @@ -107,16 +74,6 @@ export default {
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,
... ... @@ -151,17 +108,6 @@ export default {
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() : '';
},
... ... @@ -174,7 +120,7 @@ export default {
this.$emit('on-follow', this.data, follow);
},
onPraise() {
this.$refs.footer.onPraise();
this.$emit('on-praise');
},
onChangeSlide({index}) {
this.slideIndex = index;
... ... @@ -183,12 +129,8 @@ export default {
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();
if (this.$refs.commentList && this.scrollTo) {
this.scrollTo({scrollTop: this.$refs.commentList.$el.offsetTop - get(this.$refs, 'header.$el.offsetHeight', 0)});
}
},
onMore() {
... ... @@ -196,50 +138,16 @@ export default {
}
},
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;
... ... @@ -273,33 +181,4 @@ export default {
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>
... ...
... ... @@ -24,6 +24,7 @@ function processArticleListData(listData) {
templateKey: 'text',
}
],
hasComment: true,
commentCount: 0,
comments: [],
dataType: listData.dataType,
... ...
{
"name": "yoho-community-web",
"version": "6.9.5-18",
"version": "6.9.5-beta5",
"private": true,
"description": "A New Yohobuy Project With Express",
"repository": {
... ...