【uni-app】使用天气API做一个天气APP(全过程)- 实况、逐小时、40日

头一次使用uni-app写代码, 现学现卖, 写的不好的地方见谅, 申请个appid就可以运行
切换城市界面比较简单, 城市名称需要符合天气api参数规则, 录入的城市不要带市区县; 格式如: 青岛、铁西、海淀、沛县

APP效果

在这里插入图片描述

功能说明

  1. 实况天气
  2. 逐小时预报
  3. 未来7日天气
  4. 未来40日天气
  5. 空气质量详情
  6. 切换城市
  7. 更多功能开发中

开发准备

  1. 前往天气api接口官网 http://tianqiapi.com 注册账号, 进入用户中心获取appid和appsecret
  2. 修改App.vue下globalData字段内容
globalData:{
	appid : '你的appid',
	appsecret : '你的appsecret'
},
  1. 然后直接运行程序就 o了

代码下载

先贴上下载链接, 省的在最底下看不见
http://file.tianqiapi.com/uniapp_tianqi0802.zip

附完整代码

  1. 首页 > index.vue
  2. 40日预报 > month.vue
  3. 空气质量详情 > aqi.vue
  4. 切换城市 > city.vue

首页 > index.vue

<template>
	<image class="background" :src="bg"></image>
	<view class="content" style="padding: 10px 5px 10px; color:#fff; text-align: center;">

		<view style="display: flex; width: 100%; padding-bottom: 35px; font-size:16px;">
			<view style="width: 50%; text-align: left;">
				<navigator url="/pages/index/city">{{weather.city}}[切换]</navigator>
			</view>
			<view style="width: 50%; text-align: right; color:#f1f1f1;font-size:13px;">{{weather.update_time}}更新</view>
		</view>
		<view style="font-size: 32.76px;">{{weather.data[0].wea}}</view>

		<view style="font-size: 65.52px;">{{weather.data[0].tem}}°</view>
		<view style="font-size: 14px;">
			<navigator url="/pages/index/aqi">AQI {{weather.aqi.air_level}} [查看详情]</navigator>
		</view>
		<view style="padding: 8px 0 20px 0;"><text
				decode>最低{{weather.data[0].tem2}}°&nbsp;&nbsp;&nbsp;最高{{weather.data[0].tem1}}°</text>
		</view>
		<!--hours-->
		<view
			style="width:100%; overflow:scroll; border-bottom: 1px solid rgba(190, 185, 185, 0.5);border-top: 1px solid rgba(190, 185, 185, 0.5);padding:23.4rpx 0; height: 105px; min-height: 105px; max-height: 105px;">
			<view class="hlist">

				<view v-for="(item,index) in weather.data[0].hours" :key="item.id" class="hitem">
					<view>{{item.hours}}</view>
					<view style="padding: 10px 0 0;">
						<image :src="item.wea_img" mode="widthFix" style="width: 46.8rpx;"></image>
					</view>
					<view style="font-weight: 500; font-size:13px;text-align: center;">{{item.wea}}</view>
					<view style="font-weight: 500; font-size:14px;text-align: center;">{{item.tem}}°</view>
				</view>
			</view>
			<view style="clear:both;width:100%;height:1px;"></view>
		</view>
		<!--hours-->
		<!--week-->
		<view style="width:100%; padding-top: 10px;padding-bottom: 15px;">

			<view v-for="(item,index) in weather.data" :key="item.id" class="week">
				<text class="weekday">{{item.day}}</text>
				<view style="text-align: left; padding-left:30%; font-size: 13px;">
					<image :src="item.wea_img" mode="widthFix" style="width: 42rpx; vertical-align: middle;">
					</image> {{item.wea}} <text v-if="item.rain > 10"
						style="font-size:12px; color: #96dafb; margin-left: 5px;"
						wx:if="{{item.rain > 35}}">{{item.rain}}%</text>
				</view>
				<text class="weekmax">{{item.tem2}}°</text>
				<text class="weekmin">{{item.tem1}}°</text>
			</view>


		</view>
		<!--week-->
		<view style="padding:15px;">
			<navigator url="/pages/index/month"><button type="primary"
					style="font-size: 12px; border-radius: 30px; padding-left:30px;padding-right: 30px;">查看40日预报&gt;&gt;</button>
			</navigator>
		</view>
		<view class="smalltext">{{weather.data[0].narrative}}</view>

		<!--dayinfo-->
		<view style="width:100%;">
			<view style="padding:5px; text-align: left; ">
				<view style="display: flex; border-bottom: 1px solid rgba(190, 185, 185, 0.5); padding-bottom: 10px;">
					<view style="width: 50%;">
						<view style="color:#d8d6d6;">日出</view>
						<view style="font-size:16px;">上午 {{weather.data[0].sunrise}}</view>
					</view>
					<view style="width: 50%; ">
						<view style="color:#d8d6d6;">日落</view>
						<view style="font-size:16px;">下午 {{weather.data[0].sunset}}</view>
					</view>
					<view class="clearfix"></view>
				</view>
				<view
					style="display: flex; border-bottom: 1px solid rgba(190, 185, 185, 0.5); padding-top: 10px;padding-bottom: 10px;">
					<view style="width: 50%;">
						<view style="color:#d8d6d6;">湿度</view>
						<view style="">{{weather.data[0].humidity}}</view>
					</view>
					<view style="width: 50%;">
						<view style="color:#d8d6d6;"></view>
						<view style="">{{weather.data[0].win[0]}}
							{{weather.data[0].win_speed}}
						</view>
					</view>
					<view class="clearfix"></view>
				</view>
				<view
					style="display: flex;border-bottom: 1px solid rgba(190, 185, 185, 0.5); padding-top: 10px;padding-bottom: 10px;">
					<view style="width: 50%;">
						<view style="color:#d8d6d6;">气压</view>
						<view style="">{{weather.data[0].pressure}}兆帕</view>
					</view>
					<view style="width: 50%;">
						<view style="color:#d8d6d6;">空气质量</view>
						<view style="">{{weather.data[0].air_level}}</view>
					</view>
					<view class="clearfix"></view>
				</view>
				<view
					style="display: flex; border-bottom: 1px solid rgba(190, 185, 185, 0.5); padding-top: 10px;padding-bottom: 10px;">
					<view style="width: 50%;">
						<view style="color:#d8d6d6;">降雨量</view>
						<view style="">{{weather.data[0].rain_pcpn}}mm</view>
					</view>
					<view style="width: 50%;">
						<view style="color:#d8d6d6;">紫外线</view>
						<view style="">{{weather.data[0].uvDescription}}</view>
					</view>
					<view class="clearfix"></view>
				</view>
				<view style="font-size: 23.4rpx; color:#d8d6d6; padding:10px 0">数据来源:TianqiAPI.com</view>
			</view>
		</view>
		<!--./dayinfo-->
	</view>
