Authored by 陈峰

commit

@@ -8,13 +8,17 @@ import titleMixin from './mixins/title'; @@ -8,13 +8,17 @@ import titleMixin from './mixins/title';
8 import pluginCore from './plugins/core'; 8 import pluginCore from './plugins/core';
9 import lazyload from 'vue-lazyload'; 9 import lazyload from 'vue-lazyload';
10 import reportError from 'report-error'; 10 import reportError from 'report-error';
  11 +import dayjs from 'dayjs';
  12 +import 'dayjs/locale/zh-cn';
  13 +import relativeTime from 'dayjs/plugin/relativeTime';
11 14
12 Vue.use(lazyload, { 15 Vue.use(lazyload, {
13 preLoad: 2 16 preLoad: 2
14 }); 17 });
15 Vue.use(pluginCore); 18 Vue.use(pluginCore);
16 Vue.mixin(titleMixin); 19 Vue.mixin(titleMixin);
17 - 20 +dayjs.locale('zh-cn');
  21 +dayjs.extend(relativeTime);
18 22
19 export function createApp(context) { 23 export function createApp(context) {
20 const router = createRouter(); 24 const router = createRouter();
@@ -3,6 +3,11 @@ @@ -3,6 +3,11 @@
3 <template v-slot:item="{ data }"> 3 <template v-slot:item="{ data }">
4 <slot name="item" :data="data"></slot> 4 <slot name="item" :data="data"></slot>
5 </template> 5 </template>
  6 + <template v-slot:noMore>
  7 + <p class="">
  8 + 没有更多了
  9 + </p>
  10 + </template>
6 </RecycleList> 11 </RecycleList>
7 </template> 12 </template>
8 13
@@ -42,18 +42,15 @@ @@ -42,18 +42,15 @@
42 </div> 42 </div>
43 <div 43 <div
44 v-if="!infinite" 44 v-if="!infinite"
45 - class="cube-recycle-list-loading"  
46 - :style="{visibility: loading ? 'visible' : 'hidden'}"  
47 - > 45 + class="cube-recycle-list-loading">
48 <slot name="spinner"> 46 <slot name="spinner">
49 - <div class="cube-recycle-list-loading-content"> 47 + <div class="cube-recycle-list-loading-content" v-show="!noMore" :style="{visibility: loading ? 'visible' : 'hidden'}">
50 <cube-loading class="spinner"></cube-loading> 48 <cube-loading class="spinner"></cube-loading>
51 </div> 49 </div>
52 </slot> 50 </slot>
53 - </div>  
54 -  
55 - <div v-show="noMore" class="cube-recycle-list-noMore">  
56 - <slot name="noMore" /> 51 + <div v-show="noMore" class="cube-recycle-list-noMore">
  52 + <slot name="noMore" />
  53 + </div>
57 </div> 54 </div>
58 </div> 55 </div>
59 <div class="cube-recycle-list-fake"></div> 56 <div class="cube-recycle-list-fake"></div>
@@ -153,7 +150,7 @@ export default { @@ -153,7 +150,7 @@ export default {
153 // increase capacity of items to display tombstone 150 // increase capacity of items to display tombstone
154 this.items.length += this.size; 151 this.items.length += this.size;
155 this.loadItems(); 152 this.loadItems();
156 - } else if (!this.loading) { 153 + } else if (!this.loading && !this.noMore) {
157 this.getItems(); 154 this.getItems();
158 } 155 }
159 }, 156 },
@@ -6,10 +6,10 @@ @@ -6,10 +6,10 @@
6 v-for="(product, inx) in data" 6 v-for="(product, inx) in data"
7 :key="inx"> 7 :key="inx">
8 <div class="product-content"> 8 <div class="product-content">
9 - <ImageFormat class="product-image" :src="product.src"></ImageFormat> 9 + <ImageFormat :lazy="lazy" class="product-image" :src="product.productImage" :width="136" :height="180"></ImageFormat>
10 <div class="product-info"> 10 <div class="product-info">
11 - <p class="product-name">{{product.name}}</p>  
12 - <p class="price">¥{{product.price}}</p> 11 + <p class="product-name">{{product.productName}}</p>
  12 + <p class="price">¥{{product.salesPrice}}</p>
