Sử Dụng Dịch Vụ Định Vị Tencent trong Mini Program WeChat

Để tích hợp dịch vụ định vị Tencent (Tencent Location Based Service - LBS) vào ứng dụng Mini Program WeChat, bạn cần thực hiện một số bước cấu hình và sử dụng SDK JavaScript cung cấp bởi Tencent. Hướng dẫn này sẽ đi qua các bước cài đặt cần thiết và cung cấp ví dụ code minh họa.

Các Bước Chuẩn Bị

  1. Đăng ký khóa API (Key): Truy cập trang quản lý dịch vụ định vị Tencent để đăng ký tài khoản và tạo một khóa API mới cho ứng dụng của bạn.
  2. Kích hoạt Dịch vụ WebService API: Sau khi có khóa API, điều hướng đến phần quản lý khóa trong bảng điều khiển. Tại đây, bạn cần tìm khóa vừa tạo, chọn cài đặt và đảm bảo rằng tùy chọn "WebServiceAPI" đã được chọn và lưu lại. Mini Program SDK yêu cầu quyền truy cập vào một số dịch vụ WebService API.
  3. Tải và Nhúng SDK JavaScript: Tải phiên bản SDK JavaScript của Tencent LBS dành cho Mini Program. Có thể tìm thấy các phiên bản như v1.1 hoặc v1.2 trên trang tài liệu chính thức của Tencent LBS. Sau khi tải về, đặt file qqmap-wx-jssdk.min.js vào thư mục libs hoặc một thư mục tiện ích khác trong dự án của bạn.
  4. Cấu hình Domain Hợp lệ: Trong trang quản lý Mini Program WeChat (mục Phát triển > Quản lý phát triển > Cài đặt phát triển > "Miền máy chủ"), thêm https://apis.map.qq.com vào danh sách các domain yêu cầu hợp lệ (request合法域名).

Để biết thêm chi tiết, bạn có thể tham khảo tài liệu chính thức của Tencent LBS: https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview

Cấu Hình Khóa API Trong Ứng Dụng

Khóa API của Tencent LBS cần được lưu trữ và truy cập toàn cục trong ứng dụng Mini Program. Dưới đây là ví dụ về cách cấu hình trong file app.js:

// app.js
App({
  globalData: {
    // ... các dữ liệu toàn cục khác
    tencentMapKey: 'MDIBZ-OZ6KU-REJV2-B5G46-HUJY7-IHFXS', // Thay thế bằng khóa API của bạn
    // ...
  },
  onLaunch: function () {
    // ...
  }
});

Tạo Module Xử Lý Vị Trí

Để quản lý các tác vụ liên quan đến dịch vụ định vị một cách có tổ chức, bạn nên tạo một module JavaScript riêng biệt (ví dụ: utils/locationService.js) để đóng gói các hàm tương tác với SDK của Tencent LBS.

// utils/locationService.js

// Nhập SDK Tencent LBS
const QqMapSDK = require('../libs/qqmap-wx-jssdk.min.js');
// Dành cho async/await trong các môi trường cũ hơn
const { regeneratorRuntime } = global; 

// Khởi tạo đối tượng SDK với khóa API từ globalData
const tencentMapClient = new QqMapSDK({
  key: getApp().globalData.tencentMapKey // Bắt buộc
});

// Hàm tiện ích để hiển thị thông báo lỗi
function showErrorMessage(message) {
  wx.showToast({
    title: message,
    icon: 'none',
    duration: 2000
  });
}

/**
 * Lấy thông tin địa điểm (địa chỉ, POI) từ tọa độ (kinh độ, vĩ độ).
 * @param {object} coords - Đối tượng chứa latitude và longitude.
 * @returns {Promise<object>} Kết quả chi tiết địa điểm.
 */