</template>

<script>
	export default {
		data() {
			return {
				city: '',
				weather: [],
				title: '易客API',
				phrase_img: '',
				bg: ''
			}
		},
		onLoad(e) {
			console.log('参数如下:')
			console.log(e.city);
			this.city = e.city;
			this.getWeather();
		},
		methods: {
			formatDate(date) {
				var date = new Date(date);
				return (date.getMonth() + 1) + '-' + (date.getDate()) + ' ' + date.getHours() + ':' + date.getMinutes();
			},
			openmonth() {
				uni.navigateTo({
					url: '/pages/index/month'
				})
			},
			getWeather() {
				if (this.city == undefined) {
					this.city = '';
				}
				if (this.city == '') {
					var scity = uni.getStorageSync('storage_city');
					if (scity != '') {
						this.city = scity;
					}
				}
				/* 
				天气api接口官网 http://tianqiapi.com
				appid和appsecret请在官网注册, 进入用户中心获取, 新用户可以免费请求3000次, 接口收费350一年
				*/
				const appid = getApp().globalData.appid;
				const appsecret = getApp().globalData.appsecret;
				const url = 'http://v1.yiketianqi.com/api?version=v91&ext=hours,life&appid=' + appid + '&appsecret=' +
					appsecret + '&city=' + this.city;

				uni.request({
					url: url,
					success: (res) => {

						if (res.data.errcode == "100") {
							// 错误处理
							console.error('获取天气信息失败', res.data);
						} else {
							uni.setStorageSync('storage_city', res.data.city);
							var mydata = res.data;
							console.log(mydata.data.length);

							for (var i = 0; i < mydata.data[0].hours.length; i++) {
								mydata.data[0].hours[i]['wea_img'] = '/static/skins/' + mydata.data[0].hours[i]
									['wea_img'] +
									'.png';
							}
							for (var i = 0; i < mydata.data.length; i++) {
								mydata.data[i]['wea_img'] = '/static/skins/' + mydata.data[i]['wea_img'] +
									'.png';
							}
							mydata.update_time = this.formatDate(mydata.update_time);
							console.log(mydata);

							this.weather = mydata;
							this.phrase_img = '/static/skins/' + mydata.data[0].wea_img + '.png';
							// 计算背景图
							if (mydata.data[0].wea.match(RegExp(//))) {
								this.bg = '/static/bg_sun.jpg';
							} else {
								this.bg = '/static/bg_yin.jpg';
							}
						}
					},
					fail: (error) => {
						// 请求失败处理
						console.error('请求失败', error);
					}
				});
			}
		}
	}
</script>

<style>
	.background {
		width: 100%;
		height: 100%;
		position: fixed;
		background-size: 100% 100%;
		z-index: -1;
	}

	.hlist {
		display: inline-flex;
	}

	.hitem {
		padding: 0 23.4rpx;
		width: 50px;
		text-align: center;
	}

	.week {
		font-size: 32.76rpx;
		position: relative;
		text-align: center;
		margin-top: 11.7rpx;
	}

	.weekday {
		position: absolute;
		left: 10px;
		top: 0px;
		font-size: 13px;
	}

	.weekmax {
		position: absolute;
		right: 70px;
		top: 0px;
	}

	.weekmin {
		color: #ebe7e7;
		position: absolute;
		right: 20px;
		top: 0px;
		text-align: left;
	}

	.smalltext {
		text-align: left;
		border-top: 1px solid rgba(190, 185, 185, 0.5);
		border-bottom: 1px solid rgba(190, 185, 185, 0.5);
		padding-top: 15px;
		padding-left: 15px;
		padding-bottom: 15px;
		font-size: 28.08rpx;
		width: 100%;
	}

	.logo {
		height: 200rpx;
		width: 200rpx;
		margin-top: 200rpx;
		margin-left: auto;
		margin-right: auto;
		margin-bottom: 50rpx;
	}

	.text-area {
		display: flex;
		justify-content: center;
	}

	.title {
		font-size: 36rpx;
		color: #8f8f94;
	}

	.wea_img {
		width: 25px;
		height: 25px;
	}
</style>

40日预报 > month.vue

<template>
	<view style="background-color: rgb(0, 97, 204); height: 100%;">
		<view style="padding:10px;">
			<view style="padding: 15px; border-radius:5px; background-color: #fff; ">
				<view v-for="(item,index) in weather.data" :key="item.id">
					<view style="padding:8px 0; display: flex; border-bottom: 1px solid #f1f1f1; font-size: 14px;">
						<view style="width: 30%;font-size:13px;">{{item.date}}<br>{{item.week}}</view>
						<view style="width: 40%; text-align: left;font-size: 14px;">
							<image :src="item.wea_img" mode="widthFix" style="width: 42rpx; vertical-align: middle;">
							</image> {{item.wea}}
						</view>
						<view style="width: 30%; text-align: center;">
							<text>{{item.tem2}}° ~ {{item.tem1}}°</text>
						</view>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				city: '',
				weather: [],
				title: '易客API',
				phrase_img: '',
				bg: ''
			}
		},
		onLoad() {
			this.getWeather();
		},
		methods: {
			formatDate(date) {
				var date = new Date(date);
				return (date.getMonth() + 1) + '-' + (date.getDate());
			},
			getWeather() {
				var scity = uni.getStorageSync('storage_city');
				if (scity != '') {
					this.city = scity;
				}
				const appid = getApp().globalData.appid;
				const appsecret = getApp().globalData.appsecret;
				const url = 'http://v1.yiketianqi.com/api?unescape=1&version=v3&appid=' + appid + '&appsecret=' +
					appsecret + '&city=' + this.city;

				uni.request({
					url: url,
					success: (res) => {

						if (res.data.errcode == "100") {
							// 错误处理
							console.error('获取天气信息失败', res.data);
						} else {
							var mydata = res.data;
							for (var i = 0; i < mydata.data.length; i++) {
								mydata.data[i]['date'] = this.formatDate(mydata.data[i]['date']);
								mydata.data[i]['wea_img'] = '/static/skins/' + mydata.data[i]['wea_img'] +
									'.png';
							}
							console.log(mydata);

							this.weather = mydata;
						}
					},
					fail: (error) => {
						// 请求失败处理
						console.error('请求失败', error);
					}
				});
			}
		}
	}
