uniapp适配H5和Android-apk实现获取当前位置经纬度并调用接口

index.vue:

注意:这里的

return 'https://www.******.com/bus_admin_java'

这里是区分 H5和apk调用接口地址的不同而设置的。之后凡是调用接口的方法就使用封装后的request()

复制代码
<template>
  <view class="container">
    <view class="title">实时位置追踪器</view>

    <view
      class="btn"
      :class="{ active: tracking }"
      @click="toggleTracking"
    >
      {{ tracking ? '停止追踪' : '点击开始' }}
    </view>

    <view v-if="latitude !== null && longitude !== null" class="location-info">
      当前经度: {{ longitude }} <br />
      当前纬度: {{ latitude }}
    </view>

    <!-- 雷达扫描效果 -->
    <view class="radar" v-if="tracking">
      <view class="circle circle1"></view>
      <view class="circle circle2"></view>
      <view class="circle circle3"></view>
      <view class="pulse"></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);
  }
}
</style>

main.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
  }
}
// #endif

mainifest.json:

复制代码
{
    "name" : "bus_uniapp",
    "appid" : "__UNI__7E751FC",
    "description" : "",
    "versionName" : "1.0.0",
    "versionCode" : "100",
    "transformPx" : false,
    /* 5+App特有相关 */
    "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/, '')
      }
    }
  }
})
相关推荐
UXbot3 小时前
AI原型设计工具如何支持团队协作与快速迭代
前端·交互·个人开发·ai编程·原型模式
程序员陆业聪3 小时前
两次Flutter全屏白踩坑复盘:Layout的静默失败,以及AI结对编程的认知盲区
android
ZC跨境爬虫4 小时前
跟着MDN学HTML_day_48:(Node接口)
前端·javascript·ui·html·音视频
程序员陆业聪4 小时前
Compose Strong Skipping Mode 的真相:它并不会让你的类型变 Stable
android
PieroPc5 小时前
CAMWATCH — 局域网摄像头监控系统 Fastapi + html
前端·python·html·fastapi·监控
巴巴博一6 小时前
2026 最新:Trae / Cursor 一键接入 taste-skill 完整教程(让 AI 前端告别“AI 味”)
前端·ai·ai编程
kyriewen6 小时前
半夜三点线上崩了,AI替我背了锅——用AI排错,五分钟定位三年老bug
前端·javascript·ai编程
kyriewen7 小时前
我让 AI 当了 24 小时全年无休的“毒舌考官”
前端·ci/cd·ai编程
hexu_blog7 小时前
vue+java实现图片批量压缩
java·前端·vue.js
IT_陈寒7 小时前
为什么你应该学习JavaScript?
前端·人工智能·后端