// 门店数据处理工具:字段归一化、图片解析、定位与距离计算 // 后端 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 }) }