function layDiaChiTuToaDo(coords) {
  return new Promise((resolve, reject) => {
    tencentMapClient.reverseGeocoder({
      location: {
        latitude: coords.latitude,
        longitude: coords.longitude
      },
      get_poi: 1, // Lấy thông tin POI
      poi_options: 'policy=2;radius=2000;page_size=20;page_index=1',
      success: (response) => {
        if (response.status === 0) {
          resolve(response.result);
        } else {
          showErrorMessage('Lỗi máy chủ khi lấy địa chỉ: ' + response.message);
          reject(new Error('Lỗi dịch vụ Tencent Map: ' + response.message));
        }
      },
      fail: (error) => {
        showErrorMessage('Không thể kết nối đến dịch vụ định vị.');
        reject(error);
      }
    });
  });
}

/**
 * Chuyển đổi địa chỉ thành tọa độ (kinh độ, vĩ độ).
 * @param {string} addressStr - Chuỗi địa chỉ cần tìm.
 * @returns {Promise<object>} Tọa độ (location) và các thông tin khác.
 */
function timToaDoTuDiaChi(addressStr) {
  return new Promise((resolve, reject) => {
    tencentMapClient.geocoder({
      address: addressStr,
      success: (response) => {
        if (response.status === 0) {
          resolve(response.result);
        } else {
          showErrorMessage('Lỗi máy chủ khi tìm tọa độ: ' + response.message);
          reject(new Error('Lỗi dịch vụ Tencent Map: ' + response.message));
        }
      },
      fail: (error) => {
        showErrorMessage('Không thể kết nối đến dịch vụ định vị.');
        reject(error);
      }
    });
  });
}

/**
 * Tính toán khoảng cách giữa một điểm gốc và nhiều điểm đích.
 * @param {object} fromPoint - Điểm gốc {latitude, longitude}.
 * @param {Array<object>} toPoints - Mảng các điểm đích [{latitude, longitude}, ...].
 * @param {string} mode - Chế độ tính toán ('walking', 'driving', 'straight'). Mặc định là 'straight'.
 * @returns {Promise<Array>} Mảng các đối tượng khoảng cách.
 */
function tinhKhoangCachGiuaCacDiem(fromPoint, toPoints, mode = 'straight') {
  return new Promise((resolve, reject) => {
    tencentMapClient.calculateDistance({
      mode: mode,
      from: fromPoint,
      to: toPoints,
      success: (response) => {
        if (response.status === 0) {
          resolve(response.result.elements);
        } else {
          resolve([]); // Trả về mảng rỗng nếu có lỗi hoặc không có kết quả
        }
      },
      fail: (error) => {
        showErrorMessage('Không thể tính toán khoảng cách.');
        reject(error);
      }
    });
  });
}

/**
 * Lập kế hoạch lộ trình và tính khoảng cách (ví dụ: đi xe đạp).
 * @param {object} origin - Điểm bắt đầu {latitude, longitude}.
 * @param {object} destination - Điểm kết thúc {latitude, longitude}.
 * @param {string} transportMode - Chế độ di chuyển ('bicycling', 'driving', 'walking', etc.). Mặc định 'bicycling'.
 * @returns {Promise<number|boolean>} Khoảng cách lộ trình hoặc false nếu thất bại.
 */
function tinhLoTrinhVaKhoangCach(origin, destination, transportMode = 'bicycling') {
  return new Promise((resolve, reject) => {
    tencentMapClient.direction({
      mode: transportMode,
      from: origin,
      to: destination,
      success: (response) => {
        if (response.status === 0 && response.result.routes.length > 0) {
          resolve(response.result.routes[0].distance);
        } else {
          resolve(false);
        }
      },
      fail: (error) => {
        // showErrorMessage('Không thể tính toán lộ trình.');
        reject(new Error(error.message));
      }
    });
  });
}

/**
 * Lấy gợi ý địa điểm dựa trên từ khóa và khu vực.
 * @param {string} keyword - Từ khóa tìm kiếm.
 * @param {string} [city=''] - Thành phố để giới hạn tìm kiếm.
 * @returns {Promise<Array>} Mảng các gợi ý địa điểm.
 */
