黑马项目-头条-Mobile-频道编辑操作

频道编辑操作

需求:登录的用户,可以选择喜欢的频道

准备组件布局

目标:实现频道组件基本布局

  • 封装独立的组件

组件路径: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;
  }
}

总结:

  1. 控制弹窗的显示和隐藏
  2. 父子组件的数据传递
  3. v-model在组件上的用法

渲染我的频道

目标:渲染频道列表数据

  • 父组件传递数据给子组件


  • 子组件接收父组件数据
  props: {
    value: {
      type: Boolean,
      default: false
    },
    channels: {
      type: Array,
      default: () => []
    },
    activeIndex: {
      type: Number,
      default: 0
    }
  },
  data () {
    return {
      // 默认是未编辑状态
      editing: false,
      // 全部频道
      channels: []
    }
  },
  • 数据动态渲染
我的频道: 点击可进入频道 编辑 完成
{{item.name}}

总结:

  1. 父组件向子组件传值
  2. 属性的类型检测
  3. 类的绑定用法

总结

  • 更多操作
    • 不刚兴趣
    • 举报文章
    • 父组件将文章的id传递到子组件
    • 文章id值如果太大会有问题(js客户处理的数据大小有限)
    • 基于json-bigint包实现大值数据的处理
    • axios中统一处理后端返回的数据 transformResponse
    • 常量数据需要统一维护
  • 频道编辑
    • 组件的基本布局
    • 我的频道数据的动态渲染
    • 父子组件之间的数据交互
    • v-model在组件标签上的用法
    • Vant弹窗组件的用法

反馈

  • axios的transformResponse类似于响应拦截器
    • 响应拦截器中response参数不仅包含数据,还包括响应头,配置选项等相关信息。
    • transformResponse回调参数data表示的特指服务器返回的原始数据,专门用于处理数据。
  • axios处理大数

原生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('获取所有频道失败')
    }
},

总结:

  1. 已经拥有【我的频道】和【全部频道】数据
  2. 基于上述两种频道计算【可选频道】数据
  3. 弹窗打开时触发加载全部频道的动作(基于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}}

总结:

  1. 计算可选频道(算法)
  2. 动态渲染可选频道数据

点击进入频道

目标:控制点击进入频道

  • 事件绑定
  • 事件函数定义
enterChannel (index) {
  // 进入频道列表
  // 1、关闭窗口
  this.$emit('input', false)
  // 2、修改父组件中频道的索引activeIndex
  this.$emit('update-index', index)
}
  • 父组件监听事件


  • 简写方法
    • 父子组件之间传值的简化写法
      • 父向子传值 :属性名称 (:activeIndex=“active”)
      • 子向父传值 @自定义事件名称 (@update-index=“active=$event”)
      • 合并的写法 :activeIndex.sync="active"
      • 要求:子组件触发事件
    enterChannel (index) {
      this.$emit('input', false)
-     this.$emit('update-index', index)
+     this.$emit('update:activeIndex', index)
    },
    
+      :activeIndex.sync="active">
    

总结

  1. 如果希望传递给子组件的属性是双向绑定的,可以再属性的后面添加.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)
},

总结:

  1. 封装接口
  2. 绑定事件调用接口
  3. 删除频道(子向父组件传值,由父组件根据id删除频道)

添加我的频道接口参数设置

目标:添加我的频道参数处理

添加频道时,需要告诉后端,当前我的频道的顺序,后端不需要【推荐】频道

  • 绑定事件,提供(添加频道)数据。

  • 接口需要实现 调用后台接口与本地存储功能
    • 后台需要排序 [{id:‘频道ID’,seq,‘排序’}]
    • 本地需要 {id:‘频道ID’,name:‘频道名称’}
// 因为无法获取后台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使用

总结:

  1. 频道需要排序(每个频道添加seq属性)
  2. 提交的数据不可以包含【推荐】频道

添加我的频道

目标:封装添加频道API

  • 封装添加频道接口方法
// 添加频道
// 添加频道的接口
export const addChannel = (orderChannels) => {
  return request({
    method: 'put',
    url: 'v1_0/user/channels',
    data: {
      channels: orderChannels
    }
  })
}
  • 组件实现添加频道功能
    • 绑定添加频道的按钮的点击事件
    • 封装了添加频道的接口方法
    • 准备添加频道调用接口的相关参数
      • 要求是数组,数组中放对象,对象中包含频道id和排序序号seq
      • seq作用:告诉后端,页面中频道的顺序
      • 数组中要去掉【推荐】频道
    • 调用接口发送请求
    • 如果调用接口成功,在我的频道中添加一个新的频道(点击的添加的频道)
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('添加我的频道失败')
    }
},

你可能感兴趣的:(Vue,黑马头条项目)