uniapp基于高德地图实现地址选择,地址搜索功能。

1,081 阅读7分钟

应用场景: app添加地址,使用的是高德地图,实现功能有地图展示,移动地图切换点位,获取点位附近地址并展示,实现该功能需要去申请高德地图api。 该方案适用于h5端和app端。

前置配置: 需要将高德申请的key和密钥在manifest.json配置文件配置好

h5使用:

必须去高德申请Web端的ksy和密钥 然后再【manifest.json】配置定位地图

  • 选择高德
  • 把申请的web的key配置在key那里
  • 把申请的web的秘钥配置在securityJsCode那里

image.png

app使用: 去高德申请安卓和ios的key--安卓申请安卓、ios申请ios

  1. 定位配置--配置系统定位、高德定位,然后配置【高德用户名】【appkey】
  2. 地图配置--配置高德,然后配置【高德用户名】【appkey】

image.png

还需要一个高德的【web服务】的key,项目中会使用到

另外还使用到了两个js文件, amap-wx.js 在lbs.amap.com/api/wx/down… 下载 jweixin.js 在unpkg.com/jweixin-mod… 下载 页面代码:

<template>
    <view class="wraper-box">
        <view class="map-container" v-if="maptype == 1">
             <view class="map-box">
        <map
          id="myMap"
          :longitude="longitude"
          :latitude="latitude"
          @regionchange="regionchangetab"
          style="height: 100%; width: 100%"
        >
          <cover-image
            :class="jump == 1 ? 'controls-play-img bounce-animation' : 'controls-play-img'"
            src="@/static/mine/map-inx.png"
          ></cover-image>
        </map>
      </view>
      <view class="sou-item-list">
        <view class="u-search-box" @click="opensea">
          <view class="search-min-box">
            <image
              src="https://lbs.gtimg.com/visual/miniprogram-plugin/location-picker/assets/s_search@2x.png"
              mode=""
            ></image>
            搜索地点
          </view>
        </view>
        <view class="list-item-name-box">
          <view class="category-scroll-view">
            <scroll-view scroll-y="true" class="scroll-Y" @scrolltolower="lower">
              <view class="scroll-view-item" v-for="(item, index) in list" :key="index" @click="setpoi(item, index)">
                <view class="poi-item-name">
                  <image src="@/static/mine/item-inx.png" mode=""></image>
                  {{ item.name }}
                </view>
                <view class="poi-address">{{ item.address }}</view>

                <view class="right-icon" v-if="locinx == index"></view>
              </view>
              <view class="loading-box" v-if="list.length == 0">
                <view class="load-animation" v-if="loading">
                  <view class="spinner"></view>
                  <view class="txt-item">加载中</view>
                </view>
              </view>
            </scroll-view>
          </view>
        </view>
        <view class="but-box">
          <view class="btn-location" @click="fixAddress">确认选点</view>
        </view>
      </view>
    </view>
    <view class="map-min-2" v-if="maptype == 2">
      <view class="map-min-2-search-box">
        <input
          v-model="keyword"
          placeholder="搜索地点"
          @input="changeword"
          @confirm="changeword"
          class="locationpicker-search-content-ipt"
        />
        <view class="locationpicker-search-clear" @click="onClearInput">
          <image
            src="https://lbs.gtimg.com/visual/miniprogram-plugin/location-picker/assets/btn_close@2x.png"
            class="locationpicker-search-clear-ipt"
            mode=""
          ></image>
        </view>
        <view class="locationpicker-search--content-btn-line"></view>
        <view class="locationpicker-search-content-btn" @click="changeword">搜索</view>
      </view>
      <u-line></u-line>
      <scroll-view scroll-y="true" class="scroll-Y">
        <block>
          <view class="scroll-view-item" v-for="(item, index) in seolist" :key="index" @click="seopoi(item, index)">
            <view class="poi-item-name">
              <image src="@/static/mine/item-inx.png" mode=""></image>
              {{ item.name }}
            </view>
            <view class="poi-address">{{ item.address }}</view>
          </view>
        </block>
        <view class="loading-box" v-if="seolist.length == 0 && keyword != ''">
          <view class="load-animation" v-if="loading">
            <view class="spinner"></view>
            <view class="txt-item">加载中</view>
          </view>
          <view class="empty-box" v-if="!loading && seolist.length == 0">
            <view class="value-txt-box-2">没有搜索到数据</view>
          </view>
        </view>
        <!-- 历史搜索记录 -->
        <view class="history-item-box" v-if="history.length == 0 && keyword == ''">
          <view class="empty-box">
            <view class="value-txt-box-1">还没有历史记录</view>
            <view class="value-txt-box-2">快来体验世界吧</view>
          </view>
        </view>
        <view class="history-item-box" v-if="history.length > 0 && keyword == ''">
          <view class="scroll-view-item" v-for="(item, index) in history" :key="index" @click="seopoi(item, index)">
            <view class="poi-item-name">
              <image src="/static/Invite/item-inx.png" mode=""></image>
              {{ item.name }}
            </view>
            <view class="poi-address">{{ item.address }}</view>
          </view>
          <view class="item-but-box" v-if="history.length > 0">
            <view class="locationpicker-clear-history-btn" @click="Clearhistory">清空历史记录</view>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>