function goiYDiaDiem(keyword, city = '') {
  return new Promise((resolve, reject) => {
    tencentMapClient.getSuggestion({
      keyword: keyword,
      region: city,
      policy: 1, // Chính sách tìm kiếm
      region_fix: 1, // Ưu tiên khu vực được chỉ định
      success: (response) => {
        if (response.status === 0) {
          resolve(response.data);
        } else {
          resolve([]); // Trả về mảng rỗng nếu có lỗi hoặc không có kết quả
        }
      },
      fail: (error) => {
        showErrorMessage('Không thể lấy gợi ý địa điểm.');
        reject(error);
      }
    });
  });
}

module.exports = {
  layDiaChiTuToaDo,
  timToaDoTuDiaChi,
  tinhKhoangCachGiuaCacDiem,
  tinhLoTrinhVaKhoangCach,
  goiYDiaDiem
};

API Vị Trí Gốc của WeChat

Để lấy tọa độ hiện tại của người dùng, bạn sẽ sử dụng API gốc của WeChat wx.getLocation. Hàm này cũng nên được đóng gói vào một module tiện ích (ví dụ: utils/helpers.js) để dễ dàng quản lý.

// utils/helpers.js

/**
 * Lấy tọa độ hiện tại của người dùng.
 * @returns {Promise<object>} Đối tượng chứa latitude, longitude, và các thông tin vị trí khác.
 */
function layToaDoHienTai() {
  return new Promise((resolve) => {
    wx.getLocation({
      type: 'gcj02', // Hệ tọa độ được mã hóa, phù hợp với wx.openLocation
      success: (res) => {
        resolve(res);
      },
      fail: (err) => {
        resolve(err); // Trả về lỗi để xử lý ngoài
      }
    });
  });
}

module.exports = {
  layToaDoHienTai
};

Tham khảo thêm về wx.getLocation tại: https://developers.weixin.qq.com/miniprogram/dev/api/location/wx.getLocation.html

Sử Dụng Dịch Vụ Vị Trí Trong Trang

Dưới đây là ví dụ về cách một trang (page) trong Mini Program có thể sử dụng các hàm định vị trên để lấy và hiển thị thông tin địa điểm của người dùng.

// pages/home/home.js
const app = getApp();
const locationHelpers = require('../../utils/helpers.js');
const tencentLocationService = require('../../utils/locationService.js');

