微信小程序、H5、APP百度地图组件的实现(定位、获取定位附近地址列表、移动地图获取地图中心附近地址列表)
最新在开发公司用的百度地图实现,发现社区这方向资料参差不齐,很多都有残缺。下面看我的例子来一起做个百度地图的组件吧(仅限在H5、APP、微信小程序中使用)
在微信小程序中用了取巧的方式,使用uni自带map组件(在微信小程序中,map组件为腾讯地图),同时在百度后台不仅仅需要创建一个Web、iOS、Android的AK,同时还需要再创建一个服务器的AK,主要是用在微信小程序中,用来将地图获取的经纬度进行转换、逆地理编码、POI查询等功能。
在微信管理后台需要开启定位权限,具体看uni.getLocation的要求。
下方代码中会指出哪里使用百度地图Web AK,哪里使用百度地图服务器 AK。请详细查看下方代码注释。
1、创建一个定位公共类
起名:location.js
代码如下:
// 获取位置信息
const getLocation = () => {
return new Promise((resolve, reject) => {
uni.showLoading({title: '获取位置中'});
// #ifdef APP-PLUS || H5
uni.getLocation({
/*
* 目前国内主要有以下三种坐标系:
* WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。
* GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
* BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。
*
* 如果使用的定位为手机自带定位,那么type 使用 GCJ02。
* 如果是使用百度地图定位,那么type 使用 BD09。(需要在项目中的manifest.json文件配置 百度地图定位的AK)
* 如果是使用高德地图、腾讯地图定位,那么type 使用 GCJ02。(需要在项目中的manifest.json文件配置 高德地图定位的AK)
* 如果是使用谷歌地图定位,那么type 使用 WGS84。(需要在项目中的manifest.json文件配置 谷歌地图定位的AK)
*/
type: 'BD09',
geocode: true,
success: function(data) {
resolve(data)
},
fail: function(error) {
reject(error)
},
complete: function() {
uni.hideLoading();
}
})
// #endif
// #ifdef MP-WEIXIN
uni.getLocation({
/*
* 目前国内主要有以下三种坐标系:
* WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。
* GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
* BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。
*
* 如果使用的定位为手机自带定位,那么type 使用 GCJ02。
* 如果是使用百度地图定位,那么type 使用 BD09。(需要在项目中的manifest.json文件配置 百度地图定位的AK)
* 如果是使用高德地图、腾讯地图定位,那么type 使用 GCJ02。(需要在项目中的manifest.json文件配置 高德地图定位的AK)
* 如果是使用谷歌地图定位,那么type 使用 WGS84。(需要在项目中的manifest.json文件配置 谷歌地图定位的AK)
*/
type: 'GCJ02',
geocode: true,
success: function(data) {
resolve(data)
},
fail: function(error) {
reject(error)
},
complete: function() {
uni.hideLoading();
}
})
// #endif
})
};
/**
* 检测是否开启定位
*/
const checkOpenGPSServiceByAndroidIOS = () => {
let system = uni.getSystemInfoSync(); // 获取系统信息
if (system.platform === 'android') { // 判断平台
var context = plus.android.importClass("android.content.Context");
var locationManager = plus.android.importClass("android.location.LocationManager");
var main = plus.android.runtimeMainActivity();
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
if (!mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER)) {
uni.showModal({
title: '温馨提示',
content: '您未开启定位服务,请打开定位服务功能,以便获取您的位置!',
showCancel: true,
success() {
var Intent = plus.android.importClass('android.content.Intent');
var Settings = plus.android.importClass('android.provider.Settings');
var intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
main.startActivity(intent); // 打开系统设置GPS服务页面
}
});
return {s: 'android', b: false};
} else {
return {s: 'android', b: true};
}
} else if (system.platform === 'ios') {
var cllocationManger = plus.ios.import("CLLocationManager");
var enable = cllocationManger.locationServicesEnabled();
var status = cllocationManger.authorizationStatus();
plus.ios.deleteObject(cllocationManger);
if (enable && status != 2) {
// 手机系统的定位已经打开
return {s: 'ios', b: true};
} else {
// 手机系统的定位没有打开
uni.showModal({
title: '提示',
content: '请前往设置-隐私-定位服务打开定位服务功能',
showCancel: true,
success() {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
var setting2 = NSURL2.URLWithString("app-settings:"); // UIApplicationOpenSettingsURLString
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
}
});
return {s: 'ios', b: false};
}
} else {
return {s: 'h5', b: false};
}
}
const getLocationChange = () => {
return new Promise((resolve, reject) => {
uni.startLocationUpdate({
success: function() {
uni.onLocationChange(function (res) {
// 纬度:res.latitude 经度:res.longitude
resolve(res)
});
uni.onLocationChangeError(function (error) {
reject(error);
});
},
fail: function(error) {
reject(error);
},
complete: function() {
// 调用开启小程序接收位置消息 API 完成
}
});
})
}
const stopLocationUpdate = () => {
uni.stopLocationUpdate(function () {
// 关闭监听实时位置变化,前后台都停止消息接收。
})
}
export default {
getLocation: getLocation,
getLocationChange: getLocationChange,
stopLocationUpdate: stopLocationUpdate,
checkOpenGPSServiceByAndroidIOS: checkOpenGPSServiceByAndroidIOS,
}
2、创建一个名字叫 xn-map.vue 的组件 (不知道uniapp 如何创建组件,自行百度一下)
创建完后,记得进行引入:
import xnMap from '@/components/xn-map/xn-map.vue';
Vue.component('xn-map', xnMap);
以下是 xn-map.vue 代码:
代码中的ak记得换成自己的百度地图web ak 和 百度地图服务器ak
<template>
<view class="baidu_map">
<view :style="!showSearch ? 'width: 100%; height: 100%; position: relative;' : 'width: 100%; height: 50%; position: relative;'">
<!-- #ifdef APP-PLUS || H5 -->
<view
id="myMap"
class="myMap"
:prop="dicValue"
:change:prop="bmap.changeValue"
>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<map
class="myMap"
id="myMap"
scale="17"
enable-rotate="false"
show-location="true"
:latitude="maplatitude"
:longitude="mpalongitude"
@regionchange="regionchange"
></map>
<!-- #endif -->
<view class="location" @click="clickLocation">
<u--image :src="sLocationIcon" width="28px" height="28px"></u--image>
</view>
<view v-if="showSearch" class="marking"></view>
</view>
<view v-if="showSearch" class="address_list">
<view class="search">
<u-search :clearabled="true" v-model="sQueryValue" @search="searchQueryValue" @custom="searchQueryValue"></u-search>
</view>
<view class="list">
<view v-for="(item, index) in arrayAddress" :key="index" @click="clickAddressCell(item, index)">
<view class="location_cell">
<view class="centent">
<view class="title">
{{ isEmptyString(item.title) ? '--' : item.title }}
</view>
<view class="sub_title">
{{ item.address }}
</view>
</view>
<view class="right" v-if="item.checked">
<u-icon name="checkbox-mark" color="#2979ff" size="18"></u-icon>
</view>
</view>
<u-line></u-line>
</view>
</view>
<view class="footer" :style="sSearchStyle">
<u-button type="primary" text="保存" @click="clickFootButton"></u-button>
</view>
</view>
</view>
</template>
<script>
import location from '@/tool/location.js';
export default {
name:"crm-map",
props: {
showSearch: {
type: Boolean,
default() {
return false;
}
},
},
data() {
return {
sSearchStyle: "",
dicValue: null,
sLocationIcon: "/static/map_location.png",
sQueryValue: "",
arrayAddress: [],
iLastIndex: 0,
// #ifdef MP-WEIXIN
mapObjs: null,
needSearch: true,
city: "深圳市",
maplatitude: 22.685393,
maplongitude: 113.798274,
// #endif
};
},
mounted() {
let res = this.getSystemInfo();
this.sSearchStyle = 'margin-bottom: ' + res.safeAreaInsets.bottom + 'px;';
// #ifdef APP-PLUS || H5
this.dicValue = { type: 'needOtherAddress', needSerach: this.showSearch, guid: uni.$u.guid(), };
// #endif
// #ifdef MP-WEIXIN
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
this.clickLocation();
// #endif
},
methods: {
/*
* 在改变 dicValue 时,或许你会有疑惑,为啥字典中都要带一个自动生成 32 位的guid
* 这是因为,如果没有带这个自动生成的guid,在renderjs中,当改变的 dicValue 是同一个值,监听函数是不会被调用的(H5会被调用,APP不会)
* 所以在字典中添加一个 自动生成的 guid,有助于renderjs中监听函数changeValue的生效。
*/
// #ifdef APP-PLUS || H5
// 显示自身定位位置 回调
showMarking() {
if (this.showSearch) {
this.dicValue = { type: 'addEventListener', guid: uni.$u.guid(), }
}
setTimeout(() => {
this.clickLocation();
}, 200)
},
// #endif
// #ifdef MP-WEIXIN
regionchange(val) {
// 在安卓中是 end 事件
if (val.type === 'end' && this.needSearch) {
this.getCenterLatLong();
return;
}
// 在ios中是 regionchange
if (val.type ==='regionchange' && this.needSearch) {
this.getCenterLatLong();
return;
}
},
// 获取中心点位置
getCenterLatLong() {
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
const than = this;
this.mapObjs.getCenterLocation({
success: res => {
if (res.errMsg === 'getMapCenterLocation:ok') {
than.circularRegionRetrieval(res.latitude, res.longitude);
} else {
uni.showToast({ icon: 'none', title: res.errMsg || '获取位置出错', duration: 3000 });
}
},
fail: res => {
uni.showToast({ icon: 'none', title: res.errMsg || '获取位置出错', duration: 3000 });
},
complete: res => {}
})
},
// 设置地图中心点
setMapCenter(latitude, longitude) {
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
this.needSearch = false;
const than = this;
this.mapObjs.moveToLocation({
longitude: longitude,
latitude: latitude,
success: res => {},
fail: res => {},
complete: res => {
setTimeout(() => {
than.needSearch = true;
}, 500)
}
})
},
// 坐标转换
locationConversion(latitude, longitude, sendLocation, sendAddress, dicAddress) {
let sUrl = 'https://api.map.baidu.com/geoconv/v2/';
let params = {
ak: '百度地图服务器ak',
output: 'json',
model: '1',
coords: `${longitude},${latitude}`
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
uni.hideLoading();
if (res.status === 0) {
if (sendLocation) {
this.sendLocation({longitude: res.result[0].x, latitude: res.result[0].y})
}
if (sendAddress) {
let dicData = {
title: dicAddress.title,
address: dicAddress.address,
point: {lat: res.result[0].y, lng: res.result[0].x},
checked: true
}
this.$emit("clickSave", dicData);
}
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.hideLoading();
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// 获取当前自身坐标点 地理信息
getMyAddress(latitude, longitude, type) {
let sUrl = 'https://api.map.baidu.com/reverse_geocoding/v3/';
let params = {
ak: '百度地图服务器ak',
output: 'json',
coordtype: type,
location: `${latitude},${longitude}`
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
if (res.status === 0) {
this.city = res.result.addressComponent.city;
this.updateMyAddress({
address: res.result.formatted_address,
txPoint: res.result.location,
})
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// 圆形区域检索
circularRegionRetrieval(latitude, longitude) {
let sUrl = 'https://api.map.baidu.com/place/v2/search';
let params = {
ak: '百度地图服务器ak',
output: 'json',
query: '公司企业$房地产$美食$酒店$购物$生活服务$休闲娱乐$医疗$交通设施$政府机构',
coord_type: 2,
ret_coordtype: 'gcj02ll',
location: `${latitude},${longitude}`,
radius: 1000,
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
if (res.status === 0) {
let arrayAddressList = [];
for (let item of res.results) {
arrayAddressList.push({
title: item.name,
address: item.address,
point: item.location,
checked: false,
});
}
this.updateAddressList(arrayAddressList);
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// 地点输入检索
locationInputRetrieval() {
let sUrl = 'https://api.map.baidu.com/place/v2/suggestion';
let params = {
ak: '百度地图服务器ak',
output: 'json',
ret_coordtype: 'gcj02ll',
query: this.sQueryValue,
region: this.city,
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
if (res.status === 0) {
let arrayAddressList = [];
let i = 0;
for (let item of res.result) {
arrayAddressList.push({
title: item.name,
address: item.address,
point: item.location,
checked: i === 0 ? true: false
});
i = i + 1;
}
this.updateAddressList(arrayAddressList);
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// #endif
// 更新地址列表
updateAddressList(val) {
this.iLastIndex = 0;
this.arrayAddress = val;
},
// 向父页面发送当前坐标经纬度
sendLocation(val) {
this.$emit("sendLocation", val);
},
// 向父页面发送当前坐标地址信息
updateMyAddress(val) {
this.$emit("sendMyAddress", val);
},
// 获取自身当前经纬度
clickLocation() {
// #ifdef APP-PLUS
let dicInfo = location.checkOpenGPSServiceByAndroidIOS();
if (dicInfo.s !== 'h5') {
if (dicInfo.b) {
location.getLocation().then((resp) => {
this.dicValue = {
type: 'addMyLocationIcon',
longitude: resp.longitude,
latitude: resp.latitude,
guid: uni.$u.guid(),
}
this.sendLocation({longitude: resp.longitude, latitude: resp.latitude});
}).catch((error) => {
uni.showToast({ icon: 'none', title: error.errMsg || '获取位置出错', duration: 3000 });
});
}
} else {
this.dicValue = { type: 'getH5LocationPosition', guid: uni.$u.guid(), }
}
// #endif
// #ifdef H5
this.dicValue = { type: 'getH5LocationPosition', guid: uni.$u.guid(), }
// #endif
// #ifdef MP-WEIXIN
location.getLocation().then((resp) => {
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
this.maplatitude = resp.latitude;
this.maplongitude = resp.longitude;
this.needSearch = false;
const than = this;
this.mapObjs.moveToLocation({
longitude: resp.longitude,
latitude: resp.latitude,
success: res => {
than.getMyAddress(resp.latitude, resp.longitude, 'gcj02ll');
than.circularRegionRetrieval(resp.latitude, resp.longitude);
},
fail: res => {
uni.showToast({ icon: 'none', title: res.errMsg || '获取位置出错', duration: 3000 });
},
complete: res => {
setTimeout(() => {
than.needSearch = true;
}, 500)
}
})
this.locationConversion(resp.latitude, resp.longitude, true, false, null);
}).catch((error) => {
uni.showToast({ icon: 'none', title: error.errMsg || '获取位置出错111', duration: 3000 });
});
// #endif
},
// 搜索按钮点击事件
searchQueryValue() {
uni.hideKeyboard();
// #ifdef APP-PLUS || H5
this.dicValue = { type: 'searchQueryValue', sSearchValue: this.sQueryValue, guid: uni.$u.guid(), };
// #endif
// #ifdef MP-WEIXIN
this.locationInputRetrieval();
// #endif
},
// 地址信息Cell 点击事件
clickAddressCell(item, index) {
if (this.arrayAddress[index].checked) {
this.iLastIndex = index;
return;
}
if (this.iLastIndex === index) {
this.arrayAddress[this.iLastIndex].checked = !this.arrayAddress[this.iLastIndex].checked;
} else {
this.arrayAddress[this.iLastIndex].checked = !this.arrayAddress[this.iLastIndex].checked;
this.arrayAddress[index].checked = !this.arrayAddress[index].checked;
}
this.iLastIndex = index;
// #ifdef APP-PLUS || H5
this.dicValue = { type: 'moveMapCentre', point: {longitude: item.point.lng, latitude: item.point.lat}, guid: uni.$u.guid(), };
// #endif
// #ifdef MP-WEIXIN
this.setMapCenter(item.point.lat, item.point.lng);
// #endif
},
// 保存按钮 点击事件
clickFootButton() {
if (this.arrayAddress.length === 0) {
uni.showToast({ icon: 'none', title: '请选择您要保存的地址', duration: 3000 });
return;
}
// #ifdef APP-PLUS || H5
this.$emit("clickSave", this.arrayAddress[this.iLastIndex]);
// #endif
// #ifdef MP-WEIXIN
uni.showLoading({title:"加载中...", mask: true})
this.locationConversion(this.arrayAddress[this.iLastIndex].point.lat, this.arrayAddress[this.iLastIndex].point.lng, false, true, this.arrayAddress[this.iLastIndex]);
// #endif
},
}
}
</script>
<!-- #ifdef APP-PLUS || H5 -->
<script module="bmap" lang="renderjs">
export default {
data() {
return {
map: null,
locationImg: '/static/location.png', // require("/static/location.png"),
innerValue: null,
needOtherAddress: false,
};
},
mounted(){
// 初始化百度地图
this.initBaiDuMap();
},
methods: {
// 动态创建Script标签
createScript(url) {
return new Promise((resolve,reject) => {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = () => { resolve() };
script.onerror = () => { reject() };
document.head.appendChild(script);
})
},
initBaiDuMap() {
const ak = '百度地图web ak';
if (typeof window.BMap === 'function') {
this.initMap();
} else {
window.init = () => this.initMap();
this.createScript(`https://api.map.baidu.com/api?v=3.0ak=${ak}&callback=init`)
}
},
async initMap() {
// myMap 要渲染地图的view的id
this.map = new BMap.Map("myMap");
this.map.centerAndZoom(this.getPoint(113.804346, 22.691386), 17); // 设置中心点
var scaleCtrl = new BMap.ScaleControl(); // 添加比例尺控件
this.map.addControl(scaleCtrl);
this.$ownerInstance.callMethod("showMarking");
},
// 转换地图坐标点 经度longitude 纬度latitude
getPoint(longitude, latitude) {
return new BMap.Point(longitude, latitude);
},
// 接收Value改变时的数据,用来操作百度地图的API
changeValue(val) {
if (val === null || val === undefined) {
return
}
this.innerValue = val;
if (this.innerValue.type === 'addMyLocationIcon') {
this.addMyLocationIcon(this.innerValue);
} else if (this.innerValue.type === 'getH5LocationPosition') {
this.getH5LocationPosition();
} else if (this.innerValue.type === 'addEventListener') {
this.addBaiduEventListener();
} else if (this.innerValue.type === 'searchQueryValue') {
this.getSearchKeyAddreeList(this.innerValue.sSearchValue);
} else if (this.innerValue.type === 'moveMapCentre') {
this.moveMapCentre(this.innerValue.point);
} else if (this.innerValue.type === 'needOtherAddress') {
this.needOtherAddress = this.innerValue.needSerach;
} else {
}
this.innerValue = null;
},
// 添加自身定位坐标点 并且将地图中心点移动至定位坐标点 经度longitude 纬度latitude
addMyLocationIcon(val) {
// 清除地图上所有覆盖物
this.map.clearOverlays();
let point = this.getPoint(val.longitude, val.latitude);
// 构造函数: 以给定的图像地址和大小创建图标对象实例
var startIcon = new BMap.Icon(
this.locationImg,
new BMap.Size(30, 30)
);
// 设置图标的大小
startIcon.setImageSize(new BMap.Size(30, 30));
var overlay = new BMap.Marker(point, {icon: startIcon});
// 将覆盖物添加到地图中,一个覆盖物实例只能向地图中添加一次
this.map.addOverlay(overlay);
// 将地图中心 移动至定位点
this.map.panTo(point);
this.getLocationMyAddress(point);
},
// 将地图中心点移动某个经纬度上 经度longitude 纬度latitude
moveMapCentre(val) {
let point = this.getPoint(val.longitude, val.latitude);
// 将地图中心 移动至定位点
this.map.panTo(point);
},
// 调用浏览器H5定位接口进行定位 (优先调用浏览器H5定位接口,如果失败会调用IP定位, IP定位:根据用户IP 返回城市级别的定位结果)
getH5LocationPosition() {
/* 关于状态码
* BMAP_STATUS_SUCCESS 检索成功。对应数值“0”
* BMAP_STATUS_CITY_LIST 城市列表。对应数值“1”
* BMAP_STATUS_UNKNOWN_LOCATION 位置结果未知。对应数值“2”
* BMAP_STATUS_UNKNOWN_ROUTE 导航结果未知。对应数值“3”
* BMAP_STATUS_INVALID_KEY 非法密钥。对应数值“4”
* BMAP_STATUS_INVALID_REQUEST 非法请求。对应数值“5”
* BMAP_STATUS_PERMISSION_DENIED 没有权限。对应数值“6”。(自 1.1 新增)
* BMAP_STATUS_SERVICE_UNAVAILABLE 服务不可用。对应数值“7”。(自 1.1 新增)
* BMAP_STATUS_TIMEOUT 超时。对应数值“8”。(自 1.1 新增)
*/
const than = this;
var geolocation = new BMap.Geolocation();
geolocation.getCurrentPosition((r) => {
if(geolocation.getStatus() === BMAP_STATUS_SUCCESS){
than.addMyLocationIcon({longitude: r.point.lng, latitude: r.point.lat});
than.$ownerInstance.callMethod("sendLocation", {longitude: r.point.lng, latitude: r.point.lat});
} else {
switch(geolocation.getStatus()) {
case 2:
alert('位置结果未知,获取位置失败。');
break;
case 3:
alert('导航结果未知,获取位置失败。');
break;
case 4:
alert('非法密钥获取位置失败。');
break;
case 5:
alert('对不起,非法请求位置,获取位置失败。');
break;
case 6:
alert('对不起,当前没有权限,获取位置失败。');
break;
case 7:
alert('对不起,服务不可用,获取位置失败。');
break;
case 8:
alert('对不起,请求超时,获取位置失败。');
break;
default:
alert('定位发生未知错误,请重试!');
break;
}
}
},{ enableHighAccuracy: true }); // 指示浏览器获取高精度的位置,默认false
},
// 添加百度地图拖拽完成事件 回调
addBaiduEventListener() {
const than = this;
this.map.addEventListener('dragend', function (e) {
const center = than.map.getCenter();
than.getLocationAddress(center);
});
},
// 获取经纬度坐标 对应的地址信息
getLocationAddress(center) {
const than = this;
const geoc = new BMap.Geocoder();
geoc.getLocation(center, function(rs) {
if (rs !== undefined && rs !== null) {
var arrayAddressList = [];
arrayAddressList.push({
title: rs.business,
address: rs.address,
point: rs.point,
checked: true,
});
for (let item of rs.surroundingPois) {
arrayAddressList.push({
title: item.title,
address: item.address,
point: item.point,
checked: false
});
}
than.$ownerInstance.callMethod("updateAddressList", arrayAddressList);
}
});
},
// 获取自身经纬度坐标 对应的地址信息
getLocationMyAddress(center) {
const than = this;
const geoc = new BMap.Geocoder();
geoc.getLocation(center, function(rs) {
if (rs !== undefined && rs !== null) {
var dicAddress = {
address: rs.address,
point: rs.point,
};
than.$ownerInstance.callMethod("updateMyAddress", dicAddress);
}
});
},
// 根据搜索关键字获取地址列表信息
getSearchKeyAddreeList(sSearchKey) {
const than = this;
const locationController = new BMap.LocalSearch(this.map, {
onSearchComplete: function(results) {
var arrayAddressList = [];
var data = [];
for (var i = 0; i < results.getCurrentNumPois(); i ++) {
data.push(results.getPoi(i));
}
var i = 0;
for (let item of data) {
arrayAddressList.push({
title: item.title,
address: item.address,
point: item.point,
checked: i === 0 ? true : false,
});
i = i + 1;
}
than.$ownerInstance.callMethod("updateAddressList", arrayAddressList);
if (arrayAddressList.length > 0) {
than.moveMapCentre({longitude: arrayAddressList[0].point.lng, latitude: arrayAddressList[0].point.lat})
}
},
});
locationController.setPageCapacity(10);
locationController.search(sSearchKey);
}
},
}
</script>
<!-- #endif -->
<style scoped>
.baidu_map {
width: 100%;
height: 100%;
}
.baidu_map .myMap {
width: 100%;
height: 100%;
}
.baidu_map .location {
position: absolute;
width: 40px;
height: 40px;
top: 10px;
right: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.baidu_map .marking {
background-image: url('/static/location_dw.png');
position: absolute;
width: 40px;
height: 40px;
left: 50%;
right: 50%;
transform: translate(-50%, -40px);
top: 50%;
/* display: flex;
justify-content: center;
align-items: center; */
}
.baidu_map .address_list {
width: 100%;
height: 50%;
overflow: hidden;
}
.baidu_map .address_list .search {
padding: 10px;
background-color: #ffffff;
}
.baidu_map .address_list .list {
width: 100%;
height: calc(100% - 104px);
overflow: scroll;
}
.baidu_map .address_list .footer {
height: 40px;
background-color: #f7f7fa;
padding: 5px;
}
.location_cell {
display: flex;
padding: 5px 10px 0px 10px;
overflow: hidden;
}
.location_cell .centent {
flex: 1;
overflow: hidden;
}
.location_cell .right {
width: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.location_cell .centent .title {
height: 24px;
font-size: 16px;
font-weight: 500;
line-height: 24px;
margin-bottom: 3px;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}
.location_cell .centent .sub_title {
font-size: 14px;
color: #222222;
text-overflow: ellipsis;
overflow: hidden;
margin-bottom: 5px;
}
</style>
3、在业务页面使用它
代码如下
<template>
<view>
<u-navbar
title="坐标获取"
:border="true"
:placeholder="true"
:pagingEnabled="false"
:autoBack="true">
</u-navbar>
<!-- showSearch 控制显示模式,true为带搜索地址功能,false为纯地图 -->
<view :style="sStyle">
<xn-map
showSearch
@clickSave="clickSave"
@sendLocation="getLocation"
/>
</view>
</view>
</template>
<script >
export default {
data() {
return {
sStyle: "",
}
},
onLoad: function(option) {},
onReady() {
let res = uni.getSystemInfoSync();
let iHeight = res.windowHeight - (44.5 + res.statusBarHeight + res.safeAreaInsets.bottom);
this.sStyle = 'width: 100vw; height: ' + iHeight + 'px;';
},
methods: {
clickSave(val) {
console.log('选定了地址');
console.log(val);
},
getLocation(val) {
console.log('获取到定位信息');
console.log(val);
}
},
}
</script>
<style scoped>
</style>
上面的百度地图有2种模式,一种就是纯地图展示(含定位功能,定位功能需要自己去将项目中的manifest.json,进行配置百度地图的ak),不展示启用搜索选地址功能。
一种就是地图加地址检索和选地址功能。
代码里面都有注释,详细的去看,保证你会了解。
最新在开发公司用的百度地图实现,发现社区这方向资料参差不齐,很多都有残缺。下面看我的例子来一起做个百度地图的组件吧(仅限在H5、APP、微信小程序中使用)
在微信小程序中用了取巧的方式,使用uni自带map组件(在微信小程序中,map组件为腾讯地图),同时在百度后台不仅仅需要创建一个Web、iOS、Android的AK,同时还需要再创建一个服务器的AK,主要是用在微信小程序中,用来将地图获取的经纬度进行转换、逆地理编码、POI查询等功能。
在微信管理后台需要开启定位权限,具体看uni.getLocation的要求。
下方代码中会指出哪里使用百度地图Web AK,哪里使用百度地图服务器 AK。请详细查看下方代码注释。
1、创建一个定位公共类
起名:location.js
代码如下:
// 获取位置信息
const getLocation = () => {
return new Promise((resolve, reject) => {
uni.showLoading({title: '获取位置中'});
// #ifdef APP-PLUS || H5
uni.getLocation({
/*
* 目前国内主要有以下三种坐标系:
* WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。
* GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
* BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。
*
* 如果使用的定位为手机自带定位,那么type 使用 GCJ02。
* 如果是使用百度地图定位,那么type 使用 BD09。(需要在项目中的manifest.json文件配置 百度地图定位的AK)
* 如果是使用高德地图、腾讯地图定位,那么type 使用 GCJ02。(需要在项目中的manifest.json文件配置 高德地图定位的AK)
* 如果是使用谷歌地图定位,那么type 使用 WGS84。(需要在项目中的manifest.json文件配置 谷歌地图定位的AK)
*/
type: 'BD09',
geocode: true,
success: function(data) {
resolve(data)
},
fail: function(error) {
reject(error)
},
complete: function() {
uni.hideLoading();
}
})
// #endif
// #ifdef MP-WEIXIN
uni.getLocation({
/*
* 目前国内主要有以下三种坐标系:
* WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。
* GCJ02:又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。
* BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。
*
* 如果使用的定位为手机自带定位,那么type 使用 GCJ02。
* 如果是使用百度地图定位,那么type 使用 BD09。(需要在项目中的manifest.json文件配置 百度地图定位的AK)
* 如果是使用高德地图、腾讯地图定位,那么type 使用 GCJ02。(需要在项目中的manifest.json文件配置 高德地图定位的AK)
* 如果是使用谷歌地图定位,那么type 使用 WGS84。(需要在项目中的manifest.json文件配置 谷歌地图定位的AK)
*/
type: 'GCJ02',
geocode: true,
success: function(data) {
resolve(data)
},
fail: function(error) {
reject(error)
},
complete: function() {
uni.hideLoading();
}
})
// #endif
})
};
/**
* 检测是否开启定位
*/
const checkOpenGPSServiceByAndroidIOS = () => {
let system = uni.getSystemInfoSync(); // 获取系统信息
if (system.platform === 'android') { // 判断平台
var context = plus.android.importClass("android.content.Context");
var locationManager = plus.android.importClass("android.location.LocationManager");
var main = plus.android.runtimeMainActivity();
var mainSvr = main.getSystemService(context.LOCATION_SERVICE);
if (!mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER)) {
uni.showModal({
title: '温馨提示',
content: '您未开启定位服务,请打开定位服务功能,以便获取您的位置!',
showCancel: true,
success() {
var Intent = plus.android.importClass('android.content.Intent');
var Settings = plus.android.importClass('android.provider.Settings');
var intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
main.startActivity(intent); // 打开系统设置GPS服务页面
}
});
return {s: 'android', b: false};
} else {
return {s: 'android', b: true};
}
} else if (system.platform === 'ios') {
var cllocationManger = plus.ios.import("CLLocationManager");
var enable = cllocationManger.locationServicesEnabled();
var status = cllocationManger.authorizationStatus();
plus.ios.deleteObject(cllocationManger);
if (enable && status != 2) {
// 手机系统的定位已经打开
return {s: 'ios', b: true};
} else {
// 手机系统的定位没有打开
uni.showModal({
title: '提示',
content: '请前往设置-隐私-定位服务打开定位服务功能',
showCancel: true,
success() {
var UIApplication = plus.ios.import("UIApplication");
var application2 = UIApplication.sharedApplication();
var NSURL2 = plus.ios.import("NSURL");
var setting2 = NSURL2.URLWithString("app-settings:"); // UIApplicationOpenSettingsURLString
application2.openURL(setting2);
plus.ios.deleteObject(setting2);
plus.ios.deleteObject(NSURL2);
plus.ios.deleteObject(application2);
}
});
return {s: 'ios', b: false};
}
} else {
return {s: 'h5', b: false};
}
}
const getLocationChange = () => {
return new Promise((resolve, reject) => {
uni.startLocationUpdate({
success: function() {
uni.onLocationChange(function (res) {
// 纬度:res.latitude 经度:res.longitude
resolve(res)
});
uni.onLocationChangeError(function (error) {
reject(error);
});
},
fail: function(error) {
reject(error);
},
complete: function() {
// 调用开启小程序接收位置消息 API 完成
}
});
})
}
const stopLocationUpdate = () => {
uni.stopLocationUpdate(function () {
// 关闭监听实时位置变化,前后台都停止消息接收。
})
}
export default {
getLocation: getLocation,
getLocationChange: getLocationChange,
stopLocationUpdate: stopLocationUpdate,
checkOpenGPSServiceByAndroidIOS: checkOpenGPSServiceByAndroidIOS,
}
2、创建一个名字叫 xn-map.vue 的组件 (不知道uniapp 如何创建组件,自行百度一下)
创建完后,记得进行引入:
import xnMap from '@/components/xn-map/xn-map.vue';
Vue.component('xn-map', xnMap);
以下是 xn-map.vue 代码:
代码中的ak记得换成自己的百度地图web ak 和 百度地图服务器ak
<template>
<view class="baidu_map">
<view :style="!showSearch ? 'width: 100%; height: 100%; position: relative;' : 'width: 100%; height: 50%; position: relative;'">
<!-- #ifdef APP-PLUS || H5 -->
<view
id="myMap"
class="myMap"
:prop="dicValue"
:change:prop="bmap.changeValue"
>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<map
class="myMap"
id="myMap"
scale="17"
enable-rotate="false"
show-location="true"
:latitude="maplatitude"
:longitude="mpalongitude"
@regionchange="regionchange"
></map>
<!-- #endif -->
<view class="location" @click="clickLocation">
<u--image :src="sLocationIcon" width="28px" height="28px"></u--image>
</view>
<view v-if="showSearch" class="marking"></view>
</view>
<view v-if="showSearch" class="address_list">
<view class="search">
<u-search :clearabled="true" v-model="sQueryValue" @search="searchQueryValue" @custom="searchQueryValue"></u-search>
</view>
<view class="list">
<view v-for="(item, index) in arrayAddress" :key="index" @click="clickAddressCell(item, index)">
<view class="location_cell">
<view class="centent">
<view class="title">
{{ isEmptyString(item.title) ? '--' : item.title }}
</view>
<view class="sub_title">
{{ item.address }}
</view>
</view>
<view class="right" v-if="item.checked">
<u-icon name="checkbox-mark" color="#2979ff" size="18"></u-icon>
</view>
</view>
<u-line></u-line>
</view>
</view>
<view class="footer" :style="sSearchStyle">
<u-button type="primary" text="保存" @click="clickFootButton"></u-button>
</view>
</view>
</view>
</template>
<script>
import location from '@/tool/location.js';
export default {
name:"crm-map",
props: {
showSearch: {
type: Boolean,
default() {
return false;
}
},
},
data() {
return {
sSearchStyle: "",
dicValue: null,
sLocationIcon: "/static/map_location.png",
sQueryValue: "",
arrayAddress: [],
iLastIndex: 0,
// #ifdef MP-WEIXIN
mapObjs: null,
needSearch: true,
city: "深圳市",
maplatitude: 22.685393,
maplongitude: 113.798274,
// #endif
};
},
mounted() {
let res = this.getSystemInfo();
this.sSearchStyle = 'margin-bottom: ' + res.safeAreaInsets.bottom + 'px;';
// #ifdef APP-PLUS || H5
this.dicValue = { type: 'needOtherAddress', needSerach: this.showSearch, guid: uni.$u.guid(), };
// #endif
// #ifdef MP-WEIXIN
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
this.clickLocation();
// #endif
},
methods: {
/*
* 在改变 dicValue 时,或许你会有疑惑,为啥字典中都要带一个自动生成 32 位的guid
* 这是因为,如果没有带这个自动生成的guid,在renderjs中,当改变的 dicValue 是同一个值,监听函数是不会被调用的(H5会被调用,APP不会)
* 所以在字典中添加一个 自动生成的 guid,有助于renderjs中监听函数changeValue的生效。
*/
// #ifdef APP-PLUS || H5
// 显示自身定位位置 回调
showMarking() {
if (this.showSearch) {
this.dicValue = { type: 'addEventListener', guid: uni.$u.guid(), }
}
setTimeout(() => {
this.clickLocation();
}, 200)
},
// #endif
// #ifdef MP-WEIXIN
regionchange(val) {
// 在安卓中是 end 事件
if (val.type === 'end' && this.needSearch) {
this.getCenterLatLong();
return;
}
// 在ios中是 regionchange
if (val.type ==='regionchange' && this.needSearch) {
this.getCenterLatLong();
return;
}
},
// 获取中心点位置
getCenterLatLong() {
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
const than = this;
this.mapObjs.getCenterLocation({
success: res => {
if (res.errMsg === 'getMapCenterLocation:ok') {
than.circularRegionRetrieval(res.latitude, res.longitude);
} else {
uni.showToast({ icon: 'none', title: res.errMsg || '获取位置出错', duration: 3000 });
}
},
fail: res => {
uni.showToast({ icon: 'none', title: res.errMsg || '获取位置出错', duration: 3000 });
},
complete: res => {}
})
},
// 设置地图中心点
setMapCenter(latitude, longitude) {
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
this.needSearch = false;
const than = this;
this.mapObjs.moveToLocation({
longitude: longitude,
latitude: latitude,
success: res => {},
fail: res => {},
complete: res => {
setTimeout(() => {
than.needSearch = true;
}, 500)
}
})
},
// 坐标转换
locationConversion(latitude, longitude, sendLocation, sendAddress, dicAddress) {
let sUrl = 'https://api.map.baidu.com/geoconv/v2/';
let params = {
ak: '百度地图服务器ak',
output: 'json',
model: '1',
coords: `${longitude},${latitude}`
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
uni.hideLoading();
if (res.status === 0) {
if (sendLocation) {
this.sendLocation({longitude: res.result[0].x, latitude: res.result[0].y})
}
if (sendAddress) {
let dicData = {
title: dicAddress.title,
address: dicAddress.address,
point: {lat: res.result[0].y, lng: res.result[0].x},
checked: true
}
this.$emit("clickSave", dicData);
}
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.hideLoading();
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// 获取当前自身坐标点 地理信息
getMyAddress(latitude, longitude, type) {
let sUrl = 'https://api.map.baidu.com/reverse_geocoding/v3/';
let params = {
ak: '百度地图服务器ak',
output: 'json',
coordtype: type,
location: `${latitude},${longitude}`
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
if (res.status === 0) {
this.city = res.result.addressComponent.city;
this.updateMyAddress({
address: res.result.formatted_address,
txPoint: res.result.location,
})
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// 圆形区域检索
circularRegionRetrieval(latitude, longitude) {
let sUrl = 'https://api.map.baidu.com/place/v2/search';
let params = {
ak: '百度地图服务器ak',
output: 'json',
query: '公司企业$房地产$美食$酒店$购物$生活服务$休闲娱乐$医疗$交通设施$政府机构',
coord_type: 2,
ret_coordtype: 'gcj02ll',
location: `${latitude},${longitude}`,
radius: 1000,
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
if (res.status === 0) {
let arrayAddressList = [];
for (let item of res.results) {
arrayAddressList.push({
title: item.name,
address: item.address,
point: item.location,
checked: false,
});
}
this.updateAddressList(arrayAddressList);
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// 地点输入检索
locationInputRetrieval() {
let sUrl = 'https://api.map.baidu.com/place/v2/suggestion';
let params = {
ak: '百度地图服务器ak',
output: 'json',
ret_coordtype: 'gcj02ll',
query: this.sQueryValue,
region: this.city,
}
uni.$u.http.get(sUrl, {params: params}).then(res => {
if (res.status === 0) {
let arrayAddressList = [];
let i = 0;
for (let item of res.result) {
arrayAddressList.push({
title: item.name,
address: item.address,
point: item.location,
checked: i === 0 ? true: false
});
i = i + 1;
}
this.updateAddressList(arrayAddressList);
} else {
uni.showToast({ icon: 'none', title: res.message || '获取位置信息出错', duration: 3000 });
}
}).catch(err => {
uni.showToast({ icon: 'none', title: err.errMsg || '获取位置信息出错', duration: 3000 });
})
},
// #endif
// 更新地址列表
updateAddressList(val) {
this.iLastIndex = 0;
this.arrayAddress = val;
},
// 向父页面发送当前坐标经纬度
sendLocation(val) {
this.$emit("sendLocation", val);
},
// 向父页面发送当前坐标地址信息
updateMyAddress(val) {
this.$emit("sendMyAddress", val);
},
// 获取自身当前经纬度
clickLocation() {
// #ifdef APP-PLUS
let dicInfo = location.checkOpenGPSServiceByAndroidIOS();
if (dicInfo.s !== 'h5') {
if (dicInfo.b) {
location.getLocation().then((resp) => {
this.dicValue = {
type: 'addMyLocationIcon',
longitude: resp.longitude,
latitude: resp.latitude,
guid: uni.$u.guid(),
}
this.sendLocation({longitude: resp.longitude, latitude: resp.latitude});
}).catch((error) => {
uni.showToast({ icon: 'none', title: error.errMsg || '获取位置出错', duration: 3000 });
});
}
} else {
this.dicValue = { type: 'getH5LocationPosition', guid: uni.$u.guid(), }
}
// #endif
// #ifdef H5
this.dicValue = { type: 'getH5LocationPosition', guid: uni.$u.guid(), }
// #endif
// #ifdef MP-WEIXIN
location.getLocation().then((resp) => {
if (this.mapObjs === undefined || this.mapObjs === null) {
this.mapObjs = uni.createMapContext('myMap', this); // 得到map实例对象
}
this.maplatitude = resp.latitude;
this.maplongitude = resp.longitude;
this.needSearch = false;
const than = this;
this.mapObjs.moveToLocation({
longitude: resp.longitude,
latitude: resp.latitude,
success: res => {
than.getMyAddress(resp.latitude, resp.longitude, 'gcj02ll');
than.circularRegionRetrieval(resp.latitude, resp.longitude);
},
fail: res => {
uni.showToast({ icon: 'none', title: res.errMsg || '获取位置出错', duration: 3000 });
},
complete: res => {
setTimeout(() => {
than.needSearch = true;
}, 500)
}
})
this.locationConversion(resp.latitude, resp.longitude, true, false, null);
}).catch((error) => {
uni.showToast({ icon: 'none', title: error.errMsg || '获取位置出错111', duration: 3000 });
});
// #endif
},
// 搜索按钮点击事件
searchQueryValue() {
uni.hideKeyboard();
// #ifdef APP-PLUS || H5
this.dicValue = { type: 'searchQueryValue', sSearchValue: this.sQueryValue, guid: uni.$u.guid(), };
// #endif
// #ifdef MP-WEIXIN
this.locationInputRetrieval();
// #endif
},
// 地址信息Cell 点击事件
clickAddressCell(item, index) {
if (this.arrayAddress[index].checked) {
this.iLastIndex = index;
return;
}
if (this.iLastIndex === index) {
this.arrayAddress[this.iLastIndex].checked = !this.arrayAddress[this.iLastIndex].checked;
} else {
this.arrayAddress[this.iLastIndex].checked = !this.arrayAddress[this.iLastIndex].checked;
this.arrayAddress[index].checked = !this.arrayAddress[index].checked;
}
this.iLastIndex = index;
// #ifdef APP-PLUS || H5
this.dicValue = { type: 'moveMapCentre', point: {longitude: item.point.lng, latitude: item.point.lat}, guid: uni.$u.guid(), };
// #endif
// #ifdef MP-WEIXIN
this.setMapCenter(item.point.lat, item.point.lng);
// #endif
},
// 保存按钮 点击事件
clickFootButton() {
if (this.arrayAddress.length === 0) {
uni.showToast({ icon: 'none', title: '请选择您要保存的地址', duration: 3000 });
return;
}
// #ifdef APP-PLUS || H5
this.$emit("clickSave", this.arrayAddress[this.iLastIndex]);
// #endif
// #ifdef MP-WEIXIN
uni.showLoading({title:"加载中...", mask: true})
this.locationConversion(this.arrayAddress[this.iLastIndex].point.lat, this.arrayAddress[this.iLastIndex].point.lng, false, true, this.arrayAddress[this.iLastIndex]);
// #endif
},
}
}
</script>
<!-- #ifdef APP-PLUS || H5 -->
<script module="bmap" lang="renderjs">
export default {
data() {
return {
map: null,
locationImg: '/static/location.png', // require("/static/location.png"),
innerValue: null,
needOtherAddress: false,
};
},
mounted(){
// 初始化百度地图
this.initBaiDuMap();
},
methods: {
// 动态创建Script标签
createScript(url) {
return new Promise((resolve,reject) => {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = () => { resolve() };
script.onerror = () => { reject() };
document.head.appendChild(script);
})
},
initBaiDuMap() {
const ak = '百度地图web ak';
if (typeof window.BMap === 'function') {
this.initMap();
} else {
window.init = () => this.initMap();
this.createScript(`https://api.map.baidu.com/api?v=3.0ak=${ak}&callback=init`)
}
},
async initMap() {
// myMap 要渲染地图的view的id
this.map = new BMap.Map("myMap");
this.map.centerAndZoom(this.getPoint(113.804346, 22.691386), 17); // 设置中心点
var scaleCtrl = new BMap.ScaleControl(); // 添加比例尺控件
this.map.addControl(scaleCtrl);
this.$ownerInstance.callMethod("showMarking");
},
// 转换地图坐标点 经度longitude 纬度latitude
getPoint(longitude, latitude) {
return new BMap.Point(longitude, latitude);
},
// 接收Value改变时的数据,用来操作百度地图的API
changeValue(val) {
if (val === null || val === undefined) {
return
}
this.innerValue = val;
if (this.innerValue.type === 'addMyLocationIcon') {
this.addMyLocationIcon(this.innerValue);
} else if (this.innerValue.type === 'getH5LocationPosition') {
this.getH5LocationPosition();
} else if (this.innerValue.type === 'addEventListener') {
this.addBaiduEventListener();
} else if (this.innerValue.type === 'searchQueryValue') {
this.getSearchKeyAddreeList(this.innerValue.sSearchValue);
} else if (this.innerValue.type === 'moveMapCentre') {
this.moveMapCentre(this.innerValue.point);
} else if (this.innerValue.type === 'needOtherAddress') {
this.needOtherAddress = this.innerValue.needSerach;
} else {
}
this.innerValue = null;
},
// 添加自身定位坐标点 并且将地图中心点移动至定位坐标点 经度longitude 纬度latitude
addMyLocationIcon(val) {
// 清除地图上所有覆盖物
this.map.clearOverlays();
let point = this.getPoint(val.longitude, val.latitude);
// 构造函数: 以给定的图像地址和大小创建图标对象实例
var startIcon = new BMap.Icon(
this.locationImg,
new BMap.Size(30, 30)
);
// 设置图标的大小
startIcon.setImageSize(new BMap.Size(30, 30));
var overlay = new BMap.Marker(point, {icon: startIcon});
// 将覆盖物添加到地图中,一个覆盖物实例只能向地图中添加一次
this.map.addOverlay(overlay);
// 将地图中心 移动至定位点
this.map.panTo(point);
this.getLocationMyAddress(point);
},
// 将地图中心点移动某个经纬度上 经度longitude 纬度latitude
moveMapCentre(val) {
let point = this.getPoint(val.longitude, val.latitude);
// 将地图中心 移动至定位点
this.map.panTo(point);
},
// 调用浏览器H5定位接口进行定位 (优先调用浏览器H5定位接口,如果失败会调用IP定位, IP定位:根据用户IP 返回城市级别的定位结果)
getH5LocationPosition() {
/* 关于状态码
* BMAP_STATUS_SUCCESS 检索成功。对应数值“0”
* BMAP_STATUS_CITY_LIST 城市列表。对应数值“1”
* BMAP_STATUS_UNKNOWN_LOCATION 位置结果未知。对应数值“2”
* BMAP_STATUS_UNKNOWN_ROUTE 导航结果未知。对应数值“3”
* BMAP_STATUS_INVALID_KEY 非法密钥。对应数值“4”
* BMAP_STATUS_INVALID_REQUEST 非法请求。对应数值“5”
* BMAP_STATUS_PERMISSION_DENIED 没有权限。对应数值“6”。(自 1.1 新增)
* BMAP_STATUS_SERVICE_UNAVAILABLE 服务不可用。对应数值“7”。(自 1.1 新增)
* BMAP_STATUS_TIMEOUT 超时。对应数值“8”。(自 1.1 新增)
*/
const than = this;
var geolocation = new BMap.Geolocation();
geolocation.getCurrentPosition((r) => {
if(geolocation.getStatus() === BMAP_STATUS_SUCCESS){
than.addMyLocationIcon({longitude: r.point.lng, latitude: r.point.lat});
than.$ownerInstance.callMethod("sendLocation", {longitude: r.point.lng, latitude: r.point.lat});
} else {
switch(geolocation.getStatus()) {
case 2:
alert('位置结果未知,获取位置失败。');
break;
case 3:
alert('导航结果未知,获取位置失败。');
break;
case 4:
alert('非法密钥获取位置失败。');
break;
case 5:
alert('对不起,非法请求位置,获取位置失败。');
break;
case 6:
alert('对不起,当前没有权限,获取位置失败。');
break;
case 7:
alert('对不起,服务不可用,获取位置失败。');
break;
case 8:
alert('对不起,请求超时,获取位置失败。');
break;
default:
alert('定位发生未知错误,请重试!');
break;
}
}
},{ enableHighAccuracy: true }); // 指示浏览器获取高精度的位置,默认false
},
// 添加百度地图拖拽完成事件 回调
addBaiduEventListener() {
const than = this;
this.map.addEventListener('dragend', function (e) {
const center = than.map.getCenter();
than.getLocationAddress(center);
});
},
// 获取经纬度坐标 对应的地址信息
getLocationAddress(center) {
const than = this;
const geoc = new BMap.Geocoder();
geoc.getLocation(center, function(rs) {
if (rs !== undefined && rs !== null) {
var arrayAddressList = [];
arrayAddressList.push({
title: rs.business,
address: rs.address,
point: rs.point,
checked: true,
});
for (let item of rs.surroundingPois) {
arrayAddressList.push({
title: item.title,
address: item.address,
point: item.point,
checked: false
});
}
than.$ownerInstance.callMethod("updateAddressList", arrayAddressList);
}
});
},
// 获取自身经纬度坐标 对应的地址信息
getLocationMyAddress(center) {
const than = this;
const geoc = new BMap.Geocoder();
geoc.getLocation(center, function(rs) {
if (rs !== undefined && rs !== null) {
var dicAddress = {
address: rs.address,
point: rs.point,
};
than.$ownerInstance.callMethod("updateMyAddress", dicAddress);
}
});
},
// 根据搜索关键字获取地址列表信息
getSearchKeyAddreeList(sSearchKey) {
const than = this;
const locationController = new BMap.LocalSearch(this.map, {
onSearchComplete: function(results) {
var arrayAddressList = [];
var data = [];
for (var i = 0; i < results.getCurrentNumPois(); i ++) {
data.push(results.getPoi(i));
}
var i = 0;
for (let item of data) {
arrayAddressList.push({
title: item.title,
address: item.address,
point: item.point,
checked: i === 0 ? true : false,
});
i = i + 1;
}
than.$ownerInstance.callMethod("updateAddressList", arrayAddressList);
if (arrayAddressList.length > 0) {
than.moveMapCentre({longitude: arrayAddressList[0].point.lng, latitude: arrayAddressList[0].point.lat})
}
},
});
locationController.setPageCapacity(10);
locationController.search(sSearchKey);
}
},
}
</script>
<!-- #endif -->
<style scoped>
.baidu_map {
width: 100%;
height: 100%;
}
.baidu_map .myMap {
width: 100%;
height: 100%;
}
.baidu_map .location {
position: absolute;
width: 40px;
height: 40px;
top: 10px;
right: 10px;
display: flex;
justify-content: center;
align-items: center;
}
.baidu_map .marking {
background-image: url('/static/location_dw.png');
position: absolute;
width: 40px;
height: 40px;
left: 50%;
right: 50%;
transform: translate(-50%, -40px);
top: 50%;
/* display: flex;
justify-content: center;
align-items: center; */
}
.baidu_map .address_list {
width: 100%;
height: 50%;
overflow: hidden;
}
.baidu_map .address_list .search {
padding: 10px;
background-color: #ffffff;
}
.baidu_map .address_list .list {
width: 100%;
height: calc(100% - 104px);
overflow: scroll;
}
.baidu_map .address_list .footer {
height: 40px;
background-color: #f7f7fa;
padding: 5px;
}
.location_cell {
display: flex;
padding: 5px 10px 0px 10px;
overflow: hidden;
}
.location_cell .centent {
flex: 1;
overflow: hidden;
}
.location_cell .right {
width: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.location_cell .centent .title {
height: 24px;
font-size: 16px;
font-weight: 500;
line-height: 24px;
margin-bottom: 3px;
overflow:hidden;
white-space:nowrap;
text-overflow:ellipsis;
}
.location_cell .centent .sub_title {
font-size: 14px;
color: #222222;
text-overflow: ellipsis;
overflow: hidden;
margin-bottom: 5px;
}
</style>
3、在业务页面使用它
代码如下
<template>
<view>
<u-navbar
title="坐标获取"
:border="true"
:placeholder="true"
:pagingEnabled="false"
:autoBack="true">
</u-navbar>
<!-- showSearch 控制显示模式,true为带搜索地址功能,false为纯地图 -->
<view :style="sStyle">
<xn-map
showSearch
@clickSave="clickSave"
@sendLocation="getLocation"
/>
</view>
</view>
</template>
<script >
export default {
data() {
return {
sStyle: "",
}
},
onLoad: function(option) {},
onReady() {
let res = uni.getSystemInfoSync();
let iHeight = res.windowHeight - (44.5 + res.statusBarHeight + res.safeAreaInsets.bottom);
this.sStyle = 'width: 100vw; height: ' + iHeight + 'px;';
},
methods: {
clickSave(val) {
console.log('选定了地址');
console.log(val);
},
getLocation(val) {
console.log('获取到定位信息');
console.log(val);
}
},
}
</script>
<style scoped>
</style>
上面的百度地图有2种模式,一种就是纯地图展示(含定位功能,定位功能需要自己去将项目中的manifest.json,进行配置百度地图的ak),不展示启用搜索选地址功能。
一种就是地图加地址检索和选地址功能。
代码里面都有注释,详细的去看,保证你会了解。
uni-app h5、app、微信小程序 全局水印组件
参考了:https://ask.dcloud.net.cn/article/35955 内容,对代码进行了修改,添加了优化
1、使用者需要在自己想要开始使用全局水印的地方开始写入,一般来说都是在首页index.vue执行的,在App.vue无法实现。本示例举例在 首页的 index.vue进行。
<!-- 水印组件.vue -->
<template>
<!-- #ifdef APP-PLUS -->
<view class="watermark_back">
<canvas class="watermark" canvas-id="watermarkCanvas" id="watermarkCanvas"></canvas>
</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="wx_watermark">
<view v-for="(item, index) in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]" :key="index">
<view class="wx_watermark-row">
<span class="wx_watermark-col" v-for="(item1, index1) in ['a1','a2','a3','a4','a5','a6']" :key="index1">{{title}}</span>
</view>
</view>
</view>
<!-- #endif -->
</template>
<script>
export default {
name:"crm-watermark",
props: {
title: {
type: String,
default() {
return '';
}
},
},
data() {
return {
};
},
mounted() {
uni.$on('initCrmWatermark', this.initCrmWatermark);
uni.$on('removeCrmWatermark', this.removeCrmWatermark);
},
destroyed() {
uni.$off('initCrmWatermark', this.initCrmWatermark)
uni.$off('removeCrmWatermark', this.removeCrmWatermark)
},
methods: {
initCrmWatermark(msg) {
// #ifdef APP-PLUS
let id = 'crm_watermark_20240423';
if (plus.nativeObj.View.getViewById(id) !== null) {
plus.nativeObj.View.getViewById(id).close();
}
let context = uni.createCanvasContext('watermarkCanvas');
context.rotate(-45 * Math.PI / 180);
context.setFontSize(15);
context.setFillStyle('rgba(200, 200, 200, 0.30)');
context.setTextAlign('left');
context.setTextBaseline('middle');
context.fillText(msg, -83, 86);
context.draw(false, function() {
uni.canvasToTempFilePath({
canvasId: "watermarkCanvas",
width: 126, // 与下方 tarArr position width 保持一致
height: 126, // 与下方 tarArr position height 保持一致
success: function(res) {
let path = res.tempFilePath;
uni.getSystemInfo({
success: function (res) {
// 水印排列行数
let row = Math.floor(res.windowHeight / 126);
let tarArr = [];
for (let i = 0; i < row + 1; i++) {
for (let j = 0; j < row + 1; j++){
tarArr.push({
tag: 'img',
src: path,
position: {
top: (126 * i) + 'px',
left: (126 * j) + 'px',
width: '126px',
height: '126px',
}
});
}
}
var watermarkView = new plus.nativeObj.View(
id,
{top:'0px', left:'0px', right: '0px', bottom: '0px'},
tarArr
);
// 拦截View控件的触屏事件,将事件穿透给下一层view
watermarkView.interceptTouchEvent(false);
watermarkView.show();
context.clearRect(0, 0, 126, 126);
context.draw();
}
});
}
});
});
// #endif
// #ifdef H5
let id = 'crm_watermark_20240423';
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id));
}
let canvas = document.createElement('canvas');
canvas.width = 126;
canvas.height = 126;
let canvas2 = canvas.getContext('2d');
canvas2.rotate(-45 * Math.PI / 180);
canvas2.font = '15px Vedana';
canvas2.fillStyle = 'rgba(200, 200, 200, 0.3)';
canvas2.textAlign = 'left';
canvas2.textBaseline = 'middle';
canvas2.fillText(msg, -83, 86);
let div = document.createElement('div');
div.id = id;
div.style.pointerEvents = 'none';
div.style.top = '0px';
div.style.left = '0px';
div.style.bottom = '0px';
div.style.right = '0px';
div.style.position = 'fixed';
div.style.zIndex = '100000';
div.style.background = 'url(' + canvas.toDataURL('image/png') + ') left top repeat';
document.body.appendChild(div);
return id;
// #endif
},
// 删除水印(仅APP与H5有效)
removeCrmWatermark() {
// #ifdef APP-PLUS
if (plus.nativeObj.View.getViewById('crm_watermark_20240423') !== null) {
plus.nativeObj.View.getViewById('crm_watermark_20240423').close();
}
// #endif
// #ifdef H5
if (document.getElementById('crm_watermark_20240423') !== null) {
document.body.removeChild(document.getElementById('crm_watermark_20240423'));
}
// #endif
},
},
}
</script>
<style scoped>
/* #ifdef APP-PLUS */
.watermark_back {
position: relative;
width: 126px;
height: 126px;
z-index: -1;
}
.watermark {
position: absolute;
left: -126px;
top: -126px;
}
/* #endif */
/* #ifdef MP-WEIXIN */
.wx_watermark {
position: fixed;
width: 200vw;
height: 150vh;
top: -20vw;
left: -50vw;
color: rgba(200, 200, 200, 0.3);
font-size: 15px;
opacity: 1;
z-index: 1000000; // 放在顶层
pointer-events: none; // 点击穿透,不影响页面交互
transform: rotate(-45deg); // 水印倾斜角度
}
.wx_watermark-col {
display: inline-block;
padding: 50rpx 40rpx;
}
.wx_watermark-row {
white-space: nowrap;
}
.wx_watermark-row:nth-child(2n+1) {
transform: translateX(10%); // 奇偶数行水印错开
}
/* #endif */
</style>
业务代码
<template>
<view>
<!-- 其他业务代码 -->
<!-- 添加水印 app与H5 只需要调用一次,即可配置全局水印 微信小程序需要每个页面都引入 -->
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
<crm-watermark :title="sWatermark"></crm-watermark>
<!-- #endif -->
</view>
</template>
<script>
data() {
return {
// 微信小程序需要每个页面都引入,并且设置水印
// #ifdef MP-WEIXIN || APP-PLUS
sWatermark: "",
// #endif
}
},
methods: {
showWatermark() {
// app与H5 只需要调用一次,即可配置全局水印
// #ifdef APP-PLUS || H5
uni.$emit('initCrmWatermark', sTitle);
// #endif
// 微信小程序需要每个页面都引入,并且设置水印
// #ifdef MP-WEIXIN || APP-PLUS
this.sWatermark = sTitle;
// #endif
},
}
如果渲染出来 在APP水印页面发现多了个 格格不入的水印,请进行 APP-PLUS 中代码的微调,这是正常的。下面让我来解释一下 (H5不会出现这个问题)
在 APP-PLUS 模式下,先是使用 uni.createCanvasContext('watermarkCanvas'),找到页面中的 canvas。并且对 canvas进行设置(如字体、旋转角度、字体颜色、文字对齐方式、文字水平对齐方式、及填充文字,并且进行 X、Y轴设置)
接着 使用 context.draw,进行渲染,这个时候,其实页面渲染成功就会出现一个 水印了,但后续调用 uni.canvasToTempFilePath(把当前画布指定区域的内容导出生成指定大小的图片) 在其中做了 for循环,将画布内容又生产了N个,并且通过plus.nativeObj.View方式加入到了页面中。这个时候页面就会多出一个看起来格格不入的水印,这个水印就是前面提到的 context.draw 的时候页面已经出来一个了。
那么如何解决这个格格不入的水印问题呢,我的方式是覆盖后将其删除。将一个通过context.draw渲染出来的水印,通过大小调整为与 uni.canvasToTempFilePath for 循环出来的 水印大小一致,将其盖在上面,这样虽然第一个水印看起来颜色深了一个点,但其他并无异常。在uni.canvasToTempFilePath for 循环成功后加入 context.clearRect(0, 0, 126, 126); context.draw(); 将其原来绘制的水印进行删除。
具体看我代码注释:下面是关键代码
width: 126, // 与下方 tarArr position width 保持一致
height: 126, // 与下方 tarArr position height 保持一致
context.clearRect(0, 0, 126, 126);
context.draw();
微信小程序由于无法实现全局水印,只能在每个页面都加入该组件,并且使用条件编译
<!-- #ifdef MP-WEIXIN-->
<crm-watermark :title="sWatermark"></crm-watermark>
<!-- #endif -->
// #ifdef MP-WEIXIN
sWatermark: "",
// #endif
// #ifdef MP-WEIXIN
this.sWatermark = sTitle;
// #endif
参考了:https://ask.dcloud.net.cn/article/35955 内容,对代码进行了修改,添加了优化
1、使用者需要在自己想要开始使用全局水印的地方开始写入,一般来说都是在首页index.vue执行的,在App.vue无法实现。本示例举例在 首页的 index.vue进行。
<!-- 水印组件.vue -->
<template>
<!-- #ifdef APP-PLUS -->
<view class="watermark_back">
<canvas class="watermark" canvas-id="watermarkCanvas" id="watermarkCanvas"></canvas>
</view>
<!-- #endif -->
<!-- #ifdef H5 -->
<view>
</view>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="wx_watermark">
<view v-for="(item, index) in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]" :key="index">
<view class="wx_watermark-row">
<span class="wx_watermark-col" v-for="(item1, index1) in ['a1','a2','a3','a4','a5','a6']" :key="index1">{{title}}</span>
</view>
</view>
</view>
<!-- #endif -->
</template>
<script>
export default {
name:"crm-watermark",
props: {
title: {
type: String,
default() {
return '';
}
},
},
data() {
return {
};
},
mounted() {
uni.$on('initCrmWatermark', this.initCrmWatermark);
uni.$on('removeCrmWatermark', this.removeCrmWatermark);
},
destroyed() {
uni.$off('initCrmWatermark', this.initCrmWatermark)
uni.$off('removeCrmWatermark', this.removeCrmWatermark)
},
methods: {
initCrmWatermark(msg) {
// #ifdef APP-PLUS
let id = 'crm_watermark_20240423';
if (plus.nativeObj.View.getViewById(id) !== null) {
plus.nativeObj.View.getViewById(id).close();
}
let context = uni.createCanvasContext('watermarkCanvas');
context.rotate(-45 * Math.PI / 180);
context.setFontSize(15);
context.setFillStyle('rgba(200, 200, 200, 0.30)');
context.setTextAlign('left');
context.setTextBaseline('middle');
context.fillText(msg, -83, 86);
context.draw(false, function() {
uni.canvasToTempFilePath({
canvasId: "watermarkCanvas",
width: 126, // 与下方 tarArr position width 保持一致
height: 126, // 与下方 tarArr position height 保持一致
success: function(res) {
let path = res.tempFilePath;
uni.getSystemInfo({
success: function (res) {
// 水印排列行数
let row = Math.floor(res.windowHeight / 126);
let tarArr = [];
for (let i = 0; i < row + 1; i++) {
for (let j = 0; j < row + 1; j++){
tarArr.push({
tag: 'img',
src: path,
position: {
top: (126 * i) + 'px',
left: (126 * j) + 'px',
width: '126px',
height: '126px',
}
});
}
}
var watermarkView = new plus.nativeObj.View(
id,
{top:'0px', left:'0px', right: '0px', bottom: '0px'},
tarArr
);
// 拦截View控件的触屏事件,将事件穿透给下一层view
watermarkView.interceptTouchEvent(false);
watermarkView.show();
context.clearRect(0, 0, 126, 126);
context.draw();
}
});
}
});
});
// #endif
// #ifdef H5
let id = 'crm_watermark_20240423';
if (document.getElementById(id) !== null) {
document.body.removeChild(document.getElementById(id));
}
let canvas = document.createElement('canvas');
canvas.width = 126;
canvas.height = 126;
let canvas2 = canvas.getContext('2d');
canvas2.rotate(-45 * Math.PI / 180);
canvas2.font = '15px Vedana';
canvas2.fillStyle = 'rgba(200, 200, 200, 0.3)';
canvas2.textAlign = 'left';
canvas2.textBaseline = 'middle';
canvas2.fillText(msg, -83, 86);
let div = document.createElement('div');
div.id = id;
div.style.pointerEvents = 'none';
div.style.top = '0px';
div.style.left = '0px';
div.style.bottom = '0px';
div.style.right = '0px';
div.style.position = 'fixed';
div.style.zIndex = '100000';
div.style.background = 'url(' + canvas.toDataURL('image/png') + ') left top repeat';
document.body.appendChild(div);
return id;
// #endif
},
// 删除水印(仅APP与H5有效)
removeCrmWatermark() {
// #ifdef APP-PLUS
if (plus.nativeObj.View.getViewById('crm_watermark_20240423') !== null) {
plus.nativeObj.View.getViewById('crm_watermark_20240423').close();
}
// #endif
// #ifdef H5
if (document.getElementById('crm_watermark_20240423') !== null) {
document.body.removeChild(document.getElementById('crm_watermark_20240423'));
}
// #endif
},
},
}
</script>
<style scoped>
/* #ifdef APP-PLUS */
.watermark_back {
position: relative;
width: 126px;
height: 126px;
z-index: -1;
}
.watermark {
position: absolute;
left: -126px;
top: -126px;
}
/* #endif */
/* #ifdef MP-WEIXIN */
.wx_watermark {
position: fixed;
width: 200vw;
height: 150vh;
top: -20vw;
left: -50vw;
color: rgba(200, 200, 200, 0.3);
font-size: 15px;
opacity: 1;
z-index: 1000000; // 放在顶层
pointer-events: none; // 点击穿透,不影响页面交互
transform: rotate(-45deg); // 水印倾斜角度
}
.wx_watermark-col {
display: inline-block;
padding: 50rpx 40rpx;
}
.wx_watermark-row {
white-space: nowrap;
}
.wx_watermark-row:nth-child(2n+1) {
transform: translateX(10%); // 奇偶数行水印错开
}
/* #endif */
</style>
业务代码
<template>
<view>
<!-- 其他业务代码 -->
<!-- 添加水印 app与H5 只需要调用一次,即可配置全局水印 微信小程序需要每个页面都引入 -->
<!-- #ifdef MP-WEIXIN || APP-PLUS -->
<crm-watermark :title="sWatermark"></crm-watermark>
<!-- #endif -->
</view>
</template>
<script>
data() {
return {
// 微信小程序需要每个页面都引入,并且设置水印
// #ifdef MP-WEIXIN || APP-PLUS
sWatermark: "",
// #endif
}
},
methods: {
showWatermark() {
// app与H5 只需要调用一次,即可配置全局水印
// #ifdef APP-PLUS || H5
uni.$emit('initCrmWatermark', sTitle);
// #endif
// 微信小程序需要每个页面都引入,并且设置水印
// #ifdef MP-WEIXIN || APP-PLUS
this.sWatermark = sTitle;
// #endif
},
}
如果渲染出来 在APP水印页面发现多了个 格格不入的水印,请进行 APP-PLUS 中代码的微调,这是正常的。下面让我来解释一下 (H5不会出现这个问题)
在 APP-PLUS 模式下,先是使用 uni.createCanvasContext('watermarkCanvas'),找到页面中的 canvas。并且对 canvas进行设置(如字体、旋转角度、字体颜色、文字对齐方式、文字水平对齐方式、及填充文字,并且进行 X、Y轴设置)
接着 使用 context.draw,进行渲染,这个时候,其实页面渲染成功就会出现一个 水印了,但后续调用 uni.canvasToTempFilePath(把当前画布指定区域的内容导出生成指定大小的图片) 在其中做了 for循环,将画布内容又生产了N个,并且通过plus.nativeObj.View方式加入到了页面中。这个时候页面就会多出一个看起来格格不入的水印,这个水印就是前面提到的 context.draw 的时候页面已经出来一个了。
那么如何解决这个格格不入的水印问题呢,我的方式是覆盖后将其删除。将一个通过context.draw渲染出来的水印,通过大小调整为与 uni.canvasToTempFilePath for 循环出来的 水印大小一致,将其盖在上面,这样虽然第一个水印看起来颜色深了一个点,但其他并无异常。在uni.canvasToTempFilePath for 循环成功后加入 context.clearRect(0, 0, 126, 126); context.draw(); 将其原来绘制的水印进行删除。
具体看我代码注释:下面是关键代码
width: 126, // 与下方 tarArr position width 保持一致
height: 126, // 与下方 tarArr position height 保持一致
context.clearRect(0, 0, 126, 126);
context.draw();
微信小程序由于无法实现全局水印,只能在每个页面都加入该组件,并且使用条件编译
<!-- #ifdef MP-WEIXIN-->
<crm-watermark :title="sWatermark"></crm-watermark>
<!-- #endif -->
// #ifdef MP-WEIXIN
sWatermark: "",
// #endif
// #ifdef MP-WEIXIN
this.sWatermark = sTitle;
// #endif
全栈开发在线接单,可接急单大单
本人全栈,因现在本职工作是产品了,担心久了不写代码,丢了吃饭的家伙,所以想接点写代码的单子。
主要是应用开发,小程序、微信网页应用,或是AI应用、区块链应用都做过,大佬们有需求可以聊聊,vx:laifeilim。
不急的单我可以周末自己抽空写,成本低;
急单,我得通过单位来接,这样我上班的时候也可以写。
大一点的单,单位这边应该也可以接下来的:团队百人+,目前CMMI3,年内CMMI5,注册资本3亿。
我自己从2021年就在插件市场发布插件,前端插件、云端一体页面模板、Admin插件、SDK都有:https://ext.dcloud.net.cn/publisher?id=743749
挑战过不到3天开发上线一个在线报名选课缴费应用,也在各种技术分享会安利unicloud:https://www.bilibili.com/video/BV1nW421N7XC/?spm_id_from=333.999.0.0
本人全栈,因现在本职工作是产品了,担心久了不写代码,丢了吃饭的家伙,所以想接点写代码的单子。
主要是应用开发,小程序、微信网页应用,或是AI应用、区块链应用都做过,大佬们有需求可以聊聊,vx:laifeilim。
不急的单我可以周末自己抽空写,成本低;
急单,我得通过单位来接,这样我上班的时候也可以写。
大一点的单,单位这边应该也可以接下来的:团队百人+,目前CMMI3,年内CMMI5,注册资本3亿。
我自己从2021年就在插件市场发布插件,前端插件、云端一体页面模板、Admin插件、SDK都有:https://ext.dcloud.net.cn/publisher?id=743749
挑战过不到3天开发上线一个在线报名选课缴费应用,也在各种技术分享会安利unicloud:https://www.bilibili.com/video/BV1nW421N7XC/?spm_id_from=333.999.0.0
免费帮忙开发 安卓 ios 原生插件,刚学完练练手,有需要的留言(太难的就算了)
免费开发安卓 ios 原生插件,刚学完练练手,有需要的留言(太难的就算了)
免费开发安卓 ios 原生插件,刚学完练练手,有需要的留言(太难的就算了)
uni-app排坑指南-uni-push获取cid异常问题的处理
uni-push2配置完成后,根据官方文档使用uni.getPushClientId方法获取cid,
可能会报错getPushClientId:fail uniPush is not enabled,此时重新打自定义调试基座也没有作用。
app端可以使用
let pinf = plus.push.getClientInfo();
let cid = pinf && pinf.clientid || ''; //客户端标识
uni.setStorageSync('unipush_id', cid)
plus.push.getClientInfoAsync((info) => {
cid = info.clientid;
uni.setStorageSync('unipush_id', cid)
console.log(cid)
}, err => {});
使用plus方法可以稳定获取到cid,但是这个方法有局限性,只能在APP-PLUS中使用。
如果想在h5中使用,就需要在app.vue文件中引入一下uni-push模块
// #ifdef H5
require('@dcloudio/vue-cli-plugin-uni/packages/uni-push/dist/uni-push.es.js')
// #endif
使用这个方法获取到的cid后台会直接检测为小程序,不管是否勾选离线推送都只能在线推送。
根据自己编译环境具体看使用什么方法
uni-push2配置完成后,根据官方文档使用uni.getPushClientId方法获取cid,
可能会报错getPushClientId:fail uniPush is not enabled,此时重新打自定义调试基座也没有作用。
app端可以使用
let pinf = plus.push.getClientInfo();
let cid = pinf && pinf.clientid || ''; //客户端标识
uni.setStorageSync('unipush_id', cid)
plus.push.getClientInfoAsync((info) => {
cid = info.clientid;
uni.setStorageSync('unipush_id', cid)
console.log(cid)
}, err => {});
使用plus方法可以稳定获取到cid,但是这个方法有局限性,只能在APP-PLUS中使用。
如果想在h5中使用,就需要在app.vue文件中引入一下uni-push模块
// #ifdef H5
require('@dcloudio/vue-cli-plugin-uni/packages/uni-push/dist/uni-push.es.js')
// #endif
使用这个方法获取到的cid后台会直接检测为小程序,不管是否勾选离线推送都只能在线推送。
根据自己编译环境具体看使用什么方法
uni-app的app和web平台定位异常;uni-app已支持鸿蒙next;uni-app x 3端发布;uniCloud阿里云版Nodejs8终止支持;oppo商店误报广告
uni-app的app和web平台定位异常
该问题已修复,请升级到uni-app 4.24。原因分析见下:
uni-app x 3端正式发布
随着4.14正式版发布,uni-app x的Web、Android、iOS均已上线。详见
欢迎阅读评测报告,了解uni-app x为何是性能最好的跨平台框架。详见
uni-app 已支持鸿蒙next
update@20240706:uni-app的鸿蒙版已灰度发布,若你想抢鲜体验,可下载最新的HBuilderX Alpha版本,手动打开鸿蒙入口,详见uni-app 开发鸿蒙应用
关联阅读,参考:星河璀璨,uni-app 亮相华为 HDC2024 开发者大会
2023底举办的插件大赛圆满结束
本次插件大赛在数千名候选插件中,评选出了82个优秀插件。这些插件为uni-app x的生态做出重要的贡献。
至此,大部分常见插件在uni-app x中均已提供。
获奖名单详见:https://ask.dcloud.net.cn/article/40812
关于 App Store 提交的隐私更新
Appstore要求2024年5月1日以后提交的包,都必须包含隐私清单。
Apple公告另见:关于 App Store 提交的隐私更新
使用 Transporter 工具上传到 AppStore 会提示以下警告:
ITMS-91053: Missing API declaration - Your app’s code in the “HBuilder” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategoryFileTimestamp. While no action is required at this time, starting May 1, 2024, when you upload a new app or app update, you must include a NSPrivacyAccessedAPITypes array in your app’s privacy manifest to provide approved reasons for these APIs used by your app’s code. For more details about this policy, including a list of required reason APIs and approved reasons for usage, visit: https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api.
此警告只是提示信息,在2024年5月1日前仍可继续提交 AppStore。
2024年4月24号起,云打包支持隐私清单配置。
- uni-app、5+App项目请更新到HBuilderX4.08及以上版本,重新提交云端打包
- uni-app x项目需更新到HBuilderX4.13及以上版本重新提交云端打包
配置方法教程:详见
离线打包和uni小程序sdk也见上面的链接。
uniCloud阿里云版Nodejs8终止支持
uniCloud阿里云版服务空间云函数运行时目前支持Nodejs8、Nodejs12、Nodejs14、Nodejs16四个版本。接阿里云通知,其中Nodejs8即将终止支持,计划如下:
版本 | 终止支持阶段一:禁止新建 | 终止支持阶段二:禁止新建和更新 |
---|---|---|
Node.js8 | 2024年06月01日 | 2024年09月01日 |
终止支持仅影响运行时为Nodejs8的云函数更新,不影响已有云函数继续运行,建议您及时升级到更高的版本。
所有已部署到服务空间的云函数
(并非公共模块)均需开发者自行升级,升级方式:修改云函数package.json
中cloudfunction-config
节点下的runtime
,然后重新上传云函数。如果云函数的package.json没有cloudfunction-config
相关配置,可参考文档进行添加,点此查看相关文档。
uni一键登录SDK升级
接到联通运营商通知,旧版联通一键认证SDK将于2024年5月31日正式下线。
之前我们已经发布过升级公告,另见:uni一键登录SDK升级公告 。
注意:
云端打包:如果您已经使用HBuilderX3.99及以上版本重新打包并发布了一键登录的App,请忽略此公告。
离线打包:请更新Android、IOS离线SDK至最新版本
vivo商店提示无合理使用场景频繁采集oaid的问题
详见:https://ask.dcloud.net.cn/article/41143
oppo商店误报有广告的问题
推荐使用uni加固解决,详见:https://uniapp.dcloud.net.cn/tutorial/app-security.html
后续官方会升级代码,减少误检测误报。
uni-app的app和web平台定位异常
该问题已修复,请升级到uni-app 4.24。原因分析见下:
uni-app x 3端正式发布
随着4.14正式版发布,uni-app x的Web、Android、iOS均已上线。详见
欢迎阅读评测报告,了解uni-app x为何是性能最好的跨平台框架。详见
uni-app 已支持鸿蒙next
update@20240706:uni-app的鸿蒙版已灰度发布,若你想抢鲜体验,可下载最新的HBuilderX Alpha版本,手动打开鸿蒙入口,详见uni-app 开发鸿蒙应用
关联阅读,参考:星河璀璨,uni-app 亮相华为 HDC2024 开发者大会
2023底举办的插件大赛圆满结束
本次插件大赛在数千名候选插件中,评选出了82个优秀插件。这些插件为uni-app x的生态做出重要的贡献。
至此,大部分常见插件在uni-app x中均已提供。
获奖名单详见:https://ask.dcloud.net.cn/article/40812
关于 App Store 提交的隐私更新
Appstore要求2024年5月1日以后提交的包,都必须包含隐私清单。
Apple公告另见:关于 App Store 提交的隐私更新
使用 Transporter 工具上传到 AppStore 会提示以下警告:
ITMS-91053: Missing API declaration - Your app’s code in the “HBuilder” file references one or more APIs that require reasons, including the following API categories: NSPrivacyAccessedAPICategoryFileTimestamp. While no action is required at this time, starting May 1, 2024, when you upload a new app or app update, you must include a NSPrivacyAccessedAPITypes array in your app’s privacy manifest to provide approved reasons for these APIs used by your app’s code. For more details about this policy, including a list of required reason APIs and approved reasons for usage, visit: https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api.
此警告只是提示信息,在2024年5月1日前仍可继续提交 AppStore。
2024年4月24号起,云打包支持隐私清单配置。
- uni-app、5+App项目请更新到HBuilderX4.08及以上版本,重新提交云端打包
- uni-app x项目需更新到HBuilderX4.13及以上版本重新提交云端打包
配置方法教程:详见
离线打包和uni小程序sdk也见上面的链接。
uniCloud阿里云版Nodejs8终止支持
uniCloud阿里云版服务空间云函数运行时目前支持Nodejs8、Nodejs12、Nodejs14、Nodejs16四个版本。接阿里云通知,其中Nodejs8即将终止支持,计划如下:
版本 | 终止支持阶段一:禁止新建 | 终止支持阶段二:禁止新建和更新 |
---|---|---|
Node.js8 | 2024年06月01日 | 2024年09月01日 |
终止支持仅影响运行时为Nodejs8的云函数更新,不影响已有云函数继续运行,建议您及时升级到更高的版本。
所有已部署到服务空间的云函数
(并非公共模块)均需开发者自行升级,升级方式:修改云函数package.json
中cloudfunction-config
节点下的runtime
,然后重新上传云函数。如果云函数的package.json没有cloudfunction-config
相关配置,可参考文档进行添加,点此查看相关文档。
uni一键登录SDK升级
接到联通运营商通知,旧版联通一键认证SDK将于2024年5月31日正式下线。
之前我们已经发布过升级公告,另见:uni一键登录SDK升级公告 。
注意:
云端打包:如果您已经使用HBuilderX3.99及以上版本重新打包并发布了一键登录的App,请忽略此公告。
离线打包:请更新Android、IOS离线SDK至最新版本
vivo商店提示无合理使用场景频繁采集oaid的问题
详见:https://ask.dcloud.net.cn/article/41143
oppo商店误报有广告的问题
推荐使用uni加固解决,详见:https://uniapp.dcloud.net.cn/tutorial/app-security.html
后续官方会升级代码,减少误检测误报。
【建议】建议压缩包添加一个脚本文件创建桌面快捷方式
@echo off
echo Set oWS = WScript.CreateObject("WScript.Shell") > CreateShortcut.vbs
echo sLinkFile = "%USERPROFILE%\Desktop\HBuilder X.lnk" >> CreateShortcut.vbs
echo Set oLink = oWS.CreateShortcut(sLinkFile) >> CreateShortcut.vbs
echo oLink.TargetPath = "%CD%\HBuilderX.exe" >> CreateShortcut.vbs
echo oLink.WorkingDirectory = "%CD%\" >> CreateShortcut.vbs
echo oLink.Save >> CreateShortcut.vbs
cscript CreateShortcut.vbs
del CreateShortcut.vbs
@echo off
echo Set oWS = WScript.CreateObject("WScript.Shell") > CreateShortcut.vbs
echo sLinkFile = "%USERPROFILE%\Desktop\HBuilder X.lnk" >> CreateShortcut.vbs
echo Set oLink = oWS.CreateShortcut(sLinkFile) >> CreateShortcut.vbs
echo oLink.TargetPath = "%CD%\HBuilderX.exe" >> CreateShortcut.vbs
echo oLink.WorkingDirectory = "%CD%\" >> CreateShortcut.vbs
echo oLink.Save >> CreateShortcut.vbs
cscript CreateShortcut.vbs
del CreateShortcut.vbs
收起阅读 »
吐槽:云数据库数据导入
今天导数据弄了半天,一开始直接上传json文件一直失败,查了一下发现要用jsonl格式,结果导入文件只支持json文件格式。。。无语
今天导数据弄了半天,一开始直接上传json文件一直失败,查了一下发现要用jsonl格式,结果导入文件只支持json文件格式。。。无语