Vue3 项目中集成百度地图 API 实现地址自动提取经纬度(附完整代码)
目录
3. 跨域错误(Access to fetch at ...)
在物流、外卖、本地生活等业务系统中,经纬度是实现地图定位、路径规划的核心数据。手动输入经纬度不仅效率低,还容易出错。本文将以实际项目为例,详细讲解如何在 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,结合表单交互逻辑和异常处理,实现高效、稳定的地址转经纬度功能。
更多推荐
所有评论(0)