13 </div> 13 </div>
14 </div> 14 </div>
15 <div class="btn-fav hover-opacity" v-if="!product.isFav">收藏</div> 15 <div class="btn-fav hover-opacity" v-if="!product.isFav">收藏</div>
@@ -29,6 +29,10 @@ export default { @@ -29,6 +29,10 @@ export default {
29 default() { 29 default() {
30 return []; 30 return [];
31 } 31 }
  32 + },
  33 + lazy: {
  34 + type: Boolean,
  35 + default: true
32 } 36 }
33 }, 37 },
34 computed: { 38 computed: {
@@ -94,6 +98,8 @@ export default { @@ -94,6 +98,8 @@ export default {
94 color: #9b9b9b; 98 color: #9b9b9b;
95 letter-spacing: -0.25PX; 99 letter-spacing: -0.25PX;
96 height: 104px; 100 height: 104px;
  101 + display: flex;
  102 + align-content: center;
97 } 103 }
98 104
99 .price { 105 .price {
@@ -5,6 +5,7 @@ import WidgetLike from './widget-like'; @@ -5,6 +5,7 @@ import WidgetLike from './widget-like';
5 import WidgetShare from './widget-share'; 5 import WidgetShare from './widget-share';
6 import WidgetTopic from './widget-topic'; 6 import WidgetTopic from './widget-topic';
7 import WidgetAvatar from './widget-avatar'; 7 import WidgetAvatar from './widget-avatar';
  8 +import WidgetFollow from './widget-follow';
8 9
9 export default [ 10 export default [
10 WidgetAvatarGroup, 11 WidgetAvatarGroup,
@@ -13,5 +14,6 @@ export default [ @@ -13,5 +14,6 @@ export default [
13 WidgetLike, 14 WidgetLike,
14 WidgetShare, 15 WidgetShare,
15 WidgetTopic, 16 WidgetTopic,
16 - WidgetAvatar 17 + WidgetAvatar,
  18 + WidgetFollow
17 ]; 19 ];
1 <template> 1 <template>
2 - <ImageFormat class="img-avatar" :src="src" :width="width" :height="height"></ImageFormat> 2 + <ImageFormat :lazy="lazy" class="img-avatar" :src="src" :width="width" :height="height"></ImageFormat>
3 </template> 3 </template>
4 4
5 <script> 5 <script>
@@ -17,6 +17,10 @@ export default { @@ -17,6 +17,10 @@ export default {
17 height: { 17 height: {
18 type: Number, 18 type: Number,
19 default: 35 19 default: 35
  20 + },
  21 + lazy: {
  22 + type: Boolean,
  23 + default: false
20 } 24 }
21 }, 25 },
22 computed: { 26 computed: {
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 export default { 6 export default {
7 name: 'WidgetFav', 7 name: 'WidgetFav',
8 props: { 8 props: {
9 - num: String, 9 + num: [String, Number],
10 option: Object 10 option: Object
11 } 11 }
12 }; 12 };
  1 +<template>
  2 + <button class="btn-follow hover-opacity" v-if="!followed">关注</button>
  3 + <button class="btn-follow followed hover-opacity" v-else>已关注</button>
  4 +</template>
  5 +
  6 +<script>
  7 +export default {
  8 + name: 'WidgetFollow',
  9 + props: {
  10 + authorUid: Number,
  11 + followed: Boolean
  12 + }
  13 +};
  14 +</script>
  15 +
  16 +<style lang="scss" scoped>
  17 +.btn-follow {
  18 + width: 120px;
  19 + height: 50px;
  20 + padding: 0;
  21 + font-size: 26px;
  22 + border-radius: 3PX;
  23 + background-color: #222;
  24 + color: #fff;
  25 + display: flex;
  26 + align-items: center;
  27 + justify-content: center;
  28 +
  29 + &.followed {
  30 + border: solid 1px #4a4a4a;
  31 + background-color: #fff;
  32 + color: #000;
  33 + }
  34 +}
  35 +</style>
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 export default { 6 export default {
7 name: 'WidgetLike', 7 name: 'WidgetLike',
8 props: { 8 props: {
9 - num: String, 9 + num: [String, Number],
10 option: Object 10 option: Object
11 } 11 }
12 }; 12 };
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 export default { 6 export default {
7 name: 'WidgetShare', 7 name: 'WidgetShare',
8 props: { 8 props: {
9 - num: String, 9 + num: [String, Number],
10 option: Object 10 option: Object
11 } 11 }
12 }; 12 };
1 <template> 1 <template>
2 <Article :on-fetch="onFetch"> 2 <Article :on-fetch="onFetch">
3 <template v-slot:thumb> 3 <template v-slot:thumb>
4 - <ArticleItem v-for="data in articleCurrentList" :key="data.articleId" :data="data"></ArticleItem> 4 + <ArticleItem v-for="data in currentList" :key="data.articleId" :data="data"></ArticleItem>
5 </template> 5 </template>
6 </Article> 6 </Article>
7 </template> 7 </template>
8 8
9 <script> 9 <script>
  10 +import {get} from 'lodash';
10 import Article from './components/article/article'; 11 import Article from './components/article/article';
11 import ArticleItem from './components/article/article-item'; 12 import ArticleItem from './components/article/article-item';
12 import {createNamespacedHelpers} from 'vuex'; 13 import {createNamespacedHelpers} from 'vuex';
@@ -20,27 +21,43 @@ export default { @@ -20,27 +21,43 @@ export default {
20 }; 21 };
21 }, 22 },
22 serverPrefetch() { 23 serverPrefetch() {
  24 + console.log('serverPrefetch')
23 return this.onFetch(); 25 return this.onFetch();
24 }, 26 },
25 computed: { 27 computed: {
26 - ...mapState(['articleCurrentList']) 28 + ...mapState(['articleCurrentList']),
  29 + currentList() {
  30 + if (this.articleCurrentList.length > 2) {
  31 + return this.articleCurrentList.slice(0, 2);
  32 + }
  33 + return this.articleCurrentList;
  34 + }
27 }, 35 },
28 methods: { 36 methods: {
29 ...mapActions(['fetchArticleList']), 37 ...mapActions(['fetchArticleList']),
30 async onFetch() { 38 async onFetch() {
31 if (this.page === 1 && this.articleCurrentList.length) { 39 if (this.page === 1 && this.articleCurrentList.length) {
  40 + this.page++;
32 return this.articleCurrentList; 41 return this.articleCurrentList;
33 } 42 }
  43 + const articleId = parseInt(this.$route.params.id, 10);
  44 +
  45 + if (!articleId) {
  46 + return;
  47 + }
34 const result = await this.fetchArticleList({ 48 const result = await this.fetchArticleList({
35 - articleId: this.$route.params.id, 49 + articleId: parseInt(this.$route.params.id, 10),
36 page: this.page 50 page: this.page
37 }); 51 });
38 52
39 if (result.code === 200) { 53 if (result.code === 200) {
40 - this.page++;  
41 - return Promise.resolve(result.data.detailList); 54 + if (get(result, 'data.detailList', []).length) {
  55 + this.page++;
  56 + return Promise.resolve(result.data.detailList);
  57 + }
  58 + return Promise.resolve(false);
42 } else { 59 } else {
43 - this.$createToast({ 60 + this.$createToast && this.$createToast({
44 txt: result.message || '服务器开小差了', 61 txt: result.message || '服务器开小差了',
45 type: 'warn', 62 type: 'warn',
46 time: 1000 63 time: 1000
1 <template> 1 <template>
2 <div class="article-item-comment"> 2 <div class="article-item-comment">
3 - <p class="comment-item" v-for="(comment, inx) in comments" :key="inx">  
4 - <span class="user-name">{{comment.name}}:</span> 3 + <p class="comment-item" v-for="(comment, inx) in data.comments" :key="inx">
  4 + <span class="user-name">{{comment.userName}}:</span>
5 <span class="comment-content">{{comment.content}}</span> 5 <span class="comment-content">{{comment.content}}</span>
6 </p> 6 </p>
7 <div class="comment"> 7 <div class="comment">
8 <div class="comment-input hover-opacity">添加回复:赞美是一种美德</div> 8 <div class="comment-input hover-opacity">添加回复:赞美是一种美德</div>
9 </div> 9 </div>
10 <div class="total-comment"> 10 <div class="total-comment">
11 - <div class="total hover-opacity">查看{{count}}条评论</div>  
12 - <div class="last-time">{{date}}</div> 11 + <div class="total hover-opacity">查看{{data.commentCount}}条评论</div>
  12 + <div class="last-time">{{data.date}}</div>
13 </div> 13 </div>
14 </div> 14 </div>
15 </template> 15 </template>
@@ -19,20 +19,12 @@ import {Input} from 'cube-ui'; @@ -19,20 +19,12 @@ import {Input} from 'cube-ui';
19 export default { 19 export default {
20 name: 'ArticleItemComment', 20 name: 'ArticleItemComment',
21 props: { 21 props: {
22 - comments: {  
23 - type: Array, 22 + data: {
  23 + type: Object,
24 default() { 24 default() {
25 - return []; 25 + return {};
26 } 26 }
27 }, 27 },
28 - count: {  
29 - type: Number,  
30 - default: 0  
31 - },  
32 - date: {  
33 - type: String,  
34 - default: 0  
35 - }  
36 }, 28 },
37 components: {CubeInput: Input} 29 components: {CubeInput: Input}
38 }; 30 };
1 <template> 1 <template>
2 <div class="article-item-header"> 2 <div class="article-item-header">
3 <div class="avatar"> 3 <div class="avatar">
4 - <WidgetAvatar class="widget-avatar" :src="data.authorHeadIco" :width="70" :height="70"></WidgetAvatar> 4 + <WidgetAvatar :lazy="lazy" class="widget-avatar" :src="data.authorHeadIco" :width="70" :height="70"></WidgetAvatar>
5 <span class="name">{{data.authorName}}</span> 5 <span class="name">{{data.authorName}}</span>
6 </div> 6 </div>
7 <div class="opts"> 7 <div class="opts">
8 - <button class="btn-follow hover-opacity" v-if="true">关注</button>  
9 - <button class="btn-follow followed hover-opacity" v-else>已关注</button> 8 + <WidgetFollow :article-id="data.authorUid" :followed="data.hasAttention === 'Y'"></WidgetFollow>
10 <i class="iconfont icon-more1" @click="onMore"></i> 9 <i class="iconfont icon-more1" @click="onMore"></i>
11 </div> 10 </div>
12 </div> 11 </div>
@@ -21,6 +20,10 @@ export default { @@ -21,6 +20,10 @@ export default {
21 default() { 20 default() {
22 return {}; 21 return {};
23 } 22 }
  23 + },
  24 + lazy: {
  25 + type: Boolean,
  26 + default: true
24 } 27 }
25 }, 28 },
26 methods: { 29 methods: {
@@ -75,25 +78,6 @@ export default { @@ -75,25 +78,6 @@ export default {
75 align-items: center; 78 align-items: center;
76 justify-content: flex-end; 79 justify-content: flex-end;
77 80
78 - .btn-follow {  
79 - width: 120px;  
80 - height: 50px;  
81 - padding: 0;  
82 - font-size: 26px;  
83 - border-radius: 3PX;  
84 - background-color: #222;  
85 - color: #fff;  
86 - display: flex;  
87 - align-items: center;  
88 - justify-content: center;  
89 -  
90 - &.followed {  
91 - border: solid 1px #4a4a4a;  
92 - background-color: #fff;  
93 - color: #000;  
94 - }  
95 - }  
96 -  
97 .icon-more1 { 81 .icon-more1 {
98 font-size: 40px; 82 font-size: 40px;
99 margin-left: 30px; 83 margin-left: 30px;
@@ -2,22 +2,25 @@ @@ -2,22 +2,25 @@
2 <div class="article-item-intro"> 2 <div class="article-item-intro">
3 <div ref="intro" class="intro hover-opacity" :class="introClass" :style="introStyle" @click="onExpand"> 3 <div ref="intro" class="intro hover-opacity" :class="introClass" :style="introStyle" @click="onExpand">
4 {{intro}} 4 {{intro}}
5 - <span class="expand" v-if="!isExpand">…展开</span>  
6 - <span class="expand collapse" v-else>收起</span> 5 + <span class="expand" v-if="!isExpand && isEllipsis">…展开</span>
  6 + <span class="expand collapse" v-if="isExpand && isEllipsis">收起</span>
7 </div> 7 </div>
8 <div class="topics"> 8 <div class="topics">
9 - <WidgetTopic topic="种草1" @click.native="onTopic"></WidgetTopic>  
10 - <WidgetTopic topic="种草2" @click.native="onTopic"></WidgetTopic>  
11 - <WidgetTopic topic="种草3" @click.native="onTopic"></WidgetTopic> 9 + <WidgetTopic
  10 + :topic="label.labelName"
  11 + @click.native="onTopic(label)"
  12 + v-for="label in data.labelList"
  13 + :key="label.labelId">
  14 + </WidgetTopic>
12 </div> 15 </div>
13 <div class="widgets"> 16 <div class="widgets">
14 <div class="share"> 17 <div class="share">
15 <WidgetShare></WidgetShare> 18 <WidgetShare></WidgetShare>
16 </div> 19 </div>
17 <div class="opts"> 20 <div class="opts">
18 - <WidgetFav num="99"></WidgetFav>  
19 - <WidgetLike num="91"></WidgetLike>  
20 - <WidgetFav num="99"></WidgetFav> 21 + <WidgetFav :num="data.favoriteCount" :option="favoriteOption"></WidgetFav>
  22 + <WidgetLike :num="data.praiseCount" :option="praiseOption"></WidgetLike>
  23 + <WidgetFav :num="data.commentCount"></WidgetFav>
21 </div> 24 </div>
22 </div> 25 </div>
23 </div> 26 </div>
@@ -45,12 +48,16 @@ export default { @@ -45,12 +48,16 @@ export default {
45 introHeight: 0 48 introHeight: 0
46 }; 49 };
47 }, 50 },
  51 + created() {
  52 + if (this.data.intro.length < 66) {
  53 + this.isEllipsis = false;
  54 + }
  55 + },
48 computed: { 56 computed: {
49 intro() { 57 intro() {
50 - if (!this.isEllipsis) { 58 + if (this.isExpand || this.data.intro.length < 66) {
51 return this.data.intro; 59 return this.data.intro;
52 - }  
53 - if (this.data.intro.length > 66) { 60 + } else {
54 return this.data.intro.substring(0, 66); 61 return this.data.intro.substring(0, 66);
55 } 62 }
56 }, 63 },
@@ -63,7 +70,17 @@ export default { @@ -63,7 +70,17 @@ export default {
63 return { 70 return {
64 height: this.introHeight ? `${this.introHeight}px` : void 0 71 height: this.introHeight ? `${this.introHeight}px` : void 0
65 }; 72 };
66 - } 73 + },
  74 + favoriteOption() {
  75 + return {
  76 + selected: this.data.hasFavor === 'Y'
  77 + };
  78 + },
  79 + praiseOption() {
  80 + return {
  81 + selected: this.data.hasPraise === 'Y'
  82 + };
  83 + },
67 }, 84 },
68 mounted() { 85 mounted() {
69 this.introCollapseHeight = this.$refs.intro.scrollHeight; 86 this.introCollapseHeight = this.$refs.intro.scrollHeight;
@@ -79,8 +96,10 @@ export default { @@ -79,8 +96,10 @@ export default {
79 }); 96 });
80 }, 97 },
81 onExpand() { 98 onExpand() {
  99 + if (!this.isEllipsis) {
  100 + return;
  101 + }
82 this.isExpand = !this.isExpand; 102 this.isExpand = !this.isExpand;
83 - this.isEllipsis = false;  
84 this.$nextTick(() => { 103 this.$nextTick(() => {
85 if (this.isExpand) { 104 if (this.isExpand) {
86 this.introHeight = this.$refs.intro.scrollHeight; 105 this.introHeight = this.$refs.intro.scrollHeight;
@@ -89,9 +108,6 @@ export default { @@ -89,9 +108,6 @@ export default {
89 } 108 }
90 this.isExpanding = true; 109 this.isExpanding = true;
91 this.$emit('on-resizeing'); 110 this.$emit('on-resizeing');
92 - if (!this.isExpand) {  
93 - this.isEllipsis = true;  
94 - }  
95 this.resizeDebounce(); 111 this.resizeDebounce();
96 }); 112 });
97 }, 113 },
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <div class="article-item-slide"> 2 <div class="article-item-slide">
3 <Slide :data="data.blockList" :threshold="0.2" :auto-play="false" :loop="false" :options="scrollOption" @change="onChange"> 3 <Slide :data="data.blockList" :threshold="0.2" :auto-play="false" :loop="false" :options="scrollOption" @change="onChange">
4 <SlideItem v-for="(item, inx) in data.blockList" :key="inx"> 4 <SlideItem v-for="(item, inx) in data.blockList" :key="inx">
5 - <ImageFormat :lazy="data.index > 0" class="image-slide-item" :src="item.contentData" :width="item.width" :height="item.height"></ImageFormat> 5 + <ImageFormat :lazy="lazy" class="image-slide-item" :src="item.contentData" :width="item.width" :height="item.height"></ImageFormat>
6 </SlideItem> 6 </SlideItem>
7 <template slot="dots" slot-scope="props"> 7 <template slot="dots" slot-scope="props">
8 <span class="slide-dot" 8 <span class="slide-dot"
@@ -29,6 +29,10 @@ export default { @@ -29,6 +29,10 @@ export default {
29 default() { 29 default() {
30 return {}; 30 return {};
31 } 31 }
  32 + },
  33 + lazy: {
  34 + type: Boolean,
  35 + default: true
32 } 36 }
33 }, 37 },
34 data() { 38 data() {
1 <template> 1 <template>
2 <div class="article-item"> 2 <div class="article-item">
3 - <ArticleItemHeader :data="headerData"></ArticleItemHeader>  
4 - <ArticleItemSlide :data="slideData"></ArticleItemSlide>  
5 - <ProductGroup :data="productListData"></ProductGroup> 3 + <ArticleItemHeader :data="headerData" :lazy="lazy"></ArticleItemHeader>
  4 + <ArticleItemSlide :data="slideData" :lazy="lazy"></ArticleItemSlide>
  5 + <ProductGroup :data="productListData" :lazy="lazy"></ProductGroup>
6 <ArticleItemIntro :data="introData" @on-resize="onResize" @on-resizeing="onResizeing"></ArticleItemIntro> 6 <ArticleItemIntro :data="introData" @on-resize="onResize" @on-resizeing="onResizeing"></ArticleItemIntro>
7 - <ArticleItemComment :comments="commentData" :count="12" :date="'1天前'"></ArticleItemComment> 7 + <ArticleItemComment :data="commentData"></ArticleItemComment>
8 <div class="line"></div> 8 <div class="line"></div>
9 </div> 9 </div>
10 </template> 10 </template>
11 11
12 <script> 12 <script>
  13 +import {get} from 'lodash';
13 import ArticleItemHeader from './article-item-header'; 14 import ArticleItemHeader from './article-item-header';
14 import ArticleItemSlide from './article-item-slide'; 15 import ArticleItemSlide from './article-item-slide';
15 import ArticleItemIntro from './article-item-intro'; 16 import ArticleItemIntro from './article-item-intro';
16 import ArticleItemComment from './article-item-comment'; 17 import ArticleItemComment from './article-item-comment';
  18 +import dayjs from 'dayjs';
17 19
18 export default { 20 export default {
19 name: 'ArticleItem', 21 name: 'ArticleItem',
@@ -29,47 +31,40 @@ export default { @@ -29,47 +31,40 @@ export default {
29 headerData() { 31 headerData() {
30 return { 32 return {
31 authorName: this.data.authorName, 33 authorName: this.data.authorName,
32 - articleId: this.data.articleId,  
33 - authorHeadIco: this.data.authorHeadIco 34 + authorUid: this.data.authorUid,
  35 + authorHeadIco: this.data.authorHeadIco,
  36 + hasAttention: this.data.hasAttention,
34 }; 37 };
35 }, 38 },
36 slideData() { 39 slideData() {
37 return { 40 return {
38 - blockList: this.data.blockList,  
39 - index: this.data.index 41 + blockList: get(this.data, 'blockList', []).filter(block => block.templateKey === 'image'),
40 }; 42 };
41 }, 43 },
42 introData() { 44 introData() {
43 return { 45 return {
44 - intro: '旗下大热鞋款旗下大热鞋款旗下大热鞋款一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型旗下大热鞋款也轻松成为许多鞋迷的心头好。近日,旗下旗下大热鞋款旗下大热鞋款旗下大热鞋款一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型旗下大热鞋款也轻松成为许多鞋迷的心头好。近日,旗下大热鞋款旗下大热鞋款旗下大热鞋款旗下大热鞋款旗下大热鞋款一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型旗下大热鞋款也轻松成为许多鞋迷的心头好。近日,旗下大热鞋款旗下大热鞋款大热鞋款旗下大热鞋款 Air Max 95 一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型 Air Max 95 也轻松成为许多鞋迷的心头好。' 46 + intro: get(get(this.data, 'blockList', []).filter(block => block.templateKey === 'text'), '[0].contentData'),
  47 + labelList: this.data.labelList,
  48 + hasFavor: this.data.hasFavor,
  49 + hasPraise: this.data.hasPraise,
  50 + commentCount: this.data.commentCount,
  51 + praiseCount: this.data.praiseCount,
  52 + favoriteCount: this.data.favoriteCount
45 }; 53 };
46 }, 54 },
47 commentData() { 55 commentData() {
48 - return [{  
49 - name: 'NIKE后援团',  
50 - content: '好期待,一定要抢一双👟!',  
51 - }, {  
52 - name: 'NIKE后援团',  
53 - content: '表情表情!!!这双仔细看好好看!!哈哈哈哈哈哈😄✌️'  
54 - }]; 56 + return {
  57 + comments: this.data.comments,
  58 + commentCount: this.data.commentCount,
  59 + publishTime: this.data.publishTime,
  60 + date: dayjs(this.data.publishTime).fromNow()
  61 + };
55 }, 62 },
56 productListData() { 63 productListData() {
57 - return [{  
58 - src: '//img12.static.yhbimg.com/goodsimg/2019/01/29/15/022a23864f68c66a6e1ef398ce7bd82efc.jpg?imageView2/2/w/640/h/640/q/60',  
59 - name: 'Off-White™ x Nike Air Max 90 全新「Desert Ore」配色曝光',  
60 - price: 3299,  
61 - isFav: false  
62 - }, {  
63 - src: '//img12.static.yhbimg.com/goodsimg/2019/01/29/15/022a23864f68c66a6e1ef398ce7bd82efc.jpg?imageView2/2/w/640/h/640/q/60',  
64 - name: 'Off-White™ x Nike Air Max 90 全新「Desert Ore」配色曝光',  
65 - price: 2299,  
66 - isFav: true  
67 - }, {  
68 - src: '//img12.static.yhbimg.com/goodsimg/2019/01/29/15/022a23864f68c66a6e1ef398ce7bd82efc.jpg?imageView2/2/w/640/h/640/q/60',  
69 - name: 'Off-White™ x Nike Air Max 90 全新「Desert Ore」配色曝光',  
70 - price: 1299,  
71 - isFav: false  
72 - }]; 64 + return this.data.productList || [];
  65 + },
  66 + lazy() {
  67 + return this.data.index > 1;
73 } 68 }
74 }, 69 },
75 methods: { 70 methods: {
@@ -6,14 +6,13 @@ @@ -6,14 +6,13 @@
6 <span class="user-name">{{currentAuthor.authorName}}</span> 6 <span class="user-name">{{currentAuthor.authorName}}</span>
7 </template> 7 </template>
8 <template v-if="showHeader" v-slot:opts> 8 <template v-if="showHeader" v-slot:opts>
9 - <button class="btn-follow hover-opacity" v-if="true">关注</button>  
10 - <button class="btn-follow followed hover-opacity" v-else>已关注</button> 9 + <WidgetFollow :article-id="currentAuthor.authorUid" :followed="currentAuthor.hasAttention === 'Y'"></WidgetFollow>
11 </template> 10 </template>
12 </LayoutHeader> 11 </LayoutHeader>
13 <LayoutScroll v-if="isMounted" ref="scroll" @scroll="onScroll" :offset="1000" :on-fetch="onFetch"> 12 <LayoutScroll v-if="isMounted" ref="scroll" @scroll="onScroll" :offset="1000" :on-fetch="onFetch">
14 <template class="article-item" v-slot:item="{ data }"> 13 <template class="article-item" v-slot:item="{ data }">
15 - <ArticleItem :id="`item${data.id}`" :data="data" :data-id="data.id" @on-resize="onResize(data)" @on-resizeing="onResizeing(data)"></ArticleItem>  
16 - <div :id="`ph${data.id}`"></div> 14 + <ArticleItem :id="`item${data.index}`" :data="data" :data-id="data.index" @on-resize="onResize(data)" @on-resizeing="onResizeing(data)"></ArticleItem>
  15 + <div :id="`ph${data.index}`"></div>
17 </template> 16 </template>
18 </LayoutScroll> 17 </LayoutScroll>
19 <slot name="thumb" v-else></slot> 18 <slot name="thumb" v-else></slot>
@@ -33,7 +32,9 @@ export default { @@ -33,7 +32,9 @@ export default {
33 onFetch: Function 32 onFetch: Function
34 }, 33 },
35 mounted() { 34 mounted() {
36 - this.isMounted = true; 35 + this.$nextTick(() => {
  36 + this.isMounted = true;
  37 + })
37 }, 38 },
38 data() { 39 data() {
39 return { 40 return {
@@ -43,8 +44,10 @@ export default { @@ -43,8 +44,10 @@ export default {
43 showHeader: false, 44 showHeader: false,
44 isMounted: false, 45 isMounted: false,
45 currentAuthor: { 46 currentAuthor: {
  47 + authorUid: 0,
46 authorName: '', 48 authorName: '',
47 authorHeadIco: '', 49 authorHeadIco: '',
  50 + hasAttention: 'N',
48 opacity: 1, 51 opacity: 1,
49 isShare: false 52 isShare: false
50 } 53 }
@@ -62,8 +65,10 @@ export default { @@ -62,8 +65,10 @@ export default {
62 this.currentAuthor.opacity = 1; 65 this.currentAuthor.opacity = 1;
63 this.showHeader = false; 66 this.showHeader = false;
64 } else { 67 } else {
  68 + this.currentAuthor.authorUid = item.data.authorUid;
65 this.currentAuthor.authorName = item.data.authorName; 69 this.currentAuthor.authorName = item.data.authorName;
66 this.currentAuthor.authorHeadIco = item.data.authorHeadIco; 70 this.currentAuthor.authorHeadIco = item.data.authorHeadIco;
  71 + this.currentAuthor.hasAttention = item.data.hasAttention;
67 this.showHeader = true; 72 this.showHeader = true;
68 const offsetTop = scrollTop - item.top; 73 const offsetTop = scrollTop - item.top;
69 74
@@ -83,20 +88,20 @@ export default { @@ -83,20 +88,20 @@ export default {
83 this.$refs.scroll.init(); 88 this.$refs.scroll.init();
84 }, 89 },
85 onResize(data) { 90 onResize(data) {
86 - const $phItem = document.getElementById(`ph${data.id}`); 91 + const $phItem = document.getElementById(`ph${data.index}`);
87 92
88 $phItem.innerHTML = ''; 93 $phItem.innerHTML = '';
89 $phItem.status = 0; 94 $phItem.status = 0;
90 this.$refs.scroll.resize(); 95 this.$refs.scroll.resize();
91 }, 96 },
92 onResizeing(data) { 97 onResizeing(data) {
93 - const $phItem = document.getElementById(`ph${data.id}`); 98 + const $phItem = document.getElementById(`ph${data.index}`);
94 99
95 if ($phItem.status === 1) { 100 if ($phItem.status === 1) {
96 return; 101 return;
97 } 102 }
98 103
99 - const $nextItem = document.getElementById(`item${data.id + 1}`); 104 + const $nextItem = document.getElementById(`item${data.index + 1}`);
100 const html = $nextItem.outerHTML; 105 const html = $nextItem.outerHTML;
101 106
102 $phItem.innerHTML = html; 107 $phItem.innerHTML = html;
@@ -11,6 +11,9 @@ export default { @@ -11,6 +11,9 @@ export default {
11 }); 11 });
12 12
13 if (result && result.code === 200) { 13 if (result && result.code === 200) {
  14 + if (!result.data.detailList) {
  15 + result.data.detailList = [];
  16 + }
14 result.data.detailList = result.data.detailList.map((item, inx) => { 17 result.data.detailList = result.data.detailList.map((item, inx) => {
15 item.index = (page - 1) * limit + inx; 18 item.index = (page - 1) * limit + inx;
16 return item; 19 return item;
@@ -39,6 +39,7 @@ @@ -39,6 +39,7 @@
39 "connect-redis": "^3.4.0", 39 "connect-redis": "^3.4.0",
40 "cookie-parser": "^1.4.3", 40 "cookie-parser": "^1.4.3",
41 "cube-ui": "^1.12.6", 41 "cube-ui": "^1.12.6",
  42 + "dayjs": "^1.8.5",
42 "express": "^4.16.4", 43 "express": "^4.16.4",
43 "express-session": "^1.15.6", 44 "express-session": "^1.15.6",
44 "fastclick": "^1.0.6", 45 "fastclick": "^1.0.6",
@@ -2057,6 +2057,10 @@ date-now@^0.1.4: @@ -2057,6 +2057,10 @@ date-now@^0.1.4:
2057 version "0.1.4" 2057 version "0.1.4"
2058 resolved "http://npm.yohops.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" 2058 resolved "http://npm.yohops.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
2059 2059
  2060 +dayjs@^1.8.5:
  2061 + version "1.8.5"
  2062 + resolved "http://npm.yohops.com/dayjs/-/dayjs-1.8.5.tgz#0b066770f89a20022218544989f3d23e5e8db29a"
  2063 +
2060 de-indent@^1.0.2: 2064 de-indent@^1.0.2:
2061 version "1.0.2" 2065 version "1.0.2"
2062 resolved "http://npm.yohops.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" 2066 resolved "http://npm.yohops.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"