uniapp--咸虾米壁纸项目(四)

85 阅读3分钟

补充前面的“图片预览页”中点击“返回图标”的功能:

image.png

const goBack=()=>{
  //利用uni.navigateBack
  uni.navigateBack({
     success:()=>{
       //成功返回时
       delta:1  //返回到上一页
     },
     
     fail:()=>{
       //返回失败时,强制跳转到“首页”
       //由于“首页”属于底部tabBar中的路由页面,所以跳转时利用uni.relaunch({})
       
       uni.reLaunch({
          url:"/pages/index/index"
    })
  }
}

一、完成“公告页面”的结构搭建及样式配置:

pages/notice/notice.vue:

image.png

发现该页面由:公告标题 、发布者、发布时间、二维码及文字、阅读数 组成。

<view class="noticeLayout">

   //公告标题
   <view class="title">
   
      //tag标签
      <view class="tag">
      
         //使用uniapp中的uni-tag组件(用法在官网可见)
         <uni-tag inverted text="置顶" type="error" />
      </view>
      
      //标题
      <view class="font">公告标题</view>
  </view>
  
  //发布者及发布时间(uni-dateformat组件)
  <view class="info">
      <view class="item">发布者</view>
      <view class="item">
         <uni-dateformat :date="Date.now()" format="yyyy-MM-dd hh:mm:ss"></uni-dateformat>
      </view>
  </view>
  
  //二维码及内容
  <view class="content"></view>
  
  //阅读数
  <view class="count">
       39234
  </view>
  
</view>

样式配置:

<style lang="scss" scoped>
.noticeLayout{
	padding:30rpx;
		.title{
			font-size: 40rpx;
			color:#111;
			line-height: 1.6em;
			padding-bottom:30rpx;
			display: flex;
                        
                        //给tag标签缩小显示
			.tag{
				transform: scale(0.8);
				transform-origin: left center;
				flex-shrink: 0;	
			}
			.font{
				padding-left:6rpx;
			}
		}
		.info{
			display: flex;
			align-items: center;
			color:#999;
			font-size: 28rpx;
			.item{
				padding-right: 20rpx;
			}
		}
		.content{
			padding:50rpx 0;
		}
		.count{
			color:#999;
			font-size: 28rpx;
		}
}
</style>

二、实现“公告页面”中真实数据的获取:

1、封装接口方法用于获取真实数据

image.png

2、在“公告页面”中封装一个函数用于调用接口方法,获取数据

const detail=ref({})

const getNoticeDetail=async()=>{
  const res= await apiNoticeDetail({id:noticeId})
  detail.value=res.data
}

3、在“首页”公告区域,点击公告内容,实现跳转到其对应的公告页面

跳转时需要携带id值传入:

index/index.vue:

image.png

4、在“公告页面”中接收传来的参数id,实现数据获取

let noticeId;
onLoad((e)=>{
   noticeId=e.id
   
   //调用封装好的函数,实现发送请求,获取数据
   getNoticeDetail()
})

5、将获取到的数据渲染在页面中:

image.png

三、实现“公告页面”中二维码及文字的显示:

由于在“公告区域”点击公告,跳转到对应的“公告页面”时,发送请求,获取到的公告数据是:

image.png

其中公告内容content部分的数据是一串html结构,因此要将其转化成用户能看懂的形式显示出来。

可以利用uniapp中的rich-text组件。

因此,可以这样写:

//二维码及内容
<view class="content">

   <rich-text :node="detail.content"></rich-text>
   
</view>

四、在“用户页面”中点击“订阅更新”以及“常见问题”,实现跳转到“公告页面”

user/user.vue:

image.png

五、完成“搜索页”页面的基本结构及样式搭建:

1、先完成基本结构的搭建

image.png

image.png

发现“搜索页”是由: 搜索栏、最近搜索盒子、热门搜索盒子 、搜索列表数据组成。

搜索栏可以利用uni-search-bar组件来实现

pages/search/search.vue:

<template>

	<view class="searchLayout">
        
            //搜索栏  利用uniapp中的uni-search-bar组件
		<view class="search">

			<uni-search-bar 
                        
                    //当用户点击搜索/回车后触发@confirm事件
			@confirm="onSearch"
                     
                    //当用户点击“取消”按钮时,触发@cancel事件
			@cancel="onClear"
                        
                    // 当用户点击“清除”按钮时,触发@clear事件
			@clear="onClear"

			focus 

			placeholder="搜索"

                    // 实现搜索栏关键词数据的双向绑定
			v-model="queryParams.keyword">

			</uni-search-bar>

		</view>
                
                //“最近搜索”盒子:
                
		<view v-if="!classList.length || noSearch">

			<view class="history"  v-if="historySearch.length">
                       
                 //“标题”
				<view class="topTitle">

					<view class="text">最近搜索</view>
                                        
                   //“清除”图标  --- uni-icons图标
                   
					<view class="icon" @click="removeHistory">

						<uni-icons type="trash" size="25"></uni-icons>

					</view>

				</view>
                                
                    //“历史数据”

				<view class="tabs">

					<view class="tab" v-for="tab in historySearch" :key="tab" 

					@click="clickTab(tab)">{{tab}}</view>		

				</view>

			</view>

	    //“热门搜索”盒子

			<view class="recommend">

				<view class="topTitle">

					<view class="text">热门搜索</view>				

				</view>

				<view class="tabs">				

					<view class="tab" v-for="tab in recommendList" :key="tab" 

					@click="clickTab(tab)">{{tab}}</view>

				</view>

			</view>

		</view>

		

      //当没有“搜索记录”时,显示的说明数据  利用插件uv-empty
      //在浏览器中搜索“dcloud”,然后搜索插件uv-empty
      //这是一个固定语法
    

		<view class="noSearch" v-if="noSearch">

			<uv-empty mode="search" icon="http://cdn.uviewui.com/uview/empty/search.png"></uv-empty>

		</view>

		

	//搜索列表数据:	

		<view v-else>

			<view class="list">

				<navigator :url="`/pages/preview/preview?id=${item._id}`"  class="item" 

				v-for="item in classList" :key="item._id">				

					<image :src="item.smallPicurl" mode="aspectFill"></image>				

				</navigator>

			</view>		

			<view class="loadingLayout" v-if="noData || classList.length">

				<uni-load-more :status="noData?'noMore':'loading'"/>

			</view>

		</view>

		

		

	</view>

</template>

<style lang="scss" scoped>

.searchLayout{

	.search{

		padding:0 10rpx;

	}

	.topTitle{

		display: flex;

		justify-content: space-between;

		align-items: center;

		font-size: 32rpx;

		color:#999;

	}

	.history{

		padding:30rpx;		

	}

	.recommend{

		padding:30rpx;

	}

	.tabs{

		display: flex;		

		align-items: center;

		flex-wrap: wrap;

		padding-top:20rpx;

		.tab{

			background: #F4F4F4;

			font-size: 28rpx;

			color:#333;

			padding:10rpx 28rpx;

			border-radius: 50rpx;

			margin-right: 20rpx;

			margin-top: 20rpx;

		}

	}	

	.list{

		display: grid;

		grid-template-columns: repeat(3,1fr);

		gap: 5rpx;

		padding:20rpx 5rpx;		

		.item{

			height: 440rpx;

			image{

				width: 100%;

				height: 100%;

				display: block;

			}			

		}		

	}

}

</style>

2、准备一个“最近搜索”记录数组以及一个“热门搜索”记录数组:

const historySearch=ref([])
const recommendList = ref(["美女","帅哥","宠物","卡通"]);

//页面中进行数据渲染

//“最近搜索”盒子:

<view class=""history>
    <view class="topTitle"></>
    <view class="tabs">
       <view class="tab" v-for="tab in historySearch" :key="tab">
           {{tab}}
       </view>
    </view>
 </view>
 
 //“推荐搜索”盒子:
 
 <view class="recommend">
    <view class="topTitle"></>
    <view class="tabs">
       <view class="tab" v-for="item in recommendSearch" :key="tab">
           {{tab}}
       </view>
 </view>
 

3、封装接口方法用于请求“搜索列表数据”:

api/apis.js:

image.png

在“搜索页”中准备接口需要的请求参数:

image.png pages/search/search.vue:

  let queryParams=ref({
     keyword:'',
     pageNum:1,
     pageSize:12
  })
 

封装一个函数用于调用接口方法,获取数据:

//准备一个搜索列表变量
const classList=ref([])

const searchData=async()=>{
   try{
      let res=await apiSearchData(queryParams.value)
      classList.value=res.data
      }finally{
        uni.hideLoading()
      }
  }

4、实现“搜索列表”中触底加载更多数据显示:

与先前在“分类列表页”中的同理:

//设置一个变量用来表示是否还有新的数据存在

let noData=ref(false)

//当页面出现触底现象时,触发onReachBottom生命周期函数

onReachBottom(()=>{
   if(noData.value)  return;
   queryParams.value.pageNum++
   searchData()
 })
 
 //完善先前封装好的用于调用接口,获取数据的函数:
 
 const searchData=async()=>{
    try{
    const res=await apiSearchData(queryParams)
    classList.value=[...classList.value,...res.data]
    //进行判断是否还有新的数据存在
    if(queryParams.value.pageSize>res.data.length)  noData.value=true
    
    //将获取到的“搜索列表”数据存入本地中,便于之后在“图片预览页”中,可以直接从本地获取数据。
    
    uni.setStorageSync("storgClassList",classList.value)
  }
 }finally{
   uni.hideLoading()
   }
 }

5、实现当获取到了搜索结果时,“搜索列表”就显示,“最近搜索”盒子与“热门搜索”盒子就隐藏:

//设置一个变量用来表示是否有搜索结果
let noSearch=ref(false)

const searchData=async()=>{
   try{
     const res=await apiSearchData(queryParams)
     classList.value=[...classList.value,...res.data]
      //进行判断是否还有新的数据存在
    if(queryParams.value.pageSize>res.data.length)  noData.value=true
    
    //将获取到的“搜索列表”数据存入本地中,便于之后在“图片预览页”中,可以直接从本地获取数据。
    
    uni.setStorageSync("storgClassList",classList.value)
    
    //判断是否获取到了搜索列表,来决定是否存在搜索结果:
    //如果当前获取到的搜索数据长度为0并且搜索列表为空,则说明没有搜索结果。
    
    if(res.data.length===0 && classList.value.length ===0) noSearch.value=true
    
   }finally{}
 }

下面实现条件渲染页面:如果不存在搜索结果,则搜索列表不显示。否则,搜索列表显示,“最近搜索”盒子以及“热门搜索”盒子不显示。

“最近搜索”盒子只有在有搜索记录时显示

<view class="" v-if="historySearch.length"></>

“最近搜索”盒子和“热门搜索”盒子只有在当前页面中没有搜索列表显示classlist.length==0 或者 没有搜索结果时显示 noSearch === true

而“搜索列表”盒子恰好与其相反,利用v-else

<view v-if="!classList.length || noSearch">
  <view class="history" v-if="historySearch.length"></>
  <view class="recommend" v-if="recommendSearch"></>
</view>

//搜索列表盒子
<view v-else>
<view class="list">
</>
</>

!!!6、用户点击“搜索”或者按下“回车键”时,触发@confirm事件,执行搜索功能

<uni-search-bar  @confirm="onSearch"></>

const onSearch=()=>{
   uni.showLoading()
   
   //先将“搜索关键字”追加进“最近搜索”记录数组中
   //使用[...new Set(数组)]实现去重,避免有相同的关键字存在。
   
   historySearch.value = 
	[...new Set([queryParams.value.keyword,...historySearch.value])].slice(0,10);
        
   //再将“最近搜索”记录数组存入本地中,防止页面刷新后,记录丢失
   uni.setStorageSync("historySearch",historySearch.value);
   
   //每次发送请求,获取搜索列表时,都要进行初始化,避免先前搜索的列表数据存在
   initParams(queryParams.value.keyword);
   
   //再调用函数,请求搜索
   searchData()
 }
   
   
//所以“最近搜索”记录数组,在打开页面后,先从本地中拿记录数据,如果没有数据,则设置为[]

let historySearch=ref(uni.getStorageSync("historySearch") || []);


   

!!!7、实现点击“最近搜索”记录数组中的标签,也可以实现搜索功能

 <view class="tabs">
   <view class="tab" v-for="tab in historySearch" :key="tab" @click="clickTab(tab)"></>
 </>
 
 <view class="tabs">
   <view class="tab" v-for="tab in recommendSearch" :key="tab" @click="clickTab(tab)"></>
 </>
 
 const clickTab=(value)=>{
   //首先进行初始化页面,避免先前的搜索列表数据存在
   
   initParams(value)
   
   //再执行搜索功能(包括将搜索关键字追加进“最近搜索”记录数组,也包括将记录数据存入本地,也包括调用函数,请求搜素,所以直接调用onSearch方法)
   
   onSearch()
 }

8、实现页面数据初始化:

const initParams = (value='')=>{
	classList.value = [];
	noData.value = false;
	noSearch.value = false;
	queryParams.value = {
		pageNum:1,
		pageSize:12,
		keyword:"" || value
	}
}

9、实现“清除”功能以及“取消”功能:

image.png

“取消功能”:

const onClear=()=>{
  //初始化重置
	initParams();
    }

“清除关键字”功能:

image.png

const removeHistory=()=>{
  //弹出提示,提示用户是否执行清除?
  
  uni.showModal({
    title:"是否清空历史搜索?",
    success:(res)=>{
    
    //判断用户是否点击“确认”?如果是,则清除本地记录以及“最近搜索”记录数组
      if(res.confirm)
      {
        uni.removeStorageSync("historySearch");
        historySearch.value=[]
      }
  })
}

六、实现“首页”中点击“轮播图组件”中的图片,跳转到对应的“分类列表页”:

由于请求获取“轮播图组件”中的图片数据时,返回的数据是:

image.png

image.png

可以看出,属性url包含了“分类列表页”中需要的参数信息id以及导航栏标题name

所以在执行路由跳转时,要携带上参数url

image.png

const goRouter=(value)=>{
   uni.navigateTo({
	url:`/pages/classify/classlist?${value}`
  })
}

七、实现在首页中的“专题精选部分”,点击“more+”,可以跳转到“分类页”:

由于“分类页”属于底部tabBar导航栏中的路由页面,因此跳转时需要open-type="relanuch"

image.png