若依项目天气模块

article/2025/7/22 6:37:03

在若依项目里添加了一个天气模块,记录一下过程。

一、功能结构与组件布局

天气模块以卡片形式(el-card)展示,包含以下核心功能:

  1. 实时天气:显示当前城市、温度、天气状况(如晴、多云)、湿度、空气质量(AQI)。
  2. 三天预报:展示未来三天的最高 / 最低温、天气图标及状况。
  3. 加载状态:数据获取中显示加载动画,失败时提示错误。

模板结构(template部分)

<el-card class="weather-module"><div slot="header">实时天气</div><div class="weather-info"><!-- 加载中状态 --><div v-else class="weather-loading">获取天气中...</div><!-- 实时天气数据 --><div v-if="weatherData"><div class="location-area">城市名 + 预报提示</div><div class="current-weather">温度 + 天气状况 + 详情(湿度、AQI)</div><!-- 三天预报 --><el-row v-if="dailyForecast.length>0"><el-col v-for="day in dailyForecast" :key="index"><div>日期</div><div>温度范围</div><div>天气图标 + 状况</div></el-col></el-row></div></div>
</el-card>

二、数据获取与状态管理

我这里选择的第三方接口是彩云天气,也考虑过和风天气和OpenWeatherMap但注册流程不是很顺利,相比而言彩云天气更便利而且免费。

1. 数据来源与 API 调用

  • 地理位置获取:通过getIpLocation接口获取用户 IP 对应的经纬度和城市(优先使用定位,失败则用默认城市 “保定市”)。
  • 天气数据接口:调用彩云天气 API(api.caiyunapp.com),通过代理处理跨域问题(vue.config.js中配置/weather-proxy代理)。
