使用Vue开发项目(黑马头条项目)–第五天

需要实现的主要功能如下:

资讯列表、标签页切换,文章举报,频道管理、文章详情、阅读记忆,关注功能、点赞功能、评论功能、回复评论、搜索功能、登录功能、个人中心、编辑资料、小智同学 …

今天要实现的功能主要是:频道的添加删除功能

1 频道管理

1.1 了解需求

在频道列表的右侧,放一个按钮,点击这个按钮就可以弹层,以弹层组件来展示频道管理页

image.png

image.png

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 点击按钮测试效果

image.png

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 再次测试弹层功能显示内容是否为:‘这里将会放置频道管理的内容’

image.png

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 再次查看布局结构

image.png

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 查看我的频道渲染效果

image.png

3.4 可选频道的渲染

3.4.1 分析

  1. 可选频道的内容: 获取的所有频道 – 用户所选的频道

  2. 需要动态检测内容的变化 => 用户增加或删除都需要重新计算

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 检查功能是否实现

image.png

我们发现依旧有重复内容,打开vue调试工具发现我的频道中的id值是字符串格式的而我们判断时用的是全等,这就是导致问题的关键所在,解决方法有两个

  1. ===改为==(这里因为我使用的Eslint不允许我们使用==

  2. 将途中位置更改为Number(item.id) ,转换一下即可

FBWWPD2TSGOZSJBGLYQAX.png

再次检查(已经可以了)

image.png

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;
}
复制代码

查看效果:

image.png

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

image.png
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('删除成功')
      }
    },
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享