需要实现的主要功能如下:
资讯列表、标签页切换,文章举报,频道管理、文章详情、阅读记忆,关注功能、点赞功能、评论功能、回复评论、搜索功能、登录功能、个人中心、编辑资料、小智同学 …
今天要实现的功能主要是:频道的添加删除功能
1 频道管理
1.1 了解需求
在频道列表的右侧,放一个按钮,点击这个按钮就可以弹层,以弹层组件来展示频道管理页
1.2 在 views/home/home.vue
中添加按钮和弹层
<div class='index'>
<!-- v-model 双向绑定:哪一个下标的频道处于活动状态 -->
<van-tabs v-model="activeIndex">
.......
</van-tabs>
<!--按钮 通过它打开频道管理的弹层 -->
<div class="bar-btn" @click="isShowChannelEdit=true">
<van-icon name="wap-nav"/>
</div>
<!-- 弹层 -->
<van-action-sheet v-model="isShowChannelEdit" title="标题">
<p>频道管理</p>
<p>频道管理</p>
<p>频道管理</p>
</van-action-sheet>
</div>
复制代码
在data中补充一个控制频道编辑的变量
data() {
return {
// ....
+ isShowChannelEdit: false, // 是否显示频道编辑弹层
}
}
复制代码
1.3 在style中给按钮加入样式(局部样式)
// 频道管理的开关按钮
.bar-btn {
position: fixed;
right: 5px;
top: 57px;
display: flex;
align-items: center;
background-color: #fff;
opacity: 0.8;
z-index:1;
.van-icon-wap-nav{
font-size: 20px;
}
}
复制代码
1.4 点击按钮测试效果
2 构架频道管理组件
2.1 由于功能比较复杂,我们在home同级添加一个组件channelEdit.vue
<template>
<div>
这里将会放置频道管理的内容
</div>
</template>
<script>
export default {
name: 'ChannelEdit',
}
</script>
复制代码
2.2 在home.vue中引入组件,建立父子关系
+ import ChannelEdit from './channelEdit'
components: {
ArticleList,
MoreAction,
+ ChannelEdit
}
复制代码
<!-- 频道管理 -->
<van-action-sheet v-model="isShowChannelEdit" title="频道管理">
<ChannelEdit></ChannelEdit>
</van-action-sheet>
复制代码
2.3 再次测试弹层功能显示内容是否为:‘这里将会放置频道管理的内容’
3 频道管理-组件布局
3.1 home/channelEdit.vue
的内容如下:
<template>
<div class="channel-edit">
<!-- 当前登陆用户已经订阅的频道 -->
<div class="channel">
<van-cell title="我的频道" :border="false">
<van-button size="mini" type="info">编辑</van-button>
</van-cell>
<van-grid>
<van-grid-item v-for="index in 8" :key="index">
<span>频道{{index}}</span>
<van-icon name="cross" class="btn"></van-icon>
</van-grid-item>
</van-grid>
</div>
<!-- 当前登陆用户没有订阅的频道 -->
<div class="channel">
<van-cell title="可选频道" :border="false"></van-cell>
<van-grid>
<van-grid-item v-for="index in 8" :key="index">
<span>频道{{index}}</span>
</van-grid-item>
</van-grid>
</div>
</div>
</template>
<script>
export default {
name: 'ChannelEdit',
data () {
return {
}
}
}
</script>
<style lang="less" scoped>
.channel{
padding:15px;
font-size:14px;
}
.btn {
position: absolute;
top: 0;
right: 0;
font-size: 24px;
}
</style>
复制代码
3.2 再次查看布局结构
3.3 将我的频道信息导入结构中
由于我的频道信息是之前就获取过的我们只需要父传子 导入即可
在home.vue中
<!-- https://vant-contrib.gitee.io/vant/#/zh-CN/action-sheet -->
<van-action-sheet v-model="showChannelEdit" title="频道管理">
<!-- 1. 父传子。把当前订阅频道传给 频道编辑组件 -->
<channel-edit
+ :channels="channels">
</channel-edit>
</van-action-sheet>
复制代码
在channelEdit.vue中
props: ['channels']
复制代码
<!-- 当前登陆用户已经订阅的频道 -->
<div class="channel">
<van-cell title="我的频道" :border="false">
<van-button size="mini" type="info">编辑</van-button>
</van-cell>
<van-grid>
<van-grid-item v-for="channel in channels" :key="channel.id">
<span>{{channel.name}}</span>
<van-icon name="cross" class="btn"></van-icon>
</van-grid-item>
</van-grid>
</div>
复制代码
3.3 查看我的频道渲染效果
3.4 可选频道的渲染
3.4.1 分析
可选频道的内容: 获取的所有频道 – 用户所选的频道
需要动态检测内容的变化 => 用户增加或删除都需要重新计算
3.4.2 获取全部频道内容是需要调用新的接口
继续对其进行封装, 在 api/channel.js
中新增一个接口
/**
* 获取系统中所有的频道
*/
export const getAllChannels = () => {
return request({
method: 'GET',
url: 'v1_0/channels'
})
}
复制代码
3.4.3 在子组件channelEdit.vue中加载调用
将获取到的全部频道内容存到了allChannels数组中,方便之后进行减法
<script>
import { getAllChannels } from '@/api/channel.js'
export default {
name: 'ChannelEdit',
props: ['channels'],
data () {
return {
allChannels: []
}
},
created () {
this.loadAllChannels()
},
methods: {
async loadAllChannels () {
const res = await getAllChannels()
this.allChannels = res.data.data.channels
}
}
}
</script>
复制代码
3.4.4 在计算属性computed中写入代码
// 根据已有数据生成新的数据 ---- 计算属性
computed: {
recommendChannels () {
const arr = this.allChannels.filter(channel => {
const idx = this.channels.findIndex(item => item.id === channel.id)
if (idx === -1) {
return true
}
})
return arr
// return this.allChannels - this.channels
}
}
复制代码
也可以精简写为一行代码:
return this.allChannels.filter((item) => !this.channels.some(index => index.id === item.id))
复制代码
3.4.5 将做过减法的数据渲染到页面中
<!-- 当前登陆用户没有订阅的频道 -->
<div class="channel">
<van-cell title="可选频道" :border="false"></van-cell>
<van-grid>
<van-grid-item v-for="channel in recommendChannels" :key="channel.id">
<span>{{channel.name}}</span>
</van-grid-item>
</van-grid>
</div>
复制代码
3.4.6 检查功能是否实现
我们发现依旧有重复内容,打开vue调试工具发现我的频道中的id值是字符串格式的而我们判断时用的是全等,这就是导致问题的关键所在,解决方法有两个
-
将
===
改为==
(这里因为我使用的Eslint不允许我们使用==
) -
将途中位置更改为
Number(item.id)
,转换一下即可
再次检查(已经可以了)
4 实现点击频道跳转
4.1实现点击频道跳
在父组件home.vue中添加自定义事件监听change-channel
<!-- https://vant-contrib.gitee.io/vant/#/zh-CN/action-sheet -->
<van-action-sheet v-model="showChannelEdit" title="频道管理">
<!--
1. 父传子。把当前订阅频道传给 频道编辑组件
2. 监听子组件回传的事件
-->
<channel-edit
:channels="channels"
+ @change-channel="hChangeChannel"
></channel-edit>
</van-action-sheet>
复制代码
补充回调
// 处理子组件channelEdit中用户在我的频道上点击的动作
// 父组件收到了子组件中我的频道被点击
hChangeChannel (curIdx) {
console.log('父组件收到了子组件中我的频道被点击', curIdx)
// 1. 切换频道
this.active = curIdx
// 2. 关闭弹层
this.isShowChannelEdit = false
},
复制代码
4.2 子组件channelEdit组件
<!-- 当前登陆用户已经订阅的频道 -->
<div class="channel">
<van-cell title="我的频道" :border="false">
<van-button size="mini" type="info">编辑</van-button>
</van-cell>
<van-grid>
<van-grid-item
+ v-for="(channel, idx) in channels"
:key="channel.id"
+ @click="hClickMyChannel(idx)"
>
<span>{{channel.name}}</span>
<!-- <van-icon
name="cross"
class="btn"
></van-icon> -->
</van-grid-item>
</van-grid>
</div>
复制代码
// 用户点击了我的频道,要做频道跳转,通知父组件
// 用户在我的频道区域,点击了某一个频道
hClickMyChannel (idx) {
this.$emit('change-channel', idx)
},
复制代码
4.3 进入频道后高亮显示当前频道
<channel-edit
+ :curIndex="active"
@change-channel="hChangeChannel"
:channels="channels"
></channel-edit>
</van-action-sheet>
复制代码
在子组件中:
props: [
'channels', // 当前我的频道列表
+ 'curIndex' // 接收当前选中频道的索引
],
复制代码
<van-grid-item
v-for="(channel,idx) in channels"
......
+ :class="{red:idx===curIndex}"
>
<span>{{channel.name}}</span>
<!-- <van-icon name="cross" class="btn"></van-icon> -->
</van-grid-item>
</van-grid>
复制代码
补充类名加入样式
<van-cell title="我的频道" :border="false">
+ <van-button class="bg" size="mini" type="info">编辑</van-button>
</van-cell>
// 高亮显示
.red {
color: red;
font-weight: bold;
}
.bg {
background-color: #fff;
color: red;
border-color: red;
}
复制代码
查看效果:
5 添加我的频道
5.1 点击可选频道添加进我的频道中
在src/api/channels.js
中添加方法
export const addChannels = (channels) => {
return request({
url: '/v1_0/user/channels', // 接口地址
method: 'PATCH',
data: {
channels
}
})
}
复制代码
5.2 给推荐频道中的频道注册点击事件
<!-- 当前登陆用户没有订阅的频道 -->
<div class="channel">
<van-cell
title="可选频道"
:border="false"
></van-cell>
<van-grid>
<van-grid-item
v-for="channel in recommendChannels"
:key="channel.id"
+ @click="hAddChannel(channel)"
>
<span>{{channel.name}}</span>
</van-grid-item>
</van-grid>
</div>
复制代码
5.3 完成核心功能
// 用户在可选的频道区域,点击了某一个频道,做增加
async hAddChannel (channel) {
try {
// 1. 调用接口
const res = await addChannels([channel])
// 2. 更新视图
this.$toast.success('添加频道成功')
console.log(res)
} catch (err) {
console.log(err)
this.$toast.fail('添加频道失败')
}
},
复制代码
5.4 添加我的频道-完成视图上的更新
在上步更新视图中加入代码
this.channels.push(channel)
复制代码
6 频道管理-删除我的频道
需求:用户点击”编辑”,在我的频道上,显示X,再点X,就可以删除指定的频道了。
6.1添加isEditing数据项,实现视图交互
在子组件中加入
data () {
return {
+ isEditing: false, // 是否处于编辑状态
allChannels: [] // 所有的频道
}
}
复制代码
修改视图
<div class="channel">
<van-cell
title="我的频道"
:border="false"
>
<van-button
size="mini"
class="editBtn"
+ @click="isEditing=!isEditing"
+ >{{isEditing ? "取消" : "编辑"}}</van-button>
</van-cell>
<van-grid>
<!-- 如果当前的下标与传入的curIndex一样,则补充一个类:red -->
<!-- :class="{类名: bool值}" -->
<van-grid-item
v-for="(channel, idx) in channels"
:key="channel.id"
:class="{red: idx===curIndex}"
@click="hClickMyChannel(idx)"
>
<span>{{channel.name}}</span>
+ <!-- 只有当: isEditing为true 并且 不是推荐 , 显示X -->
<van-icon
+ v-show="isEditing&&idx!==0"
name="cross"
class="btn"
></van-icon>
</van-grid-item>
</van-grid>
</div>
复制代码
6.2 实现删除我的频道
在src\api\channel.js补充一个接口:
/**
* 频道删除
* @param {*} channelId 要删除的频道id
* @returns
*/
export const delChannel = (channelId) => {
return request({
url: '/v1_0/user/channels/' + channelId,
method: 'DELETE'
})
}
复制代码
6.3 添加事件处理
import { getAllChannels, addChannel, delChannel } from '@/api/channel.js'
// 用户在我的频道区域,点击了某一个频道
// 1. 正常情况:点了之后,做频道跳转
// 2. 编辑状态: 点了之后,做频道删除
hClickMyChannel (idx) {
if (this.isEditing) {
// 做删除
this.doDeleteChannel(idx)
} else {
this.$emit('change-channel', idx)
}
},
async doDeleteChannel (idx) {
// 1. 根据下标找到id
const channelId = this.channels[idx].id
// 2. 做删除
try {
await delChannel(channelId)
// 我的频道 少1项, 删除当前项
this.channels.splice(idx, 1)
this.$toast.success('删除成功')
} catch (err) {
console.log(err)
}
},
复制代码
6.4 修复BUG
6.4.1
在channelEdit.vue
中
async doDeleteChannel (idx) {
// 1. 根据下标找到id
const id = this.channels[idx].id
console.log('你要删除的频道id是', id)
// 2. 发请求
try {
const res = await delChannel(id)
console.log(res)
this.$toast.success('删除频道成功')
// 如果你删除的频道在当前的频道之前,则要去更新一下父组件中的
// active
+ if (idx < this.curIndex) {
+ this.$emit('fixed-active-index')
+ }
// 3. 更新视图
// 从我的频道中,把当前项删除
this.channels.splice(idx, 1)
} catch (err) {
console.log(err)
this.$toast.fail('删除频道失败')
}
},
复制代码
在home.vue中
<channel-edit
:curIndex="active"
@change-channel="hChangeChannel"
+ @fixed-active-index="hFixedActiveIndex"
:channels="channels"
></channel-edit>
复制代码
补充
// 当频道管理组件中,删除的 频道 在当前频道 之前,
// 要把active-1
hFixedActiveIndex () {
this.active--
},
复制代码
6.4.2 编辑频道关闭打开还是编辑状态问题
在home.vue
中加入以下代码:
<!-- 频道列表 开关 通过定位 通过它打开频道管理的弹层 -->
+ <div class="bar-btn" @click="openChannelList">
<van-icon name="wap-nav" />
</div>
<!-- 弹出层 -->
<van-action-sheet v-model="isShowChannelEdit" title="标题">
<!-- 注意:由于组件的外层包含一个van-action-sheet,在页面刚打开或是刷新之后 这个组件是没有被创建出来的,点击一次才会被创建 -->
<channelEdit
:channels="channels"
+ ref="channelEdit"
@change-channel="hchangeChannel"
:curIdx="active"
@update-active="hUpdateActive"
></channelEdit>
</van-action-sheet>
复制代码
在methods中补充方法:
openChannelList () {
this.isShowChannelEdit = true
if (this.$refs.channelEdit) { this.$refs.channelEdit.isEditing = false }
},
复制代码
6.4.3 编辑状态可以删除推荐的问题
在channelEdit.vue
中加入以下代码
// 对删除文章的封装
async doDeleteChannel (index) {
const id = this.channels[index].id
try {
await delChannels(id)
} catch (err) {
console.log(err)
}
// 如果删除的频道下标小于当前频道的下标
// 要通知父组件修改active的值
if (index < this.curIdx) {
this.$emit('update-active')
}
+ if (index !== 0) {
+ // 在channels减去当前这项 删除当前项(判断当前id不能为0的项可以删除,推荐不允许删除)
+ this.channels.splice(index, 1)
+ this.$toast.success('删除成功')
}
},
复制代码