需求:登录的用户,可以选择喜欢的频道
目标:实现频道组件基本布局
组件路径:
src/components/ChannelEdit.vue
我的频道:
点击可进入频道
编辑
完成
频道{{index}}
可选频道:
频道{{index}}
props: {
value: {
type: Boolean,
default: false
}
}
data () {
return {
editing: false
}
}
<van-icon @click='handleEdit' name="wap-nav">van-icon>
span>
// data中添加数据控制组件弹窗
isEdit: false
.van-popup--bottom{
&.van-popup--round{
border-radius: 0;
}
}
.van-action-sheet {
max-height: 100%;
height: 100%;
.van-action-sheet__header {
background: #3296fa;
color: #fff;
.van-icon-close {
color: #fff;
}
}
}
.channel {
padding: 10px;
.tit{
line-height: 3;
.tip {
font-size: 10px;
color: #999;
}
}
.van-button {
float: right;
margin-top: 7px;
}
.btn{
position: absolute;
bottom: 0;
right: 0;
background: #ddd;
font-size: 12px;
color: #fff;
}
.f12{
font-size:12px;
color: #555;
}
.red{
color: red;
}
}
总结:
- 控制弹窗的显示和隐藏
- 父子组件的数据传递
- v-model在组件上的用法
目标:渲染频道列表数据
props: {
value: {
type: Boolean,
default: false
},
channels: {
type: Array,
default: () => []
},
activeIndex: {
type: Number,
default: 0
}
},
data () {
return {
// 默认是未编辑状态
editing: false,
// 全部频道
channels: []
}
},
我的频道:
点击可进入频道
编辑
完成
{{item.name}}
总结:
- 父组件向子组件传值
- 属性的类型检测
- 类的绑定用法
原生js能处理的数值最大值是有限制的,如果一个超出了最大值,那么js就无法准确存储。
可以基于第三方包json-bigint处理大数(本质上就是把大数作为字符串拆分成多端进行存储)
从使用的角度:JSONBig.parse() 类似于JSON.parse() 方法的功能,但是要多出一个功能:对json中的超大数值进行自动处理,转换为一个BigNumber的实例对象(其中包含了一个数组)。该对象在使用的时候需要调用toString()方法转换数值为字符串。
目标:渲染可选频道
import request from '@/utils/request.js'
// 获取所有频道数据
export const getChannels = () => {
return request({
method: 'get',
url: 'v1_0/channels'
})
}
// 弹窗打开时触发
async handleOpen () {
// 调用接口获取所有的频道数据
try {
const ret = await getChannels()
this.allChannels = ret.data.channels
} catch {
this.$toast('获取所有频道失败')
}
},
总结:
- 已经拥有【我的频道】和【全部频道】数据
- 基于上述两种频道计算【可选频道】数据
- 弹窗打开时触发加载全部频道的动作(基于Vant组件的事件open)
computed: {
// 计算可选频道的数据
optionChannels () {
// 已知所有频道数据
// 当前我的频道数据
return this.allChannels.filter((item) => {
// 过滤的条件:item不在我的频道数据中
// arr.some方法作用:判断数组中是否包含符合条件的数据,只要有一项符合,就返回true
return !this.channels.some((channel) => {
// 让item和我的频道中每一项数据相比
return channel.id === item.id
})
})
}
// optionChannels () {
// // 已知所有频道数据
// // 当前我的频道数据
// const arr = this.allChannels.filter((item) => {
// // 过滤的条件:item不在我的频道数据中
// // arr.some方法作用:判断数组中是否包含符合条件的数据,只要有一项符合,就返回true
// const ret = this.channels.some((channel) => {
// // 让item和我的频道中每一项数据相比
// return channel.id === item.id
// })
// return !ret
// })
// return arr
// }
},
可选频道:
{{channel.name}}
总结:
- 计算可选频道(算法)
- 动态渲染可选频道数据
目标:控制点击进入频道
enterChannel (index) {
// 进入频道列表
// 1、关闭窗口
this.$emit('input', false)
// 2、修改父组件中频道的索引activeIndex
this.$emit('update-index', index)
}
:activeIndex.sync="active"
enterChannel (index) {
this.$emit('input', false)
- this.$emit('update-index', index)
+ this.$emit('update:activeIndex', index)
},
+ :activeIndex.sync="active">
总结
- 如果希望传递给子组件的属性是双向绑定的,可以再属性的后面添加.sync即可,但是有一个要求:子组件触发的事件必须是如下的格式 update:属性名称
this.$emit('update:activeIndex', index)
目标:实现删除我的频道功能
// 调用接口删除频道
export const delChannel = async (channelId) => {
return request({
method: 'delete',
url: 'v1_0/user/channels/' + channelId
})
}
// 删除频道
async handleDelete (id) {
try {
await delChannel(id)
// 通知父组件删除该频道
this.$emit('del-channel', id)
} catch {
this.$toast('删除频道失败')
}
},
// 删除频道
delChannel (id) {
// 根据id删除对应的频道
const index = this.channels.findIndex(item => {
return item.id === id
})
this.channels.splice(index, 1)
},
总结:
- 封装接口
- 绑定事件调用接口
- 删除频道(子向父组件传值,由父组件根据id删除频道)
目标:添加我的频道参数处理
添加频道时,需要告诉后端,当前我的频道的顺序,后端不需要【推荐】频道
// 因为无法获取后台seq顺号 只能前端排序让后端统一即可。
// 后端需要 完整的排好序的 数组 [{id,seq},...] 注意:不需要推荐
// 本地需要 {id, name} 综合一下:格式如下
// 添加频道
addChannel (channel) {
// 准备参数:对每一个频道进行排序(添加一个seq属性进行编号);去掉【推荐】频道
// 先对之前的频道排序
const orderChannels = this.channels.map((item, index) => {
return {
id: item.id,
name: item.name,
seq: index
}
})
// orderChannels = [{id: '',name:'', seq: 0}, {}, {}]
// 添加新的频道
orderChannels.push({ id: channel.id, name: channel.name, seq: orderChannels.length })
// 删除最开始的【推荐】频道
orderChannels.splice(0, 1)
console.log(orderChannels)
},
orderChannels 提供给API使用
总结:
- 频道需要排序(每个频道添加seq属性)
- 提交的数据不可以包含【推荐】频道
目标:封装添加频道API
// 添加频道
// 添加频道的接口
export const addChannel = (orderChannels) => {
return request({
method: 'put',
url: 'v1_0/user/channels',
data: {
channels: orderChannels
}
})
}
import { addChannel } from '@/api/channel.js'
// 添加频道
addChannel (channel) {
// 准备参数:对每一个频道进行排序(添加一个seq属性进行编号);去掉【推荐】频道
// 先对之前的频道排序
const orderChannels = this.channels.map((item, index) => {
return {
id: item.id,
name: item.name,
seq: index
}
})
// orderChannels = [{id: '',name:'', seq: 0}, {}, {}]
// 添加新的频道
orderChannels.push({ id: channel.id, name: channel.name, seq: orderChannels.length })
// 删除最开始的【推荐】频道
orderChannels.splice(0, 1)
// 调用接口实现添加频道
try {
// 调用接口成功
addChannel(orderChannels)
// 添加频道成功后,添加页码的频道
const newChannel = {
// 频道的id
id: channel.id,
// 频道的标签名称
name: channel.name,
// 文章列表加载状态
loading: false,
// 下拉刷新的完成状态
isLoading: false,
// 上拉列表加载完成的标志
finished: false,
// 下拉刷新完成的提示信息
pullText: '加载成功',
// 时间戳,用于实现列表的分页查询
timestamp: +new Date(),
// 文章列表
articles: []
}
// 把新的频道数据传递给父组件,让父组件去添加
this.$emit('add-channel', newChannel)
} catch {
this.$toast('添加我的频道失败')
}
},