Showing
17 changed files
with
159 additions
and
43 deletions
1 | <template> | 1 | <template> |
2 | - <button class="btn-follow hover-opacity" v-if="!followed">关注</button> | ||
3 | - <button class="btn-follow followed hover-opacity" v-else>已关注</button> | 2 | + <button class="btn-follow hover-opacity" :class="followClass" @click="onFollow">{{followText}}</button> |
4 | </template> | 3 | </template> |
5 | 4 | ||
6 | <script> | 5 | <script> |
6 | +import {createNamespacedHelpers} from 'vuex'; | ||
7 | +const {mapActions} = createNamespacedHelpers('user'); | ||
8 | + | ||
7 | export default { | 9 | export default { |
8 | name: 'WidgetFollow', | 10 | name: 'WidgetFollow', |
9 | props: { | 11 | props: { |
10 | authorUid: Number, | 12 | authorUid: Number, |
11 | - followed: Boolean | 13 | + follow: Boolean |
14 | + }, | ||
15 | + data() { | ||
16 | + return { | ||
17 | + loading: false, | ||
18 | + followStatus: this.follow | ||
19 | + }; | ||
20 | + }, | ||
21 | + watch: { | ||
22 | + follow(val) { | ||
23 | + this.followStatus = val; | ||
24 | + } | ||
25 | + }, | ||
26 | + computed: { | ||
27 | + followClass() { | ||
28 | + return { | ||
29 | + loading: this.loading, | ||
30 | + follow: this.followStatus | ||
31 | + }; | ||
32 | + }, | ||
33 | + followText() { | ||
34 | + return this.followStatus ? '已关注' : '关注'; | ||
35 | + } | ||
36 | + }, | ||
37 | + methods: { | ||
38 | + ...mapActions(['followUser']), | ||
39 | + async onFollow() { | ||
40 | + if (this.loading) { | ||
41 | + return; | ||
42 | + } | ||
43 | + this.loading = true; | ||
44 | + const result = await this.followUser({followUid: this.authorUid, status: this.followStatus ? 1 : 0}); | ||
45 | + | ||
46 | + this.loading = false; | ||
47 | + if (result.code === 200) { | ||
48 | + this.followStatus = !this.followStatus; | ||
49 | + this.$emit('on-follow', this.followStatus); | ||
50 | + } else { | ||
51 | + this.$createToast && this.$createToast({ | ||
52 | + txt: result.message || '服务器开小差了', | ||
53 | + type: 'warn', | ||
54 | + time: 1000 | ||
55 | + }).show(); | ||
56 | + } | ||
57 | + } | ||
12 | } | 58 | } |
13 | }; | 59 | }; |
14 | </script> | 60 | </script> |
@@ -26,7 +72,7 @@ export default { | @@ -26,7 +72,7 @@ export default { | ||
26 | align-items: center; | 72 | align-items: center; |
27 | justify-content: center; | 73 | justify-content: center; |
28 | 74 | ||
29 | - &.followed { | 75 | + &.follow { |
30 | border: solid 1px #4a4a4a; | 76 | border: solid 1px #4a4a4a; |
31 | background-color: #fff; | 77 | background-color: #fff; |
32 | color: #000; | 78 | color: #000; |
@@ -21,24 +21,23 @@ export default { | @@ -21,24 +21,23 @@ export default { | ||
21 | }; | 21 | }; |
22 | }, | 22 | }, |
23 | serverPrefetch() { | 23 | serverPrefetch() { |
24 | - console.log('serverPrefetch') | ||
25 | return this.onFetch(); | 24 | return this.onFetch(); |
26 | }, | 25 | }, |
27 | computed: { | 26 | computed: { |
28 | - ...mapState(['articleCurrentList']), | 27 | + ...mapState(['articleList']), |
29 | currentList() { | 28 | currentList() { |
30 | - if (this.articleCurrentList.length > 2) { | ||
31 | - return this.articleCurrentList.slice(0, 2); | 29 | + if (this.articleList.length > 2) { |
30 | + return this.articleList.slice(0, 2); | ||
32 | } | 31 | } |
33 | - return this.articleCurrentList; | 32 | + return this.articleList; |
34 | } | 33 | } |
35 | }, | 34 | }, |
36 | methods: { | 35 | methods: { |
37 | ...mapActions(['fetchArticleList']), | 36 | ...mapActions(['fetchArticleList']), |
38 | async onFetch() { | 37 | async onFetch() { |
39 | - if (this.page === 1 && this.articleCurrentList.length) { | 38 | + if (this.page === 1 && this.articleList.length) { |
40 | this.page++; | 39 | this.page++; |
41 | - return this.articleCurrentList; | 40 | + return this.articleList; |
42 | } | 41 | } |
43 | const articleId = parseInt(this.$route.params.id, 10); | 42 | const articleId = parseInt(this.$route.params.id, 10); |
44 | 43 |
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
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 | - <WidgetFollow :article-id="data.authorUid" :followed="data.hasAttention === 'Y'"></WidgetFollow> | 8 | + <WidgetFollow :author-uid="data.authorUid" :follow="data.hasAttention === 'Y'" @on-follow="onFollow"></WidgetFollow> |
9 | <i class="iconfont icon-more1" @click="onMore"></i> | 9 | <i class="iconfont icon-more1" @click="onMore"></i> |
10 | </div> | 10 | </div> |
11 | </div> | 11 | </div> |
@@ -40,6 +40,9 @@ export default { | @@ -40,6 +40,9 @@ export default { | ||
40 | }).show(); | 40 | }).show(); |
41 | } | 41 | } |
42 | }).show(); | 42 | }).show(); |
43 | + }, | ||
44 | + onFollow(follow) { | ||
45 | + this.$emit('on-follow', follow); | ||
43 | } | 46 | } |
44 | } | 47 | } |
45 | }; | 48 | }; |
1 | <template> | 1 | <template> |
2 | <div class="article-item"> | 2 | <div class="article-item"> |
3 | - <ArticleItemHeader :data="headerData" :lazy="lazy"></ArticleItemHeader> | 3 | + <ArticleItemHeader :data="headerData" :lazy="lazy" @on-follow="onFollow"></ArticleItemHeader> |
4 | <ArticleItemSlide :data="slideData" :lazy="lazy"></ArticleItemSlide> | 4 | <ArticleItemSlide :data="slideData" :lazy="lazy"></ArticleItemSlide> |
5 | <ProductGroup :data="productListData" :lazy="lazy"></ProductGroup> | 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> |
@@ -73,6 +73,9 @@ export default { | @@ -73,6 +73,9 @@ export default { | ||
73 | }, | 73 | }, |
74 | onResizeing() { | 74 | onResizeing() { |
75 | this.$emit('on-resizeing'); | 75 | this.$emit('on-resizeing'); |
76 | + }, | ||
77 | + onFollow(follow) { | ||
78 | + this.$emit('on-follow', follow); | ||
76 | } | 79 | } |
77 | }, | 80 | }, |
78 | components: {ArticleItemHeader, ArticleItemSlide, ArticleItemIntro, ArticleItemComment} | 81 | components: {ArticleItemHeader, ArticleItemSlide, ArticleItemIntro, ArticleItemComment} |
@@ -6,12 +6,17 @@ | @@ -6,12 +6,17 @@ | ||
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 | - <WidgetFollow :article-id="currentAuthor.authorUid" :followed="currentAuthor.hasAttention === 'Y'"></WidgetFollow> | 9 | + <WidgetFollow class="widget-follow" :author-uid="currentAuthor.authorUid" :follow="currentAuthor.hasAttention === 'Y'" @on-follow="follow => onFollow(currentAuthor, follow)"></WidgetFollow> |
10 | </template> | 10 | </template> |
11 | </LayoutHeader> | 11 | </LayoutHeader> |
12 | <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"> |
13 | <template class="article-item" v-slot:item="{ data }"> | 13 | <template class="article-item" v-slot:item="{ data }"> |
14 | - <ArticleItem :id="`item${data.index}`" :data="data" :data-id="data.index" @on-resize="onResize(data)" @on-resizeing="onResizeing(data)"></ArticleItem> | 14 | + <ArticleItem |
15 | + :id="`item${data.index}`" | ||
16 | + :data="data" :data-id="data.index" | ||
17 | + @on-resize="onResize(data)" | ||
18 | + @on-resizeing="onResizeing(data)" | ||
19 | + @on-follow="follow => onFollow(data, follow)"></ArticleItem> | ||
15 | <div :id="`ph${data.index}`"></div> | 20 | <div :id="`ph${data.index}`"></div> |
16 | </template> | 21 | </template> |
17 | </LayoutScroll> | 22 | </LayoutScroll> |
@@ -21,6 +26,8 @@ | @@ -21,6 +26,8 @@ | ||
21 | 26 | ||
22 | <script> | 27 | <script> |
23 | import ArticleItem from './article-item'; | 28 | import ArticleItem from './article-item'; |
29 | +import {createNamespacedHelpers} from 'vuex'; | ||
30 | +const {mapMutations} = createNamespacedHelpers('article'); | ||
24 | 31 | ||
25 | export default { | 32 | export default { |
26 | name: 'Article', | 33 | name: 'Article', |
@@ -59,6 +66,7 @@ export default { | @@ -59,6 +66,7 @@ export default { | ||
59 | } | 66 | } |
60 | }, | 67 | }, |
61 | methods: { | 68 | methods: { |
69 | + ...mapMutations(['CHANGE_AUTHOR_FOLLOW']), | ||
62 | onScroll({item, scrollTop}) { | 70 | onScroll({item, scrollTop}) { |
63 | this.scrollTop = scrollTop; | 71 | this.scrollTop = scrollTop; |
64 | if (scrollTop === 0) { | 72 | if (scrollTop === 0) { |
@@ -108,6 +116,12 @@ export default { | @@ -108,6 +116,12 @@ export default { | ||
108 | $phItem.style.zIndex = 999; | 116 | $phItem.style.zIndex = 999; |
109 | $phItem.status = 1; | 117 | $phItem.status = 1; |
110 | }, | 118 | }, |
119 | + onFollow(data, follow) { | ||
120 | + this.CHANGE_AUTHOR_FOLLOW({authorUid: data.authorUid, follow}); | ||
121 | + if (data.authorUid === this.currentAuthor.authorUid) { | ||
122 | + this.currentAuthor.hasAttention = follow ? 'Y' : 'N'; | ||
123 | + } | ||
124 | + } | ||
111 | }, | 125 | }, |
112 | components: { | 126 | components: { |
113 | ArticleItem | 127 | ArticleItem |
@@ -125,24 +139,8 @@ export default { | @@ -125,24 +139,8 @@ export default { | ||
125 | height: 52px; | 139 | height: 52px; |
126 | } | 140 | } |
127 | 141 | ||
128 | -.btn-follow { | ||
129 | - width: 120px; | ||
130 | - height: 50px; | ||
131 | - padding: 0; | ||
132 | - font-size: 26px; | ||
133 | - border-radius: 3PX; | ||
134 | - background-color: #222; | ||
135 | - color: #fff; | ||
136 | - display: flex; | ||
137 | - align-items: center; | ||
138 | - justify-content: center; | 142 | +.widget-follow { |
139 | margin-right: 30px; | 143 | margin-right: 30px; |
140 | - | ||
141 | - &.followed { | ||
142 | - border: solid 1px #4a4a4a; | ||
143 | - background-color: #fff; | ||
144 | - color: #222; | ||
145 | - } | ||
146 | } | 144 | } |
147 | 145 | ||
148 | .user-name { | 146 | .user-name { |
@@ -14,12 +14,9 @@ export default { | @@ -14,12 +14,9 @@ export default { | ||
14 | if (!result.data.detailList) { | 14 | if (!result.data.detailList) { |
15 | result.data.detailList = []; | 15 | result.data.detailList = []; |
16 | } | 16 | } |
17 | - result.data.detailList = result.data.detailList.map((item, inx) => { | ||
18 | - item.index = (page - 1) * limit + inx; | ||
19 | - return item; | ||
20 | - }); | ||
21 | commit(Types.FETCH_ARTICLE_DETAIL_SUCCESS, { | 17 | commit(Types.FETCH_ARTICLE_DETAIL_SUCCESS, { |
22 | data: result.data.detailList, | 18 | data: result.data.detailList, |
19 | + page | ||
23 | }); | 20 | }); |
24 | } else { | 21 | } else { |
25 | commit(Types.FETCH_ARTICLE_DETAIL_FAILD); | 22 | commit(Types.FETCH_ARTICLE_DETAIL_FAILD); |
@@ -6,7 +6,7 @@ export default function() { | @@ -6,7 +6,7 @@ export default function() { | ||
6 | namespaced: true, | 6 | namespaced: true, |
7 | state: { | 7 | state: { |
8 | fetchArticleList: false, | 8 | fetchArticleList: false, |
9 | - articleCurrentList: [], | 9 | + articleList: [], |
10 | articleDetail: null, | 10 | articleDetail: null, |
11 | }, | 11 | }, |
12 | actions, | 12 | actions, |
@@ -4,15 +4,28 @@ export default { | @@ -4,15 +4,28 @@ export default { | ||
4 | [Types.FETCH_ARTICLE_DETAIL_REQUEST](state) { | 4 | [Types.FETCH_ARTICLE_DETAIL_REQUEST](state) { |
5 | state.fetchArticleList = true; | 5 | state.fetchArticleList = true; |
6 | }, | 6 | }, |
7 | - [Types.FETCH_ARTICLE_DETAIL_SUCCESS](state, {data}) { | 7 | + [Types.FETCH_ARTICLE_DETAIL_SUCCESS](state, {data, page}) { |
8 | state.fetchArticleList = false; | 8 | state.fetchArticleList = false; |
9 | - state.articleCurrentList = data; | 9 | + if (page === 1) { |
10 | + state.articleList = []; | ||
11 | + } | ||
12 | + state.articleList = state.articleList.concat(data); | ||
13 | + | ||
14 | + state.articleList.forEach((item, index) => { | ||
15 | + item.index = index; | ||
16 | + }); | ||
10 | }, | 17 | }, |
11 | [Types.FETCH_ARTICLE_DETAIL_FAILD](state) { | 18 | [Types.FETCH_ARTICLE_DETAIL_FAILD](state) { |
12 | state.fetchArticleList = false; | 19 | state.fetchArticleList = false; |
13 | }, | 20 | }, |
14 | [Types.FETCH_GUANG_REQUEST](state, data) { | 21 | [Types.FETCH_GUANG_REQUEST](state, data) { |
15 | state.articleDetail = data; | 22 | state.articleDetail = data; |
23 | + }, | ||
24 | + [Types.CHANGE_AUTHOR_FOLLOW](state, {authorUid, follow}) { | ||
25 | + state.articleList.forEach(article => { | ||
26 | + if (article.authorUid === authorUid) { | ||
27 | + article.hasAttention = follow ? 'Y' : 'N'; | ||
28 | + } | ||
29 | + }); | ||
16 | } | 30 | } |
17 | - | ||
18 | }; | 31 | }; |
1 | export const FETCH_ARTICLE_DETAIL_REQUEST = 'FETCH_ARTICLE_DETAIL_REQUEST'; | 1 | export const FETCH_ARTICLE_DETAIL_REQUEST = 'FETCH_ARTICLE_DETAIL_REQUEST'; |
2 | export const FETCH_ARTICLE_DETAIL_FAILD = 'FETCH_ARTICLE_DETAIL_FAILD'; | 2 | export const FETCH_ARTICLE_DETAIL_FAILD = 'FETCH_ARTICLE_DETAIL_FAILD'; |
3 | export const FETCH_ARTICLE_DETAIL_SUCCESS = 'FETCH_ARTICLE_DETAIL_SUCCESS'; | 3 | export const FETCH_ARTICLE_DETAIL_SUCCESS = 'FETCH_ARTICLE_DETAIL_SUCCESS'; |
4 | + | ||
4 | export const FETCH_GUANG_REQUEST = 'FETCH_GUANG_REQUEST'; | 5 | export const FETCH_GUANG_REQUEST = 'FETCH_GUANG_REQUEST'; |
5 | export const FETCH_GUANG_FAILED = 'FETCH_GUANG_FAILED'; | 6 | export const FETCH_GUANG_FAILED = 'FETCH_GUANG_FAILED'; |
6 | export const FETCH_GUANG_SUCCESS = 'FETCH_GUANG_SUCCESS'; | 7 | export const FETCH_GUANG_SUCCESS = 'FETCH_GUANG_SUCCESS'; |
8 | + | ||
9 | +export const CHANGE_AUTHOR_FOLLOW = 'CHANGE_AUTHOR_FOLLOW'; |
@@ -3,6 +3,7 @@ import Vuex from 'vuex'; | @@ -3,6 +3,7 @@ import Vuex from 'vuex'; | ||
3 | import {createApi} from 'create-api'; | 3 | import {createApi} from 'create-api'; |
4 | import storeYoho from './yoho'; | 4 | import storeYoho from './yoho'; |
5 | import storeArticle from './article'; | 5 | import storeArticle from './article'; |
6 | +import storeUser from './user'; | ||
6 | import plugin from './plugin'; | 7 | import plugin from './plugin'; |
7 | 8 | ||
8 | Vue.use(Vuex); | 9 | Vue.use(Vuex); |
@@ -12,7 +13,8 @@ export function createStore(context) { | @@ -12,7 +13,8 @@ export function createStore(context) { | ||
12 | namespaced: true, | 13 | namespaced: true, |
13 | modules: { | 14 | modules: { |
14 | yoho: storeYoho(), | 15 | yoho: storeYoho(), |
15 | - article: storeArticle() | 16 | + article: storeArticle(), |
17 | + user: storeUser() | ||
16 | }, | 18 | }, |
17 | strict: process.env.NODE_ENV !== 'production', | 19 | strict: process.env.NODE_ENV !== 'production', |
18 | plugins: [plugin] | 20 | plugins: [plugin] |
apps/store/user/actions.js
0 → 100644
1 | +export default { | ||
2 | + async followUser(actions, {followUid, status}) { | ||
3 | + const result = await this.$api.get('/api/grass/updateAttention', { | ||
4 | + followUid, | ||
5 | + status, | ||
6 | + attentionType: 1 | ||
7 | + }); | ||
8 | + | ||
9 | + return result; | ||
10 | + }, | ||
11 | + async followTopic(actions, {topicId, status}) { | ||
12 | + const result = await this.$api.get('/api/grass/updateAttention', { | ||
13 | + topicId, | ||
14 | + status, | ||
15 | + attentionType: 0 | ||
16 | + }); | ||
17 | + | ||
18 | + return result; | ||
19 | + }, | ||
20 | +}; |
apps/store/user/index.js
0 → 100644
apps/store/user/mutations.js
0 → 100644
apps/store/user/types.js
0 → 100644
@@ -8,11 +8,19 @@ module.exports = { | @@ -8,11 +8,19 @@ module.exports = { | ||
8 | params: { | 8 | params: { |
9 | page: {type: Number, require: false}, | 9 | page: {type: Number, require: false}, |
10 | limit: {type: Number, require: false}, | 10 | limit: {type: Number, require: false}, |
11 | - uid: {type: Number, require: false}, | ||
12 | articleId: {type: Number}, | 11 | articleId: {type: Number}, |
13 | columnType: {type: Number} | 12 | columnType: {type: Number} |
14 | } | 13 | } |
15 | }, | 14 | }, |
15 | + '/api/grass/updateAttention': { | ||
16 | + api: 'app.grass.updateAttention', | ||
17 | + params: { | ||
18 | + topicId: {type: Number, require: false}, | ||
19 | + followUid: {type: Number, require: false}, | ||
20 | + status: {type: Number}, | ||
21 | + attentionType: {type: Number} | ||
22 | + } | ||
23 | + }, | ||
16 | '/api/guang/article/detail': { | 24 | '/api/guang/article/detail': { |
17 | service: true, | 25 | service: true, |
18 | api: URI_PACKAGE_ARTICLE, | 26 | api: URI_PACKAGE_ARTICLE, |
-
Please register or login to post a comment