需要实现的主要功能如下:
资讯列表、标签页切换,文章举报,频道管理、文章详情、阅读记忆,关注功能、点赞功能、评论功能、回复评论、搜索功能、登录功能、个人中心、编辑资料、小智同学 ...
首先回顾一下:昨天我们实现了二级路由,封装了时间过滤器,图片懒加载等功能
今天我们要实现的功能主要是:文章的不感兴趣、举报功能
1 token功能的优化
1.1 封装token(由于之前token存储在vuex中,导致每次刷新页面都会清空token,还需要重新登录)
在utils中新建页面storage.js
// 封装对token的三个操作(存储,删除,获取)
// 消除魔法字符串
const TOKEN = 'token'
// 存储
export const saveToken = (tokenObj) => {
localStorage.setItem(TOKEN, JSON.stringify(tokenObj))
}
// 获取
export const getToken = () => {
return JSON.parse(localStorage.getItem(TOKEN))
}
// 删除
export const delToken = () => {
localStorage.removeItem(TOKEN)
}
1.2 在src/store/index.js中加入以下代码:
+ import { saveToken, getToken } from '@/utils/storage'
state: {
// 优先从localstroage中取出
+ tokenInfo: getToken() || {}
},
actions: {
// context对象会自动传入,它与store示例具有相同的方法和context对象action的名字: function(context, 载荷) {// 1. 发异步请求, 请求数据// 2. commit调用mutation来修改/保存数据// context.commit('mutation名', 载荷)}
async setToken (context, userInfo) {
try {
// console.log(2)
const { data: { data } } = await login(userInfo)
// console.log(data)
// console.log(3)
context.commit('getToken', data)
// 将token存储到浏览器本地
+ saveToken(data)
// this.$toast.success('登录成功')
} catch (err) {
// 将错误抛给调用这个actions的函数
throw new Error(err)
}
}
},
便可解决每次刷新页面后token被清空的问题
2.开始今天功能
2.1 目标功能展示
2.2 在articleList.vue中加入一个×图标
<!-- 文字区域 -->
<div class="meta">
<span>{{ item.aut_name }}</span>
<span>{{ item.comm_count }}评论</span>
<span>{{ item.pubdate | relativeTime }}</span>
<span class="close">
<van-icon name="cross" />
</span>
</div>
给其加入样式,让它显示在右侧
.meta {
span {
margin-right: 10px;
}
display: flex;
// 让它在最右边
.close {
// 它的父级容器是flex的,给当前元素设置左外边距为auto,会让这个元素在父级容器
// 的最右边
margin-left: auto;
}
}
2.2 这个图标只有在用户登录时才可以看到,所以要加入判定
判定在vuex的state的变量中token是否存在,若存在则显示,反之不显示
<span class="close">
<van-icon name="cross" v-if="$store.state.tokenInfo.token"/>
</span>
2.3 给关闭按钮创建点击事件
<span class="close">
<van-icon name="cross"
v-if="$store.state.tokenInfo.token"
@click="hActionMore(item.art_id)"
/>
</span>
methods:{
data(){
return{
isShowMoreAction:false, //默认不显示弹出层
articleId:null,
}
}
MoreAction (id) {
// 点击显示弹出层
this.isShowMoreAction = true
// 保存当前id
this.articleId = id
},
}
3 使用弹层组件(popup组件)
因为弹出层之中的内容较为复杂,我们将其拆出为一个新的组件
3.1在src/views/home/新建moreAction.vue文件
<template>
<div class="more-action">
<!-- 状态一 -->
<van-cell-group>
<van-cell>不感兴趣</van-cell>
<van-cell is-link>反馈垃圾内容</van-cell>
<van-cell>拉黑作者</van-cell>
</van-cell-group>
<!-- 状态二 反馈-->
<van-cell-group>
<van-cell icon="arrow-left">返回</van-cell>
<van-cell>侵权</van-cell>
<van-cell>色情</van-cell>
<van-cell>暴力</van-cell>
<van-cell>低俗</van-cell>
<van-cell>不适</van-cell>
<van-cell>错误</van-cell>
<van-cell>其他</van-cell>
</van-cell-group>
</div>
</template>
<script>
export default {
name: 'MoreAction',
}
</script>
3.2 在articleList.vue中和其建立父子组件关系
+ import MoreAction from './moreAction'
+ components: {
MoreAction
},
放在van-pull-refresh标签的外面(放在循环的外面)
<!-- 弹出层 -->
<van-popup v-model="isShowMoreAction" :style="{ width: '80%' }">
<MoreAction></MoreAction>
</van-popup>
3.3 查看调试工具
两个弹出层一起显示了,我们让其分开显示两个只能显示一个
<template>
<div class="more-action">
<!-- 状态一 -->
+ <van-cell-group v-if="!isReport">
<van-cell>不感兴趣</van-cell>
+ <van-cell is-link @click="isReport=true">举报垃圾内容</van-cell>
<van-cell>拉黑作者</van-cell>
</van-cell-group>
<!-- 状态二 举报-->
<van-cell-group v-else>
+ <van-cell icon="arrow-left" @click="isReport=false">返回</van-cell>
// --- 省略其他
</van-cell-group>
</div>
</template>
<script>
export default {
name: 'MoreAction',
data () {
return {
+ isReport: false // 是否显示 举报
}
}
}
</script>
4 实现不感兴趣功能
4.1思路
去调用接口。让服务器之后不要再推类似的文章给我了。
关闭弹层
把我点击的文章删除掉。
在当前articleList组件中删除文章
4.2 利用子传父原理(自定义事件)实现
在父组件articleList.vue中监听事件,及回调
<van-popup v-model="isShowMoreAction" :style="{ width: '80%' }">
<moreAction
+ @un-like="hUnlike"
></moreAction>
</van-popup>
回调函数
async hUnlike () {
try {
// 1. 调用接口,传文章编号
// 2. 关闭弹层
this.isShowMoreAction = false
// 3. 删除文章
this.$toast.success('操作成功')
} catch (err) {
this.$toast.fail('操作失败')
}
}
在子组件moreAction.vue中抛出事件
<!-- 直接抛出事件,具体功能由父组件index.vue去做 -->
<van-cell @click="hUnlike">不感兴趣</van-cell>
methods: {
// 用户点击不感兴趣
// 要做三件事,但是,这三件事都在父组件articleList中去做
hUnlike () {
this.$emit('un-like')
}
}
4.3 封装请求函数在上步回调函数中调用
在src/api/article.js中添加一个方法
export const unLike = (articleId) => {
return request({
url: 'v1_0/article/dislikes',
method: 'POST',
data: {
target: articleId
}
})
}
调用
import { unLike } from '@/api/article.js'
async hUnlike () {
// 1. 发请求(通知后端,以后不要推荐这类文章给我)
try {
await unLike(this.articleId)
this.$toast.success('操作成功')
// 2. 删除文章:根据文章的编号在list中删除
// 3. 关闭弹层
this.isShowMoreAction = false
} catch (err) {
console.log(err)
this.$toast.fail('操作失败')
}
}
4.4 删除文章
之后的功能可能还会使用到这个功能,所以给他封装到局部methods中,使用时调用即可
delAirticle (articleId) {
const idx = this.list.findIndex(item => { return item.art_id === articleId })
if (idx !== -1) { this.list.splice(idx, 1) }
},
调用(在上步删除文章中加入下面代码)
this.delAirticle()
5 举报文章
5.1 举报类型封装
按照应给后端传递的数据格式内容较为复杂,我们依旧是将其封装到一个新页面中,
在src/constant/新建reports.js文件
// 以模块的方式导出 举报文章 时,后端接口约定的举报类型
const reports = [
{
value: 0,
label: '其它问题'
},
{
value: 1,
label: '标题夸张'
},
{
value: 2,
label: '低俗色情'
},
{
value: 3,
label: '错别字多'
},
{
value: 4,
label: '旧闻重复'
},
{
value: 5,
label: '广告软文'
},
{
value: 6,
label: '内容不实'
},
{
value: 7,
label: '涉嫌违法犯罪'
},
{
value: 8,
label: '侵权'
}
]
export default reports
5.2 对页面进行渲染
在moreAction.vue中
// 引用提前定义好的常量数据
+ import reports from '@/constant/reports.js'
export default {
name: 'MoreAction',
data () {
return {
+ reports: reports,
isReport: false // 是否是处于状态二:反馈
}
},
<van-cell-group v-else>
<van-cell icon="arrow-left" @click="isReport=false">返回</van-cell>
<van-cell
v-for="report in reports"
:key="report.value"
>
{{report.label}}
</van-cell>
</van-cell-group>
实现效果
5.3 实现举报文章功能
依旧重复上次子传父功能(自定义事件) 在home/articleList.vue中监听事件。
<more-action
ref="moreAction"
+ @report="hReport"
@un-like="hUnlike"
></more-action>
// 用户点击了举报中的某一个
hReport (reportTypeId) {
console.log('收到的举报类型是', reportTypeId)
}
在子组件汇总抛出事件
<van-cell
v-for="report in reports"
:key="report.value"
@click="$emit('report', report.value)"
>
{{report.label}}
</van-cell>
5.4 封装接口
在src/api/article.js中补充一个方法
export const report = (articleId, reportType) => {
return request({
method: 'POST',
url: 'v1_0/article/reports',
data: {
target: articleId,
type: reportType
}
})
}
在父组件src/home/articleList.vue中,引入api(删除文章功能直接调用即可)
import { dislike, report } from '@/api/article.js'
async hReport (reportTypeId) {
try {
// 1. 发请求,举报
await report(this.articleId, reportTypeId)
// 2. 删除文章:根据文章的编号在list中删除
this.delArticle(this.articleId)
// 3. 关闭弹层
this.isShowMoreAction = false
this.$toast.success('操作成功')
} catch (err) {
console.log(err)
this.$toast.fail('操作失败')
}
}
5.5 BUG(当在举报页面关闭,再次点开还是举报页面的问题)
点开显示举报弹窗是由moreAction中的isReport控制的,我们只需要在一打开就把isReport设置为false即可
这需要用到ref属性(父组件操控子组件的数据)
将子组件中的isreport设置为false
如何在父组件中修改子组件的数据?
(1)在父组件中获取组件的引用 (ref 用来获取,this.$refs.XXXX 用来找到组件)
在组件上添加ref, 这个组件就被自动收集到this.$refs中。
(2)为所欲为 第一次点击会报错isReport is undefined, 因为第一次点击时 弹出层还未创建,所以会报错 判断当第一次它创建完毕之后再次更改即可解决
加入以下代码
<more-action
+ ref="moreAction"
@un-like="hUnlike"
></more-action>
hMoreAction (id) {
// 1. 显示 更多操作
this.isShowMoreAction = true
// 2. 记下当前被操作的文章的编号
this.articleId = id
// 用ref获取子组件的引用,然后通过引用来修改子组件中的数据
// 第一次打开弹层时,子组件并没有被创建出来,所以这里要先判断一下
+ if (this.$refs.moreAction) {
+ this.$refs.moreAction.isReport = false
+ }
}