</script>

<style>

</style>

空气质量详情 > aqi.vue

<template>
	<view style="background-color: #E7ECF4; height: 100%;">
		<view style="padding:10px 10px 200px;">
			<view style="font-size: 12px; color:#333; text-align: right;">{{weather.aqi.update_time}}更新</view>
			<view style="text-align: center; padding-top: 20px;">
				<view style="font-size: 46px; ">
					<text style="position: relative; padding:10px;">{{weather.aqi.air}}<text
							style="background-color: #FFEF01; border-radius: 50%; padding:5px 8px; font-size: 14px; position: absolute; top: 0px; right:-20px;">{{weather.aqi.air_level}}</text></text>

				</view>
				<view style="padding: 10px 0;"><text
						style="font-size: 16px; background-color: #fff; color: #14C355; border-radius:8px; padding:5px;">AQI指数</text>
				</view>
				<view style="font-size: 13px; padding:10px 30px 20px;">{{weather.aqi.air_tips}}</view>
			</view>

			<view
				style="font-size: 14px; padding: 10px; border-radius:5px; background-color: #fff; display: flex; text-align: center; ">
				<view style="width: 16.66%;">
					<view style="font-size: 16px;">{{weather.aqi.pm10}}</view>PM10
				</view>
				<view style="width: 16.66%;">
					<view style="font-size: 16px;">{{weather.aqi.pm25}}</view>PM2.5
				</view>
				<view style="width: 16.66%;">
					<view style="font-size: 16px;">{{weather.aqi.no2}}</view>NO2
				</view>
				<view style="width: 16.66%;">
					<view style="font-size: 16px;">{{weather.aqi.so2}}</view>SO2
				</view>
				<view style="width: 16.66%;">
					<view style="font-size: 16px;">{{weather.aqi.o3}}</view>O3
				</view>
				<view style="width: 16.66%;">
					<view style="font-size: 16px;">{{weather.aqi.co}}</view>CO
				</view>
			</view>

			<view
				style="margin-top:15px; font-size: 14px; padding: 10px; border-radius:5px; background-color: #fff;  text-align: center; ">
				<view style="text-align: left;">{{weather.city}}未来5日天气</view>
				<view style="display: flex; ">
					<view style="width: 20%;">
						<view class="itema">
							{{weather.data[1].date}}</view>
						<view class="itemb">{{weather.data[1].wea}}
						</view>
						<view class="itemc">
							{{weather.data[1].tem2}}~{{weather.data[1].tem1}}°</view>
						<view class="itemd">
							<text
								class="iteme">{{weather.data[1].air_level}}</text>
						</view>
					</view>
					<!--2-->
					<view style="width: 20%;">
						<view class="itema">
							{{weather.data[2].date}}</view>
						<view class="itemb">{{weather.data[2].wea}}
						</view>
						<view class="itemc">
							{{weather.data[2].tem2}}~{{weather.data[2].tem1}}°</view>
						<view class="itemd">
							<text
								class="iteme">{{weather.data[2].air_level}}</text>
						</view>
					</view>
					<!--./2-->
					<!--3-->
					<view style="width: 20%;">
						<view class="itema">
							{{weather.data[3].date}}</view>
						<view class="itemb">{{weather.data[3].wea}}
						</view>
						<view class="itemc">
							{{weather.data[3].tem2}}~{{weather.data[3].tem1}}°</view>
						<view class="itemd">
							<text
								class="iteme">{{weather.data[3].air_level}}</text>
						</view>
					</view><!--./3--><!--4-->
					<view style="width: 20%;">
						<view class="itema">
							{{weather.data[4].date}}</view>
						<view class="itemb">{{weather.data[4].wea}}
						</view>
						<view class="itemc">
							{{weather.data[4].tem2}}~{{weather.data[4].tem1}}°</view>
						<view class="itemd">
							<text
								class="iteme">{{weather.data[4].air_level}}</text>
						</view>
					</view><!--./4--><!--5-->
					<view style="width: 20%;">
						<view class="itema">
							{{weather.data[5].date}}</view>
						<view class="itemb">{{weather.data[5].wea}}
						</view>
						<view class="itemc">
							{{weather.data[5].tem2}}~{{weather.data[5].tem1}}°</view>
						<view class="itemd">
							<text
								class="iteme">{{weather.data[5].air_level}}</text>
						</view>
					</view><!--./5-->
				</view>
			</view>


		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				city:'',
				weather: [],
				title: '易客API',
				phrase_img: '',
				bg: ''
			}
		},
		onLoad() {
			this.getWeather();
		},
		methods: {
			formatDate(date) {
				var date = new Date(date);
				return (date.getMonth() + 1) + '-' + (date.getDate());
			},
			getWeather() {
				var scity = uni.getStorageSync('storage_city');
				if (scity != '') {
					this.city = scity;
				}
				const appid = getApp().globalData.appid;
				const appsecret = getApp().globalData.appsecret;
				const url = 'http://v1.yiketianqi.com/api?unescape=1&version=v91&appid=' + appid + '&appsecret=' +
					appsecret + '&city=' + this.city;

				uni.request({
					url: url,
					success: (res) => {

						if (res.data.errcode == "100") {
							// 错误处理
							console.error('获取天气信息失败', res.data);
						} else {
							var mydata = res.data;
							for (var i = 0; i < mydata.data.length; i++) {
								mydata.data[i]['date'] = this.formatDate(mydata.data[i]['date']);
								mydata.data[i]['wea_img'] = '/static/skins/' + mydata.data[i]['wea_img'] +
									'.png';
							}
							console.log(mydata);

							this.weather = mydata;
							// update title
							uni.setNavigationBarTitle({
								title: this.weather.city + '空气质量'
							});
						}
					},
					fail: (error) => {
						// 请求失败处理
						console.error('请求失败', error);
					}
				});
			}
		}
	}
