小程序实现搜索效果
首页入口
进入搜索页
搜索结果页
后台提供三个接口
<?php
/**
* 搜索商品
*/
class SearchAction extends CommonAction{
public function _initialize(){
parent::_initialize();
}
/**
* 获取搜索历史和热门搜索
*/
public function getHotAndHistory() {
if (!$uid = $_POST['uid']) {
$this->json->setErr('10001', '缺少用户id');
$this->json->Send();
}
// 历史
$data['history'] = [];
$search_history = M('search_history');
$search_history_list = $search_history->where(['uid'=>$uid,'status'=>1])->order('id desc')->select();
foreach ($search_history_list as $k=>$v) {
$data['history'][] = $v['keywords'];
}
// 热门
$configs = M('configs');
$hot_search = $configs->where(['this_key'=>'hot_search'])->getField('this_value');
$data['hot'] = explode(',',$hot_search); // 爆炸成数组
$this->json->S($data);
}
/**
* 搜索数据
*/
public function searchData() {
if (!$uid = $_POST['uid']) {
$this->json->setErr('10001', '缺少用户id');
$this->json->Send();
}
if (!$keywords = $_POST['keywords']) {
$this->json->setErr('10001', '请输入要查询的内容');
$this->json->Send();
}
if (!isset($_POST['page']) || $_POST['page'] < 1) {
$_POST['page'] = 1;
}
if (!isset($_POST['page_size']) || $_POST['page_size'] < 1) {
$_POST['page_size'] = C('PAGE_NORMAL_COUNT');
}
$page = $_POST['page'];
$page_size = $_POST['page_size'];
if ($page == 1) {
// 删除之前的记录
$search_history = M('search_history');
$search_history->where(['uid'=>$uid,'keywords'=>$keywords])->save(['status'=>0,'update_time'=>time()]);
// 添加新的记录
$search_history->add(['uid'=>$uid,'keywords'=>$keywords,'update_time'=>time(),'create_time'=>time()]);
}
$where['title'] = ['like','%'.$keywords.'%'];
$where['status'] = 1; // 显示
$where['is_del'] = 0; // 未删除
$where['spec_main'] = 1; // 是否主商品
// 获取总数
$product = M('product');
$count = $product->where($where)->count();
$total_page = ceil($count / $page_size);
if ($page > $total_page) {
$return_data = ['data_list' => [], 'total_page' => $total_page, 'current_page' => $page];
$this->json->S($return_data, '没有更多了');
}
$data_list = $product->where($where)
->order('id desc')
->limit((($page - 1) * $page_size) . ',' . $page_size)
->field('id,title,title_img,sales_count,price')
->select();
if ($data_list) {
vendor('Func.Math');
foreach ($data_list as $k => &$v) {
$v['price'] = Math::div($v['price'], 100);
}
}
$out_data = $data_list ?: [];
$return_data = ['data_list' => $out_data, 'total_page' => $total_page, 'current_page' => $page];
$this->json->S($return_data);
}
/**
* 清除数据
*/
public function clearData() {
if (!$uid = $_POST['uid']) {
$this->json->setErr('10001', '缺少用户id');
$this->json->Send();
}
// 删除之前的记录
$search_history = M('search_history');
$clear_res = $search_history->where(['uid'=>$uid])->save(['status'=>0,'update_time'=>time()]);
if ($clear_res !== false) {
$this->json->S();
} else {
$this->json->E('清理失败');
}
}
}
搜索页面
<view class="search">
<view class="search-container">
<view class="search-left">
<image class="search-image" src="/images/index/search.png" />
<input class="search-input" placeholder="请输入你喜欢的商品" bindinput='watchSearch'/>
</view>
<view class="search-btn" data-keywords="{{keywords}}" bindtap="go_to_search_result">搜索</view>
</view>
</view>
<view class="history">
<view class="history-title">
<view class="history-title-left">搜索历史</view>
<view class="history-title-right" bindtap="clearSearchHistory">清除历史</view>
</view>
<view class="history-content">
<block wx:if="{{index_data.history != false}}">
<block wx:for="{{index_data.history}}" wx:key="id">
<view class="history-item" data-keywords="{{item}}" bindtap="go_to_search_result">{{item}}</view>
</block>
</block>
</view>
</view>
<view class="hot">
<view class="hot-title">热门搜索</view>
<view class="hot-content">
<block wx:if="{{index_data.hot != false}}">
<block wx:for="{{index_data.hot}}" wx:key="id">
<view class="hot-item" data-keywords="{{item}}" bindtap="go_to_search_result">{{item}}</view>
</block>
</block>
</view>
</view>
搜索页面业务逻辑
import { initNoPage } from "../../common/requestData";
import Storage from "../../common/auth/Storage";
import tips from "../../common/tips";
import request from "../../common/request";
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
index_data: [],
keywords: ''
},
// 清理
clearSearchHistory: function () {
tips.showConfirm("您确认清理搜索历史吗").then(() => {
const uid = app.globalData.uid || Storage.get().uid;
request("clearData", { uid: uid })
.then(() => {
tips.showMsg("清理成功");
this.setData({
"index_data.history": []
});
})
.catch(({ errdesc }) => {
return tips.showMsg(errdesc);
});
});
},
// 监听输入
watchSearch: function (event) {
console.log(event.detail.value);
let keywords = event.detail.value;
// 设置值
this.setData({
"keywords": keywords
});
},
go_to_search_result({
currentTarget: {
dataset: { keywords }
}
}) {
console.log(keywords);
if (keywords == '') {
return tips.showMsg("请输入要搜索的内容");
}
wx.navigateTo({
url: "/pages/search/result/index?keywords=" + keywords
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 展示
*/
onShow: function (options) {
const uid = app.globalData.uid || Storage.get().uid;
console.log(uid);
// 初始化
initNoPage(this, [
{
api: "getHotAndHistory",
outDataName: "index_data",
inData: { 'uid': uid }
}
]);
setTimeout(() => {
this.setData({
wo_title: app.globalData.wo_title
});
}, 300);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 用户点击右上角分享
*/
onShareAppMessage() { },
});
搜索页面样式
.search {
height: 124rpx;
background-color: #F7F7F7;
display: flex;
justify-content: center;
.search-container {
margin-top: 30rpx;
height: 64rpx;
// border:1px solid red;
margin-left:22rpx;
margin-right:33rpx;
width: 695rpx;
display: flex;
.search-left {
display: flex;
background-color: #FFFFFF;
border-radius: 32rpx;
.search-input {
// border:1px solid blue;
height: 64rpx;
line-height: 64rpx;
font-size: 28rpx;
width: 500rpx;
}
.search-image {
width:34rpx;
height: 34rpx;
margin-left: 30rpx;
margin-top:15rpx;
margin-right: 15rpx;
}
}
.search-btn {
font-size: 32rpx;
line-height: 64rpx;
font-weight: bold;
color:#313131;
text-align: center;
margin-left:30rpx;
}
}
}
.history {
margin:0 auto;
width: 710rpx;
margin-top:30rpx;
.history-title {
display: flex;
justify-content: space-between;
.history-title-left {
font-size: 28rpx;
color:#313131;
}
.history-title-right {
font-size: 28rpx;
color:#AAAAAA;
}
margin-bottom: 15rpx;
}
.history-content {
display: flex;
flex-wrap: wrap;
overflow: hidden;
.history-item {
margin-top:15rpx;
margin-left:10rpx;
margin-right:10rpx;
padding-left:20rpx;
padding-right: 20rpx;
height: 48rpx;
background-color: #eeeeee;
border-radius: 24rpx;
font-size: 24rpx;
line-height: 48rpx;
}
}
}
.hot {
margin:0 auto;
width: 710rpx;
margin-top:50rpx;
.hot-title {
display: flex;
justify-content: space-between;
font-size: 28rpx;
color:#313131;
margin-bottom: 15rpx;
}
.hot-content {
display: flex;
flex-wrap: wrap;
overflow: hidden;
.hot-item {
margin-top:15rpx;
margin-left:10rpx;
margin-right:10rpx;
padding-left:20rpx;
padding-right: 20rpx;
height: 48rpx;
background-color: #eeeeee;
border-radius: 24rpx;
font-size: 24rpx;
line-height: 48rpx;
}
}
}
搜索结果页面
<view class="search">
<view class="search-container">
<view class="search-left">
<image class="search-image" src="/images/index/search.png" />
<input class="search-input" placeholder="请输入你喜欢的商品" value="{{keywords}}" bindinput='watchSearch' />
</view>
<view class="search-btn" data-keywords="{{keywords}}" bindtap="go_to_search_result">搜索</view>
</view>
</view>
<block wx:if="{{noData == 1}}">
<view class="no-data">
<view class="img">
<image class="search-image" src="/images/search/nodata.png" />
</view>
<view class="msg">什么也没搜到哦</view>
</view>
</block>
<block wx:else>
<view class="data-list">
<block wx:for="{{searchResult}}" wx:key="id">
<view class="data-item">
<view class="item-img">
<image src="{{item.title_img}}" />
</view>
<view class="item-content">
<view class="item-content-title">{{item.title}}</view>
<view class="item-content-property">
<view class="property-item">销量:{{item.sales_count}}</view>
<!-- <view class="property-item">库存:1970</view> -->
</view>
<view class="item-content-price">¥{{item.price}}</view>
<view class="item-content-add">
<image src="/images/common/buy_more.png" catch:tap="openTypeSelect" data-id="{{item.id}}" lazy-load="{{true}}"/>
</view>
</view>
</view>
</block>
<block wx:if="{{noMore==1}}">
<view class="no-more">无更多结果</view>
</block>
</view>
</block>
<goodsType id="goodsType" />
搜索结果页逻辑
import { initInPage } from "../../../common/requestData";
import Storage from "../../../common/auth/Storage";
import tips from "../../../common/tips";
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
index_data: [],
keywords: ''
},
openTypeSelect({
currentTarget: {
dataset: { id }
}
}) {
this.selectComponent("#goodsType").openTypeSelect(id);
},
// 监听输入
watchSearch: function (event) {
console.log(event.detail.value);
let keywords = event.detail.value;
// 设置值
this.setData({
"keywords": keywords
});
},
go_to_search_result({
currentTarget: {
dataset: { keywords }
}
}) {
console.log(keywords);
if (keywords == '') {
return tips.showMsg("请输入要搜索的内容");
}
const uid = app.globalData.uid || Storage.get().uid;
console.log(uid);
// 初始化
initInPage(
this,
"searchData",
{ 'uid': uid, 'keywords': keywords, page: 1, page_size: 5 },
{ inDataName: "inData", outDataName: "searchResult" }
);
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
// 能获取到参数
console.log(options.keywords);
// 设置值
this.setData({
"keywords": options.keywords
});
},
/**
* 展示
*/
onShow: function (options) {
// 获取不到参数
let keywords = this.data.keywords;
const uid = app.globalData.uid || Storage.get().uid;
console.log(uid);
// 初始化
initInPage(
this,
"searchData",
{ 'uid': uid, 'keywords': keywords, page: 1, page_size: 5 },
{ inDataName: "inData", outDataName: "searchResult" }
);
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 用户点击右上角分享
*/
onShareAppMessage() { },
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
return initInPage(this, "searchData", this.inData, { inDataName: "inData", outDataName: "searchResult" });
},
});
搜索结果页样式
.search {
height: 124rpx;
background-color: #F7F7F7;
display: flex;
justify-content: center;
.search-container {
margin-top: 30rpx;
height: 64rpx;
// border:1px solid red;
margin-left:22rpx;
margin-right:33rpx;
width: 695rpx;
display: flex;
.search-left {
display: flex;
background-color: #FFFFFF;
border-radius: 32rpx;
.search-input {
// border:1px solid blue;
height: 64rpx;
line-height: 64rpx;
font-size: 28rpx;
width: 500rpx;
}
.search-image {
width:34rpx;
height: 34rpx;
margin-left: 30rpx;
margin-top:15rpx;
margin-right: 15rpx;
}
}
.search-btn {
font-size: 32rpx;
line-height: 64rpx;
font-weight: bold;
color:#313131;
text-align: center;
margin-left:30rpx;
}
}
}
.data-list {
margin-top:16rpx;
overflow: hidden;
.data-item {
height: 240rpx;
border-bottom: 1rpx solid #E2E2E2;
padding:30rpx 20rpx;
display: flex;
.item-img {
width: 180rpx;
height: 180rpx;
// border: 1rpx solid red;
image {
width: 180rpx;
height: 180rpx;
}
}
.item-content {
margin-left:20rpx;
width: 510rpx;
height: 180rpx;
// border:1rpx solid blue;
position: relative;
.item-content-title {
font-size:26rpx;
color:#313131;
}
.item-content-property {
margin-top:20rpx;
font-size: 24rpx;
height:24rpx; /* 防止上下空隙 */
line-height:24rpx;
color:#AAAAAA;
display: flex;
.property-item:nth-child(n+2) {
margin-left:20rpx;
}
}
.item-content-price {
margin-top:46rpx;
font-size:32rpx;
font-weight: bold;
color:#F6001F;
height:32rpx;
line-height:32rpx;
}
.item-content-add {
position: absolute;
right: 0rpx;
bottom: 0rpx;
line-height: 46rpx;
font-size: 46rpx;
height: 46rpx;
color: #F6001F;
image {
width: 46rpx;
height: 46rpx;
}
}
}
}
.no-more {
text-align: center;
color:#E2E2E2;
font-size: 32rpx;
font-weight: bold;
margin-top:30rpx;
}
}
.no-data {
// display: none; /* 暂时先隐藏 */
margin:0 auto;
margin-top:163rpx;
height: 420rpx;
// border:1px solid red;
.img {
width: 350rpx;
height: 313rpx;
// border:1px solid blue;
margin:0 auto;
image {
width: 350rpx;
height: 313rpx;
}
}
.msg {
text-align: center;
font-size: 32rpx;
color:#BFBFBF;
margin-top:69rpx;
}
}