- 新增地址类型定义,增强前端地址数据结构 - 新增地址编辑页面,支持地址智能识别和定位选点功能 - 地址编辑支持省市区选择及默认地址设置 - 新增地址列表页面,支持地址展示、删除、编辑和选择功能 - 实现售后申请页面,支持选择售后类型和退款原因 - 售后申请支持商品选择、退款金额计算和凭证上传 - 新增售后详情页面,支持售后状态展示及申请取消 - 优化页面加载和用户交互体验,增加错误提示和权限处理
132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
export type LngLat = { lng: number; lat: number }
|
|
|
|
function normalizeLngLat(a: number, b: number): LngLat | null {
|
|
if (!Number.isFinite(a) || !Number.isFinite(b)) return null
|
|
const looksLikeLngLat = Math.abs(a) <= 180 && Math.abs(b) <= 90
|
|
const looksLikeLatLng = Math.abs(a) <= 90 && Math.abs(b) <= 180
|
|
if (looksLikeLngLat) return { lng: a, lat: b }
|
|
if (looksLikeLatLng) return { lng: b, lat: a }
|
|
return null
|
|
}
|
|
|
|
function parsePointLike(v: any): LngLat | null {
|
|
if (!v) return null
|
|
if (Array.isArray(v) && v.length >= 2) {
|
|
return normalizeLngLat(Number(v[0]), Number(v[1]))
|
|
}
|
|
if (typeof v === 'object') {
|
|
const a = v.lng ?? v.lon ?? v.longitude ?? v.x
|
|
const b = v.lat ?? v.latitude ?? v.y
|
|
if (a !== undefined && b !== undefined) {
|
|
return normalizeLngLat(Number(a), Number(b))
|
|
}
|
|
}
|
|
if (typeof v === 'string') {
|
|
return parseLngLatFromText(v)
|
|
}
|
|
return null
|
|
}
|
|
|
|
export function parseLngLatFromText(raw: string | undefined): LngLat | null {
|
|
const text = (raw || '').trim()
|
|
if (!text) return null
|
|
const parts = text.split(/[,\s]+/).filter(Boolean)
|
|
if (parts.length < 2) return null
|
|
const a = parts[0]
|
|
const b = parts[1]
|
|
if (!a || !b) return null
|
|
return normalizeLngLat(parseFloat(a), parseFloat(b))
|
|
}
|
|
|
|
/**
|
|
* Parse fence "points" into a polygon point list.
|
|
*/
|
|
export function parseFencePoints(pointsRaw: string | undefined): LngLat[] {
|
|
const text = (pointsRaw || '').trim()
|
|
if (!text) return []
|
|
|
|
if (text.startsWith('[') || text.startsWith('{')) {
|
|
try {
|
|
const parsed = JSON.parse(text)
|
|
if (Array.isArray(parsed)) {
|
|
const list = parsed.map(parsePointLike).filter(Boolean) as LngLat[]
|
|
if (list.length) return list
|
|
if (Array.isArray(parsed[0])) {
|
|
const inner = (parsed[0] as any[]).map(parsePointLike).filter(Boolean) as LngLat[]
|
|
if (inner.length) return inner
|
|
}
|
|
}
|
|
} catch (_e) {
|
|
// fall through
|
|
}
|
|
}
|
|
|
|
const segments = text.split(/[;|\n\r]+/).map(s => s.trim()).filter(Boolean)
|
|
if (segments.length > 1) {
|
|
const list = segments.map(seg => {
|
|
const nums = seg.match(/-?\d+(\.\d+)?/g) || []
|
|
if (nums.length < 2) return null
|
|
const a = nums[0]
|
|
const b = nums[1]
|
|
if (!a || !b) return null
|
|
return normalizeLngLat(parseFloat(a), parseFloat(b))
|
|
}).filter(Boolean) as LngLat[]
|
|
if (list.length) return list
|
|
}
|
|
|
|
const nums = text.match(/-?\d+(\.\d+)?/g) || []
|
|
if (nums.length >= 6 && nums.length % 2 === 0) {
|
|
const list: LngLat[] = []
|
|
for (let i = 0; i < nums.length; i += 2) {
|
|
const a = nums[i]
|
|
const b = nums[i + 1]
|
|
if (!a || !b) continue
|
|
const p = normalizeLngLat(parseFloat(a), parseFloat(b))
|
|
if (p) list.push(p)
|
|
}
|
|
if (list.length) return list
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
function pointOnSegment(p: LngLat, a: LngLat, b: LngLat, eps = 1e-9): boolean {
|
|
const cross = (b.lat - a.lat) * (p.lng - a.lng) - (b.lng - a.lng) * (p.lat - a.lat)
|
|
if (Math.abs(cross) > eps) return false
|
|
const dot = (p.lng - a.lng) * (b.lng - a.lng) + (p.lat - a.lat) * (b.lat - a.lat)
|
|
if (dot < -eps) return false
|
|
const lenSq = (b.lng - a.lng) ** 2 + (b.lat - a.lat) ** 2
|
|
return dot <= lenSq + eps
|
|
}
|
|
|
|
export function pointInPolygon(p: LngLat, polygon: LngLat[]): boolean {
|
|
if (!polygon || polygon.length < 3) return false
|
|
|
|
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
const a = polygon[j]
|
|
const b = polygon[i]
|
|
if (pointOnSegment(p, a, b)) return true
|
|
}
|
|
|
|
let inside = false
|
|
const x = p.lng
|
|
const y = p.lat
|
|
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
const xi = polygon[i].lng
|
|
const yi = polygon[i].lat
|
|
const xj = polygon[j].lng
|
|
const yj = polygon[j].lat
|
|
|
|
const intersect = (yi > y) !== (yj > y) && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi
|
|
if (intersect) inside = !inside
|
|
}
|
|
return inside
|
|
}
|
|
|
|
export function pointInAnyPolygon(p: LngLat, polygons: LngLat[][]): boolean {
|
|
for (const poly of polygons) {
|
|
if (pointInPolygon(p, poly)) return true
|
|
}
|
|
return false
|
|
}
|