store.js 3.71 KB
// 门店数据处理工具:字段归一化、图片解析、定位与距离计算
// 后端 GetPickupStores 返回字段:id/name/city/address/phone/longitude/latitude/images/description/businessHours

/**
 * 解析门店图片字段(可能是 JSON 数组字符串或逗号分隔字符串)
 * @param {String} raw
 * @returns {String[]}
 */
export function parseStoreImages(raw) {
  if (!raw) return []
  if (Array.isArray(raw)) return raw.filter(Boolean)
  const str = String(raw).trim()
  if (!str) return []
  if (str[0] === '[') {
    try {
      const arr = JSON.parse(str)
      return Array.isArray(arr) ? arr.filter(Boolean) : []
    } catch (e) {
      // 落到逗号分隔解析
    }
  }
  return str.split(',').map(s => s.trim()).filter(Boolean)
}

/**
 * 归一化后端门店记录为 UI 友好结构
 * @param {Object} raw
 * @returns {Object}
 */
export function normalizeStore(raw) {
  const store = raw || {}
  const images = parseStoreImages(store.images)
  const lng = store.longitude !== undefined && store.longitude !== null && store.longitude !== '' ? Number(store.longitude) : null
  const lat = store.latitude !== undefined && store.latitude !== null && store.latitude !== '' ? Number(store.latitude) : null
  return {
    id: store.id,
    name: store.name || '门店',
    city: store.city || '',
    district: store.city || '',
    address: store.address || '',
    phone: store.phone || '',
    longitude: isNaN(lng) ? null : lng,
    latitude: isNaN(lat) ? null : lat,
    images,
    cover: images[0] || '',
    description: store.description || '',
    hours: store.businessHours || '',
    // 距离,稍后按用户定位填充
    distanceKm: null,
    distance: ''
  }
}

/**
 * 获取用户定位(gcj02),失败不抛错,返回 null
 * @returns {Promise<{latitude:number, longitude:number}|null>}
 */
export function getUserLocation() {
  return new Promise((resolve) => {
    uni.getLocation({
      type: 'gcj02',
      success: (res) => resolve({ latitude: res.latitude, longitude: res.longitude }),
      fail: () => resolve(null)
    })
  })
}

/**
 * 计算两点间距离(km,haversine)
 */
export function distanceKm(lat1, lng1, lat2, lng2) {
  if ([lat1, lng1, lat2, lng2].some(v => v === null || v === undefined || isNaN(v))) return null
  const toRad = (d) => (d * Math.PI) / 180
  const R = 6371
  const dLat = toRad(lat2 - lat1)
  const dLng = toRad(lng2 - lng1)
  const a = Math.sin(dLat / 2) ** 2 +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
  return R * c
}

/**
 * 距离文本格式化
 */
export function formatDistance(km) {
  if (km === null || km === undefined || isNaN(km)) return ''
  if (km < 1) return `${Math.round(km * 1000)}m`
  return `${km.toFixed(1)}km`
}

/**
 * 为门店列表填充距离;location 为空或门店无坐标则距离为空
 * @param {Object[]} list 归一化后的门店列表
 * @param {{latitude:number, longitude:number}|null} location
 */
export function attachDistance(list, location) {
  if (!Array.isArray(list)) return []
  list.forEach((s) => {
    if (location && s.latitude !== null && s.longitude !== null) {
      const km = distanceKm(location.latitude, location.longitude, s.latitude, s.longitude)
      s.distanceKm = km
      s.distance = formatDistance(km)
    } else {
      s.distanceKm = null
      s.distance = ''
    }
  })
  return list
}

/**
 * 按距离升序排序(无距离的排最后)
 */
export function sortByDistance(list) {
  return (list || []).slice().sort((a, b) => {
    const da = a.distanceKm
    const db = b.distanceKm
    if (da === null && db === null) return 0
    if (da === null) return 1
    if (db === null) return -1
    return da - db
  })
}