<script>
var amapFile = require('@/static/map/amap-wx.js')
// #ifdef WEB
var wx = require('@/static/map/jweixin.js')
// #endif
import storage from '@/utils/storage.js'
export default {
  data() {
    return {
      longitude: 116.3912757,
      latitude: 39.906217,
      jump: 1,
      list: [], //附近地址列表
      loading: true,
      myAmapFun: '',
      mapObj: '', //地图对象
      locinx: '',
      location: '', //经纬度字符串
      history: [], // 搜索记录
      keyword: '',
      maptype: 1,
      page: 1,
      activeValue: {}
    }
  },
  onLoad(e) {
    // 进入页面传入的经纬度,用于编辑,可传可不传
    console.log(e, '传入的')
    if (e.lng == 'undefined') {
      let a = storage.getLocalAddress()
      if (a == '' || a == null) {
        this.location = `${this.latitude},${this.longitude}`
        return
      }
      this.longitude = a.longitude
      this.latitude = a.latitude
      this.location = `${this.latitude},${this.longitude}`
    } else {
      this.longitude = e.lng
      this.latitude = e.lat
      this.location = `${this.latitude},${this.longitude}`
    }
  },
  created: function () {
    var that = this
    that.locinx = 0
    that.mapObj = uni.createMapContext('myMap', this)
    that.myAmapFun = new amapFile.AMapWX({
      key: ************* // 高德申请的web服务key
    })
    that.attachments()
  },
  methods: {
    // 选择定位坐标地址
    setpoi(e, inx) {
      var that = this
      this.locinx = inx
      this.changel = 2
      let lon = e.location.split(',')
      this.latitude = lon[1]
      this.longitude = lon[0]
    },
    // 搜索选择
    seopoi(e) {
      var that = this
      this.changel = 2
      let lon = e.location.split(',')
      this.latitude = lon[1]
      this.longitude = lon[0]
      this.page = 1
      this.list.push(e)
      const val = this.history.find(k => k.id == e.id)
      if (!val) {
        this.history.unshift(e)
        uni.setStorage({
          key: 'locationHistory',
          data: this.history,
          success: function () {
            console.log('success')
          }
        })
      }
      that.location = e.location
      that.attachments()
      setTimeout(() => {
        that.changel = 1
      }, 1000)
      this.maptype = 1
      this.keyword = ''
    },
    // 搜索跳转
    opensea() {
      this.list = []
      this.seolist = []
      this.maptype = 2
      let _this = this
      uni.getStorage({
        key: 'locationHistory',
        success: function (res) {
          _this.history = res.data
          console.log(_this.history, 5656)
        }
      })
    },
    // 搜索
    changeword() {
      var that = this
      that.loading = true
      that.seolist = []
      let formData = {}
      if (that.Radius) {
        formData = {
          location: that.location,
          keywords: that.keyword,
          city: that.Radius,
          citylimit: true
        }
      } else {
        formData = {
          location: that.location,
          keywords: that.keyword
        }
      }
      that.myAmapFun.getInputtips({
        ...formData,
        success: function (data) {
          console.log(data, '--1--')
          data.tips.forEach((item, index) => {
            //成功回调
            if (item.location != '' && item.district != '') {
              let province = ''
              if (item.district.match(/^(.*?省|.*?自治区)/)) {
                province = item.district.match(/^(.*?省|.*?自治区)/)[0]
              }
              let city = ''
              if (item.district.replace(province, '').match(/^(.*?市|.*?地区)/)) {
                city = item.district.replace(province, '').match(/^(.*?市|.*?地区)/)[0]
              }
              let district = ''
              if ((district = item.district.replace(province + city, '').match(/^.*?(区|县|镇)/))) {
                district = item.district.replace(province + city, '').match(/^.*?(区|县|镇)/)[0]
              }
              that.seolist.push({
                id: item.id,
                name: item.name,
                address: item.address,
                location: item.location,
                province: province,
                city: city,
                district: district,
                adcode: item.adcode
              })
            }
          })
          that.count = data.count
          that.loading = false
          uni.hideLoading()
        },
        fail: function (info) {
          //失败回调
          console.log(info.errCode, 2)
          if (info.errCode == '10044') {
            console.log('######################')
            that.$u.toast('账号维度日调用量超出限制,超出部分的请求被拒绝。')
          }
        }
      })
    },
    // 确定地址
    fixAddress() {
      this.activeValue = this.list[this.locinx]
      console.log(this.activeValue)
      let routes = getCurrentPages() // 获取当前打开过的页面路由数组
      let prevPage = routes[routes.length - 2] //获取上一级路由
      let data = prevPage.data
      prevPage.$vm.getPath(this.activeValue)
      uni.navigateBack()
    },
    isEmpty(obj) {
      return Object.keys(obj).length === 0
    },
    Clearhistory() {
      let _this = this
      uni.showModal({
        title: '提示',
        content: '确定清空全部历史记录?',
        success: function (res) {
          if (res.confirm) {
            uni.removeStorage({
              key: 'locationHistory',
              success: function (res) {
                console.log('success')
                _this.history = []
              }
            })
          } else if (res.cancel) {
            console.log('用户点击取消')
          }
        }
      })
    },
    // 发生变化定位
    regionchangetab(e) {
      let _this = this
      if (e.type == 'end') {
				let location = [e.detail.centerLocation.longitude,e.detail.centerLocation.latitude]
				this.location = location
        this.page = 1
        this.list = []
        this.attachments()
      }
    },
    // 获取附近poi
    attachments() {
      var that = this
      that.myAmapFun.getPoiAround({
        location: that.location,
        page: that.page,
        success: function (data) {
          if (data) {
            data.poisData.forEach((item, index) => {
              //成功回调
              that.list.push({
                id: item.id,
                name: item.name,
                address: item.address,
                location: item.location,
                province: item.pname,
                city: item.cityname,
                district: item.adname,
                pcode: item.pcode,
                citycode: item.citycode,
                adcode: item.adcode
              })
            })
            if (!that.list[0].pcode) {
              that.list[0].pcode = that.list[1].pcode
              that.list[0].citycode = that.list[1].citycode
            }
            that.count = data.count
          }
          that.activeValue = that.list[0]
          that.loading = false
          uni.hideLoading()
        },
        fail: function (info) {
          //失败回调
          console.log(info, '错误信息')
          if (info.errCode == '10044') {
            that.$u.toast('账号维度日调用量超出限制,超出部分的请求被拒绝。')
          }
        }
      })
    },
    // 下拉加载
    lower() {
      if (this.list.length != this.count) {
        this.page++
        uni.showLoading({
          title: '加载中...'
        })
        this.attachments()
      }
    }
  }
}
</script>

