手记

uni-app: 根据定位获取天气(附城市控件)

通过本章节你能学到那些?

1、Uni-App 测试数据封装
2、Uni-App 城市控件(Uni-App元素操作相关)
3、ES6 多种遍历方式区别

下面我们来具体看看:

Uni-App 测试数据封装

城市控件,我们就查询接口了,根据高德提供的城市数据,我们进行处理后,放到一个文件中。

// Json.js
const cityList = [{
  "firstLetter":"A",
  "cityList":[{
    "cityID":"513209",
    "city":"阿坝县",
    "abbr":"阿坝县",
    "firstSpell":"abx",
    "spell":"abaxian",
    "latitude":32.908167,
    "longitude":101.712951,
    "isSecond":1
  }, {
    ...
  }]
}, {
  "firstLetter":"B",
  "cityList":[{
    "cityID":"110000",
    "city":"北京市",
    "abbr":"北京",
    "firstSpell":"bjs",
    "spell":"beijingshi",
    "latitude":39.929986,
    "longitude":116.395645,
    "code":"010",
    "sort":1,
    "isSecond":0
  }, {
    ...
  }]
})

export default {
	cityList
}

然后我们在main.js里面引入,使其可以共用。

// main.js
...
import Json from './Json'

const json = type=>{
	// 模拟异步请求数据
	return new Promise(resolve=>{
		setTimeout(()=>{
			resolve(Json[type]);
		}, 500)
	})
}

Vue.prototype.$api = {json};

下面,我们就可以在任意vue页面使用this.$api.json(‘xxxx’),来获取Json.js里面暴露的对象了。

this.cityList = await this.$api.json('cityList');

下面我们对cityList的数据进行渲染

Uni-App 城市控件

1、选择pages目录,右击新建页面,记得勾选自动在pages.json中注册,否则需要手动去配置它。

新建完成后,pages.json会多一段配置

{
  "path" : "pages/city/city",
  "style" : {
    // 手动配置title
    "navigationBarTitleText": "城市选择"
  }
}

同时,pages目录下会多一个目录city,我们的城市选择页面就写到city/city.vue页面。

2、获取城市数据

// city/city.vue
data() {
  return {
    cityList: []
  };
},
async onLoad() {
  this.cityList = await this.$api.json('city');
  console.log(this.cityList);
},

打印数据如下图:

3、html结构

// 对应样式
.filter{
	position: fixed;
	top: 80upx;
	font-size: 24upx;
	right: 0;
	line-height: 40upx;
	text-align: right;
	z-index: 10;
	.li{
		padding-left: 20upx;
		padding-right: 20px;
	}
}
.city-list{
	.letter{
		padding: 10upx 20upx;
		background: #DCDFE660;
		display: block;
	}
	.city{
		padding: 0upx 20upx;
		line-height: 64upx;
		border-bottom: 1upx solid #F8F6FC;
		font-size: 30upx;
		&:last-child{
			border-bottom: none;
		}
	}
}

H5预览图:

4、事件处理
(1)选择城市列表事件

choose (item) {
  // 选择城市后,将城市名字,adcode,经纬度缓存给vuex
  this.$store.commit('setCity', {
    city: item.city,
    adcode: item.cityID,
    latitude: item.latitude,
    longitude: item.longitude
  })
  // 然后根据经纬度查询具体位置
  this.$store.dispatch('getLocation', {
    location: `${item.longitude},${item.latitude}`
  })
  // 最后返回首页
  uni.navigateBack({
      delta: 1
  });
}

下面看store.js实现

// store/index.js
mutations: {
  ...
  setCity(state, data){
    state.location = {
      address: data
    };
  },
  setLocation(state, data){
    data = data.regeocode.addressComponent;
    state.location = {
      address: {
        adcode: data.adcode,
        city: data.city.length && data.city || data.province,
        district: data.district
      }
    };
  }
},
actions: {
  getWeather({ commit }, params){
    ...
  },
  getLocation({ commit, dispatch}, params){
    // 高德逆地址解析,根据经纬度获取具体位置
    http({
      methods: 'get',
      url: 'https://restapi.amap.com/v3/geocode/regeo',
      data: {
        key: 'd9xxx7d4xx7bx91xx61cxx5',
        location: params.location,
        output: 'json'
      }
    }).then((data) =>{
      commit('setLocation', data)
      dispatch('getWeather', {
        city: data.regeocode.addressComponent.adcode
      })
    }, (err) => {
      console.log(err)
    })
  }
}

逆地址解析数据结构如下:

这里逆地址解析后,又查询了一次天气,就可以更新到首页天气数据了,我们选择城市后,是直接返回的,所以这里查询一次天气,首页就不需要处理了。

(2)城市筛选事件
第一次,我们的解决方案是这样:

filter (name, index) {
  let scrollTop = this.$refs.letter[index].$el.offsetTop;
  uni.showToast({
    title: name,
    icon: 'none',
    duration: 500
  });
  uni.pageScrollTo({
    scrollTop: scrollTop,
    duration: 500
  });
}

