location.uts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. type QQMapApi = {
  2. IP_LOCATION: string
  3. GEOCODER: string
  4. DISTANCE: string
  5. SCAN_QR_CODE: string
  6. }
  7. type QQMapConfig = {
  8. KEY: string
  9. API: QQMapApi
  10. }
  11. type LocationPoint = {
  12. longitude: number
  13. latitude: number
  14. }
  15. type LocationResult = {
  16. code: number
  17. data?: UTSJSONObject
  18. message: string
  19. result?: any
  20. }
  21. const QQ_MAP: QQMapConfig = {
  22. KEY: 'SIFBZ-2UJEZ-FZIXF-TAVLV-6GWVK-ZEBZX',
  23. API: {
  24. IP_LOCATION: 'https://apis.map.qq.com/ws/location/v1/ip',
  25. GEOCODER: 'https://apis.map.qq.com/ws/geocoder/v1/',
  26. DISTANCE: 'https://apis.map.qq.com/ws/distance/v1/',
  27. SCAN_QR_CODE: 'https://apis.map.qq.com/ws/barcode/v1/scan',
  28. }
  29. }
  30. const showLocationSettingModal = () : void => {
  31. uni.showModal({
  32. title: '提示',
  33. content: '请先开启定位权限后重试',
  34. showCancel: false,
  35. confirmText: '我知道了',
  36. })
  37. }
  38. const buildLocationData = (point: LocationPoint, num: number) : UTSJSONObject => {
  39. return {
  40. location: {
  41. longitude: point.longitude,
  42. latitude: point.latitude,
  43. },
  44. num: num,
  45. } as UTSJSONObject
  46. }
  47. /**
  48. * 扫描二维码
  49. */
  50. export const scanQRCode = async () : Promise<any> => {
  51. return new Promise<any>((resolve: (value: any) => void, reject: (reason?: any) => void) => {
  52. uni.scanCode({
  53. scanType: ['qrCode', 'barCode'],
  54. success: (res) => {
  55. resolve((res.result as string | null) ?? '')
  56. },
  57. fail: (err) => {
  58. reject(err)
  59. }
  60. })
  61. })
  62. }
  63. /**
  64. * 请求位置授权
  65. */
  66. export const requestLocationAuth = async () : Promise<void> => {
  67. // #ifdef MP-WEIXIN
  68. const settings = await uni.getSetting({
  69. withSubscriptions: false,
  70. })
  71. const authSetting = settings.authSetting as UTSJSONObject | null
  72. const hasLocationAuth = (authSetting?.['scope.userLocation'] as boolean | null) ?? false
  73. if (!hasLocationAuth) {
  74. await uni.authorize({
  75. scope: 'scope.userLocation',
  76. success: () => {
  77. console.log('用户已授权')
  78. },
  79. fail: () => {
  80. console.log('用户拒绝授权')
  81. showLocationSettingModal()
  82. },
  83. })
  84. }
  85. // #endif
  86. }
  87. /**
  88. * 发起定位相关请求
  89. */
  90. export const requestJsonp = async (url: string, params: UTSJSONObject | null) : Promise<any> => {
  91. try {
  92. const requestParams = params ?? ({} as UTSJSONObject)
  93. return await new Promise<any>((resolve: (value: any) => void, reject: (reason?: any) => void) => {
  94. uni.request({
  95. url: url,
  96. method: 'GET',
  97. data: requestParams,
  98. success: (res) => {
  99. const response = res.data as UTSJSONObject | null
  100. if (response == null) {
  101. reject(new Error('定位服务响应为空'))
  102. return
  103. }
  104. const status = (response['status'] as number | null) ?? 0
  105. if (status === 0) {
  106. resolve(response['result'] ?? response)
  107. return
  108. }
  109. reject(response)
  110. },
  111. fail: (err) => {
  112. reject(err)
  113. }
  114. })
  115. })
  116. } catch (error) {
  117. console.error('定位请求错误:', error)
  118. return null
  119. }
  120. }
  121. /**
  122. * 通过城市名字搜索
  123. */
  124. export const getCityNameInfo = async (cityName: string = '') : Promise<UTSJSONObject | null> => {
  125. try {
  126. const params = {
  127. address: cityName,
  128. key: QQ_MAP.KEY,
  129. } as UTSJSONObject
  130. const res = await requestJsonp(QQ_MAP.API.GEOCODER, params) as UTSJSONObject | null
  131. if (res == null) {
  132. return null
  133. }
  134. return {
  135. area_code: ((res['ad_info'] as UTSJSONObject | null)?.['adcode'] as string | null) ?? '',
  136. city: ((res['address_components'] as UTSJSONObject | null)?.['city'] as string | null) ?? '',
  137. latitude: ((res['location'] as UTSJSONObject | null)?.['lat'] as number | null) ?? 0,
  138. longitude: ((res['location'] as UTSJSONObject | null)?.['lng'] as number | null) ?? 0,
  139. } as UTSJSONObject
  140. } catch (error) {
  141. console.error('城市定位请求错误:', error)
  142. return null
  143. }
  144. }
  145. /**
  146. * 初始化微信配置
  147. */
  148. export const initWxConfig = async () : Promise<UTSJSONObject> => {
  149. return Promise.resolve({
  150. code: 200,
  151. message: '当前平台无需微信初始化',
  152. } as UTSJSONObject)
  153. }
  154. /**
  155. * 获取当前位置信息
  156. */
  157. export const getCurrentLocation = async () : Promise<LocationResult> => {
  158. return new Promise<LocationResult>((resolve: (value: LocationResult) => void, reject: (reason?: any) => void) => {
  159. let hasResolved = false
  160. const fallbackToIpLocation = async () : Promise<void> => {
  161. if (hasResolved) {
  162. return
  163. }
  164. try {
  165. const locationData = await requestJsonp(QQ_MAP.API.IP_LOCATION, {
  166. key: QQ_MAP.KEY,
  167. } as UTSJSONObject) as UTSJSONObject | null
  168. const location = locationData?.['location'] as UTSJSONObject | null
  169. if (location != null) {
  170. hasResolved = true
  171. resolve({
  172. code: 200,
  173. data: buildLocationData({
  174. longitude: (location['lng'] as number | null) ?? 0,
  175. latitude: (location['lat'] as number | null) ?? 0,
  176. }, 1),
  177. message: '获取IP定位成功',
  178. })
  179. return
  180. }
  181. reject({ code: 202, message: '获取位置失败' } as UTSJSONObject)
  182. } catch (e) {
  183. reject({ code: 202, message: 'IP定位异常' } as UTSJSONObject)
  184. }
  185. }
  186. const timeoutInfo = setTimeout(() => {
  187. fallbackToIpLocation()
  188. }, 3000)
  189. uni.getLocation({
  190. type: 'gcj02',
  191. success: (res) => {
  192. if (hasResolved) {
  193. return
  194. }
  195. hasResolved = true
  196. clearTimeout(timeoutInfo)
  197. const point: LocationPoint = {
  198. longitude: res.longitude,
  199. latitude: res.latitude,
  200. }
  201. uni.setStorageSync('locationInfo', point)
  202. resolve({
  203. code: 200,
  204. data: buildLocationData(point, 2),
  205. message: '获取位置成功',
  206. })
  207. },
  208. fail: () => {
  209. clearTimeout(timeoutInfo)
  210. showLocationSettingModal()
  211. reject({
  212. code: 202,
  213. message: '获取位置失败',
  214. } as UTSJSONObject)
  215. },
  216. })
  217. })
  218. }
  219. /**
  220. * 解析地理位置信息
  221. */
  222. export const parseLocation = async (location: UTSJSONObject) : Promise<LocationResult> => {
  223. try {
  224. const latitude = (location['latitude'] as number | null) ?? 0
  225. const longitude = (location['longitude'] as number | null) ?? 0
  226. const geoLocation = await requestJsonp(QQ_MAP.API.GEOCODER, {
  227. key: QQ_MAP.KEY,
  228. location: `${latitude},${longitude}`,
  229. } as UTSJSONObject)
  230. uni.setStorageSync('userAddress', geoLocation)
  231. return {
  232. code: 200,
  233. data: {
  234. geoLocation: geoLocation,
  235. } as UTSJSONObject,
  236. message: '解析地址成功',
  237. }
  238. } catch (error) {
  239. return {
  240. code: 500,
  241. message: '解析地址失败',
  242. }
  243. }
  244. }
  245. /**
  246. * 计算两点间距离
  247. */
  248. export const calculateDistance = async (startPoint: UTSJSONObject, endPoint: UTSJSONObject) : Promise<LocationResult> => {
  249. try {
  250. const startLatitude = (startPoint['latitude'] as number | null) ?? 0
  251. const startLongitude = (startPoint['longitude'] as number | null) ?? 0
  252. const endLatitude = (endPoint['latitude'] as number | null) ?? 0
  253. const endLongitude = (endPoint['longitude'] as number | null) ?? 0
  254. const result = await requestJsonp(QQ_MAP.API.DISTANCE, {
  255. from: `${startLatitude},${startLongitude}`,
  256. to: `${endLatitude},${endLongitude}`,
  257. key: QQ_MAP.KEY,
  258. mode: 'driving',
  259. } as UTSJSONObject) as UTSJSONObject | null
  260. const elements = result?.['elements'] as UTSArray<UTSJSONObject> | null
  261. if (elements != null && elements.length > 0) {
  262. const firstElement = elements[0]
  263. return {
  264. code: 200,
  265. data: {
  266. distance: (firstElement['distance'] as number | null) ?? 0,
  267. } as UTSJSONObject,
  268. result: result,
  269. message: '计算距离成功',
  270. }
  271. }
  272. throw new Error('无效的距离数据')
  273. } catch (error) {
  274. return {
  275. code: 202,
  276. message: '计算距离失败',
  277. }
  278. }
  279. }
  280. /**
  281. * 格式化数字为千分位
  282. */
  283. export const formatThousands = (number: number = 0) : string => {
  284. return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
  285. }
  286. /**
  287. * 手机号码脱敏处理
  288. */
  289. export const maskPhoneNumber = (phone: string = '') : string => {
  290. return phone.length > 0 ? phone.replace(/(\d{3})\d{6}(\d{2})/, '$1******$2') : ''
  291. }
  292. /**
  293. * 拨打电话
  294. */
  295. export const makePhoneCall = (phoneNumber: string) : Promise<any> => {
  296. const phone = phoneNumber.length > 0 ? phoneNumber : '19806196313'
  297. return new Promise<any>((resolve: (value: any) => void, reject: (reason?: any) => void) => {
  298. uni.makePhoneCall({
  299. phoneNumber: phone,
  300. success: () => {
  301. resolve({
  302. code: 200,
  303. message: '拨号成功',
  304. })
  305. },
  306. fail: (err) => {
  307. reject({
  308. code: 500,
  309. message: '拨号失败',
  310. error: err,
  311. })
  312. },
  313. })
  314. })
  315. }