Showing
20 changed files
with
192 additions
and
118 deletions
@@ -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: { |
apps/components/widgets/widget-follow.vue
0 → 100644
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> |
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" |
-
Please register or login to post a comment