一、首页布局
pages.json的代码如下
{
"pages": [
{
"path": "pages/main/main",
"style": {
"navigationBarTitleText": "点餐小程序",
"navigationStyle": "custom",
"enablePullDownRefresh": true
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "点餐小程序",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8",
"onReachBottomDistance": 50
}
}
- navigationStyle属性值为custom,设置成自定义导航;
- enablePullDownRefresh属性值为true,表示下拉刷新;
- onReachBottomDistance属性值未50,表示上拉触底事件触发时距底部的距离,单位为px;
二、异步数据流对接配合地图定位显示附近的商铺
uni-app已内置Vuex,无须安装即可使用。
1.首先配置Vuex,在根目录创建store->index.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store=new Vuex.Store({
modules:{}
});
export default store;
2.将Vuex注册到Vue中,main.js文件中的代码如下:
import Vue from 'vue'
import App from './App'
import store from "./store";
Vue.config.productionTip = false
App.mpType = 'app'
const app = new Vue({
...App,
store
})
app.$mount()
3.封装request()方法:在static->js->utils->request.js文件
function request(url,method="get",data={}){
return new Promise(((resolve, reject) => {
uni.request({
url: url,
data: data,
method:method.toLocaleUpperCase(),
header: {
'content-type': 'application/x-www-form-urlencoded'
},
success: (res) => {
resolve(res.data)
},
fail:(res)=>{
reject(res)
}
});
}))
}
export {
request
}
4.配置公共的参数,其中存放接口地址。在static->js->conf->config.js文件
let baseApi="https://diancan.glbuys.com/api";
export default {
baseApi
}
5.请求服务端数据:根目录创建api文件夹,在该文件夹下创建business/index.js文件。
import config from "../../static/js/conf/config";
import {request} from "../../static/js/utils/request";
//显示首页商家列表
export function getShopData(data){
return request(config.baseApi+"/v1/business/shop","get",data)
}
6.与Vuex对接:store->business->index.js
import {getShopData} from "../../api/business";
export default {
namespaced: true,
state:{
shops:[]
},
mutations:{
//设置商铺列表
["SET_SHOPS"](state,payload){
state.shops=payload.shops;
}
},
actions:{
//显示首页商家列表
getShop(conText,payload){
getShopData(payload).then(res=>{
if(res.code==200){
conText.commit("SET_SHOPS",{shops:res.data});
}
})
}
}
}
7.首页是自定义导航,需要先解决iPhoneX“刘海”的兼容性问题。
由于这个项目会有多个页面需要自定义导航,因此我们需要一个全局变量去识别是否在iPhoneX中,全局首选Vuex。
store->system->index.js中:
export default {
namespaced:true,
state:{
isIpx:false, //是否iPhoneX
platform:1//平台类型。值:1:微信小程序,2:微信公众号
},
mutations:{
//设置isIpx
["SET_IPX"](state,payload){
state.isIpx=payload.isIpx;
}
}
}
注册到Vuex中,store->index.js文件中新增代码如下:
import Vue from "vue";
import Vuex from "vuex";
import system from "./system";
import business from "./business";
Vue.use(Vuex);
const store=new Vuex.Store({
modules:{
system,
business
}
});
export default store;
判断是否在iPhoneX中进行,在App.vue中新增代码如下:
onLaunch: function() {
uni.getSystemInfo({
success: res=> {
if(res.model.indexOf('iPhone X')>-1){
this.$store.commit("system/SET_IPX",{isIpx:true});
}
}
});
},
将Vuex的数据对接到pages/main/main.vue中,该文件中的代码如下:
(1)使用getSetting()方法获取用户的当前设置,利用返回值res.authSetting['scope.userLocation']判断用户是否开启地理位置。
如果没有开启,使用openSetting()方法调起客户端小程序设置界面。
用户开启地理位置后,使用getLocation()方法获取地理位置。
注意:为例定位准确,务必使用GCJ-02获取坐标,最后调用getShop()方法获取商品列表数据
(2)如果用户没有关闭地理位置功能。可以直接获取地理位置并获取商铺列表数据。
<template>
<view class="page">
<view class="status_bar"></view>
<view class="header">
<view :class="{'search-header':true,ipx:isIpx}">
<view class="search-wrap">
<view class="icon"></view>
<view class="text">请输入商家名或菜品</view>
</view>
</view>
</view>
<view class="shop-main">
<view class="shop-list" v-for="item in shops" :key="item.branch_shop_id">
<view class="shop-wrap">
<view class="image">
<image :src="item.logo"></image>
</view>
<view class="shop-info">
<view class="shop-name">{{item.branch_shop_name}}</view>
<view class="distance">{{item.distance}}</view>
<view class="address">{{item.address}}</view>
<view class="pack-btn">自提</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import {mapState,mapActions} from "vuex";
export default {
data() {
return {
}
},
onLoad() {
this.lng=0;//经度
this.lat=0;//纬度
},
onShow(){
uni.getSetting({
success:(res)=> {
//用户没有开启地位位置
if(!res.authSetting['scope.userLocation']){
uni.showModal({
title: '开启获取地理位置',
content: '请打开"位置信息"权限,找到附近的店铺',
success: (res)=> {
if (res.confirm) {
//调起客户端小程序设置界面
uni.openSetting({
success:(res2)=> {
//如果用户打开了地理位置
if(res2.authSetting['scope.userLocation']){
uni.getLocation({
type: 'gcj02',
success: (res)=> {
this.lng=res.longitude;
this.lat=res.latitude;
this.getShop({page:1,lng:this.lng,lat:this.lat});
}
});
}
}
});
}
}
});
}
}
})
uni.getLocation({
type: 'gcj02',
success: (res)=> {
this.lng=res.longitude;
this.lat=res.latitude;
this.getShop({page:1,lng:this.lng,lat:this.lat});
}
});
},
methods: {
...mapActions({
getShop:"business/getShop"
})
},
computed:{
...mapState({
isIpx:state=>state.system.isIpx,
shops:state=>state.business.shops
})
},
onShareAppMessage(res) {
return {
title: '点餐小程序',
path: '/pages/main/main'
}
}
}
</script>
<style scoped>
.page{width:100%;min-height:100vh;orverflow:hidden;}
.header{width:100%;background-color:#eb1625;overflow:hidden;position: fixed;left:0;top:0;z-index:90;}
.header .search-header{width:100%;height:170rpx;margin-top:40rpx;padding-bottom:20rpx;display:flex;justify-content: center;align-items: flex-end;}
.header .search-header.ipx{height:210rpx;}
.header .search-wrap{width:80%;height:52rpx;background-color:rgba(255,255,255,0.9);border-radius: 5px;display: flex;align-items: center;}
.header .icon{width:44rpx;height:44rpx;background-image:url("@/static/images/main/search_icon.png");background-size:100%;background-position: center;background-repeat: no-repeat;margin:0 20rpx;}
.header .text{color:#999999;font-size:28rpx;}
.shop-main{width:100%;margin-top:220rpx;}
.shop-main .shop-list{width:100%;border-bottom:1px solid #EFEFEF;box-sizing: border-box;padding:20rpx 0;}
.shop-main .shop-list .shop-wrap{width:92%;margin:0 auto;display:flex;}
.shop-main .shop-list .shop-wrap .image{width:160rpx;height:160rpx;margin-right:20rpx;}
.shop-main .shop-list .shop-wrap .image image{width:100%;height:100%;border-radius: 5px;}
.shop-main .shop-list .shop-info{width:72%;clear: both;}
.shop-main .shop-list .shop-info .shop-name{width:100%;height:44rpx;overflow:hidden;white-space: nowrap;text-overflow: ellipsis;font-size:32rpx;font-weight: bold;}
.shop-main .shop-list .shop-info .distance{font-size:28rpx;margin-top:10rpx;color:#666666}
.shop-main .shop-list .shop-info .address{font-size:28rpx;margin-top:10rpx;color:#666666;width:100%;height:44rpx;overflow:hidden;white-space: nowrap;text-overflow: ellipsis;}
.shop-main .shop-list .shop-info .pack-btn{padding:10rpx 20rpx;background-color:#eb1625;font-size:28rpx;color:#FFFFFF;display: table;border-radius: 5px;float: right;margin-top:10rpx;}
</style>