</script>

<style>
.itema{font-size: 16px;padding-top: 10px; padding-bottom: 5px;font-size: 13px;}
.itemb{font-size: 16px;padding-bottom: 5px;font-size: 12px; white-space: nowrap;}
.itemc{font-size: 16px;padding-bottom: 10px;font-size: 14px;}
.itemd{font-size: 16px;font-size: 12px;padding-bottom: 10px;}
.iteme{background-color: #FFEF01; border-radius: 10px; padding:2px 5px;}
</style>

切换城市 > city.vue

<template>
	<view style="padding: 10px;">
		<input class="uni-input" style="border: 1px solid #dcdcdc; border-radius: 15px; padding:10px 20px;" focus
			placeholder="请输入城市名称" confirm-type="search" inputmode="search" @confirm="gocity" @input="onKeyInput" />
	</view>
</template>

<script>
	export default {
		data() {
			return {
				inputValue: ''
			}
		},
		methods: {
			onKeyInput: function(event) {
				this.inputValue = event.detail.value;
				console.log(this.inputValue);
			},
			gocity() {
				console.log(this.inputValue);
				//uni.setStorageSync('city', 'hello');
				uni.reLaunch({
					url: '/pages/index/index?city=' + this.inputValue
				});
			}
		}
	}
</script>

<style>

</style>

感谢阅读

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值