findme-miniprogram-frontend/utils/map-feed-helpers.js
2025-12-27 17:16:03 +08:00

164 lines
4.6 KiB
JavaScript

const DEFAULT_THUMBNAILS = {
image: '/images/placeholder.svg',
video: 'https://res.wx.qq.com/a/wx_fed/weui-design/assets/component/picker/image-default.png',
mixed: '/images/placeholder.svg'
};
const MAP_FEED_MARKER_STYLE = {
single: {
background: '#f02d05',
color: '#FFFFFF',
icon: 'https://res.wx.qq.com/a/wx_fed/weui-design/assets/icon_nav/icon_nav_location_blue.png'
},
cluster: {
background: '#BB10BB',
color: '#FFFFFF',
icon: 'https://res.wx.qq.com/a/wx_fed/weui-design/assets/icon_nav/icon_nav_discover.png'
},
administrative: {
background: '#f0500a',
color: '#FFFFFF',
icon: 'https://res.wx.qq.com/a/wx_fed/weui-design/assets/icon_nav/icon_nav_map.png'
},
stranger: {
background: '#f0500a',
color: '#FFFFFF',
icon: 'https://res.wx.qq.com/a/wx_fed/weui-design/assets/icon_nav/icon_nav_map.png'
},
friend: {
background: '#44a186',
color: '#FFFFFF',
icon: 'https://res.wx.qq.com/a/wx_fed/weui-design/assets/icon_nav/icon_nav_map.png'
}
};
const MAP_FEED_SCALE_BUCKETS = [
{ mode: 'nearby', level: 'street', minScale: 11 },
{ mode: 'administrative', level: 'district', minScale: 9 },
{ mode: 'administrative', level: 'city', minScale: 7 },
{ mode: 'administrative', level: 'province', minScale: 5 },
{ mode: 'administrative', level: 'country', minScale: 0 }
];
const MAP_FEED_DEBOUNCE = 400;
const MAP_FEED_CACHE_TTL = 3 * 60 * 1000;
const MAP_FEED_BUBBLE_LIMIT = 5;
function getFeedScalePreset(scale) {
if (typeof scale !== 'number') {
return MAP_FEED_SCALE_BUCKETS[0];
}
return MAP_FEED_SCALE_BUCKETS.find((bucket) => scale >= bucket.minScale) || MAP_FEED_SCALE_BUCKETS[MAP_FEED_SCALE_BUCKETS.length - 1];
}
function pickCorner(bounds = {}, key) {
return bounds[key] || bounds[key?.toLowerCase?.()] || bounds[key?.replace?.(/([A-Z])/g, '_$1').toLowerCase?.()] || null;
}
function normalizeCorner(corner) {
if (!corner) {
return null;
}
if (corner.latitude !== undefined && corner.longitude !== undefined) {
return {
latitude: Number(corner.latitude),
longitude: Number(corner.longitude)
};
}
if (corner.lat !== undefined && corner.lng !== undefined) {
return {
latitude: Number(corner.lat),
longitude: Number(corner.lng)
};
}
return null;
}
function buildBoundsPayload(bounds) {
if (!bounds) {
return null;
}
const northEastRaw = pickCorner(bounds, 'northEast') || pickCorner(bounds, 'northeast');
const southWestRaw = pickCorner(bounds, 'southWest') || pickCorner(bounds, 'southwest');
const northEast = normalizeCorner(northEastRaw);
const southWest = normalizeCorner(southWestRaw);
if (!northEast || !southWest) {
return null;
}
if (Number.isNaN(northEast.latitude) || Number.isNaN(northEast.longitude) || Number.isNaN(southWest.latitude) || Number.isNaN(southWest.longitude)) {
return null;
}
return {
bounds: {
northEast,
southWest
}
};
}
function resolveFeedThumbnail(point = {}) {
let candidate = point.thumbnail || point.cover || point.preview || (Array.isArray(point.media) && point.media.length > 0 ? point.media[0]?.url : null);
if (candidate && typeof candidate === 'string') {
if (candidate.startsWith('/')) {
return candidate;
}
const lower = candidate.toLowerCase();
if (lower.includes('example.com') || lower.startsWith('http://example.com') || lower.includes('placeholder')) {
return DEFAULT_THUMBNAILS.mixed;
}
return candidate;
}
const typeKey = point.contentType || point.type || 'mixed';
return DEFAULT_THUMBNAILS[typeKey] || DEFAULT_THUMBNAILS.mixed;
}
function buildCacheKey({ mode, level }, payload = {}) {
const boundsKey = payload?.bounds
? [
payload.bounds.northEast?.latitude?.toFixed(4),
payload.bounds.northEast?.longitude?.toFixed(4),
payload.bounds.southWest?.latitude?.toFixed(4),
payload.bounds.southWest?.longitude?.toFixed(4)
].join(',')
: 'unknown';
const filterParts = [
payload?.timeFilter?.enabled ? payload.timeFilter.range : 'all',
Array.isArray(payload?.contentTypes) ? payload.contentTypes.sort().join('|') : 'all',
payload?.level || 'none'
];
return `${mode}:${level}:${boundsKey}:${filterParts.join(':')}`;
}
function isCacheFresh(entry, ttl = MAP_FEED_CACHE_TTL) {
if (!entry || !entry.timestamp) {
return false;
}
return Date.now() - entry.timestamp < ttl;
}
module.exports = {
DEFAULT_THUMBNAILS,
MAP_FEED_SCALE_BUCKETS,
MAP_FEED_DEBOUNCE,
MAP_FEED_CACHE_TTL,
MAP_FEED_BUBBLE_LIMIT,
MAP_FEED_MARKER_STYLE,
getFeedScalePreset,
buildBoundsPayload,
resolveFeedThumbnail,
buildCacheKey,
isCacheFresh
};