css样式:

<style lang="scss" scoped>
.wraper-box {
  height: calc(100vh - 44px);
}
.map-container {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  height: 100%;
}
.map-box {
  width: 100%;
  height: 55%;
}
.position-play-img {
  bottom: 32rpx;
  height: 66rpx;
  right: 16rpx;
  width: 66rpx;
  border-radius: 50%;
  background-color: #fff;
  overflow: hidden;
  z-index: 9999;
  position: absolute;
}
.controls-play-img {
  height: 88rpx;
  left: 50%;
  margin-left: -40rpx;
  margin-top: -88rpx;
  top: 50%;
  width: 80rpx;
  z-index: 9999;
  position: absolute;
}
.load-animation {
  height: 100rpx;
  margin: auto;
}
.spinner {
  width: 50rpx;
  margin: auto;
  height: 50rpx;
  border: 6rpx solid rgba(0, 0, 0, 0.3);
  border-top-color: #ffffff;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
.sou-item-list {
  width: 100%;
  height: 45%;

  .u-search-box {
    padding: 20rpx 24rpx;
    .search-min-box {
      background: #f2f2f2;
      border-radius: 14rpx;
      box-sizing: border-box;
      color: #999;
      font-size: 28rpx;
      height: 68rpx;
      line-height: 40rpx;
      padding: 14rpx 22rpx;

      image {
        display: inline-block;
        height: 32rpx;
        vertical-align: middle;
        width: 32rpx;
      }
    }
  }
  .bounce-animation {
    animation: bounce 0.6s alternate;
    /* 动画名称、持续时间、重复次数和交替方向 */
  }
  @keyframes bounce {
    0% {
      top: 50%;
      animation-timing-function: ease-in;
    }

    /* 动画开始时图片位于原始位置 */
    50% {
      top: 40%;
    }

    /* 动画进行到一半时,图片向上移动10像素 */
    100% {
      top: 50%;
      animation-timing-function: ease-out;
    }

    /* 动画结束时回到原始位置 */
  }

  .list-item-name-box {
    height: calc(100% - 108rpx - 100rpx);
    // width: calc(100% - 60rpx);
    padding: 0 20rpx 0 40rpx;

    .category-scroll-view {
      width: 100%;
      height: 100%;

      .scroll-Y {
        width: 100%;
        height: 100%;

        .scroll-view-item {
          border-bottom: 2rpx solid #e5e5e5;
          margin-right: 20rpx;
          padding: 32rpx 0 30rpx;
          position: relative;

          .poi-item-name {
            color: #333;
            font-size: 32rpx;
            line-height: 44rpx;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            width: 90%;

            image {
              display: inline-block;
              height: 36rpx;
              margin-right: 8rpx;
              margin-top: -8rpx;
              vertical-align: middle;
              width: 36rpx;
            }
          }

          .poi-address {
            color: #666;
            font-size: 26rpx;
            line-height: 36rpx;
            margin-left: 44rpx;
            margin-top: 10rpx;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            width: 80%;
          }

          .right-icon {
            border-bottom: 4rpx solid #427cff;
            border-left: 4rpx solid #427cff;
            height: 8rpx;
            margin-top: -2rpx;
            position: absolute;
            right: 0;
            top: 50%;
            -webkit-transform: rotate(-45deg);
            transform: rotate(-45deg);
            width: 20rpx;
          }
        }

        .loading-box {
          height: 100%;
          width: 100%;
          display: flex;
        }
      }
    }
  }

  .but-box {
    padding: 20rpx 40rpx 22rpx;
    border-top: 2rpx solid #e5e5e5;
    padding-bottom: 22rpx;

    .btn-location {
      background: #427cff;
      border-radius: 200rpx;
      color: #fff;
      font-size: 32rpx;
      height: 68rpx;
      line-height: 68rpx;
      text-align: center;
    }
  }
}
.map-min-2 {
  padding-top: 30rpx;
}
// 搜索页面
.map-min-2-search-box {
  height: 84rpx;
  background: #efefef;
  border-radius: 7px;
  height: 42px;
  margin: 0 14px 10px;
  display: -webkit-flex;
  display: flex;
  -webkit-justify-content: space-around;
  justify-content: space-around;

  input {
    cursor: auto;
    display: block;
    font-family: UICTFontTextStyleBody;
    height: 1.4rem;
    min-height: 1.4rem;
    overflow: hidden;
    text-overflow: clip;
    white-space: nowrap;
  }

  .locationpicker-search-content-ipt {
    height: 42px;
    padding-left: 10px;
    width: 70%;
  }

  .locationpicker-search-clear {
    height: 43px;
    text-align: center;
    width: 33px;

    .locationpicker-search-clear-ipt {
      height: 16px;
      margin-top: 13px;
      width: 16px;
    }
  }

  .locationpicker-search--content-btn-line {
    background: #c7c7c7;
    height: 16px;
    margin: 13px -10px 0 0;
    width: 1px;
  }

  .locationpicker-search-content-btn {
    color: #427cff;
    height: 42px;
    line-height: 42px;
    padding: 0 10px 0 20px;
  }
}
.scroll-Y {
  height: calc(100vh - 87px - 150rpx);
  width: 100vw;

  .scroll-view-item {
    border-bottom: 2rpx solid #e5e5e5;
    padding: 30rpx 40rpx 30rpx 40rpx;
    position: relative;

    .poi-item-name {
      color: #333;
      font-size: 32rpx;
      line-height: 44rpx;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: 90%;

      image {
        display: inline-block;
        height: 36rpx;
        margin-right: 8rpx;
        margin-top: -8rpx;
        vertical-align: middle;
        width: 36rpx;
      }
    }

    .poi-address {
      color: #666;
      font-size: 26rpx;
      line-height: 36rpx;
      margin-left: 44rpx;
      margin-top: 10rpx;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      width: 80%;
    }
  }

  // 历史
  .history-item-box {
    width: 100vw;
    height: 100%;

    .empty-box {
      padding: 200rpx 0;
      text-align: center;
      width: 100%;

      .value-txt-box-1 {
        margin: 10rpx auto;
      }

      .value-txt-box-2 {
        color: #666;
        font-size: 26rpx;
        margin: 0 auto;
      }
    }

    .item-but-box {
      height: 160rpx;
      padding-top: 20rpx;

      .locationpicker-clear-history-btn {
        border: 2rpx solid #c7c7c7;
        border-radius: 32rpx;
        color: #666;
        font-size: 26rpx;
        height: 60rpx;
        line-height: 60rpx;
        margin: 0 auto;
        text-align: center;
        width: 268rpx;
      }
    }

    .scroll-view-item {
      border-bottom: 2rpx solid #e5e5e5;
      padding: 30rpx 40rpx 30rpx 40rpx;
      position: relative;

      .poi-item-name {
        color: #333;
        font-size: 32rpx;
        line-height: 44rpx;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        width: 90%;

        image {
          display: inline-block;
          height: 36rpx;
          margin-right: 8rpx;
          margin-top: -8rpx;
          vertical-align: middle;
          width: 36rpx;
        }
      }

      .poi-address {
        color: #666;
        font-size: 26rpx;
        line-height: 36rpx;
        margin-left: 44rpx;
        margin-top: 10rpx;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        width: 80%;
      }
    }
  }
}
.loading-box {
  height: 100%;
  width: 100%;
  display: flex;

  .empty-box {
    padding: 200rpx 0;
    text-align: center;
    width: 100%;

    .value-txt-box-1 {
      margin: 10rpx auto;
    }

    .value-txt-box-2 {
      color: #666;
      font-size: 26rpx;
      margin: 0 auto;
    }
  }
}
</style>