一、页面搜索栏布局
1、搜索栏布局和样式
<van-nav-bar class="nav-bar">
<van-button
class="search-btn"
slot="title"
type="info"
size="small"
round
icon="search">
搜索
</van-button>
</van-nav-bar>
.nav-bar{
background-color:
height: 129px
.search-btn{
width: 445px
height: 65px
background-color:
margin-top: 36px
}
.van-icon {
font-size: 32px
}
}
2、推荐的话题专栏模块先套用vant组件布局
<van-tabs v-model="active">
<van-tab title="推荐">推荐</van-tab>
<van-tab title="热门话题">热门话题</van-tab>
<van-tab title="科技动态">科技动态</van-tab>
<van-tab title="区块链">区块链</van-tab>
</van-tabs>
data () {
return {
active: 2
}
}
3、封装获取所有频道的api
export const getChannels = () => {
return request({
method: 'GET',
url: '/v1_0/channels'
})
}
调用api的数据
data () {
return {
active: 2,
channels: {}
}
},
computed: {
...mapState(['user'])
},
watch: {},
created () {
this.loadChannels()
},
mounted () {},
methods: {
async loadChannels () {
try {
const { data: { data } } = await getChannels()
this.channels = data.channels
console.log(data)
} catch (err) {
this.$toast('获取频道失败')
}
}
}
渲染页面
<van-tab v-for="channel in channels"
:key="channel.id" :title="channel.name">频道内容{{ channel.name }}</van-tab>
二、新闻频道栏目
1、用van-tab组件来布局新闻频道栏
<van-tabs class="channel-tab" v-model="active" swipeable animated>
<van-tab title="标签1">内容1</van-tab>
<van-tab title="标签2">内容2</van-tab>
<van-tab title="标签3">内容3</van-tab>
<van-tab title="标签4">内容4</van-tab>
<van-tab title="标签5">内容5</van-tab>
<van-tab title="标签6">内容6</van-tab>
<van-tab title="标签7">内容7</van-tab>
</van-tabs>
设置对应样式,这里注意 要加上/deep/或者>>>样式才会生效
/deep/ .channel-tab{
.van-tab{
min-width: 200px;
text-align: center;
font-size: 27px;
margin-bottom: 30px;
border-right: 1px solid #edeff3;
}
}
2、设置汉堡按钮
使用slot插槽 nav-right属性把按钮插入新闻频道栏右侧
<van-tabs class="channel-tab" v-model="active" swipeable animated>
<van-tab title="标签1">内容1</van-tab>
<van-tab title="标签2">内容2</van-tab>
<van-tab title="标签3">内容3</van-tab>
<van-tab title="标签4">内容4</van-tab>
<van-tab title="标签5">内容5</van-tab>
<van-tab title="标签6">内容6</van-tab>
<van-tab title="标签7">内容7</van-tab>
<div slot="nav-right" class="humbarger-btn">
<i class="iconfont icon-gengduo"></i>
</div>
</van-tabs>
调整样式,把汉堡按钮固定在窗口右侧,在按钮前面加一个样式使用$:before
.humbarger-btn{
position: fixed;
right: 0;
opacity: 0.9;
width: 66px;
height: 82px;
display: flex;
justify-content: center;
align-items: center;
color: #333333;
i .iconfont{
font-size: 33px;
}
固定头部之后,发现有两行文章被覆盖住了,需要在home-container加上padding-top: 174px ;
三、铺设文章列表页面
1、用List组件铺设文章列表
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell v-for="item in list" :key="item" :title="item" />
</van-list>
定义方法methods
onLoad () {
setTimeout(() => {
for (let i = 0; i < 10; i++) {
this.list.push(this.list.length + 1)
}
this.loading = false
if (this.list.length >= 40) {
this.finished = true
}
}, 1000)
}
2、固定文章列表头部位置
在头部加上fixed 属性固定搜索栏
<van-nav-bar class="nav-bar" fixed>
place属性加上样式 让新闻栏也固定定位
<div slot="nav-right" class="place"></div>
.place{
position: fixed;
top: 92px;
z-index: 1;
left: 0;
right: 0;
width: 66px;
height: 82px;
flex-shrink: 0;//不参与默认空间的计算,否则宽不生效
}
四、获取推荐频道
1、封装推荐频道的api 新建文件在api/article.js
import request from '@/utils/request'
export const getArticles = params => {
return request({
method: 'GET',
url: ' /v1_0/articles',
params
})
}
2、调用api的数据
async onLoad () {
try {
const { data } = await getArticles({
channel_id: this.channels.id,
timestamp: this.timestamp || Date.now()
})
const { results } = data.data
this.list.push(...results)
this.loading = false
if (results.length) {
this.timestamp = data.data.pre_timestamp
} else {
this.finished = true
}
console.log(data)
} catch (err) {
this.$toast('新闻推荐获取失败')
this.loading = false
}
}
3、渲染页面
<div class="article-list">
<van-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<van-cell
v-for=" (article , index) in list"
:key="index"
:title="article.title" />
</van-list>
tips:后面api接口访问数据出现这种情况,然后导致了页面渲染不成功
{
"data": null,
"message": "频道id或者时间戳参数缺失"
}
五、频道编辑
1、设置popup弹出层
<van-popup
class="edit-channel-popup"
v-model="isChannelEditShow"
position="bottom"
:style="{ height: '100%' }"
closeable
close-icon="close"></van-popup>
//样式
.edit-channel-popup {
padding-top: 100px;
box-sizing: border-box
}
//给汉堡按钮添加点击事件
<i class="iconfont icon-gengduo" @click="isChannelEditShow=true"></i>
2、铺设频道页面
在views下的home/components创建channel-edit文件
<template>
<div class="channel-edit">频道编辑</div>
</template>
<script>
export default {
name: 'ChannelEdit',
components: {},
props: {},
data () {
return {}
},
computed: {},
watch: {},
created () {},
mounted () {},
methods: {}
}
</script>
<style scoped lang="less">
</style>
在home/index.vue下引入组件并注册
//1、引入
import ChannelEdit from '@/views/home/components/channel-edit.vue'
//2、注册
components: {
ArticleList,
ChannelEdit
},
//3、使用
<van-popup
class="edit-channel-popup"
v-model="isChannelEditShow"
position="bottom"
:style="{ height: '100%' }"
closeable
close-icon="close">
<ChannelEdit></ChannelEdit>
</van-popup>
布局频道和编辑按钮以及
<div class="channel-edit">
<div class="my-channel">
<van-cell>
<div slot="title">我的频道</div>
<van-button round size="mini"
type="danger"
style="margin-top: 10px;"
plain>编辑</van-button>
</van-cell>
<van-grid :gutter="10">
<van-grid-item class="grid-item"
v-for="value in 8"
:key="value"
text="文字" />
</van-grid>
</div>
<div class="channel-recommand">
<van-cell style="font-size: medium;" title="频道推荐" />
<van-grid :gutter="10" >
<van-grid-item class="grid-item"
v-for="value in 8"
:key="value"
text="文字" />
</van-grid>
</div>
</div>
设置样式
.channel-edit{
padding:85px,0;
}
/deep/.grid-item{
width: 160px;
height: 86px;
.van-grid-item__content{
background-color: #f4f5f6;
}
}
最终版的样式 给按钮添加删除符号和添加符号
<div class="channel-edit">
<!-- 我的频道 -->
<div class="my-channel">
<van-cell>
<div slot="title">我的频道</div>
<van-button round size="mini"
type="danger"
style="margin-top: 10px;"
plain>编辑</van-button>
</van-cell>
<van-grid :gutter="10">
<van-grid-item class="grid-item"
v-for="value in 8"
:key="value"
icon="clear"
text="文字" />
</van-grid>
</div>
<!-- 频道推荐 -->
<div class="channel-recommand">
<van-cell style="font-size: medium;" title="频道推荐" />
<van-grid :gutter="10" >
<van-grid-item class="grid-item"
v-for="value in 8"
:key="value"
icon="plus"
text="文字" />
</van-grid>
</div>
</div>
.channel-edit{
padding:85px,0
/deep/.grid-item{
width: 160px
height: 86px
.grid-item text{
color:
}
.van-icon-clear{
position: absolute
top: -10px
right: -10px
font-size: 30px
color:
}
.van-grid-item__content{
background-color:
}
}
}
/deep/.channel-recommand{
.grid-item{
.van-grid-item__content{
flex-direction:row
white-space: nowrap
.van-icon-plus{
font-size: 28px
margin-top: 15px
margin-right: 10px
}
.van-grid-item__text{
margin-top: 0
}
}
}
}
3、展示我的频道
我的频道的数据跟首页的推荐频道数据一样,这里可以不用获取api和封装来渲染,直接选择父传子(点击高亮的效果也一样)。在主页面home/index.vue中父传子实现我的频道标签显示,在channel-edit中接收
<ChannelEdit :mychannels="channels"/>
props: {
mychannels: {
type: Array,
require: true
}
}
渲染我的频道页面
<van-grid :gutter="10">
<van-grid-item class="grid-item"
v-for="(channels,index) in mychannels"
:key="index"
icon="clear"
text="channels.name" />
</van-grid>
高亮效果:
<van-grid :gutter="10">
<van-grid-item class="grid-item"
v-for="(channels,index) in mychannels"
:key="index"
icon="clear"
/>
<span class="text"
:class="{active: index === active}">{{channels.name}}</span>
</van-grid>
props: {
mychannels: {
type: Array,
require: true
},
myactive: {
type: Number,
require: true
}
}
4、展示推荐频道
先获取所有频道(由于没有推荐频道的接口 我们可以采用所有频道-我的频道 )
computed: {
recommendChannels () {
return this.allchannels.filter(channel => {
return !this.mychannels.find(mychannel => {
return mychannel.id === channel.id
})
})
}
}
接着渲染页面:
<!-- 频道推荐 -->
<div class="channel-recommand">
<van-cell style="font-size: medium;" title="频道推荐" />
<van-grid :gutter="10" >
<van-grid-item class="grid-item"
v-for="(channel,id) in recommendChannels"
:key="id"
icon="plus"
:text="channel.name"
/>
</van-grid>
</div>
5、添加频道
频道推荐绑定点击事件
@click="onAddChannel(channel)
点击添加频道
onAddChannel (channel) {
this.localmyChannel.push(channel)
}
在data中修改props值的方法之一
data: function () {
return {
localmyChannel: this.mychannels,
allchannels: []
}
}
至此成功实现添加频道功能
6、频道处理编辑状态
把删除按钮重新设置属性,给类名来控制样式
<van-icon slot="icon" name="clear"></van-icon>
在data中声明变量isEdit控制编辑状态的显示,通过v-show来控制显示删除按钮与否
isEdit: false
<van-icon v-show="isEdit"
slot="icon" name="clear"></van-icon>
在我的频道的编辑按钮绑定点击事件
<van-cell>
<div slot="title">我的频道</div>
<van-button round size="mini"
type="danger"
style="margin-top: 10px;"
@click="isEdit=!isEdit"
plain>{{isEdit?'完成':'编辑'}}</van-button>
</van-cell>
最后设置默认的推荐频道不允许被删除,在data属性中添加固定频道
fixChannel: [0]
//再判断一次便可以实现该功能了
<van-icon v-show="isEdit&&!fixChannel.includes(channel.id)"
slot="icon" name="clear"></van-icon>
7、删除频道
定义一个删除函数onDelete
<van-grid-item class="grid-item"
v-for="(channel,index) in mychannels"
:key="index"
:text="channel.name"
@click="onDelete(channel,index)"
>
点击删除,这里要删除mychannels,但是mychannels在props里,所以定义了localmyChannel: this.mychannels
onDelete (channel, index) {
console.log(channel, index)
if (this.isEdit) {
this.localmyChannel.splice(index, 1)
} else {
this.$emit('updateactive', index)
}
8、点击跳转频道
无法在在props里改变active的值,在子组件里$emit()传参,在父组件中定义接收updateactive事件
@updateactive="onUpdateactive"
onUpdateactive (index) {
this.active = index
this.isChannelEditShow = false
}
9、数据持久化
添加数据频道持久化
export const AddUserChannels = channel => {
return request({
method: 'PATCH',
url: '/v1_0/user/channels',
data: {
channels: [channel]
}
})
}
async onAddChannel (channel) {
this.localmyChannel.push(channel)
if (this.user) {
try {
await AddUserChannels({
id: channel.id,
seq: this.mychannels.length
})
} catch (err) {
this.$toast('保存失败')
}
} else {
setItem('TOUTIAO-Channel', channel)
}
}
删除数据持久化
export const DeleteUserChannels = channelId => {
return request({
method: 'DELETE',
url: `/v1_0/user/channels/${channelId}`
})
}
onDelete (channel, index) {
console.log(channel, index)
if (this.isEdit) {
this.localmyChannel.splice(index, 1)
if (index <= this.active) {
this.$emit('updateactive', this.active - 1, true)
}
this.deleteChannel(channel)
} else {
this.$emit('updateactive', index)
}
},
async deleteChannel (channel) {
try {
if (this.user) {
await DeleteUserChannels(channel.id)
} else {
setItem('TOUTIAO-Channel', this.mychannels)
}
} catch (err) {
this.$toast('请求失败,稍后重试')
}
}