封装搜索组件

182 阅读1分钟

大致实现页面如下:

image.png

分析:

在这里其实热门搜索以及搜索历史共用的是同一个组件--card组件这里暂时起名为 分析不难发现这两部分皆是由头部和躯体两部分组成,只是显示的标题不同,内容不同。搜索记录头部比热门搜索多了一个“清除搜索记录”,那么这里完全可以利用插槽实现,其主体内容部分也可以由插槽实现,即实现头部和主体区域要展示什么皆由“我”自己决定

封装card组件: components/common/card/card.vue

<template>
  <view class="card" :style="cardStyle">
    <!-- 头部 -->
    <view v-if="showHead" class="p-2 main-border-color d-flex a-center j-sb" :class="getHeadClass">
      <slot name="title">
        <text v-if="headerTitle" class="font-md" :class="headerTitleWeight ? 'font-weight' : ''">{{headerTitle}}</text>
      </slot>
      <slot name="right"></slot>
    </view>
    <!-- 内容 -->
    <view :class="getBodyClass" :style="bodyStyle">
      <image v-if="bodyCover" :src="bodyCover" mode="widthFix" style="height: 300rpx;width: 750rpx;"></image>
      <slot name="body"></slot>
    </view>
  </view>
</template>

<script>
  export default {
    props: {
      // 头部标题
      headerTitle: String,
      // 封面图片
      bodyCover: String,
      // 是否显示头部
      showHead: {
        type:Boolean,
        default: true
      },
      // 头部标题是否加粗
      headerTitleWeight: {
        type: Boolean,
        default: true
      },
      //是否显示下边
      headBorderBottom: {
        type: Boolean,
        default: true
      },
      // 是否给body加padding
      bodyPadding: {
        type: Boolean,
        default: false
      },
      // body样式
      bodyStyle: String,
      cardStyle:{
      	type:String,
      	default:""
      }
    },
    name:"card",
    data() {
      return {
      };
    },
    computed: {
      getHeadClass() {
        let borderBottom = this.headBorderBottom ? 'border-bottom' : ''
        return `${borderBottom}`
      },
      getBodyClass(){
        let padding = this.bodyPadding ? 'p-2' : ''
        return `${padding}`
      },
    }
  }
</script>

<style lang="scss" scoped>
</style>

搜索实现 search.vue

<template>
  <view>
    <card headerTitle="热门搜索" bodyCover="/static/images/demo/search-banner.png"></card>

    <!-- 多色按钮 -->
    <view class="px-1 mb-2">
      <colorTag v-for="(item ,index) in hot" :key="item.id" :item="item" :color="true" />
    </view>

    <!-- 常用分类 -->
    <card headerTitle="常用分类" :bodyPadding="true" :headBorderBottom="false">
      <template slot="body">
        <colorTag v-for="(item,index) in cate" :key="index" :item="item" :color="false" />
      </template>
    </card>

    <!-- 搜索记录 -->
    <template v-if="historyList.length > 0">
      <!-- 分割线 -->
      <divider></divider>

      <!-- 搜索记录 -->
      <card headerTitle="搜索记录">
        <view class="font text-primary" slot="right" @click="clearHistory">
          清除搜索记录
        </view>
        <template slot="body">
          <uni-list>
            <uni-list-item v-for="(item,index) in historyList" :key="index" :title="item"></uni-list-item>
          </uni-list>
        </template>
      </card>
    </template>
  </view>
</template>

<script>
  import colorTag from '@/components/search/color-tag.vue';
  import card from '@/components/common/card/card.vue';

  export default {
    components: {
      colorTag,
      card,
    },
    data() {
      return {
        // 热门
        hot: [{
            name: "领劵中心",
            id: 1
          },
          {
            name: "Redmi K20",
            id: 2
          },
          {
            name: "RedmiBook 14",
            id: 3
          },
          {
            name: "智能积木,越野四驱车",
            id: 4
          },
          {
            name: "手环 腕带",
            id: 5
          }
        ],
        // 常用分类
        cate: [{
            name: '耳机',
            id: 1
          }, {
            name: '手机',
            id: 2
          },
          {
            name: '音箱',
            id: 3
          },
          {
            name: '手表',
            id: 4
          },
          {
            name: '配件',
            id: 5
          },
          {
            name: '网关/传感器',
            id: 6
          },
          {
            name: '健康',
            id: 7
          },
          {
            name: '酷玩',
            id: 8
          }
        ],
        // 搜索历史
        historyList: [],
        // 搜索关键字
        keyword: "",
      }
    },
    // 监听顶部搜索按钮
    onNavigationBarButtonTap(e) {
      if (e.index === 0) {
       this.search();
      }
    },
    // 监听原生搜索框的变化
    onNavigationBarSearchInputChanged(e) {
      this.keyword = e.text;
    },
    // 监听原生搜索框的提交事件
    onNavigationBarSearchInputConfirmed() {
      this.search();
    },
    onLoad() {
      // 加載搜索历史记录
      let history = uni.getStorageSync('searchHistory');
      this.historyList = history ? JSON.parse(history) : [];
    },
    methods: {
      // 搜索方法
      search() {
        if (this.keyword === '') {
          return uni.showToast({
            icon: 'none',
            title: '关键词不能为空'
          })
        }
        // 在手机上点击搜索的时候隐藏键盘
        // #ifdef APP-PLUS
        plus.key.hideSoftKeybord(); // APP端隐藏键盘
        // #endif
        // #ifndef APP-PLUS
        uni.hideKeyboard();
        // #endif

        // 搜索逻辑
        this.addHistory();
      },
      // 加入搜索记录的方法
      addHistory() {
        let index = this.historyList.indexOf(this.keyword);
        // 判断当前搜索的关键词是否已经存在
        if(index === -1){
          this.historyList.unshift(this.keyword);
        } else {
          // 若当前搜索的关键词已经存在,那么就将在搜索历史数组里的
          // 关键词删除掉,将当前的放在头部--置顶效果
          this.historyList.unshift(this.historyList.splice(index,1)[0]);
        }
        // 本地存储
        uni.setStorageSync('searchHistory', JSON.stringify(this.historyList));
      },
      // 清除搜索记录
      clearHistory() {
        uni.showModal({
          title: '提示',
          content: '是否要清除搜索历史',
          cancelText: '取消',
          confirmText: '清除',
          success: res => {
            if(res.confirm) {
              uni.clearStorageSync('searchHistory');
              this.historyList = [];
            }
          }
        })
      },
    }
  }
</script>

最终效果:

62.gif