Hướng dẫn Implement Component Tìm kiếm và Chọn lọc Tùy chỉnh trên WeChat Mini Program

Tổng quan chức năng

Component này hỗ trợ người dùng nhập từ khóa vào ô text, hệ thống sẽ tự động lọc danh sách phù hợp theo thời gian thực. Khi người dùng chọn một mục từ danh sách gợi ý, giá trị sẽ được cập nhật vào ô nhập liệu và gửi sự kiện về trang cha.

Các bước tích hợp

Để sử dụng component này trong dự án, hãy thực hiện theo quy trình sau:

  1. Sao chép thư mục component vào directory chứa các thành phần chung của project.
  2. Khai báo component trong file cấu hình JSON của page cần sử dụng.
  3. Chèn tag component vào file WXML và binding dữ liệu tương ứng.
  4. Xử lý sự kiện trả về trong file JS của page.

Ví dụ minh họa

1. Cấu hình JSON của Page:

{
  "usingComponents": {
    "smart-picker": "/components/smart-picker/smart-picker"
  }
}

2. Sử dụng trong WXML:

<smart-picker 
  listSource="{{ clinicList }}" 
  presetValue="{{ currentClinic }}" 
  hintText="Tìm kiếm phòng khám" 
  bind:onChoose="handleClinicPick">
</smart-picker>

3. Xử lý logic trong JS:

Page({
  data: {
    clinicList: [
      { id: 101, label: 'Phòng khám A' },
      { id: 102, label: 'Phòng khám B' },
      { id: 103, label: 'Phòng khám C' }
    ],
    currentClinic: ''
  },
  handleClinicPick(event) {
    const item = event.detail.item;
    this.setData({
      currentClinic: item.label
    });
  }
})

Chi tiết Implementation

File logic (smart-picker.js)

Component({
  properties: {
    listSource: {
      type: Array,
      value: []
    },
    presetValue: {
      type: String,
      value: ''
    },
    hintText: {
      type: String,
      value: 'Nhập để tìm...'
    }
  },
  data: {
    dropdownActive: false,
    matchList: [],
    searchTerm: '',
    noResult: false,
    searchTimer: null
  },
  observers: {
    listSource(newList) {
      this.setData({ matchList: newList });
    },
    presetValue(val) {
      this.setData({ searchTerm: val });
    }
  },
  methods: {
    activateDropdown() {
      this.setData({ dropdownActive: true });
      this.queryList(this.data.searchTerm);
    },
    onTextChange(e) {
      const val = e.detail.value;
      if (this.data.searchTimer) {
        clearTimeout(this.data.searchTimer);
      }
      this.data.searchTimer = setTimeout(() => {
        this.queryList(val);
      }, 400);
    },
    queryList(keyword) {
      if (!keyword) {
        this.setData({ matchList: [], noResult: true });
        return;
      }
      const results = this.data.listSource.filter(item => {
        return item.label.indexOf(keyword) > -1;
      });
      this.setData({
        searchTerm: keyword,
        matchList: results,
        noResult: results.length === 0
      });
    },
    closeDropdown() {
      this.setData({ dropdownActive: false });
    },
    confirmSelection(e) {
      const item = e.currentTarget.dataset.item;
      this.setData({
        searchTerm: item.label,
        dropdownActive: false,
        noResult: false
      });
      this.triggerEvent('onChoose', { item: item });
    }
  }
});

File cấu hình (smart-picker.json)

{
  "component": true,
  "usingComponents": {},
  "styleIsolation": "isolated"
}

File giao diện (smart-picker.wxml)

<view class="smart-picker">
  <view class="input-wrapper">
    <input
      class="search-field"
      type="text"
      value="{{ searchTerm }}"
      placeholder="{{ hintText }}"
      bindfocus="activateDropdown"
      bindblur="closeDropdown"
      bindinput="onTextChange"
      readonly="{{ !dropdownActive }}"
    />
    <view class="arrow-icon {{ dropdownActive ? 'active' : '' }}"></view>
  </view>
  <view class="result-container" wx:if="{{ dropdownActive }}">
    <view class="result-list">
      <view
        class="list-item {{ searchTerm == item.label ? 'highlight' : '' }}"
        wx:for="{{ matchList }}"
        wx:key="id"
        data-item="{{ item }}"
        catchtap="confirmSelection"
      >
        {{ item.label }}
      </view>
      <view class="empty-msg" wx:if="{{ noResult }}">Không tìm thấy kết quả</view>
    </view>
  </view>
</view>

File style (smart-picker.wxss)

.search-field {
  flex: 1;
  border: 0;
  outline: 0;
  z-index: 100;
}

.smart-picker {
  position: relative;
}

.input-wrapper {
  display: flex;
  align-items: center;
  border-bottom: 1rpx solid #e0e0e0;
  padding: 18rpx 24rpx;
}

.result-container {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  min-height: 100vh;
  z-index: 20;
}

.result-list {
  top: 100%;
  left: 0;
  right: 0;
  box-sizing: border-box;
  border-top: none;
  max-height: 280px;
  overflow-y: auto;
  z-index: 20;
  border-radius: 8rpx;
  padding: 10rpx 16rpx 8rpx 16rpx;
  box-shadow: 0rpx 0rpx 2rpx 1rpx rgba(0, 0, 0, 0.15) inset;
  background-color: #ffffff;
  margin: 0 24rpx;
}

.list-item {
  padding: 18rpx 8rpx;
  cursor: pointer;
  border-bottom: 1rpx solid #f5f5f5;
}

.list-item.highlight {
  background-color: #e8e8e8;
}

.list-item:last-child {
  border-bottom: none;
}

.empty-msg {
  padding: 12px;
  text-align: center;
  color: #aaa;
}

.arrow-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  right: 24rpx;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border: 8rpx solid transparent;
  border-top: 8rpx solid #888;
  transition: transform 0.2s ease;
}

.arrow-icon.active::after {
  transform: translateY(-50%) rotate(180deg);
}

Thẻ: wechat-miniprogram custom-component JavaScript ui-patterns frontend-development

Đăng vào ngày 26 tháng 9 lúc 07:32