目录

一、功能场景与实现效果

二、前期准备工作

1. 开发环境说明

2. 百度地图开放平台配置

步骤 1:注册并登录百度地图开放平台

步骤 2:创建应用

步骤 3:获取 AK

三、核心代码实现

1. 定义数据与状态

2. 实现地理编码核心函数

3. 实现自动触发逻辑

4. 表单模板实现

5. 表单验证规则

四、核心原理解析

1. 为什么用 JSONP?

2. 百度地图 Geocoding API 参数说明

3. 异常处理设计

五、常见问题及解决方案

1. "Invalid AK" 错误

2. 地址解析失败(status 非 0)

3. 跨域错误(Access to fetch at ...)

4. 调用频率限制

六、完整代码汇总

六、总结


在物流、外卖、本地生活等业务系统中,经纬度是实现地图定位、路径规划的核心数据。手动输入经纬度不仅效率低,还容易出错。本文将以实际项目为例,详细讲解如何在 Vue3+Element Plus 项目中集成百度地图 API,实现 "地址输入后自动提取经纬度" 功能,堪称保姆级教程,小白也能轻松上手。

一、功能场景与实现效果

先明确我们要实现的功能:在订单录入 / 编辑页面中,用户输入详细地址后,系统自动调用百度地图 API 将地址转换为经纬度,并填充到表单中。具体效果如下:

  • 地址输入框失去焦点时,自动触发经纬度提取
  • 提供 "获取坐标" 按钮,支持手动触发提取
  • 提取过程中显示加载状态,避免重复操作
  • 提取成功后自动填充经度、纬度输入框
  • 异常情况(如地址无效、网络错误)给出友好提示

这种功能在实际业务中非常实用,能极大提升数据录入效率和准确性。

二、前期准备工作

1. 开发环境说明

本文基于以下技术栈实现,如果你使用其他版本,核心逻辑相同,只需微调语法:

  • Vue:3.2.x(使用<script setup>语法糖)
  • UI 框架:Element Plus 2.3.x
  • 构建工具:Vite 4.x
  • 百度地图 API:Geocoding v3(地理编码服务)

2. 百度地图开放平台配置

使用百度地图 API 需要先申请密钥(AK),步骤如下:

步骤 1:注册并登录百度地图开放平台

访问百度地图开放平台,使用百度账号登录(无账号需先注册)。

步骤 2:创建应用

