鸿蒙OS&UniApp 开发的商品筛选器与排序功能#三方框架 #Uniapp

发布于:2025-05-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

使用 UniApp 开发的商品筛选器与排序功能

前言

在电商类移动应用中,商品筛选与排序功能是提升用户体验和转化率的关键。一个优秀的筛选器不仅能帮助用户快速定位心仪商品,还能提升页面性能和交互流畅度。随着鸿蒙(HarmonyOS)生态的壮大,越来越多的开发者选择 UniApp 进行多端开发。本文将以实际案例为基础,详细讲解如何用 UniApp 实现高效、易扩展的商品筛选与排序功能,并给出鸿蒙平台的适配建议。

一、需求与设计思路

1. 需求分析

  • 支持多条件筛选(如分类、价格区间、品牌等)
  • 支持多种排序方式(如价格、销量、上新等)
  • 筛选与排序结果实时反馈
  • 兼容鸿蒙平台,适配不同分辨率
  • 组件化设计,便于复用和扩展

2. 设计思路

  • 使用弹窗(popup)或侧边栏实现筛选面板
  • 通过 v-for 渲染筛选项,支持动态扩展
  • 排序采用顶部Tab或下拉菜单
  • 筛选与排序参数通过 props 和事件与父组件通信
  • 结合 CSS 动画提升交互体验

二、核心代码实现

1. 商品筛选与排序组件结构

<template>
  <view class="filter-bar">
    <view class="sort-tabs">
      <view
        v-for="(item, idx) in sortOptions"
        :key="item.value"
        :class="['sort-tab', { active: sortValue === item.value }]"
        @click="onSort(item.value)"
      >
        {{ item.label }}
      </view>
      <view class="filter-btn" @click="showFilter = true">筛选</view>
    </view>
    <view v-if="showFilter" class="filter-mask" @click="closeFilter"></view>
    <view v-if="showFilter" class="filter-panel">
      <view class="filter-section">
        <view class="filter-title">分类</view>
        <view class="filter-options">
          <view
            v-for="cat in categories"
            :key="cat.id"
            :class="['filter-option', { selected: filter.category === cat.id }]"
            @click="filter.category = cat.id"
          >
            {{ cat.name }}
          </view>
        </view>
      </view>
      <view class="filter-section">
        <view class="filter-title">价格区间</view>
        <input v-model="filter.minPrice" type="number" placeholder="最低价" class="price-input" />
        <input v-model="filter.maxPrice" type="number" placeholder="最高价" class="price-input" />
      </view>
      <view class="filter-section">
        <view class="filter-title">品牌</view>
        <view class="filter-options">
          <view
            v-for="brand in brands"
            :key="brand.id"
            :class="['filter-option', { selected: filter.brand === brand.id }]"
            @click="filter.brand = brand.id"
          >
            {{ brand.name }}
          </view>
        </view>
      </view>
      <view class="filter-actions">
        <button @click="resetFilter">重置</button>
        <button class="confirm" @click="applyFilter">确定</button>
      </view>
    </view>
  </view>
</template>

2. 脚本逻辑

<script>
export default {
  name: 'GoodsFilter',
  props: {
    sortOptions: { type: Array, default: () => ([
      { label: '综合', value: 'default' },
      { label: '价格', value: 'price' },
      { label: '销量', value: 'sales' },
      { label: '上新', value: 'new' },
    ]) },
    categories: { type: Array, default: () => [] },
    brands: { type: Array, default: () => [] },
  },
  data() {
    return {
      showFilter: false,
      sortValue: 'default',
      filter: {
        category: '',
        minPrice: '',
        maxPrice: '',
        brand: '',
      },
    };
  },
  methods: {
    onSort(val) {
      this.sortValue = val;
      this.$emit('sort-change', val);
    },
    closeFilter() {
      this.showFilter = false;
    },
    resetFilter() {
      this.filter = { category: '', minPrice: '', maxPrice: '', brand: '' };
    },
    applyFilter() {
      this.showFilter = false;
      this.$emit('filter-change', { ...this.filter });
    },
  },
};
</script>

3. 样式设计

