小程序实现搜索效果

Posted 认真生活、快乐工作 - 马云

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小程序实现搜索效果相关的知识,希望对你有一定的参考价值。

首页入口

进入搜索页

搜索结果页

后台提供三个接口

<?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;
    }
}

以上是关于小程序实现搜索效果的主要内容,如果未能解决你的问题,请参考以下文章

小程序实现搜索效果

微信小程序使用uni-app——开发首页搜索框导航栏(可同时兼容APPH5小程序)

微信小程序代码片段

小程序根据索引滚动指定的位置

在业务代码中使用redis实现缓存效果

小程序实现顶部选中效果