type QQMapApi = { IP_LOCATION: string GEOCODER: string DISTANCE: string SCAN_QR_CODE: string } type QQMapConfig = { KEY: string API: QQMapApi } type LocationPoint = { longitude: number latitude: number } type LocationResult = { code: number data?: UTSJSONObject message: string result?: any } const QQ_MAP: QQMapConfig = { KEY: 'SIFBZ-2UJEZ-FZIXF-TAVLV-6GWVK-ZEBZX', API: { IP_LOCATION: 'https://apis.map.qq.com/ws/location/v1/ip', GEOCODER: 'https://apis.map.qq.com/ws/geocoder/v1/', DISTANCE: 'https://apis.map.qq.com/ws/distance/v1/', SCAN_QR_CODE: 'https://apis.map.qq.com/ws/barcode/v1/scan', } } const showLocationSettingModal = () : void => { uni.showModal({ title: '提示', content: '请先开启定位权限后重试', showCancel: false, confirmText: '我知道了', }) } const buildLocationData = (point: LocationPoint, num: number) : UTSJSONObject => { return { location: { longitude: point.longitude, latitude: point.latitude, }, num: num, } as UTSJSONObject } /** * 扫描二维码 */ export const scanQRCode = async () : Promise => { return new Promise((resolve: (value: any) => void, reject: (reason?: any) => void) => { uni.scanCode({ scanType: ['qrCode', 'barCode'], success: (res) => { resolve((res.result as string | null) ?? '') }, fail: (err) => { reject(err) } }) }) } /** * 请求位置授权 */ export const requestLocationAuth = async () : Promise => { // #ifdef MP-WEIXIN const settings = await uni.getSetting({ withSubscriptions: false, }) const authSetting = settings.authSetting as UTSJSONObject | null const hasLocationAuth = (authSetting?.['scope.userLocation'] as boolean | null) ?? false if (!hasLocationAuth) { await uni.authorize({ scope: 'scope.userLocation', success: () => { console.log('用户已授权') }, fail: () => { console.log('用户拒绝授权') showLocationSettingModal() }, }) } // #endif } /** * 发起定位相关请求 */ export const requestJsonp = async (url: string, params: UTSJSONObject | null) : Promise => { try { const requestParams = params ?? ({} as UTSJSONObject) return await new Promise((resolve: (value: any) => void, reject: (reason?: any) => void) => { uni.request({ url: url, method: 'GET', data: requestParams, success: (res) => { const response = res.data as UTSJSONObject | null if (response == null) { reject(new Error('定位服务响应为空')) return } const status = (response['status'] as number | null) ?? 0 if (status === 0) { resolve(response['result'] ?? response) return } reject(response) }, fail: (err) => { reject(err) } }) }) } catch (error) { console.error('定位请求错误:', error) return null } } /** * 通过城市名字搜索 */ export const getCityNameInfo = async (cityName: string = '') : Promise => { try { const params = { address: cityName, key: QQ_MAP.KEY, } as UTSJSONObject const res = await requestJsonp(QQ_MAP.API.GEOCODER, params) as UTSJSONObject | null if (res == null) { return null } return { area_code: ((res['ad_info'] as UTSJSONObject | null)?.['adcode'] as string | null) ?? '', city: ((res['address_components'] as UTSJSONObject | null)?.['city'] as string | null) ?? '', latitude: ((res['location'] as UTSJSONObject | null)?.['lat'] as number | null) ?? 0, longitude: ((res['location'] as UTSJSONObject | null)?.['lng'] as number | null) ?? 0, } as UTSJSONObject } catch (error) { console.error('城市定位请求错误:', error) return null } } /** * 初始化微信配置 */ export const initWxConfig = async () : Promise => { return Promise.resolve({ code: 200, message: '当前平台无需微信初始化', } as UTSJSONObject) } /** * 获取当前位置信息 */ export const getCurrentLocation = async () : Promise => { return new Promise((resolve: (value: LocationResult) => void, reject: (reason?: any) => void) => { let hasResolved = false const fallbackToIpLocation = async () : Promise => { if (hasResolved) { return } try { const locationData = await requestJsonp(QQ_MAP.API.IP_LOCATION, { key: QQ_MAP.KEY, } as UTSJSONObject) as UTSJSONObject | null const location = locationData?.['location'] as UTSJSONObject | null if (location != null) { hasResolved = true resolve({ code: 200, data: buildLocationData({ longitude: (location['lng'] as number | null) ?? 0, latitude: (location['lat'] as number | null) ?? 0, }, 1), message: '获取IP定位成功', }) return } reject({ code: 202, message: '获取位置失败' } as UTSJSONObject) } catch (e) { reject({ code: 202, message: 'IP定位异常' } as UTSJSONObject) } } const timeoutInfo = setTimeout(() => { fallbackToIpLocation() }, 3000) uni.getLocation({ type: 'gcj02', success: (res) => { if (hasResolved) { return } hasResolved = true clearTimeout(timeoutInfo) const point: LocationPoint = { longitude: res.longitude, latitude: res.latitude, } uni.setStorageSync('locationInfo', point) resolve({ code: 200, data: buildLocationData(point, 2), message: '获取位置成功', }) }, fail: () => { clearTimeout(timeoutInfo) showLocationSettingModal() reject({ code: 202, message: '获取位置失败', } as UTSJSONObject) }, }) }) } /** * 解析地理位置信息 */ export const parseLocation = async (location: UTSJSONObject) : Promise => { try { const latitude = (location['latitude'] as number | null) ?? 0 const longitude = (location['longitude'] as number | null) ?? 0 const geoLocation = await requestJsonp(QQ_MAP.API.GEOCODER, { key: QQ_MAP.KEY, location: `${latitude},${longitude}`, } as UTSJSONObject) uni.setStorageSync('userAddress', geoLocation) return { code: 200, data: { geoLocation: geoLocation, } as UTSJSONObject, message: '解析地址成功', } } catch (error) { return { code: 500, message: '解析地址失败', } } } /** * 计算两点间距离 */ export const calculateDistance = async (startPoint: UTSJSONObject, endPoint: UTSJSONObject) : Promise => { try { const startLatitude = (startPoint['latitude'] as number | null) ?? 0 const startLongitude = (startPoint['longitude'] as number | null) ?? 0 const endLatitude = (endPoint['latitude'] as number | null) ?? 0 const endLongitude = (endPoint['longitude'] as number | null) ?? 0 const result = await requestJsonp(QQ_MAP.API.DISTANCE, { from: `${startLatitude},${startLongitude}`, to: `${endLatitude},${endLongitude}`, key: QQ_MAP.KEY, mode: 'driving', } as UTSJSONObject) as UTSJSONObject | null const elements = result?.['elements'] as UTSArray | null if (elements != null && elements.length > 0) { const firstElement = elements[0] return { code: 200, data: { distance: (firstElement['distance'] as number | null) ?? 0, } as UTSJSONObject, result: result, message: '计算距离成功', } } throw new Error('无效的距离数据') } catch (error) { return { code: 202, message: '计算距离失败', } } } /** * 格式化数字为千分位 */ export const formatThousands = (number: number = 0) : string => { return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') } /** * 手机号码脱敏处理 */ export const maskPhoneNumber = (phone: string = '') : string => { return phone.length > 0 ? phone.replace(/(\d{3})\d{6}(\d{2})/, '$1******$2') : '' } /** * 拨打电话 */ export const makePhoneCall = (phoneNumber: string) : Promise => { const phone = phoneNumber.length > 0 ? phoneNumber : '19806196313' return new Promise((resolve: (value: any) => void, reject: (reason?: any) => void) => { uni.makePhoneCall({ phoneNumber: phone, success: () => { resolve({ code: 200, message: '拨号成功', }) }, fail: (err) => { reject({ code: 500, message: '拨号失败', error: err, }) }, }) }) }