通过ref找到对应的A-Z,得出他们距离顶部的距离offsetTop,然后滚动(uni.pageScrollTo)到对应位置,H5预览效果:

然而,小程序/APP报错:

这里的this.$refs是空的,小程序压根就不能这样操作元素。好吧,只能换方式了。

第二次解决方案:

filter (name, index) {
  uni.createSelectorQuery().select('.city-list').boundingClientRect(data=>{
      uni.createSelectorQuery().select('#letter' + name + '').boundingClientRect((res)=>{
          uni.showToast({
            title: name,
            icon: 'none',
            duration: 500
          });
      uni.pageScrollTo({
              duration: 200,
              scrollTop:res.top - data.top
          })
      }).exec()
  }).exec();
}

这里为什么要嵌套2层,这是因为滚动到实际距离是元素距离顶部的距离减去最外层盒子的滚动距离

小程序预览图:

IOS真机预览图:

Uni-App API解析:
uni.createSelectorQuery():返回一个 SelectorQuery 对象实例。可以在这个实例上使用 select 等方法选择节点,并使用 boundingClientRect 等方法选择需要查询的信息,必须在生命周期 mounted 后进行调用。

SelectorQuery提供以下方法:
(1)、selectorQuery.in(component):将选择器的选取范围更改为自定义组件 component 内,返回一个 SelectorQuery 对象实例。(初始时,选择器仅选取页面范围的节点,不会选取任何自定义组件中的节点)。

(2)、selectorQuery.select(selector):在当前页面下选择第一个匹配选择器 selector 的节点,返回一个 NodesRef(用于获取节点信息的对象) 对象实例,可以用于获取节点信息。

注意:selector 类似于 CSS 的选择器,但仅支持下列语法。
ID选择器:#the-id

class选择器(可以连续指定多个):.a-class.another-class

子元素选择器:.the-parent > .the-child

后代选择器:.the-ancestor .the-descendant

跨自定义组件的后代选择器:.the-ancestor >>> .the-descendant

多选择器的并集:#a-node, .some-other-nodes

const query = uni.createSelectorQuery().in(this);
query.select('#id').boundingClientRect(data => {
  console.log("得到布局位置信息" + JSON.stringify(data));
  console.log("节点离页面顶部的距离为" + data.top);
}).exec();

(3)、selectorQuery.selectAll(selector):在当前页面下选择匹配选择器 selector 的所有节点,返回一个 NodesRef(用于获取节点信息的对象) 对象实例,可以用于获取节点信息

(4)、selectorQuery.selectViewport():选择显示区域,可用于获取显示区域的尺寸、滚动位置等信息,返回一个 NodesRef 对象实例。

(5)、selectorQuery.exec(callback):执行所有的请求。请求结果按请求次序构成数组,在callback的第一个参数中返回。

下面我们来加强城市控件功能,添加搜索

<!-- pages/city/city.vue -->
<!-- 搜索功能 -->
<view class="search">
  <input class="keyword" v-model="keyword" type="text" placeholder="北京/bj/beijing">
</view>
<!-- 城市列表 -->
<view class="ul city-list" v-if="!keyword">
...
</view>
<view class="city-list" v-else>
  <view class="li city" @click="choose(city)" v-for="(city, cindex) in showCityList" v-bind:key="cindex">
    {{city.city}}
  </view>
</view>
<!-- 快速筛选A-Z -->
<view class="ul filter" v-if="!keyword">
</view>

我们用计算属性对原有的城市数据处理

data() {
  return {
    keyword: '',
    cityList: []
  };
},
computed:{
  // 将所有城市数据整理到citys里面
  citys() {
    let list = [];
    this.cityList.map(item=>{
      list.push(...item.cityList)
    })
    console.log(list);
    return list;
  },
  // 搜索结果展示数据
  showCityList(){
    return this.citys.filter(item => {
      let name = item.city + item.spell;
      return name.indexOf(this.keyword) > -1;
    })
  }
}

citys数据结构:

预览效果图:

这里用到了数据的2个方法,filter和map,我们来温故一下数组的几种遍历方式,和他们各有什么区别。

1、forEach() 迭代数组每一项,没有返回值

2、every() 迭代数组每一项,每项都符合条件的才返回true,反之false

3、some() 迭代数组每一项,只要有一项符合条件就返回true,如果全部不符合才返回false

4、map() 迭代数组每一项,可以给特定条件会返回重新组成新的数组

5、filter() 迭代数组每一项,可以给特定的条件进行筛选返回新的数组

总结

今天你学到了什么?今天的核心内容:
1、数据的处理(数组操作)
2、uni-app元素操作,筛选滚动

下一章节,天气数据处理(图标、多场景等)

最后,谢谢大家支持。


喜欢的可以关注我哦!

0人推荐
随时随地看视频
慕课网APP