4.8、文章收藏
封装组件
处理视图
功能处理
思路:
- 给收藏按钮注册点击事件
- 如果已经收藏了,则取消收藏
- 如果没有收藏,则添加收藏
下面是具体实现。
1、封装组件
创建src/components/collect-article/index.vue
<template>
<van-button icon="star-o"></van-button>
</template>
<script>
export default {
name:'CollectArticle',
props:{},
data(){
return{
}
},
methods:{},
created(){}
}
</script>
<style scoped lang="less">
</style>
在src/views/article/index.vue
中导入注册,使用组件
import CollectArticle from '@/components/collect-article'
components:{
// ...其他注册
CollectArticle
}
<collect-article class="btn-item"/>
2、处理视图
在父组件article/index.vue
中通过v-model
绑定文章的收藏状态
<collect-article
class="btn-item"
v-model="article.is_collected"
/>
子组件中collect-article/index.vue
中定义props接收,且去渲染视图
props:{
// 接收文章收藏状态
value:{
type:Boolean,
required:true
}
}
<van-button
icon="value ? 'star' : 'star-o'"
:class="{ collected: value }"
></van-button>
<style scoped lang="less">
.collected {
.van-icon {
color: #ffa500;
}
}
</style>
3、功能处理
在 api/article.js
添加封装数据接口
/**
* 收藏文章
*/
export const addCollect = target => {
return request({
method: 'POST',
url: '/v1_0/article/collections',
data: {
target
}
})
}
/**
* 取消收藏文章
*/
export const deleteCollect = target => {
return request({
method: 'DELETE',
url: `/v1_0/article/collections/${target}`
})
}
<template>
<van-button
:icon="value ? 'star' : 'star-o'"
:class="{ collected: value }"
:loading="loading"
@click="onCollect"
/>
3、父组件传递文章id给子组件,书写处理函数
article/index.vue
中传递文章id
<collect-article
class="btn-item"
v-model="article.is_collected"
:article-id="article.art_id"
/>
collect-article/index.js
定义props
接收; 定义loading
变量, 导入请求方法,书写事件函数,完成功能
<script>
import { addCollect, deleteCollect } from '@/api/article'
export default {
name: 'CollectArticle',
components: {},
props: {
// 接收文章收藏状态
value: {
type: Boolean,
required: true
},
// 接收文章id
articleId: {
type: [Number, String, Object],
required: true
}
},
data () {
return {
// 加载中效果
loading: false
}
},
methods: {
async onCollect () {
this.loading = true
try {
if (this.value) {
// 已收藏,要取消收藏
await deleteCollect(this.articleId)
} else {
// 没有收藏,要添加收藏
await addCollect(this.articleId)
}
// 更新视图
// 自定义事件修改数据并不是立即的
this.$emit('input', !this.value)
this.$toast.success(!this.value ? '收藏成功' : '取消收藏')
} catch (err) {
this.$toast.fail('操作失败,请重试!')
}
this.loading = false
}
}
}
拓展: 升级讨论一下v-model
的名称问题,默认是自定义属性value
和 自定义事件input
,但是这个名称不太语义化!希望用更加语义化的名称来实现v-model
同样的效果
export default {
name: 'CollectArticle',
// 配置 v-model 对应的自定义属性名和自定义事件名称
model:{
prop:'isCollected',
event:'updateCollect'
}
}
// 接收文章收藏状态
//value: {
isCollected:{
type: Boolean,
required: true
},
<template>
<van-button
:icon="isCollected ? 'star' : 'star-o'"
:class="{ collected: isCollected }"
:loading="loading"
@click="onCollect"
/>
async onCollect () {
this.loading = true
try {
if (this.isCollected) {
// 已收藏,要取消收藏
await deleteCollect(this.articleId)
} else {
// 没有收藏,要添加收藏
await addCollect(this.articleId)
}
// 更新视图
// 自定义事件修改数据并不是立即的
this.$emit('updateCollect', !this.isCollected)
this.$toast.success(!this.isCollected ? '收藏成功' : '取消收藏')
} catch (err) {
this.$toast.fail('操作失败,请重试!')
}
this.loading = false
}