Authored by htoooth

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	apps/store/article/types.js
... ... @@ -15,6 +15,7 @@
],
"rules": {
"camelcase": "off",
"max-len": "off"
"max-len": "off",
"new-cap": "off"
}
}
... ...
... ... @@ -8,13 +8,17 @@ import titleMixin from './mixins/title';
import pluginCore from './plugins/core';
import lazyload from 'vue-lazyload';
import reportError from 'report-error';
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import relativeTime from 'dayjs/plugin/relativeTime';
Vue.use(lazyload, {
preLoad: 2
});
Vue.use(pluginCore);
Vue.mixin(titleMixin);
dayjs.locale('zh-cn');
dayjs.extend(relativeTime);
export function createApp(context) {
const router = createRouter();
... ...
... ... @@ -3,6 +3,11 @@
<template v-slot:item="{ data }">
<slot name="item" :data="data"></slot>
</template>
<template v-slot:noMore>
<p class="">
没有更多了
</p>
</template>
</RecycleList>
</template>
... ...
... ... @@ -42,18 +42,15 @@
</div>
<div
v-if="!infinite"
class="cube-recycle-list-loading"
:style="{visibility: loading ? 'visible' : 'hidden'}"
>
class="cube-recycle-list-loading">
<slot name="spinner">
<div class="cube-recycle-list-loading-content">
<div class="cube-recycle-list-loading-content" v-show="!noMore" :style="{visibility: loading ? 'visible' : 'hidden'}">
<cube-loading class="spinner"></cube-loading>
</div>
</slot>
</div>
<div v-show="noMore" class="cube-recycle-list-noMore">
<slot name="noMore" />
<div v-show="noMore" class="cube-recycle-list-noMore">
<slot name="noMore" />
</div>
</div>
</div>
<div class="cube-recycle-list-fake"></div>
... ... @@ -153,7 +150,7 @@ export default {
// increase capacity of items to display tombstone
this.items.length += this.size;
this.loadItems();
} else if (!this.loading) {
} else if (!this.loading && !this.noMore) {
this.getItems();
}
},
... ...
... ... @@ -6,10 +6,10 @@
v-for="(product, inx) in data"
:key="inx">
<div class="product-content">
<ImageFormat class="product-image" :src="product.src"></ImageFormat>
<ImageFormat :lazy="lazy" class="product-image" :src="product.productImage" :width="136" :height="180"></ImageFormat>
<div class="product-info">
<p class="product-name">{{product.name}}</p>
<p class="price">¥{{product.price}}</p>
<p class="product-name">{{product.productName}}</p>
<p class="price">¥{{product.salesPrice}}</p>
</div>
</div>
<div class="btn-fav hover-opacity" v-if="!product.isFav">收藏</div>
... ... @@ -29,6 +29,10 @@ export default {
default() {
return [];
}
},
lazy: {
type: Boolean,
default: true
}
},
computed: {
... ... @@ -94,6 +98,8 @@ export default {
color: #9b9b9b;
letter-spacing: -0.25PX;
height: 104px;
display: flex;
align-content: center;
}
.price {
... ...
... ... @@ -5,6 +5,7 @@ import WidgetLike from './widget-like';
import WidgetShare from './widget-share';
import WidgetTopic from './widget-topic';
import WidgetAvatar from './widget-avatar';
import WidgetFollow from './widget-follow';
export default [
WidgetAvatarGroup,
... ... @@ -13,5 +14,6 @@ export default [
WidgetLike,
WidgetShare,
WidgetTopic,
WidgetAvatar
WidgetAvatar,
WidgetFollow
];
... ...
<template>
<ImageFormat class="img-avatar" :src="src" :width="width" :height="height"></ImageFormat>
<ImageFormat :lazy="lazy" class="img-avatar" :src="src" :width="width" :height="height"></ImageFormat>
</template>
<script>
... ... @@ -17,6 +17,10 @@ export default {
height: {
type: Number,
default: 35
},
lazy: {
type: Boolean,
default: false
}
},
computed: {
... ...
... ... @@ -6,7 +6,7 @@
export default {
name: 'WidgetFav',
props: {
num: String,
num: [String, Number],
option: Object
}
};
... ...
<template>
<button class="btn-follow hover-opacity" :class="followClass" @click="onFollow">{{followText}}</button>
</template>
<script>
import {createNamespacedHelpers} from 'vuex';
const {mapActions} = createNamespacedHelpers('user');
export default {
name: 'WidgetFollow',
props: {
authorUid: Number,
follow: Boolean
},
data() {
return {
loading: false,
followStatus: this.follow
};
},
watch: {
follow(val) {
this.followStatus = val;
}
},
computed: {
followClass() {
return {
loading: this.loading,
follow: this.followStatus
};
},
followText() {
return this.followStatus ? '已关注' : '关注';
}
},
methods: {
...mapActions(['followUser']),
async onFollow() {
if (this.loading) {
return;
}
this.loading = true;
const result = await this.followUser({followUid: this.authorUid, status: this.followStatus ? 1 : 0});
this.loading = false;
if (result.code === 200) {
this.followStatus = !this.followStatus;
this.$emit('on-follow', this.followStatus);
} else {
this.$createToast && this.$createToast({
txt: result.message || '服务器开小差了',
type: 'warn',
time: 1000
}).show();
}
}
}
};
</script>
<style lang="scss" scoped>
.btn-follow {
width: 120px;
height: 50px;
padding: 0;
font-size: 26px;
border-radius: 3PX;
background-color: #222;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
&.follow {
border: solid 1px #4a4a4a;
background-color: #fff;
color: #000;
}
}
</style>
... ...
... ... @@ -6,7 +6,7 @@
export default {
name: 'WidgetLike',
props: {
num: String,
num: [String, Number],
option: Object
}
};
... ...
... ... @@ -6,7 +6,7 @@
export default {
name: 'WidgetShare',
props: {
num: String,
num: [String, Number],
option: Object
}
};
... ...
<template>
<Article :on-fetch="onFetch">
<template v-slot:thumb>
<ArticleItem v-for="data in articleCurrentList" :key="data.articleId" :data="data"></ArticleItem>
<ArticleItem v-for="data in currentList" :key="data.articleId" :data="data"></ArticleItem>
</template>
</Article>
</template>
<script>
import {get} from 'lodash';
import Article from './components/article/article';
import ArticleItem from './components/article/article-item';
import {createNamespacedHelpers} from 'vuex';
... ... @@ -23,24 +24,39 @@ export default {
return this.onFetch();
},
computed: {
...mapState(['articleCurrentList'])
...mapState(['articleList']),
currentList() {
if (this.articleList.length > 2) {
return this.articleList.slice(0, 2);
}
return this.articleList;
}
},
methods: {
...mapActions(['fetchArticleList']),
async onFetch() {
if (this.page === 1 && this.articleCurrentList.length) {
return this.articleCurrentList;
if (this.page === 1 && this.articleList.length) {
this.page++;
return this.articleList;
}
const articleId = parseInt(this.$route.params.id, 10);
if (!articleId) {
return;
}
const result = await this.fetchArticleList({
articleId: this.$route.params.id,
articleId: parseInt(this.$route.params.id, 10),
page: this.page
});
if (result.code === 200) {
this.page++;
return Promise.resolve(result.data.detailList);
if (get(result, 'data.detailList', []).length) {
this.page++;
return Promise.resolve(result.data.detailList);
}
return Promise.resolve(false);
} else {
this.$createToast({
this.$createToast && this.$createToast({
txt: result.message || '服务器开小差了',
type: 'warn',
time: 1000
... ...
<template>
<div class="article-item-comment">
<p class="comment-item" v-for="(comment, inx) in comments" :key="inx">
<span class="user-name">{{comment.name}}:</span>
<p class="comment-item" v-for="(comment, inx) in data.comments" :key="inx">
<span class="user-name">{{comment.userName}}:</span>
<span class="comment-content">{{comment.content}}</span>
</p>
<div class="comment">
<div class="comment-input hover-opacity">添加回复:赞美是一种美德</div>
</div>
<div class="total-comment">
<div class="total hover-opacity">查看{{count}}条评论</div>
<div class="last-time">{{date}}</div>
<div class="total hover-opacity">查看{{data.commentCount}}条评论</div>
<div class="last-time">{{data.date}}</div>
</div>
</div>
</template>
... ... @@ -19,20 +19,12 @@ import {Input} from 'cube-ui';
export default {
name: 'ArticleItemComment',
props: {
comments: {
type: Array,
data: {
type: Object,
default() {
return [];
return {};
}
},
count: {
type: Number,
default: 0
},
date: {
type: String,
default: 0
}
},
components: {CubeInput: Input}
};
... ...
<template>
<div class="article-item-header">
<div class="avatar">
<WidgetAvatar class="widget-avatar" :src="data.authorHeadIco" :width="70" :height="70"></WidgetAvatar>
<WidgetAvatar :lazy="lazy" class="widget-avatar" :src="data.authorHeadIco" :width="70" :height="70"></WidgetAvatar>
<span class="name">{{data.authorName}}</span>
</div>
<div class="opts">
<button class="btn-follow hover-opacity" v-if="true">关注</button>
<button class="btn-follow followed hover-opacity" v-else>已关注</button>
<WidgetFollow :author-uid="data.authorUid" :follow="data.hasAttention === 'Y'" @on-follow="onFollow"></WidgetFollow>
<i class="iconfont icon-more1" @click="onMore"></i>
</div>
</div>
... ... @@ -21,6 +20,10 @@ export default {
default() {
return {};
}
},
lazy: {
type: Boolean,
default: true
}
},
methods: {
... ... @@ -37,6 +40,9 @@ export default {
}).show();
}
}).show();
},
onFollow(follow) {
this.$emit('on-follow', follow);
}
}
};
... ... @@ -75,25 +81,6 @@ export default {
align-items: center;
justify-content: flex-end;
.btn-follow {
width: 120px;
height: 50px;
padding: 0;
font-size: 26px;
border-radius: 3PX;
background-color: #222;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
&.followed {
border: solid 1px #4a4a4a;
background-color: #fff;
color: #000;
}
}
.icon-more1 {
font-size: 40px;
margin-left: 30px;
... ...
... ... @@ -2,22 +2,25 @@
<div class="article-item-intro">
<div ref="intro" class="intro hover-opacity" :class="introClass" :style="introStyle" @click="onExpand">
{{intro}}
<span class="expand" v-if="!isExpand">…展开</span>
<span class="expand collapse" v-else>收起</span>
<span class="expand" v-if="!isExpand && isEllipsis">…展开</span>
<span class="expand collapse" v-if="isExpand && isEllipsis">收起</span>
</div>
<div class="topics">
<WidgetTopic topic="种草1" @click.native="onTopic"></WidgetTopic>
<WidgetTopic topic="种草2" @click.native="onTopic"></WidgetTopic>
<WidgetTopic topic="种草3" @click.native="onTopic"></WidgetTopic>
<WidgetTopic
:topic="label.labelName"
@click.native="onTopic(label)"
v-for="label in data.labelList"
:key="label.labelId">
</WidgetTopic>
</div>
<div class="widgets">
<div class="share">
<WidgetShare></WidgetShare>
</div>
<div class="opts">
<WidgetFav num="99"></WidgetFav>
<WidgetLike num="91"></WidgetLike>
<WidgetFav num="99"></WidgetFav>
<WidgetFav :num="data.favoriteCount" :option="favoriteOption"></WidgetFav>
<WidgetLike :num="data.praiseCount" :option="praiseOption"></WidgetLike>
<WidgetFav :num="data.commentCount"></WidgetFav>
</div>
</div>
</div>
... ... @@ -45,12 +48,16 @@ export default {
introHeight: 0
};
},
created() {
if (this.data.intro.length < 66) {
this.isEllipsis = false;
}
},
computed: {
intro() {
if (!this.isEllipsis) {
if (this.isExpand || this.data.intro.length < 66) {
return this.data.intro;
}
if (this.data.intro.length > 66) {
} else {
return this.data.intro.substring(0, 66);
}
},
... ... @@ -63,7 +70,17 @@ export default {
return {
height: this.introHeight ? `${this.introHeight}px` : void 0
};
}
},
favoriteOption() {
return {
selected: this.data.hasFavor === 'Y'
};
},
praiseOption() {
return {
selected: this.data.hasPraise === 'Y'
};
},
},
mounted() {
this.introCollapseHeight = this.$refs.intro.scrollHeight;
... ... @@ -79,8 +96,10 @@ export default {
});
},
onExpand() {
if (!this.isEllipsis) {
return;
}
this.isExpand = !this.isExpand;
this.isEllipsis = false;
this.$nextTick(() => {
if (this.isExpand) {
this.introHeight = this.$refs.intro.scrollHeight;
... ... @@ -89,9 +108,6 @@ export default {
}
this.isExpanding = true;
this.$emit('on-resizeing');
if (!this.isExpand) {
this.isEllipsis = true;
}
this.resizeDebounce();
});
},
... ...
... ... @@ -2,7 +2,7 @@
<div class="article-item-slide">
<Slide :data="data.blockList" :threshold="0.2" :auto-play="false" :loop="false" :options="scrollOption" @change="onChange">
<SlideItem v-for="(item, inx) in data.blockList" :key="inx">
<ImageFormat :lazy="data.index > 0" class="image-slide-item" :src="item.contentData" :width="item.width" :height="item.height"></ImageFormat>
<ImageFormat :lazy="lazy" class="image-slide-item" :src="item.contentData" :width="item.width" :height="item.height"></ImageFormat>
</SlideItem>
<template slot="dots" slot-scope="props">
<span class="slide-dot"
... ... @@ -29,6 +29,10 @@ export default {
default() {
return {};
}
},
lazy: {
type: Boolean,
default: true
}
},
data() {
... ...
<template>
<div class="article-item">
<ArticleItemHeader :data="headerData"></ArticleItemHeader>
<ArticleItemSlide :data="slideData"></ArticleItemSlide>
<ProductGroup :data="productListData"></ProductGroup>
<ArticleItemHeader :data="headerData" :lazy="lazy" @on-follow="onFollow"></ArticleItemHeader>
<ArticleItemSlide :data="slideData" :lazy="lazy"></ArticleItemSlide>
<ProductGroup :data="productListData" :lazy="lazy"></ProductGroup>
<ArticleItemIntro :data="introData" @on-resize="onResize" @on-resizeing="onResizeing"></ArticleItemIntro>
<ArticleItemComment :comments="commentData" :count="12" :date="'1天前'"></ArticleItemComment>
<ArticleItemComment :data="commentData"></ArticleItemComment>
<div class="line"></div>
</div>
</template>
<script>
import {get} from 'lodash';
import ArticleItemHeader from './article-item-header';
import ArticleItemSlide from './article-item-slide';
import ArticleItemIntro from './article-item-intro';
import ArticleItemComment from './article-item-comment';
import dayjs from 'dayjs';
export default {
name: 'ArticleItem',
... ... @@ -29,47 +31,40 @@ export default {
headerData() {
return {
authorName: this.data.authorName,
articleId: this.data.articleId,
authorHeadIco: this.data.authorHeadIco
authorUid: this.data.authorUid,
authorHeadIco: this.data.authorHeadIco,
hasAttention: this.data.hasAttention,
};
},
slideData() {
return {
blockList: this.data.blockList,
index: this.data.index
blockList: get(this.data, 'blockList', []).filter(block => block.templateKey === 'image'),
};
},
introData() {
return {
intro: '旗下大热鞋款旗下大热鞋款旗下大热鞋款一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型旗下大热鞋款也轻松成为许多鞋迷的心头好。近日,旗下旗下大热鞋款旗下大热鞋款旗下大热鞋款一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型旗下大热鞋款也轻松成为许多鞋迷的心头好。近日,旗下大热鞋款旗下大热鞋款旗下大热鞋款旗下大热鞋款旗下大热鞋款一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型旗下大热鞋款也轻松成为许多鞋迷的心头好。近日,旗下大热鞋款旗下大热鞋款大热鞋款旗下大热鞋款 Air Max 95 一直以来在街头造型当中的能见度都算高,凭藉其舒适脚感与百搭外型 Air Max 95 也轻松成为许多鞋迷的心头好。'
intro: get(get(this.data, 'blockList', []).filter(block => block.templateKey === 'text'), '[0].contentData'),
labelList: this.data.labelList,
hasFavor: this.data.hasFavor,
hasPraise: this.data.hasPraise,
commentCount: this.data.commentCount,
praiseCount: this.data.praiseCount,
favoriteCount: this.data.favoriteCount
};
},
commentData() {
return [{
name: 'NIKE后援团',
content: '好期待,一定要抢一双👟!',
}, {
name: 'NIKE后援团',
content: '表情表情!!!这双仔细看好好看!!哈哈哈哈哈哈😄✌️'
}];
return {
comments: this.data.comments,
commentCount: this.data.commentCount,
publishTime: this.data.publishTime,
date: dayjs(this.data.publishTime).fromNow()
};
},
productListData() {
return [{
src: '//img12.static.yhbimg.com/goodsimg/2019/01/29/15/022a23864f68c66a6e1ef398ce7bd82efc.jpg?imageView2/2/w/640/h/640/q/60',
name: 'Off-White™ x Nike Air Max 90 全新「Desert Ore」配色曝光',
price: 3299,
isFav: false
}, {
src: '//img12.static.yhbimg.com/goodsimg/2019/01/29/15/022a23864f68c66a6e1ef398ce7bd82efc.jpg?imageView2/2/w/640/h/640/q/60',
name: 'Off-White™ x Nike Air Max 90 全新「Desert Ore」配色曝光',
price: 2299,
isFav: true
}, {
src: '//img12.static.yhbimg.com/goodsimg/2019/01/29/15/022a23864f68c66a6e1ef398ce7bd82efc.jpg?imageView2/2/w/640/h/640/q/60',
name: 'Off-White™ x Nike Air Max 90 全新「Desert Ore」配色曝光',
price: 1299,
isFav: false
}];
return this.data.productList || [];
},
lazy() {
return this.data.index > 1;
}
},
methods: {
... ... @@ -78,6 +73,9 @@ export default {
},
onResizeing() {
this.$emit('on-resizeing');
},
onFollow(follow) {
this.$emit('on-follow', follow);
}
},
components: {ArticleItemHeader, ArticleItemSlide, ArticleItemIntro, ArticleItemComment}
... ...
... ... @@ -6,14 +6,18 @@
<span class="user-name">{{currentAuthor.authorName}}</span>
</template>
<template v-if="showHeader" v-slot:opts>
<button class="btn-follow hover-opacity" v-if="true">关注</button>
<button class="btn-follow followed hover-opacity" v-else>已关注</button>
<WidgetFollow class="widget-follow" :author-uid="currentAuthor.authorUid" :follow="currentAuthor.hasAttention === 'Y'" @on-follow="follow => onFollow(currentAuthor, follow)"></WidgetFollow>
</template>
</LayoutHeader>
<LayoutScroll v-if="isMounted" ref="scroll" @scroll="onScroll" :offset="1000" :on-fetch="onFetch">
<template class="article-item" v-slot:item="{ data }">
<ArticleItem :id="`item${data.id}`" :data="data" :data-id="data.id" @on-resize="onResize(data)" @on-resizeing="onResizeing(data)"></ArticleItem>
<div :id="`ph${data.id}`"></div>
<ArticleItem
:id="`item${data.index}`"
:data="data" :data-id="data.index"
@on-resize="onResize(data)"
@on-resizeing="onResizeing(data)"
@on-follow="follow => onFollow(data, follow)"></ArticleItem>
<div :id="`ph${data.index}`"></div>
</template>
</LayoutScroll>
<slot name="thumb" v-else></slot>
... ... @@ -22,6 +26,8 @@
<script>
import ArticleItem from './article-item';
import {createNamespacedHelpers} from 'vuex';
const {mapMutations} = createNamespacedHelpers('article');
export default {
name: 'Article',
... ... @@ -33,7 +39,9 @@ export default {
onFetch: Function
},
mounted() {
this.isMounted = true;
this.$nextTick(() => {
this.isMounted = true;
})
},
data() {
return {
... ... @@ -43,8 +51,10 @@ export default {
showHeader: false,
isMounted: false,
currentAuthor: {
authorUid: 0,
authorName: '',
authorHeadIco: '',
hasAttention: 'N',
opacity: 1,
isShare: false
}
... ... @@ -56,14 +66,17 @@ export default {
}
},
methods: {
...mapMutations(['CHANGE_AUTHOR_FOLLOW']),
onScroll({item, scrollTop}) {
this.scrollTop = scrollTop;
if (scrollTop === 0) {
this.currentAuthor.opacity = 1;
this.showHeader = false;
} else {
this.currentAuthor.authorUid = item.data.authorUid;
this.currentAuthor.authorName = item.data.authorName;
this.currentAuthor.authorHeadIco = item.data.authorHeadIco;
this.currentAuthor.hasAttention = item.data.hasAttention;
this.showHeader = true;
const offsetTop = scrollTop - item.top;
... ... @@ -83,26 +96,32 @@ export default {
this.$refs.scroll.init();
},
onResize(data) {
const $phItem = document.getElementById(`ph${data.id}`);
const $phItem = document.getElementById(`ph${data.index}`);
$phItem.innerHTML = '';
$phItem.status = 0;
this.$refs.scroll.resize();
},
onResizeing(data) {
const $phItem = document.getElementById(`ph${data.id}`);
const $phItem = document.getElementById(`ph${data.index}`);
if ($phItem.status === 1) {
return;
}
const $nextItem = document.getElementById(`item${data.id + 1}`);
const $nextItem = document.getElementById(`item${data.index + 1}`);
const html = $nextItem.outerHTML;
$phItem.innerHTML = html;
$phItem.style.zIndex = 999;
$phItem.status = 1;
},
onFollow(data, follow) {
this.CHANGE_AUTHOR_FOLLOW({authorUid: data.authorUid, follow});
if (data.authorUid === this.currentAuthor.authorUid) {
this.currentAuthor.hasAttention = follow ? 'Y' : 'N';
}
}
},
components: {
ArticleItem
... ... @@ -120,24 +139,8 @@ export default {
height: 52px;
}
.btn-follow {
width: 120px;
height: 50px;
padding: 0;
font-size: 26px;
border-radius: 3PX;
background-color: #222;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
.widget-follow {
margin-right: 30px;
&.followed {
border: solid 1px #4a4a4a;
background-color: #fff;
color: #222;
}
}
.user-name {
... ...
... ... @@ -684,6 +684,10 @@ img[lazy=loaded] {
&:active {
opacity: 0.8;
}
&.loading {
opacity: 0.8;
}
}
button {
... ...
... ... @@ -14,12 +14,12 @@ export default {
});
if (result && result.code === 200) {
result.data.detailList = result.data.detailList.map((item, inx) => {
item.index = (page - 1) * limit + inx;
return item;
});
if (!result.data.detailList) {
result.data.detailList = [];
}
commit(Types.FETCH_ARTICLE_DETAIL_SUCCESS, {
data: result.data.detailList,
page
});
} else {
commit(Types.FETCH_ARTICLE_DETAIL_FAILD);
... ...
... ... @@ -6,7 +6,7 @@ export default function() {
namespaced: true,
state: {
fetchArticleList: false,
articleCurrentList: [],
articleList: [],
articleDetail: null,
},
actions,
... ...
... ... @@ -4,14 +4,28 @@ export default {
[Types.FETCH_ARTICLE_DETAIL_REQUEST](state) {
state.fetchArticleList = true;
},
[Types.FETCH_ARTICLE_DETAIL_SUCCESS](state, {data}) {
[Types.FETCH_ARTICLE_DETAIL_SUCCESS](state, {data, page}) {
state.fetchArticleList = false;
state.articleCurrentList = data;
if (page === 1) {
state.articleList = [];
}
state.articleList = state.articleList.concat(data);
state.articleList.forEach((item, index) => {
item.index = index;
});
},
[Types.FETCH_ARTICLE_DETAIL_FAILD](state) {
state.fetchArticleList = false;
},
[Types.FETCH_GUANG_SUCCESS](state, data) {
state.articleDetail = data;
},
[Types.CHANGE_AUTHOR_FOLLOW](state, {authorUid, follow}) {
state.articleList.forEach(article => {
if (article.authorUid === authorUid) {
article.hasAttention = follow ? 'Y' : 'N';
}
});
}
};
... ...
export const FETCH_ARTICLE_DETAIL_REQUEST = 'FETCH_ARTICLE_DETAIL_REQUEST';
export const FETCH_ARTICLE_DETAIL_FAILD = 'FETCH_ARTICLE_DETAIL_FAILD';
export const FETCH_ARTICLE_DETAIL_SUCCESS = 'FETCH_ARTICLE_DETAIL_SUCCESS';
export const FETCH_GUANG_REQUEST = 'FETCH_GUANG_REQUEST';
export const FETCH_GUANG_FAILED = 'FETCH_GUANG_FAILED';
export const FETCH_GUANG_SUCCESS = 'FETCH_GUANG_SUCCESS';
export const FETCH_FAV_SUCCESS = 'FETCH_FAV_SUCCESS';
export const FETCH_ZAN_SUCCESS = 'FETCH_ZAN_SUCCESS';
export const CHANGE_AUTHOR_FOLLOW = 'CHANGE_AUTHOR_FOLLOW';
... ...
... ... @@ -3,6 +3,7 @@ import Vuex from 'vuex';
import {createApi} from 'create-api';
import storeYoho from './yoho';
import storeArticle from './article';
import storeUser from './user';
import plugin from './plugin';
Vue.use(Vuex);
... ... @@ -12,7 +13,8 @@ export function createStore(context) {
namespaced: true,
modules: {
yoho: storeYoho(),
article: storeArticle()
article: storeArticle(),
user: storeUser()
},
strict: process.env.NODE_ENV !== 'production',
plugins: [plugin]
... ...
export default {
async followUser(actions, {followUid, status}) {
const result = await this.$api.get('/api/grass/updateAttention', {
followUid,
status,
attentionType: 1
});
return result;
},
async followTopic(actions, {topicId, status}) {
const result = await this.$api.get('/api/grass/updateAttention', {
topicId,
status,
attentionType: 0
});
return result;
},
};
... ...
import actions from './actions';
import mutations from './mutations';
export default function() {
return {
namespaced: true,
state: {
},
actions,
mutations
};
}
... ...
import * as Types from './types';
export default {
};
... ...
export const FETCH_ARTICLE_DETAIL_REQUEST = 'FETCH_ARTICLE_DETAIL_REQUEST';
export const FETCH_ARTICLE_DETAIL_FAILD = 'FETCH_ARTICLE_DETAIL_FAILD';
export const FETCH_ARTICLE_DETAIL_SUCCESS = 'FETCH_ARTICLE_DETAIL_SUCCESS';
\ No newline at end of file
... ...
... ... @@ -9,11 +9,19 @@ module.exports = {
params: {
page: {type: Number, require: false},
limit: {type: Number, require: false},
uid: {type: Number, require: false},
articleId: {type: Number},
columnType: {type: Number}
}
},
'/api/grass/updateAttention': {
api: 'app.grass.updateAttention',
params: {
topicId: {type: Number, require: false},
followUid: {type: Number, require: false},
status: {type: Number},
attentionType: {type: Number}
}
},
'/api/guang/article/detail': {
service: true,
api: URI_PACKAGE_ARTICLE,
... ...
... ... @@ -39,6 +39,7 @@
"connect-redis": "^3.4.0",
"cookie-parser": "^1.4.3",
"cube-ui": "^1.12.6",
"dayjs": "^1.8.5",
"express": "^4.16.4",
"express-session": "^1.15.6",
"fastclick": "^1.0.6",
... ...
... ... @@ -2057,6 +2057,10 @@ date-now@^0.1.4:
version "0.1.4"
resolved "http://npm.yohops.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
dayjs@^1.8.5:
version "1.8.5"
resolved "http://npm.yohops.com/dayjs/-/dayjs-1.8.5.tgz#0b066770f89a20022218544989f3d23e5e8db29a"
de-indent@^1.0.2:
version "1.0.2"
resolved "http://npm.yohops.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
... ...