index.vue:注意这里的return https://www.******.com/bus_admin_java这里是区分 H5和apk调用接口地址的不同而设置的。之后凡是调用接口的方法就使用封装后的requesttemplate view classcontainer view classtitle实时位置追踪器/view view classbtn :class{ active: tracking } clicktoggleTracking {{ tracking ? 停止追踪 : 点击开始 }} /view view v-iflatitude ! null longitude ! null classlocation-info 当前经度: {{ longitude }} br / 当前纬度: {{ latitude }} /view !-- 雷达扫描效果 -- view classradar v-iftracking view classcircle circle1/view view classcircle circle2/view view classcircle circle3/view view classpulse/view /view /view /template script const BASE_URL (() { // #ifdef H5 return /api // #endif // #ifdef APP-PLUS return https://www.******.com/bus_admin_java // #endif return /api })() function request(options) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { content-type: application/json, ...(options.header || {}) }, timeout: 15000, success: (res) { resolve(res) }, fail: (err) { reject(err) } }) }) } export default { data() { return { latitude: null, longitude: null, tracking: false, timer: null } }, methods: { toggleTracking() { if (this.tracking) { this.tracking false this.stopTracking() } else { this.startTracking() } }, startTracking() { this.stopTracking() // 先尝试获取一次位置 this.getLocation(true) }, stopTracking() { if (this.timer) { clearInterval(this.timer) this.timer null } }, getLocation(isFirst false) { uni.getLocation({ type: wgs84, success: (res) { this.latitude res.latitude this.longitude res.longitude console.log(当前位置, res) uni.showToast({ title: 经度${res.longitude.toFixed(4)} 纬度${res.latitude.toFixed(4)}, icon: none, duration: 3000 }) this.sendPosition(res.longitude, res.latitude) // 第一次成功后再开启轮询 if (isFirst) { this.tracking true this.timer setInterval(() { this.getLocation(false) }, 3000) } }, fail: (err) { console.error(获取位置失败, err) this.tracking false this.stopTracking() this.handleLocationFail(err) } }) }, handleLocationFail(err) { let content 获取位置失败请检查定位服务是否开启 // #ifdef APP-PLUS content 请先开启系统定位服务并允许当前应用使用定位权限是否前往设置 // #endif // #ifdef H5 content H5 获取定位失败请确认浏览器已允许定位并且当前页面运行在 HTTPS 或 localhost 环境下 // #endif uni.showModal({ title: 定位失败, content, success: (res) { if (!res.confirm) return // App 端支持打开设置 // #ifdef APP-PLUS uni.openSetting({ success: (settingRes) { console.log(设置页返回, settingRes) uni.showToast({ title: 请重新点击开始定位, icon: none }) }, fail: (settingErr) { console.error(打开设置失败, settingErr) uni.showToast({ title: 无法打开设置页, icon: none }) } }) // #endif // H5 没有统一的 openSetting给提示即可 // #ifdef H5 uni.showToast({ title: 请在浏览器地址栏附近开启定位权限, icon: none, duration: 3000 }) // #endif } }) }, sendPosition(longitude, latitude) { request({ url: /bus/position/save, method: POST, data: { longitude: longitude, latitude: latitude }, success: (res) { console.log(上传成功, res.data) }, fail: (err) { console.error(上传失败, err) } }) } }, beforeDestroy() { this.stopTracking() }, onUnload() { this.stopTracking() } } /script style .container { display: flex; flex-direction: column; justify-content: flex-start; align-items: center; padding: 40px 20px; background: radial-gradient(circle at center, #0a0a0a, #001010); min-height: 100vh; color: #00ffff; font-family: Microsoft YaHei, sans-serif; } .title { font-size: 22px; font-weight: bold; margin-bottom: 20px; } .location-info { margin-top: 20px; font-size: 16px; text-align: center; line-height: 1.8; } .btn { margin-top: 20px; padding: 12px 24px; background: #002f2f; color: #00ffff; border: 1px solid #00ffff; border-radius: 8px; font-size: 16px; text-align: center; transition: all 0.3s ease; } .btn.active { background: #00ffff; color: #001010; box-shadow: 0 0 10px #00ffff; } .btn:active { transform: scale(0.96); } /* 雷达扫描效果 */ .radar { position: relative; width: 200px; height: 200px; margin-top: 40px; } .circle { position: absolute; border: 2px solid rgba(0, 255, 255, 0.3); border-radius: 50%; animation: pulseScale 3s linear infinite; } .circle1 { width: 60px; height: 60px; top: 70px; left: 70px; animation-delay: 0s; } .circle2 { width: 120px; height: 120px; top: 40px; left: 40px; animation-delay: 1s; } .circle3 { width: 180px; height: 180px; top: 10px; left: 10px; animation-delay: 2s; } .pulse { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 255, 255, 0.1); border-radius: 50%; animation: rotateRadar 4s linear infinite; } keyframes pulseScale { 0% { transform: scale(0.2); opacity: 0.5; } 50% { transform: scale(1); opacity: 0.2; } 100% { transform: scale(0.2); opacity: 0.5; } } keyframes rotateRadar { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /stylemain.js:import App from ./App.vue // #ifndef VUE3 import Vue from vue import ./uni.promisify.adaptor Vue.config.productionTip false App.mpType app const app new Vue({ ...App }) app.$mount() // #endif // #ifdef VUE3 import { createSSRApp } from vue export function createApp() { const app createSSRApp(App) return { app } } // #endifmainifest.json:{ name : bus_uniapp, appid : __UNI__7E751FC, description : , versionName : 1.0.0, versionCode : 100, transformPx : false, /* 5App特有相关 */ app-plus : { permissions : { Location : { desc : 用于获取当前位置 } }, usingComponents : true, nvueStyleCompiler : uni-app, compilerVersion : 3, splashscreen : { alwaysShowBeforeRender : true, waiting : true, autoclose : true, delay : 0 }, /* 模块配置 */ modules : {}, /* 应用发布信息 */ distribute : { /* android打包配置 */ android : { permissions : [ uses-permission android:name\android.permission.ACCESS_COARSE_LOCATION\/, uses-permission android:name\android.permission.ACCESS_FINE_LOCATION\/, uses-permission android:name\android.permission.CHANGE_NETWORK_STATE\/, uses-permission android:name\android.permission.MOUNT_UNMOUNT_FILESYSTEMS\/, uses-permission android:name\android.permission.VIBRATE\/, uses-permission android:name\android.permission.READ_LOGS\/, uses-permission android:name\android.permission.ACCESS_WIFI_STATE\/, uses-feature android:name\android.hardware.camera.autofocus\/, uses-permission android:name\android.permission.ACCESS_NETWORK_STATE\/, uses-permission android:name\android.permission.CAMERA\/, uses-permission android:name\android.permission.GET_ACCOUNTS\/, uses-permission android:name\android.permission.READ_PHONE_STATE\/, uses-permission android:name\android.permission.CHANGE_WIFI_STATE\/, uses-permission android:name\android.permission.WAKE_LOCK\/, uses-permission android:name\android.permission.FLASHLIGHT\/, uses-feature android:name\android.hardware.camera\/, uses-permission android:name\android.permission.WRITE_SETTINGS\/ ] }, /* ios打包配置 */ ios : { dSYMs : false }, /* SDK配置 */ sdkConfigs : {} } }, /* 快应用特有相关 */ quickapp : {}, /* 小程序特有相关 */ mp-weixin : { appid : , setting : { urlCheck : false }, usingComponents : true }, mp-alipay : { usingComponents : true }, mp-baidu : { usingComponents : true }, mp-toutiao : { usingComponents : true }, uniStatistics : { enable : false }, vueVersion : 3 }注意开通vite.config.js: (解决H5的跨域的问题)// vite.config.js import { defineConfig } from vite import uni from dcloudio/vite-plugin-uni // https://vitejs.dev/config/ export default defineConfig({ plugins: [ uni(), ], server: { // 1. 配置代理规则 proxy: { // 2. /api 是你自定义的请求前缀 /api: { // 3. target 是你的目标后端接口的真实地址 // target: http://localhost:9002, target: https://www.*****.com/bus_admin_java, // 4. 改变请求源头解决大部分跨域问题必须为 true changeOrigin: true, // 5. (可选) 重写路径移除 /api 前缀 rewrite: (path) path.replace(/^\/api/, ) } } } })