274 lines
8.2 KiB
Vue
274 lines
8.2 KiB
Vue
<template>
|
||
<view class="searchLayout">
|
||
<!-- cancel是"取消",clear是那个"×" -->
|
||
<view class="search">
|
||
<uni-search-bar
|
||
@confirm="onSearch"
|
||
@cancel="onClear"
|
||
@clear="onClear"
|
||
:focus="true"
|
||
placeholder="搜索"
|
||
v-model="queryParams.keyword"
|
||
>
|
||
</uni-search-bar>
|
||
</view>
|
||
|
||
<!-- 当有图片的时候,就不展示这些东西标签啥的。 -->
|
||
<!-- 点击标签,但是没有数据时,noSearch会变成true,也可以显示这些标签 -->
|
||
<view v-if="!classList.length || noSearch">
|
||
<!-- 这个最近搜索的展示是根据有没有历史记录来展示的
|
||
如果没有历史记录,就不展示这一框
|
||
如果有历史记录,就展示这一框
|
||
-->
|
||
<view class="history" v-if="historySearch.length">
|
||
<view class="topTitle" >
|
||
<view class="text">最近搜索</view>
|
||
<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>
|
||
|
||
<!-- 这里我本来的:key="index" ,但是人家却是:key="tab"-->
|
||
<view class="tabs">
|
||
<view class="tab" v-for="tab in recommendList" :key="tab"
|
||
@click="clickTab(tab)">{{tab}}</view>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
|
||
<!-- 没有搜索结果的情况,这是一个插件 -->
|
||
<view class="noSearch" v-if="noSearch">
|
||
<uv-empty mode="search" ></uv-empty>
|
||
</view>
|
||
|
||
<!-- 有搜索结果的展示区域 -->
|
||
<view v-else>
|
||
<view class="list">
|
||
<!-- 这里你单独的进行传id也是不能够看到预览页面的 -->
|
||
<!-- 因为预览页面呢个够被看到是取决你有没有缓存这个预览页面的数据的 -->
|
||
<!-- 而缓存预览页面的数据我们可以在请求网络的数据的时候就进行存储 -->
|
||
<!--
|
||
:url是:url="`/pages/preview/preview?id=${item._id}`"
|
||
不是:url="`pages/preview/preview?id=${item._id}`"
|
||
前面还要有一个 “/”
|
||
-->
|
||
|
||
<!-- <navigator :url="`pages/preview/preview?id=${item._id}`" class="item"
|
||
v-for="item in classList" :key="item._id"> -->
|
||
|
||
<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 v-if="noData || classList.length" class="loadingLayout">
|
||
<uni-load-more :status="noData?'noMore':'loading'"/>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {ref} from "vue"
|
||
import {onReachBottom,onUnload,onLoad} from "@dcloudio/uni-app"
|
||
import {apiSearchData} from "@/api/apis.js"
|
||
|
||
//传递的参数
|
||
const queryParams = ref({
|
||
pageNum:1,
|
||
pageSize:12,
|
||
keyword:""
|
||
})
|
||
|
||
//搜索历史词
|
||
//获取历史搜索词,我们需要使其从缓存里面读取,保证刷新不会重新被赋值
|
||
// 当用户没有缓存时,给一个空数组,避免出现获取不到null的情况,进而影响后续操作
|
||
const historySearch = ref(uni.getStorageSync("historySearch") || []);
|
||
|
||
//热门推荐词
|
||
const recommendList = ref(["美女","帅哥","宠物","卡通"])
|
||
|
||
//进到标签页面时可能发生的情况
|
||
//没有数据了
|
||
const noData = ref(false)
|
||
//没有搜索结果
|
||
const noSearch = ref(false)
|
||
|
||
//搜索结果列表 搜索的结果最终是要在preview页面进行查看的
|
||
// classList初始化为空,只有在调用了searchData()函数之后才会被赋值
|
||
const classList = ref([])
|
||
|
||
|
||
//点击清除按钮
|
||
const onClear = ()=>{
|
||
initParams()
|
||
}
|
||
|
||
//点击标签进行搜索.需要一个参数接收传入的标签名
|
||
// 问题:1.搜索框的值怎么表示? --》 v-model 绑定,监视:value和@input事件
|
||
// 2.怎么得到值后就自动”comfirm“
|
||
const clickTab = (value) =>{
|
||
/* 因为你在点击了标签的内容之后,还可以在搜索框继续进行搜索,那此时你新搜索的内容会跟之前一样,
|
||
被添加到原来的内容下面。因此你需要对其进行初始化,并将keyword换成最新的关键词 */
|
||
initParams(value)
|
||
// 点击标签之后不是触发searchData(),因为这样不会这个点击的标签不会被添加到历史记录里面
|
||
// 点击标签之后要触发的函数是onSearch()
|
||
// searchData()
|
||
onSearch()
|
||
}
|
||
|
||
//清空最近搜索历史记录
|
||
const removeHistory = ()=>{
|
||
uni.showModal({
|
||
title:"是否清空历史搜索",
|
||
success:(res) =>{
|
||
if(res.confirm){
|
||
// console.log("确认删除");
|
||
uni.removeStorageSync("historySearch")
|
||
historySearch.value = []
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
//1.点击搜索,点击了之后,搜索记录里面要多加一个记录
|
||
const onSearch = ()=>{
|
||
//加一个加载页,提高用户体验。那时候时候取消呢?在searchData()获取数据那时候进行操作
|
||
uni.showLoading()
|
||
// console.log(queryParams.value.keyword);
|
||
// 第一种添加的方式就是直接加到前面,老的历史词就直接展开
|
||
// 第二种就是直接用数组的添加方法
|
||
|
||
//slice进行切片操作,确保只有10个历史记录
|
||
historySearch.value = [...new Set([queryParams.value.keyword,...historySearch.value])].slice(0,10)
|
||
uni.setStorageSync("historySearch",historySearch.value)
|
||
/* 因为你在点击了标签的内容之后,还可以在搜索框继续进行搜索,那此时你新搜索的内容会跟之前一样,
|
||
被添加到原来的内容下面。因此你需要对其进行初始化,并将keyword换成最新的关键词 */
|
||
initParams(queryParams.value.keyword)
|
||
// 只有点击“搜索”之后才要触发这个函数
|
||
searchData()
|
||
|
||
}
|
||
|
||
|
||
//搜索之后所有的要做的功能的合集
|
||
const searchData = async () =>{
|
||
// 加一个try、catch进行操作,捕捉“拿不到数据”的情况。
|
||
try{
|
||
//使res等于请求过来的数据
|
||
let res = await apiSearchData(queryParams.value)
|
||
classList.value = [...classList.value,...res.data]
|
||
|
||
//将数据保存到缓存里面,方便上面点击标签后要预览页面(preview)
|
||
uni.setStorageSync("storgClassList",classList.value)
|
||
|
||
//如果后面没有数据了,就需要把noData变成true
|
||
if(queryParams.value.pageSize > res.data.length) noData.value = true
|
||
|
||
//下面这个是判断有没有搜索结果的
|
||
//如果先点击了有图片的标签,那么当你再点击没图片的标签时,就还是展示原来那个有图片的标签
|
||
//如果原本classList就是空的,并且你点击的标签也没有图片时,才会展示“没有搜索结果”
|
||
if(res.data.length == 0 && classList.value.length == 0) noSearch.value = true
|
||
// console.log(res);
|
||
}finally {
|
||
uni.hideLoading()
|
||
}
|
||
|
||
|
||
}
|
||
|
||
//初始化搜索框,清空原本的内容
|
||
//什么时候用呢?
|
||
// 1.当我们点击“取消”和"×"的时候进行调用
|
||
// 2.当我们点击新的标签时就要先初始化
|
||
const initParams = (value='')=>{
|
||
//清空原先已经展示的数据
|
||
classList.value = []
|
||
noData.value = false
|
||
noSearch.value = false
|
||
queryParams.value = {
|
||
pageNum:1,
|
||
pageSize:12,
|
||
// 如果有传入值,就把这个值赋给keyword,没有值的话就默认变成""
|
||
keyword:value || ""
|
||
}
|
||
}
|
||
|
||
//触底加载更多
|
||
onReachBottom(()=>{
|
||
//这个if表示如果后面没有数据了,就直接return结束,不要再进行请求数据了
|
||
if(noData.value) return
|
||
queryParams.value.pageNum++
|
||
searchData()
|
||
})
|
||
|
||
//关闭页面时将数据清除,能够提高性能
|
||
onUnload(()=>{
|
||
uni.removeStorageSync("storgClassList",classList.value)
|
||
})
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.searchLayout{
|
||
.search{
|
||
padding: 0 10rpx;
|
||
}
|
||
.topTitle{
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
.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{
|
||
height: 100%;
|
||
width: 100%;
|
||
display: block;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|