<style scoped>
.filter-bar {
  background: #fff;
  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  position: sticky;
  top: 0;
  z-index: 10;
}
.sort-tabs {
  display: flex;
  align-items: center;
  padding: 0 24rpx;
  height: 88rpx;
  border-bottom: 1rpx solid #f0f0f0;
}
.sort-tab {
  margin-right: 32rpx;
  font-size: 28rpx;
  color: #666;
  padding: 0 8rpx;
  line-height: 88rpx;
}
.sort-tab.active {
  color: #007aff;
  font-weight: bold;
  border-bottom: 4rpx solid #007aff;
}
.filter-btn {
  margin-left: auto;
  color: #007aff;
  font-size: 28rpx;
}
.filter-mask {
  position: fixed;
  left: 0; top: 0; right: 0; bottom: 0;
  background: rgba(0,0,0,0.3);
  z-index: 99;
}
.filter-panel {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  background: #fff;
  border-top-left-radius: 24rpx;
  border-top-right-radius: 24rpx;
  z-index: 100;
  padding: 32rpx 24rpx 48rpx 24rpx;
  animation: slideUp 0.25s;
}
@keyframes slideUp {
  from { transform: translateY(100%); }
  to { transform: translateY(0); }
}
.filter-section {
  margin-bottom: 32rpx;
}
.filter-title {
  font-size: 28rpx;
  color: #333;
  margin-bottom: 16rpx;
}
.filter-options {
  display: flex;
  flex-wrap: wrap;
}
.filter-option {
  background: #f5f5f5;
  color: #666;
  border-radius: 8rpx;
  padding: 12rpx 28rpx;
  margin-right: 16rpx;
  margin-bottom: 12rpx;
  font-size: 26rpx;
}
.filter-option.selected {
  background: #007aff;
  color: #fff;
}
.price-input {
  width: 160rpx;
  margin-right: 16rpx;
  padding: 12rpx;
  border: 1rpx solid #eee;
  border-radius: 8rpx;
  font-size: 26rpx;
}
.filter-actions {
  display: flex;
  justify-content: space-between;
  margin-top: 24rpx;
}
button {
  flex: 1;
  margin: 0 8rpx;
  height: 72rpx;
  border-radius: 12rpx;
  font-size: 28rpx;
  background: #f5f5f5;
  color: #333;
  border: none;
}
button.confirm {
  background: #007aff;
  color: #fff;
}
</style>

三、父页面集成与使用示例

<template>
  <goods-filter
    :categories="categoryList"
    :brands="brandList"
    @sort-change="onSortChange"
    @filter-change="onFilterChange"
  />
  <view v-for="item in goodsList" :key="item.id" class="goods-item">
    <text>{{ item.name }}</text>
    <text>{{ item.price }}</text>
  </view>
</template>

<script>
import GoodsFilter from '@/components/GoodsFilter.vue';
export default {
  components: { GoodsFilter },
  data() {
    return {
      categoryList: [
        { id: '1', name: '手机' },
        { id: '2', name: '电脑' },
        { id: '3', name: '配件' },
      ],
      brandList: [
        { id: 'a', name: '华为' },
        { id: 'b', name: '荣耀' },
        { id: 'c', name: '小米' },
      ],
      goodsList: [],
      filterParams: {},
      sortValue: 'default',
    };
  },
  methods: {
    onSortChange(val) {
      this.sortValue = val;
      this.fetchGoods();
    },
    onFilterChange(params) {
      this.filterParams = params;
      this.fetchGoods();
    },
    async fetchGoods() {
      // 这里以模拟接口为例,实际可对接后端API
      const res = await uni.request({
        url: 'https://api.example.com/goods',
        data: { ...this.filterParams, sort: this.sortValue },
      });
      this.goodsList = res.data.list || [];
    },
  },
  mounted() {
    this.fetchGoods();
  },
};
</script>

<style scoped>
.goods-item {
  background: #fff;
  margin: 16rpx 24rpx;
  border-radius: 12rpx;
  padding: 32rpx 24rpx;
  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 28rpx;
}
</style>

四、鸿蒙平台适配与优化建议

  1. 分辨率适配:全程使用 rpx 单位,保证鸿蒙不同设备下的显示一致。
  2. 性能优化:筛选与排序建议本地处理,减少接口请求,提升鸿蒙设备流畅度。
  3. 动画与交互:鸿蒙设备对交互反馈要求高,建议筛选面板弹出/关闭使用 CSS3 动画。
  4. 安全区域适配:如有底部导航,注意 env(safe-area-inset-bottom)
  5. 无障碍适配:鸿蒙生态重视无障碍体验,建议为筛选项添加 aria-label。

五、实际应用案例

  • 电商App:商品列表支持多条件筛选与排序,提升转化率。
  • 二手交易App:支持按价格、发布时间、距离等多维度筛选。
  • 外卖App:餐厅、菜品多条件筛选,支持销量、评分等排序。

六、总结与展望

商品筛选与排序功能是电商类App的核心能力。通过 UniApp 的组件化和跨平台特性,我们可以高效实现兼容鸿蒙的高性能筛选与排序功能。未来还可结合智能推荐、个性化排序等进一步提升用户体验。希望本文的讲解和代码示例能为你的项目带来启发,欢迎留言交流更多鸿蒙适配经验!