登录后进入「控制台」→「应用管理」→「我的应用」,点击「创建应用」:

  • 应用名称:自定义(如 "订单系统地理编码")
  • 应用类型:选择「浏览器端」(关键!否则会出现跨域问题)
  • Referer 白名单:开发阶段填*(允许所有域名调用),生产环境需填写实际域名(如*.yourdomain.com
步骤 3:获取 AK

创建成功后,在应用列表中会生成一个「访问应用(AK)」,这串字符就是我们调用 API 的密钥,稍后会用到。

三、核心代码实现

下面分模块讲解实现过程,完整代码会在文末给出。

1. 定义数据与状态

首先在组件中定义需要用到的响应式数据,包括表单数据、加载状态等:

// 引入Vue相关API
import { ref } from "vue";
import { ElMessage } from "element-plus";

// 地理编码状态管理
const geocoding = ref(false); // 控制"获取坐标"按钮的加载状态

// 表单数据(订单信息)
const editForm = ref({
  user_address: "", // 用户地址
  user_longitude: null, // 经度
  user_latitude: null, // 纬度
  // 其他表单字段...
});

2. 实现地理编码核心函数

核心逻辑是调用百度地图地理编码 API,将地址转换为经纬度。由于浏览器跨域限制,这里使用 JSONP 方式实现:

/**
 * 地址转经纬度(地理编码)
 */
const geocodeUserAddress = async () => {
  // 输入验证:地址为空时提示
  if (!editForm.value.user_address) {
    ElMessage.warning("请先输入用户地址");
    return;
  }

  // 开始加载状态
  geocoding.value = true;
  
  try {
    // 生成唯一回调函数名(避免全局冲突)
    const callbackName = `jsonp_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
    
    // 创建script标签(JSONP核心)
    const script = document.createElement("script");
    
    // 定义回调函数(百度API返回结果后会执行)
    window[callbackName] = (data) => {
      // 清理工作:移除script标签和全局函数
      document.head.removeChild(script);
      delete window[callbackName];
      
      // 处理API返回结果
      if (data.status === 0 && data.result) {
        // 提取经纬度并填充到表单
        editForm.value.user_longitude = data.result.location.lng;
        editForm.value.user_latitude = data.result.location.lat;
        ElMessage.success("经纬度获取成功");
      } else {
        // 地址解析失败(如地址不存在)
        ElMessage.warning(`地址解析失败:${data.msg || "请检查地址是否正确"}`);
      }
      
      // 结束加载状态
      geocoding.value = false;
    };
    
    // 构造百度地图API请求URL
    const baseUrl = "https://api.map.baidu.com/geocoding/v3/";
    const address = encodeURIComponent(editForm.value.user_address); // 地址编码(处理特殊字符)
    const ak = "你的AK"; // 替换为你的百度地图AK
    
    // 设置script标签的src属性
    script.src = `${baseUrl}?address=${address}&output=json&callback=${callbackName}&ak=${ak}`;
    
    // 处理网络错误(如API不可达)
    script.onerror = () => {
      document.head.removeChild(script);
      delete window[callbackName];
      ElMessage.error("地理编码服务请求失败,请稍后重试");
      geocoding.value = false;
    };
    
    // 发送请求(将script标签添加到页面)
    document.head.appendChild(script);
  } catch (error) {
    // 捕获其他可能的错误
    console.error("经纬度提取失败:", error);
    ElMessage.error("经纬度提取失败,请手动输入");
    geocoding.value = false;
  }
};

3. 实现自动触发逻辑

为提升用户体验,添加 "地址输入框失焦时自动提取经纬度" 的逻辑:

/**
 * 地址输入框失焦时触发
 */
const handleUserAddressBlur = () => {
  // 只有当地址存在且经纬度为空时才自动提取(避免重复请求)
  if (editForm.value.user_address && (!editForm.value.user_longitude || !editForm.value.user_latitude)) {
    geocodeUserAddress();
  }
};

4. 表单模板实现

在 Vue 模板中添加地址、经度、纬度相关的表单元素:

<el-form
  :model="editForm"
  label-width="100px"
  :rules="rules"
  ref="editFormRef"
>
  <!-- 地址输入框 -->
  <el-form-item label="用户地址" prop="user_address">
    <div style="display: flex; gap: 10px;">
      <el-input
        v-model="editForm.user_address"
        maxlength="200"
        placeholder="请输入详细地址(如:北京市海淀区中关村大街1号)"
        @blur="handleUserAddressBlur"  <!-- 失焦时自动提取 -->
      ></el-input>
      <el-button 
        type="primary" 
        @click="geocodeUserAddress"  <!-- 手动触发提取 -->
        :loading="geocoding"        <!-- 加载状态控制 -->
        :disabled="!editForm.user_address"  <!-- 地址为空时禁用 -->
      >
        获取坐标
      </el-button>
    </div>
  </el-form-item>
  
  <!-- 经度输入框 -->
  <el-form-item label="经度" prop="user_longitude">
    <el-input-number
      v-model="editForm.user_longitude"
      :precision="6"  <!-- 保留6位小数 -->
      :step="0.000001"
      :min="-180"     <!-- 经度范围校验 -->
      :max="180"
      placeholder="自动提取或手动输入"
    ></el-input-number>
  </el-form-item>
  
  <!-- 纬度输入框 -->
  <el-form-item label="纬度" prop="user_latitude">
    <el-input-number
      v-model="editForm.user_latitude"
      :precision="6"
      :step="0.000001"
      :min="-90"      <!-- 纬度范围校验 -->
      :max="90"
      placeholder="自动提取或手动输入"
    ></el-input-number>
  </el-form-item>
</el-form>

5. 表单验证规则

添加经纬度的表单验证规则,确保数据合法性:

const rules = {
  user_address: [
    { required: true, message: "请输入用户地址", trigger: "blur" },
    { min: 5, message: "地址至少5个字符", trigger: "blur" }
  ],
  user_longitude: [
    { required: true, message: "请输入经度", trigger: "blur" },
    { 
      validator: (rule, value, callback) => {
        if (value < -180 || value > 180) {
          callback(new Error("经度必须在-180到180之间"));
        } else {
          callback();
        }
      },
      trigger: "blur"
    }
  ],
  user_latitude: [
    { required: true, message: "请输入纬度", trigger: "blur" },
    { 
      validator: (rule, value, callback) => {
        if (value < -90 || value > 90) {
          callback(new Error("纬度必须在-90到90之间"));
        } else {
          callback();
        }
      },
      trigger: "blur"
    }
  ]
};

四、核心原理解析

1. 为什么用 JSONP?

百度地图 API 是第三方服务,直接用 axios 调用会触发浏览器的跨域限制(同源策略)。JSONP 是解决跨域问题的经典方案,原理是:

  • 动态创建<script>标签(不受跨域限制)
  • 将回调函数名作为参数传递给 API
  • API 返回一段 JavaScript 代码,调用该回调函数并传入数据
  • 前端在回调函数中处理返回结果

2. 百度地图 Geocoding API 参数说明

我们调用的地理编码 API 参数如下:

参数名 说明 示例
address 待解析的地址(需 URL 编码) 北京市海淀区中关村大街 1 号
output 返回数据格式 json
callback JSONP 回调函数名 jsonp_1689000000000_123
ak 百度地图开放平台 AK 你的 AK(必填)

返回结果中,status=0表示成功,result.location包含经纬度(lng为经度,lat为纬度)。

3. 异常处理设计

为保证系统健壮性,代码中实现了多层异常处理:

  • 前置校验:地址为空时不发起请求
  • 网络错误:通过script.onerror捕获
  • API 返回错误:通过status字段判断(非 0 为错误)
  • 未知异常:通过 try-catch 捕获

五、常见问题及解决方案

1. "Invalid AK" 错误

原因:AK 无效或不匹配。
解决

  • 检查 AK 是否正确(注意前后空格)
  • 确认应用类型为 "浏览器端"
  • 开发环境 Referer 白名单设为*(生产环境需具体配置)

2. 地址解析失败(status 非 0)

原因:地址不明确或不存在。
解决

  • 提示用户输入更详细的地址(包含省市区)
  • 增加地址联想功能(可结合百度地图输入提示 API)
  • 失败时允许手动输入经纬度

3. 跨域错误(Access to fetch at ...)

原因:未使用 JSONP 方式,直接用 axios 等工具调用 API。
解决:严格按照本文的 JSONP 方式实现(动态创建 script 标签)。

4. 调用频率限制

原因:百度地图 API 有免费额度限制(日调用量 6000 次)。
解决

  • 生产环境建议升级为企业账号
  • 前端添加节流处理,避免频繁调用
  • 实现本地缓存,相同地址不重复请求

六、完整代码汇总

为方便使用,这里汇总核心代码(可直接复制到组件中):

<template>
  <el-form
    :model="editForm"
    label-width="100px"
    :rules="rules"
    ref="editFormRef"
  >
    <!-- 地址输入框 -->
    <el-form-item label="用户地址" prop="user_address">
      <div style="display: flex; gap: 10px;">
        <el-input
          v-model="editForm.user_address"
          maxlength="200"
          placeholder="请输入详细地址(如:北京市海淀区中关村大街1号)"
          @blur="handleUserAddressBlur"
        ></el-input>
        <el-button 
          type="primary" 
          @click="geocodeUserAddress"
          :loading="geocoding"
          :disabled="!editForm.user_address"
        >
          获取坐标
        </el-button>
      </div>
    </el-form-item>
    
    <!-- 经度输入框 -->
    <el-form-item label="经度" prop="user_longitude">
      <el-input-number
        v-model="editForm.user_longitude"
        :precision="6"
        :step="0.000001"
        :min="-180"
        :max="180"
        placeholder="自动提取或手动输入"
      ></el-input-number>
    </el-form-item>
    
    <!-- 纬度输入框 -->
    <el-form-item label="纬度" prop="user_latitude">
      <el-input-number
        v-model="editForm.user_latitude"
        :precision="6"
        :step="0.000001"
        :min="-90"
        :max="90"
        placeholder="自动提取或手动输入"
      ></el-input-number>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { ref } from "vue";
import { ElMessage } from "element-plus";

// 表单数据
const editForm = ref({
  user_address: "",
  user_longitude: null,
  user_latitude: null
});

// 加载状态
const geocoding = ref(false);

// 表单验证规则
const rules = {
  user_address: [
    { required: true, message: "请输入用户地址", trigger: "blur" },
    { min: 5, message: "地址至少5个字符", trigger: "blur" }
  ],
  user_longitude: [
    { required: true, message: "请输入经度", trigger: "blur" },
    { 
      validator: (rule, value, callback) => {
        if (value < -180 || value > 180) {
          callback(new Error("经度必须在-180到180之间"));
        } else {
          callback();
        }
      },
      trigger: "blur"
    }
  ],
  user_latitude: [
    { required: true, message: "请输入纬度", trigger: "blur" },
    { 
      validator: (rule, value, callback) => {
        if (value < -90 || value > 90) {
          callback(new Error("纬度必须在-90到90之间"));
        } else {
          callback();
        }
      },
      trigger: "blur"
    }
  ]
};

// 地址失焦时自动提取经纬度
const handleUserAddressBlur = () => {
  if (editForm.value.user_address && (!editForm.value.user_longitude || !editForm.value.user_latitude)) {
    geocodeUserAddress();
  }
};

// 手动提取经纬度
const geocodeUserAddress = async () => {
  if (!editForm.value.user_address) {
    ElMessage.warning("请先输入用户地址");
    return;
  }

  geocoding.value = true;
  
  try {
    const callbackName = `jsonp_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
    const script = document.createElement("script");
    
    window[callbackName] = (data) => {
      document.head.removeChild(script);
      delete window[callbackName];
      
      if (data.status === 0 && data.result) {
        editForm.value.user_longitude = data.result.location.lng;
        editForm.value.user_latitude = data.result.location.lat;
        ElMessage.success("经纬度获取成功");
      } else {
        ElMessage.warning(`地址解析失败:${data.msg || "请检查地址是否正确"}`);
      }
      
      geocoding.value = false;
    };
    
    const address = encodeURIComponent(editForm.value.user_address);
    const ak = "你的AK"; // 替换为你的AK
    script.src = `https://api.map.baidu.com/geocoding/v3/?address=${address}&output=json&callback=${callbackName}&ak=${ak}`;
    
    script.onerror = () => {
      document.head.removeChild(script);
      delete window[callbackName];
      ElMessage.error("地理编码服务请求失败,请稍后重试");
      geocoding.value = false;
    };
    
    document.head.appendChild(script);
  } catch (error) {
    console.error("经纬度提取失败:", error);
    ElMessage.error("经纬度提取失败,请手动输入");
    geocoding.value = false;
  }
};
</script>

六、总结

本文详细讲解了如何在 Vue3 项目中集成百度地图 API,实现地址自动提取经纬度功能。核心是通过 JSONP 方式调用百度地图 Geocoding API,结合表单交互逻辑和异常处理,实现高效、稳定的地址转经纬度功能。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