// API调用示例(传入经纬度)
export function getWeatherByLocation(lon, lat) {return request({url: `/weather-proxy/v2.6/XXXXXXXXXXXX/${lon},${lat}/weather`,params: { dailysteps: 3, hourlysteps: 48 }, // 三天预报});
}

2. 核心数据字段

  • weatherData:存储实时天气(realtime)和预报(daily)数据。
data() {return {weatherData: {realtime: {}, // 实时数据(温度、天气状况、湿度、AQI等)daily: [],    // 三天预报数据数组result: { forecast_keypoint: '' } // 天气提示},cityName: '默认城市', // 默认城市longitude: null,    // 经度(定位获取)latitude: null      // 纬度(定位获取)};
}

三、关键逻辑实现

 定位与天气数据获取流程

  • 定位逻辑fetchLocation):

    async fetchLocation() {try {const { data } = await getIpLocation(); // 获取IP定位this.longitude = data.point.x;this.latitude = data.point.y;this.cityName = data.address_detail.city || '保定市';await this.fetchWeatherByLocation(); // 定位成功后请求天气} catch (error) {this.$message.error('定位失败,使用默认城市');this.fetchDefaultWeather(); // 定位失败,用默认城市}
    }
    
  • 默认天气逻辑fetchDefaultWeather):

    async fetchDefaultWeather() {// 使用XX市固定坐标(125.4833, 32.8500)请求天气await getWeatherByLocation(125.4833, 32.8500).then(res => {this.weatherData = res.result;this.cityName = 'XX市';});
    }

 数据处理与格式化

  • 计算属性:用于动态处理数据,避免模板中复杂逻辑。
    computed: {// 实时温度(保留一位小数)currentTemp() {return this.weatherData.realtime.temperature.toFixed(1) || '-';},// 天气状况文本(如“晴”“多云”)currentSkycon() {return this.formatSkycon(this.weatherData.realtime.skycon);},// 三天预报(截取前三天并格式化温度)dailyForecast() {return this.weatherData.daily.slice(0, 3).map(day => ({temp: [day.temp.max.toFixed(0), day.temp.min.toFixed(0)],skycon: day.skycon}));}
    },
    methods: {// 天气状况映射(如CLEAR_DAY→晴)formatSkycon(skycon) {const map = { CLEAR_DAY: '晴', PARTLY_CLOUDY_DAY: '多云', ... };return map[skycon] || '未知';},// 天气图标类名(如el-icon-sunny)getSkyconIcon(skycon) {return `el-icon-${skycon.toLowerCase()}`;}
    }
    

    四、代理配置(vue.config.js

    为解决跨域问题,配置天气 API 代理:

  • devServer: {proxy: {"/weather-proxy": {target: "https://api.caiyunapp.com", // 目标域名changeOrigin: true,pathRewrite: { "^/weather-proxy": "" }, // 去除路径前缀}}
    }

结果

用户坐标定位的实现

系统通过 IP 地址定位 实现用户坐标获取

前端实现(Vue 组件) 

1. 核心方法 fetchLocation

async fetchLocation() {this.loading = true; // 显示加载状态try {// 调用后端接口获取IP定位信息const response = await getIpLocation(); // 无参数时默认获取客户端IPif (response.code === 200) {const locationData = response.data; // 后端返回的定位数据// 提取经纬度(字符串转浮点数)this.longitude = parseFloat(locationData.point.x); // 经度(x坐标)this.latitude = parseFloat(locationData.point.y); // 纬度(y坐标)// 提取城市名称(优先使用区级,否则取市级)this.cityName = locationData.address_detail.city || '保定市';// 定位成功后请求天气数据await this.$nextTick(); // 确保DOM更新完毕this.fetchWeatherByLocation(); // 调用天气接口} else {throw new Error(response.msg || '定位失败'); // 抛出异常,触发错误处理}} catch (error) {console.error('获取位置失败:', error);this.$message.error('定位失败,使用默认城市天气');this.fetchDefaultWeather(); // 定位失败时使用默认城市(保定)} finally {this.loading = false; // 隐藏加载状态}
}

2. 关键接口调用

定位接口getIpLocation 来自 @/api/system/record.js,封装了后端请求

// api/system/record.js
export function getIpLocation(ip) {return request({url: '/map/ip/location', // 后端接口路径method: 'get',params: { ip } // 可选IP参数,为空时后端自动获取客户端IP});
}

后端实现(Spring Boot 控制器)

1. 百度地图 API 集成

后端通过 百度地图 IP 定位 API 获取坐标,核心代码在 BaiduMapController 中:

@RestController
@RequestMapping("/map/ip")
public class BaiduMapController {private static final String BAIDU_MAP_URL = "https://api.map.baidu.com/location/ip?";private static final String AK = "XXXXXXXX"; // 百度地图开发者密钥(需替换为有效AK)@GetMapping("/location")public AjaxResult getIpLocation(@RequestParam(required = false) String ip) {try {// 构造请求参数Map<String, String> params = new LinkedHashMap<>();params.put("ip", ip != null ? ip : ""); // 传入客户端IP(可选,留空时百度自动获取)params.put("coor", "bd09ll"); // 坐标类型(百度经纬度坐标)params.put("ak", AK); // 开发者密钥// 发送GET请求到百度地图APIString responseJson = sendGetRequest(BAIDU_MAP_URL, params);BaiduIpLocation location = new ObjectMapper().readValue(responseJson, BaiduIpLocation.class);// 检查返回状态if (location.getStatus() != 0) {return AjaxResult.error("定位失败: " + location.getMessage());}// 返回定位结果(经纬度、城市等)return AjaxResult.success("定位成功", location.getContent());} catch (Exception e) {return AjaxResult.error("定位异常: " + e.getMessage());}}// 发送GET请求工具方法private String sendGetRequest(String url, Map<String, String> params) throws IOException {StringBuilder query = new StringBuilder(url);params.forEach((k, v) -> query.append(k).append("=").append(URLEncoder.encode(v, "UTF-8")).append("&"));query.deleteCharAt(query.length() - 1); // 移除最后一个&HttpURLConnection conn = (HttpURLConnection) new URL(query.toString()).openConnection();conn.setRequestMethod("GET");BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line;StringBuilder result = new StringBuilder();while ((line = reader.readLine()) != null) {result.append(line);}reader.close();return result.toString();}
}

 后端调用百度地图 IP 定位 API 的核心逻辑,主要分为参数构造、API 请求发送和结果解析三个步骤。

1. 参数构造
// 构造请求参数
Map<String, String> params = new LinkedHashMap<>();
params.put("ip", ip != null ? ip : ""); // 传入客户端IP(可选,留空时百度自动获取)
params.put("coor", "bd09ll"); // 坐标类型(百度经纬度坐标)
params.put("ak", AK); // 开发者密钥
参数说明:
  • ip:需要定位的 IP 地址。
    • 如果为空字符串,百度 API 会自动获取请求来源的 IP 地址进行定位(推荐方式)
    • 如果传入特定 IP(如 "202.108.22.5"),则定位该指定 IP
  • coor:返回的坐标类型
    • "bd09ll" 表示百度经纬度坐标(BD-09 坐标系)
    • 其他可选值:"wgs84"(GPS 原始坐标)、"bd09mc"(百度墨卡托坐标)
  • ak:开发者密钥(Access Key)
    • 用于标识调用者身份,需要在百度地图开放平台申请
    • 申请地址:控制台 | 百度地图开放平台
LinkedHashMap 的作用:
  • 保证参数的顺序,虽然百度 API 通常不依赖参数顺序,但某些场景下顺序可能影响签名计算
  • 与百度官方文档示例保持一致的参数顺序
2. 发送 HTTP 请求
// 发送GET请求到百度地图API
String responseJson = sendGetRequest(BAIDU_MAP_URL, params);
底层实现方法:
private String sendGetRequest(String url, Map<String, String> params) throws IOException {StringBuilder query = new StringBuilder(url);params.forEach((k, v) -> query.append(k).append("=").append(URLEncoder.encode(v, "UTF-8")).append("&"));query.deleteCharAt(query.length() - 1); // 移除最后一个&HttpURLConnection conn = (HttpURLConnection) new URL(query.toString()).openConnection();conn.setRequestMethod("GET");BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line;StringBuilder result = new StringBuilder();while ((line = reader.readLine()) != null) {result.append(line);}reader.close();return result.toString();
}
关键步骤解析:
  1. 构建 URL 参数
    • 将参数 Map 转换为 URL 查询字符串(如 "ip=&coor=bd09ll&ak = 你的密钥")
    • 使用 URLEncoder 处理特殊字符(如中文、空格等)
  2. 发送 HTTP 请求
    • 使用 HttpURLConnection 发送 GET 请求
    • 默认使用系统代理,如果需要配置代理可添加以下代码:
      System.setProperty("http.proxyHost", "proxy.example.com");
      System.setProperty("http.proxyPort", "8080");
      
  3. 读取响应
    • 按行读取响应内容并拼接成完整 JSON 字符串
    • 百度 API 返回格式示例:
      {"status": 0,"content": {"address": "北京市海淀区","point": {"x": "116.31104","y": "39.99123"},"address_detail": {"province": "北京市","city": "北京市","district": "海淀区","street": "中关村大街","street_number": "59号"}}
      }
      
3. JSON 解析与对象映射
BaiduIpLocation location = new ObjectMapper().readValue(responseJson, BaiduIpLocation.class);
核心组件:
  • ObjectMapper:Jackson 库的核心类,用于 JSON 与 Java 对象的相互转换
  • BaiduIpLocation:自定义的 Java 类,用于映射百度 API 返回的 JSON 结构
数据模型定义:
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class BaiduIpLocation {private Integer status; // 状态码(0表示成功)private Content content; // 定位结果@Datapublic static class Content {private String address; // 简要地址描述private Point point; // 坐标点private AddressDetail address_detail; // 详细地址@Datapublic static class Point {@JsonProperty("x")private String lng; // 经度@JsonProperty("y")private String lat; // 纬度}@Datapublic static class AddressDetail {private String province; // 省份private String city; // 城市private String district; // 区县private String street; // 街道private String street_number; // 门牌号}}
}
关键注解说明:
  • @Data:Lombok 注解,自动生成 getter、setter、toString 等方法
  • @JsonIgnoreProperties(ignoreUnknown = true):忽略 JSON 中存在但 Java 类中没有的字段
  • @JsonProperty("x"):指定 JSON 字段与 Java 属性的映射关系
4. 定位结果处理

后续代码中会检查状态码并返回结果:

// 检查返回状态
if (location.getStatus() != 0) {return AjaxResult.error("定位失败: " + location.getMessage());
}// 返回定位结果(经纬度、城市等)
return AjaxResult.success("定位成功", location.getContent());
常见状态码说明:
  • 0:成功
  • 1:服务器内部错误
  • 2:请求参数非法
  • 101:AK 参数不存在
  • 102:Mcode 参数不存在(移动端需要)
  • 200:APP 不存在,AK 有误请检查再重试
  • 201:APP 被用户自己禁用
  • 202:APP 被管理员删除
  • 203:IP 校验失败(需要在百度控制台配置白名单)
5. 注意事项
  1. AK 密钥安全

    • 不要将 AK 硬编码在代码中,建议使用配置文件或环境变量管理
    • 生产环境应启用 IP 白名单限制,防止密钥被滥用
  2. 请求频率限制

    • 百度地图 API 免费版有 QPS(每秒请求次数)限制(通常为 1-100 次 / 秒)
    • 超出限制会返回状态码 401,需要进行限流或升级 API 套餐
  3. 坐标转换

    • BD-09 坐标(百度坐标系)与 WGS-84 坐标(GPS 原始坐标)不同
    • 如果需要与其他地图服务(如高德、谷歌)互操作,需要进行坐标转换
    • 百度提供了坐标转换 API:坐标转换 | 百度地图API SDK
  4. 错误处理

    • 网络异常、JSON 解析失败等情况需要捕获并处理
    • 示例代码中使用了 try-catch 包裹整个方法,实际项目中建议使用统一异常处理

2. 定位结果数据结构

百度地图 API 返回的 BaiduIpLocation.Content 包含关键信息:

public class BaiduIpLocation {@Datapublic static class Content {private Point point; // 坐标点private AddressDetail address_detail; // 地址详情@Datapublic static class Point {private String lng; // 经度(x坐标)private String lat; // 纬度(y坐标)}@Datapublic static class AddressDetail {private String city; // 城市(如“北京市”)private String district; // 区(如“朝阳区”)}}
}

关键逻辑说明

1. IP 地址获取方式
  • 自动获取:后端未传入ip参数时,百度地图 API 会基于请求来源自动识别客户端 IP(推荐方式)。
  • 手动传入:如需自定义 IP(如代理场景),前端可通过getIpLocation('目标IP')传入。
2. 坐标转换
  • 百度地图返回的坐标为 BD09LL 格式(百度经纬度坐标),直接用于天气 API(如彩云天气)时需确认是否兼容。
  • 若天气 API 要求其他坐标格式(如 WGS84),需额外添加坐标转换逻辑。
3. 错误处理
  • 定位失败场景
    1. 用户网络限制或 IP 无法解析 → 提示 “定位失败,使用默认城市”。
    2. 百度地图 API 密钥失效 → 检查AK是否有效,重新申请。
  • 默认城市逻辑
    async fetchDefaultWeather() {// 使用保定固定坐标(115.4833, 38.8500)await getWeatherByLocation(115.4833, 38.8500);this.cityName = '保定市'; // 明确显示默认城市
    }
    


http://www.hkcw.cn/article/AoBselRKns.shtml

相关文章

APM32芯得 EP.06 | APM32F407移植uC/OS-III实时操作系统经验分享

《APM32芯得》系列内容为用户使用APM32系列产品的经验总结&#xff0c;均转载自21ic论坛极海半导体专区&#xff0c;全文未作任何修改&#xff0c;未经原文作者授权禁止转载。 最近我开始学习 uC/OS-III 实时操作系统&#xff0c;并着手将其移植到APM32F407 开发板上。在这个过…

图解gpt之注意力机制原理与应用

大家有没有注意到&#xff0c;当序列变长时&#xff0c;比如翻译一篇长文章&#xff0c;或者处理一个长句子&#xff0c;RNN这种编码器就有点力不从心了。它把整个序列信息压缩到一个固定大小的向量里&#xff0c;信息丢失严重&#xff0c;而且很难记住前面的细节&#xff0c;特…

更新密码--二阶注入攻击的原理

1.原理知识&#xff1a; 二阶SQL注入攻击&#xff08;Second-Order SQL Injection&#xff09;原理详解 一、基本概念 二阶注入是一种"存储型"SQL注入&#xff0c;攻击流程分为两个阶段&#xff1a; ​​首次输入​​&#xff1a;攻击者将恶意SQL片段存入数据库​…

RFID技术助力托盘运输线革新

RFID技术助力托盘运输线革新 湖北某工厂托盘运输线使用上存在的问题&#xff1a; 1、托盘在运输线上受信息录入时间等问题影响&#xff0c;导致效率低下&#xff1b; 2、原先托盘上粘贴的条码容易污损&#xff0c;并且时常需要更新更换&#xff0c;导致信息录入、出入库等步…

EasyRTC嵌入式音视频通信SDK助力1v1实时音视频通话全场景应用

一、方案概述​ 在数字化通信需求日益增长的今天&#xff0c;EasyRTC作为一款全平台互通的实时视频通话方案&#xff0c;实现了设备与平台间的跨端连接。它支持微信小程序、APP、PC客户端等多端协同&#xff0c;开发者通过该方案可快速搭建1v1实时音视频通信系统&#xff0c;适…

java.io.IOException: ZIP entry size is too large or invalid

java.io.IOException: ZIP entry size is too large or invalid 解决方案&#xff1a;pom.xml添加<nonFilteredFileExtension>xlsx</nonFilteredFileExtension>

vue3 项目配置多语言支持,如何从服务端拿多语言配置

在 Vue3 项目中实现多语言支持并从服务端获取配置&#xff0c;可以使用 Vue I18n 库。在初始化阶段可以发送请求获取多语言配置或者通过本地文件加载json文件的方式&#xff0c;都可以实现。我这里是tauri项目&#xff0c;所以使用的是invoke从tauri端拿到配置文件&#xff0c;…

龙舟竞渡与芯片制造的共通逻辑:华芯邦的文化破局之道

端午节承载着中华民族数千年的精神密码&#xff0c;龙舟最初是古人沟通天地、祈求风调雨顺的仪式载体。战国时期&#xff0c;屈原投江的悲壮故事为端午注入了家国情怀&#xff0c;龙舟竞渡从此兼具纪念英雄与祈福避疫的双重意义。这种文化内核&#xff0c;与深圳市华芯邦“以科…

OS9.【Linux】基本权限(下)

目录 1.默认权限 掩码 修改权限掩码 目录的权限说明 r权限 w权限 x权限 结论 家目录权限 2.共享目录 粘滞位t 承接OS8.【Linux】基本权限(上)文章 1.默认权限 创建用户时拥有者所属组都是该用户,而且对其他人没有任何权限 掩码 新建文件new.txt1和目录folder后…

【容器docker】启动容器kibana报错:“message“:“Error: Cannot find module ‘./logs‘

说明&#xff1a; 1、服务器数据盘挂了&#xff0c;然后将以前的数据用rsync拷贝过去&#xff0c;启动容器kibana服务&#xff0c;报错信息如下图所示&#xff1a; 2、可能是拷贝docker文件夹&#xff0c;有些文件没有拷贝过去&#xff0c;导致无论是给文件夹授权用户kibana或者…

【25-cv-05917】HSP律所代理Le Petit Prince 小王子商标维权案

Le Petit Prince 小王子 案件号&#xff1a;25-cv-05917 立案时间&#xff1a;2025年5月28日 原告&#xff1a;SOCIETE POUR LOEUVRE ET LA MEMOIRE DANTOINE DE SAINT EXUPERY - SUCCESSION DE SAINT EXUPERY-DAGAY 代理律所&#xff1a;HSP 原告介绍 《小王子》&#x…

信创国产化

一、硬件国产化 1. 飞腾E2000Q 二、操作系统国产化 1. 麒麟系统 1.1 麒麟嵌入式支持飞腾E2000Q 1.1.1 启动安装盘制作 1. 下载rufus工具,安装,下载麒麟系统ISO镜像文件。 2. 使用rufus制作启动盘,U盘插入(注先备份数据,会格式化盘符),配置参数如图。 3. 点击…

一、Sqoop历史发展及原理

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月30日 专栏&#xff1a;Sqoop教程 在大数据时代&#xff0c;数据往往分散存储在各种不同类型的系统中。其中&#xff0c;传统的关系型数据库 (RDBMS) 如 MySQL, Oracle, PostgreSQL 等&#xff0c;仍然承载着大量的关键业务…

2.从0开始搭建vue项目(node.js,vue3,Ts,ES6)

从“0到跑起来一个 Vue 项目”&#xff0c;重点是各个工具之间的关联关系、职责边界和技术演化脉络。 从你写代码 → 到代码能跑起来 → 再到代码可以部署上线&#xff0c;每一步都有不同的工具参与。 &#x1f63a;&#x1f63a;1. 安装 Node.js —— 万事的根基 Node.js 是…

包管理工具

npx工具 npx是什么捏&#xff1f; npx是npm5.2之后自带的一个命令 npx的作用非常之多&#xff0c;但是比较常见的是它用来调用项目中的某个模块的指令 现在假设一个场景&#xff1a; 你在项目里安装了webpack&#xff0c;也在全局中安装了webpack&#xff0c;但是这俩版本…

信号发生器幅值和偏置设置

Vrms是有效幅度 Vpp是幅度峰峰值 Vp是幅度最大值 幅度 2Vpp, 偏置 0V: 信号范围&#xff1a; -1V (谷底) 到 1V (峰顶) -> 中心点在 0V。 幅度 2Vpp, 偏置 1V: 信号范围&#xff1a; (-1V 1V) 0V (谷底) 到 (1V 1V) 2V (峰顶) -> 中心点在 1V。 形状和 Vpp (2…

深入浅出:Spring IOCDI

什么是IOC IOC IOC(Inversion of Control)&#xff0c;是一种设计思想&#xff0c;在之前的SpringMVC里就在类上添加RestController和Controller注解就是使用了IOC&#xff0c;这两个注解就是在Spring中创建一个对象&#xff0c;并将注解下的类交给Spring管理&#xff0c;Spr…

Java并发

一、进程和线程 进程&#xff1a; 程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态。系统运行一个程序即是一个进程从创建&#xff0c;运行到消亡的过程。 在Java中&#xff0c;当我们启动main函数时其实就是启动了一个JVM进程&#xff…

通过回调函数注册定时器触发事件

1、说明 使用回调函数&#xff0c;注册定时器触发事件的模式&#xff0c;提高定时器中断的可操作性&#xff0c;那如何实现呢&#xff1f; 2、.h文件 下面是定时器句柄的声明 3、.c文件 3.1、静态定时器句柄头 3.2、定时器回调函数处理 下面的函数是放在1ms的中断中的&#…

Visual Studio+SQL Server数据挖掘

这里写自定义目录标题 工具准备安装Visual studio 2017安装SQL Server安装SQL Server Management Studio安装analysis service SSMS连接sql serverVisual studio新建项目数据源数据源视图挖掘结构部署模型设置挖掘预测 部署易错点 工具准备 Visual studio 2017 analysis servi…