Page({
  data: {
    diaChiHienTaiNguoiDung: '',
    daChonDiaChiKhac: false, // Cờ để kiểm tra người dùng đã chọn địa chỉ khác chưa
  },

  /**
   * Kiểm tra và yêu cầu quyền truy cập vị trí từ người dùng.
   */
  kiemTraVaYeuCauQuyenViTri() {
    wx.getSetting({
      success: (res) => {
        if (!res.authSetting['scope.userLocation']) {
          wx.authorize({
            scope: 'scope.userLocation',
            success() {
              console.log('Người dùng đã cấp quyền vị trí.');
            },
            fail() {
              wx.showModal({
                title: 'Cần Quyền Vị Trí',
                content: 'Ứng dụng yêu cầu quyền truy cập vị trí để cung cấp dịch vụ tốt hơn. Vui lòng bật trong cài đặt.',
                showCancel: false,
                success: (dialogRes) => {
                  if (dialogRes.confirm) {
                    wx.openSetting({
                      success: (settingRes) => {
                        console.log('Cài đặt quyền vị trí:', settingRes.authSetting);
                      }
                    });
                  }
                }
              });
            }
          });
        }
      }
    });
  },

  /**
   * Lấy và cập nhật địa chỉ hiện tại của người dùng.
   */
  async capNhatDiaChiNguoiDung() {
    try {
      this.kiemTraVaYeuCauQuyenViTri(); // Đảm bảo quyền được kiểm tra trước
      let currentCoords = await locationHelpers.layToaDoHienTai();

      if (currentCoords.latitude && currentCoords.longitude) {
        wx.setStorageSync('userCoordinates', { // Lưu trữ tọa độ
          latitude: currentCoords.latitude,
          longitude: currentCoords.longitude
        });

        // Sử dụng dịch vụ định vị Tencent để chuyển đổi tọa độ thành địa chỉ
        let locationDetails = await <strong>tencentLocationService.layDiaChiTuToaDo</strong>(currentCoords);
        
        wx.setStorageSync('currentProvince', locationDetails.ad_info.province);
        wx.setStorageSync('currentCity', locationDetails.ad_info.city);
        app.globalData.userCity = locationDetails.ad_info.city;
        app.globalData.currentUserLocationDetails = locationDetails;

        this.setData({
          diaChiHienTaiNguoiDung: locationDetails.formatted_addresses.recommend,
        });

        // Kích hoạt hàm lấy dữ liệu khác dựa trên vị trí nếu cần
        this.loadPageData(); 
      } else {
        wx.showModal({
          title: 'Định Vị Thất Bại',
          content: 'Không thể định vị được vị trí của bạn. Vui lòng kiểm tra kết nối mạng và quyền định vị.',
          cancelText: 'Chọn thủ công',
          confirmText: 'Thử lại',
          success: (dialogRes) => {
            if (dialogRes.confirm) {
              this.capNhatDiaChiNguoiDung(); // Thử lại
            } else if (dialogRes.cancel) {
              wx.navigateTo({
                url: '/pages/address/search/search' // Chuyển đến trang tìm kiếm địa chỉ
              });
            }
          }
        });
      }
    } catch (error) {
      console.error('Lỗi khi cập nhật địa chỉ:', error);
      wx.showToast({ title: 'Đã xảy ra lỗi!', icon: 'none' });
    }
  },

  // Hàm giả định để tải dữ liệu trang
  loadPageData() {
    console.log('Tải dữ liệu trang dựa trên vị trí...');
    // Ví dụ: Gọi API để lấy danh sách cửa hàng gần nhất
  },

  onShow: function (options) {
    let globalLocInfo = app.globalData.currentUserLocationDetails;
    const { daChonDiaChiKhac } = this.data;

    // Kiểm tra xem người dùng đã chọn địa chỉ khác từ trang tìm kiếm chưa
    if (options && options.cachedAddress) {
      this.setData({ daChonDiaChiKhac: true });
    } else {
      wx.removeStorageSync('selectedManualAddress'); // Xóa địa chỉ chọn thủ công
      this.setData({ daChonDiaChiKhac: false });
    }
    
    // Nếu chưa có thông tin vị trí toàn cục hoặc địa chỉ hiển thị trống, thì cập nhật vị trí
    if (!globalLocInfo || !this.data.diaChiHienTaiNguoiDung || !daChonDiaChiKhac) {
      this.capNhatDiaChiNguoiDung();
    }
  },
});

Hiển Thị Trên Giao Diện (WXML)

Để hiển thị địa chỉ đã lấy được trên giao diện người dùng, bạn có thể sử dụng một phần tử điều hướng như sau:

<!-- pages/home/home.wxml -->
<navigator class="location-display-area" url="/pages/address/search/search" hover-class="none">
  <image class="icon-location" src="/assets/images/icon_map.png" mode="widthFix" />
  <text class="address-text single-line-ellipsis">{{diaChiHienTaiNguoiDung}}</text>
  <view class="arrow-indicator"></view>
</navigator>

Trong ví dụ trên:

  • /pages/address/search/search là đường dẫn tới trang tìm kiếm địa chỉ, cho phép người dùng chọn địa chỉ thủ công.
  • diaChiHienTaiNguoiDung là biến trong data của trang để hiển thị địa chỉ đã được định vị.

Thẻ: WeChat Mini Program Tencent LBS Geolocation API JavaScript SDK Mapping Services

Đăng vào ngày 22 tháng 8 lúc 14:15