上传代码

This commit is contained in:
2025-08-18 14:20:34 +08:00
commit 527fd07910
2408 changed files with 427370 additions and 0 deletions

View File

@ -0,0 +1,14 @@
## 1.2.02024-12-03
新增 `uni_modules/unicloud-map/components/unicloud-map/unicloud-map.uvue` 文件,支持编译到 uni-app x
## 1.1.22024-07-23
移除属性 layer-style微信小程序会报错
## 1.1.12024-04-18
新增opendb-poi表的索引初始化文件
## 1.1.02024-03-25
[重要] 支持支付宝小程序云
## 1.0.22024-03-06
新增 load 事件在云端poi数据加载完成后触发
## 1.0.12023-08-02
优化示例
## 1.0.02023-08-01
初版

View File

@ -0,0 +1,522 @@
<template>
<view class="unicloud-map">
<map :ref="mapId" :id="mapId" :style="styleCom" :latitude="latitudeCom" :longitude="longitudeCom" :scale="scale" :min-scale="minScale" :max-scale="maxScale"
:markers="markers" :polyline="polyline" :circles="circles" :controls="controls" :include-points="includePoints" :show-compass="showCompass" :enable-zoom="enableZoom"
:enable-scroll="enableScroll" :enable-rotate="enableRotate" :rotate="rotate" :enable-overlooking="enableOverlooking" :enable-satellite="enableSatellite"
:enable-traffic="enableTraffic" :enable-poi="enablePoi" :enable-building="enableBuilding" :show-location="showLocation" :polygons="polygons"
:enable-indoorMap="enableIndoorMap" @markertap="_markertap" @callouttap="_callouttap" @controltap="_controltap" @regionchange="_regionchange" @tap="_tap" @updated="_updated"
@poitap="_poitap"></map>
</view>
</template>
<script>
/**
* unicloud-map
* @description 云端一体地图组件
* @property {String} loadtime = [auto|onready|manual] 数据加载时机
* @value auto 页面就绪后或属性变化后加载数据默认为auto
* @value onready 页面就绪后不自动加载数据属性变化后加载。适合在onready中接收上个页面的参数作为where条件时。
* @value manual 手动模式不自动加载数据。需要手动调用refresh函数加载数据
* @property {String} collection 表名
* @property {Object} where 查询条件
* @property {Number} poiMaximum = 100 最大poi显示数量
* @property {Number} poiMaxDistance 查询的最大距离
* @property {Number} poiMinDistance 查询的最小距离
* @property {Number|String} width 宽度
* @property {Number|String} height 高度
* @property {String} defaultIcon 默认的POI图标
* @property {Array} customIcons 自定义图标根据POI的type来区分
* @property {String|Number} latitude 中心纬度
* @property {String|Number} longitude 中心经度
* @property {Number} scale 地图缩放等级,部分情况下会自动设置,此参数会失效
* @property {Number} minScale 最小缩放等级
* @property {Number} maxScale 最大缩放等级
* @property {String|Number} layerStyle 个性化地图
* @property {Boolean} showCompass 是否显示指南针
* @property {Boolean} enableZoom 是否支持缩放
* @property {Boolean} enableScroll 是否支持拖动
* @property {Boolean} enableRotate 是否支持旋转
* @property {Number} rotate 旋转角度当enableRotate为true时才生效
* @property {Boolean} enableOverlooking 是否开启俯视
* @property {Boolean} enableSatellite 是否开启卫星图
* @property {Boolean} enableTraffic 是否开启实时路况
* @property {Boolean} enablePoi 是否展示地图的原生 POI 点
* @property {Boolean} enableBuilding 是否展示建筑物
* @property {Boolean} showLocation 显示带有方向的当前定位点
* @property {Array} polygons 多边形
* @property {Boolean} enableIndoorMap 是否展示室内地图
* @event {Function} mounted 组件加载完成触发(此时不一定有数据)
* @event {Function} load 数据加载完成事件
* @event {Function} markertap 点击标记点时触发
* @event {Function} labeltap 点击label时触发
* @event {Function} callouttap 点击标记点对应的气泡时触发
* @event {Function} controltap 点击控件时触发
* @event {Function} regionchange 视野发生变化时触发
* @event {Function} tap 点击地图时触发; App-nvue、微信小程序2.9支持返回经纬度
* @event {Function} updated 在地图渲染更新完成时触发
* @event {Function} anchorpointtap 点击定位标时触发
* @event {Function} poitap 点击地图原生POI点时触发
* @event {Function} custom-poitap 点击自定义POI点时触发
*/
export default {
name: "unicloud-map",
emits: ["mounted", "load", "markertap", "labeltap", "callouttap", "controltap", "regionchange", "tap", "updated", "anchorpointtap", "poitap", "custom-poitap"],
props: {
collection: {
type: String,
default: "opendb-poi"
},
// 数据加载时机
loadtime: {
type: String,
default: "auto"
},
where: {
type: Object as PropType<UTSJSONObject>,
},
poiMaximum: {
type: Number,
default: 100
},
poiMaxDistance: {
type: Number,
default: 0
},
poiMinDistance: {
type: Number,
default: 0
},
// 默认的POI图标
defaultIcon: {
type: String,
default: "/static/location.png"
},
// 自定义图标根据POI的type来区分
customIcons: {
type: Array as PropType<Array<UTSJSONObject>>,
default: function () : Array<UTSJSONObject> {
return [] as Array<UTSJSONObject>
}
},
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 0
},
// 纬度
latitude: {
type: Number
},
// 经度
longitude: {
type: Number
},
// 默认纬度
defaultLatitude: {
type: Number,
default: 39.908823
},
// 默认经度
defaultLongitude: {
type: Number,
default: 116.39747
},
scale: {
type: Number,
default: 16
},
minScale: {
type: Number,
default: 3
},
maxScale: {
type: Number,
default: 20
},
showCompass: {
type: Boolean,
default: false
},
enableZoom: {
type: Boolean,
default: true
},
enableScroll: {
type: Boolean,
default: true
},
enableRotate: {
type: Boolean,
default: false
},
rotate: {
type: Number,
default: 0
},
enableOverlooking: {
type: Boolean,
default: false
},
enableSatellite: {
type: Boolean,
default: false
},
enableTraffic: {
type: Boolean,
default: false
},
enablePoi: {
type: Boolean,
default: true
},
enableBuilding: {
type: Boolean,
default: true
},
showLocation: {
type: Boolean,
default: true
},
polygons: {
type: Array as PropType<Array<Polygon>>,
default: function () : Array<Polygon> {
return [] as Array<Polygon>
}
},
enableIndoorMap: {
type: Boolean,
default: false
}
},
data() {
const mapId = `UniCloudMap_${(Math.random() * 10e5).toString(36)}` as string;
return {
mapId: mapId,
mapStyle: "",
// 标记点
markers: [] as Marker[],
// 路线
polyline: [] as Polyline[],
// 圆
circles: [] as Circle[],
// 控件
controls: [] as Control[],
// 缩放视野以包含所有给定的坐标点
includePoints: [] as LocationObject[],
// 当前pois列表数据
pois: [] as UTSJSONObject[]
};
},
mounted() {
let loadtime = this.loadtime as string;
if (loadtime == "auto") {
this.getCloudData({
needIncludePoints: true
});
}
this.$emit("mounted");
},
methods: {
// 提供给外部调用
async refresh(obj ?: UTSJSONObject) {
this.getCloudData(obj);
},
// 获取云端数据
async getCloudData(obj ?: UTSJSONObject) {
let collection = this.collection as string;
let longitude = this.longitudeCom as number;
let latitude = this.latitudeCom as number;
let poiMaxDistance = this.poiMaxDistance as number;
let poiMinDistance = this.poiMinDistance as number;
let needIncludePoints = false;
if (obj != null) {
if (obj['needIncludePoints'] != null) {
needIncludePoints = obj['needIncludePoints'] as boolean;
}
if (obj['longitude'] != null && obj['latitude'] != null) {
longitude = obj['longitude'] as number;
latitude = obj['latitude'] as number;
}
}
const db = uniCloud.databaseForJQL();
let where = Object.assign({
visible: true,
}, this.where as UTSJSONObject)
let geoNearJson = {
distanceField: 'distance', // 输出的每个记录中 distance 即是与给定点的距离
spherical: true,
near: new db.Geo.Point(longitude, latitude),
query: where,
key: 'location', // 若只有 location 一个地理位置索引的字段,则不需填
includeLocs: 'location', // 若只有 location 一个是地理位置,则不需填
} as UTSJSONObject;
if (poiMaxDistance > 0) {
geoNearJson['maxDistance'] = poiMaxDistance;
}
if (poiMinDistance > 0) {
geoNearJson['minDistance'] = poiMinDistance;
}
let res = await db.collection(collection).aggregate()
.geoNear(geoNearJson)
.orderBy("distance desc")
.limit(this.poiMaximum)
.get();
let list = res['data'] as UTSJSONObject[];
// 根据level手动排序
list.sort((a : UTSJSONObject, b : UTSJSONObject) : number => {
let levelA = a['level'] != null ? a['level'] as number : 0;
let levelB = b['level'] != null ? b['level'] as number : 0;
return levelA - levelB;
});
this.pois = list;
let markers = list.map((item : UTSJSONObject, index : number) : Marker => {
let location = item['location'] as UTSJSONObject;
let coordinates = location['coordinates'] as Array<number>;
let data = {
id: index,
latitude: coordinates[1],
longitude: coordinates[0],
iconPath: this._getIcon(item),
width: 30,
height: 30,
rotate: item['rotate'] != null ? item['rotate'] as number : 0
} as Marker;
if (item['title'] != null) {
data.title = item['title'] as string;
data.callout = {
content: item['title'] as string,
color: "#000000",
fontSize: 12,
borderRadius: 5,
borderWidth: 1,
borderColor: "#f8f8f8",
bgColor: "#ffffff",
padding: 4,
display: "ALWAYS",
textAlign: "center"
} as MapMarkerCallout;
}
return data;
});
this.markers = markers;
if (needIncludePoints) {
this.calcIncludePoints();
}
let emitData = {
pois: list,
markers,
};
this.$emit("load", emitData);
},
// 计算一组坐标的边界
_calculateBounds(coordinates : LocationObject[]) : LocationObject[] {
if (coordinates.length == 0) {
return [];
}
let minLongitude = coordinates[0].longitude;
let maxLongitude = coordinates[0].longitude;
let minLatitude = coordinates[0].latitude;
let maxLatitude = coordinates[0].latitude;
coordinates.forEach((coord : LocationObject) => {
let longitude = coord.longitude;
let latitude = coord.latitude;
minLongitude = Math.min(minLongitude, longitude);
maxLongitude = Math.max(maxLongitude, longitude);
minLatitude = Math.min(minLatitude, latitude);
maxLatitude = Math.max(maxLatitude, latitude);
})
let k = 0.008; // 额外偏移0.008,使所有坐标都在一屏中显示
const southwest = { longitude: minLongitude - k, latitude: minLatitude - k } as LocationObject;
const northeast = { longitude: maxLongitude + k, latitude: maxLatitude + k } as LocationObject;
return [
southwest,
northeast
] as LocationObject[]
},
getMarkers() : Marker[] {
return this.markers;
},
setMarkers(markers : Marker[]) {
this.markers = markers;
},
getPolyline() : Polyline[] {
return this.polyline;
},
setPolyline(polyline : Polyline[]) {
this.polyline = polyline;
this.calcIncludePoints();
},
calcIncludePoints() {
let polyline = this.polyline as Polyline[];
let markers = this.markers as Marker[];
let points = [] as LocationObject[];
if (markers.length > 0) {
let list : LocationObject[] = markers.map((item : Marker) => {
return {
latitude: item.latitude,
longitude: item.longitude
} as LocationObject
});
points = points.concat(list);
}
if (polyline.length > 0) {
polyline.forEach((item : Polyline) => {
points = points.concat(item.points);
});
}
if (points.length > 0) {
this.includePoints = this._calculateBounds(points);
} else {
this.includePoints = [
{
latitude: this.latitudeCom,
longitude: this.longitudeCom
} as LocationObject
] as LocationObject[];
}
},
getCircles() : Circle[] {
return this.circles;
},
setCircles(circles : Circle[]) {
this.circles = circles;
},
getControls() : Control[] {
return this.controls;
},
setControls(controls : Control[]) {
this.controls = controls;
},
_getIcon(obj : UTSJSONObject) : string {
let type = obj['type'] as string;
if (obj['icon'] != null) {
return obj['icon'] as string;;
}
let customIcons = this.customIcons as Array<UTSJSONObject>;
let defaultIcon = this.defaultIcon as string;
let findItem : UTSJSONObject | null = customIcons.find((item : UTSJSONObject) : boolean => {
let _type = item['type'] as string;
return _type == type ? true : false;
});
if (findItem == null) {
return defaultIcon;
}
if (findItem['icon'] == null) {
return defaultIcon;
}
return findItem['icon'] as string;
},
_markertap(e : UniMapMarkerTapEvent) {
let markerId = e.detail.markerId;
if (markerId != null) {
let poi = this.pois[markerId];
this.$emit("markertap", e);
this.$emit("custom-poitap", { poi });
}
},
_callouttap(e : UniEvent) {
this.$emit("callouttap", e);
},
_controltap(e : UniMapControlTapEvent) {
this.$emit("controltap", e);
},
getMapContext() : MapContext | null {
// @ts-expect-error
return uni.createMapContext(this.mapId, this);
},
_regionchange(e : UniMapRegionChangeEvent) {
let causedBy = e.causedBy as string | null;
// #ifdef WEB
if (!causedBy) {
// @ts-expect-error
causedBy = e.detail.causedBy as string;
}
// #endif
if (e.type !== "end" || causedBy != "drag") {
return;
}
this.$emit("regionchange", e);
const mapContext = this.getMapContext();
if (mapContext != null) {
mapContext.getCenterLocation({
success: (res) => {
this.getCloudData({
latitude: res.latitude,
longitude: res.longitude
});
}
});
}
},
_tap(e : UniMapTapEvent) {
this.$emit("tap", e);
},
_updated(e : UniMapUpdatedEvent) {
this.$emit("updated", e);
},
_poitap(e : UniMapPoiTapEvent) {
this.$emit("poitap", e);
}
},
watch: {
whereWatchCom() {
// 需要重新查询
let loadtime = this.loadtime as string;
if (loadtime !== "manual") {
this.refresh({
needIncludePoints: true
});
}
}
},
computed: {
whereWatchCom() : string {
let where = this.where as UTSJSONObject;
let collection = this.collection as string;
return JSON.stringify({ where, collection })
},
styleCom() : string {
let width = this.width as number;
let height = this.height as number
let arr = [] as Array<string>;
if (width > 0) {
arr.push(`width:${width}px;`);
} else {
arr.push("width:100%;");
}
if (height > 0) {
arr.push(`height:${height}px;`);
} else {
arr.push("height:100%;");
}
return arr.join("");
},
latitudeCom() : number {
let latitude = this.latitude as number;
let defaultLatitude = this.defaultLatitude as number;
return (latitude != 0) ? latitude : defaultLatitude;
},
longitudeCom() : number {
let longitude = this.longitude as number;
let defaultLongitude = this.defaultLongitude as number;
return (longitude != 0) ? longitude : defaultLongitude;
}
}
}
</script>
<style lang="scss" scoped>
.unicloud-map {
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,617 @@
<template>
<view class="unicloud-map">
<map ref="map" :style="styleCom" :latitude="latitudeCom" :longitude="longitudeCom" :scale="scale" :min-scale="minScale" :max-scale="maxScale"
:markers="markers" :polyline="polyline" :circles="circles" :controls="controls" :include-points="includePoints" :show-compass="showCompass"
:enable-zoom="enableZoom" :enable-scroll="enableScroll" :enable-rotate="enableRotate" :rotate="rotate" :enable-overlooking="enableOverlooking" :enable-satellite="enableSatellite"
:enable-traffic="enableTraffic" :enable-poi="enablePoi" :enable-building="enableBuilding" :show-location="showLocation" :polygons="polygons"
:enable-indoorMap="enableIndoorMap" @markertap="_markertap" @labeltap="_labeltap" @callouttap="_callouttap" @controltap="_controltap" @regionchange="_regionchange"
@tap="_tap" @updated="_updated" @anchorpointtap="_anchorpointtap" @poitap="_poitap"></map>
</view>
</template>
<script>
let timeoutArr = [];
let flagArr = [];
let timeout = null;
/**
* 节流函数
* 节流原理:在一定时间内,只能触发一次
*/
function throttle(fn, time = 500, isImmediate = true, timeoutName = "default") {
if(!timeoutArr[timeoutName]) timeoutArr[timeoutName] = null;
if (isImmediate) {
if (!flagArr[timeoutName]) {
flagArr[timeoutName] = true;
// 如果是立即执行则在time毫秒内开始时执行
if(typeof fn === 'function') fn();
timeoutArr[timeoutName] = setTimeout(() => {
flagArr[timeoutName] = false;
}, time);
}
} else {
if (!flagArr[timeoutName]) {
flagArr[timeoutName] = true;
// 如果是非立即执行则在time毫秒内的结束处执行
timeoutArr[timeoutName] = setTimeout(() => {
flagArr[timeoutName] = false;
if(typeof fn === 'function') fn();
}, time);
}
}
};
/**
* 防抖原理一定时间内只有最后一次操作再过wait毫秒后才执行函数
*/
function debounce(func, wait = 500, immediate = false) {
// 清除定时器
if (timeout !== null) clearTimeout(timeout);
// 立即执行,此类情况一般用不到
if (immediate) {
var callNow = !timeout;
timeout = setTimeout(function() {
timeout = null;
}, wait);
if (callNow) typeof func === 'function' && func();
} else {
// 设置定时器当最后一次操作后timeout不会再被清除所以在延时wait毫秒后执行func回调方法
timeout = setTimeout(function() {
typeof func === 'function' && func();
}, wait);
}
}
/**
* unicloud-map
* @description 云端一体地图组件
* @property {String} loadtime = [auto|onready|manual] 数据加载时机
* @value auto 页面就绪后或属性变化后加载数据默认为auto
* @value onready 页面就绪后不自动加载数据属性变化后加载。适合在onready中接收上个页面的参数作为where条件时。
* @value manual 手动模式不自动加载数据。需要手动调用refresh函数加载数据
* @property {Boolean} debug = [true|false] 是否开启调试模式
* @property {String} collection 表名
* @property {Object} where 查询条件
* @property {Number} poiMaximum = 100 最大poi显示数量
* @property {Number} poiMaxDistance 查询的最大距离
* @property {Number} poiMinDistance 查询的最小距离
* @property {Number|String} width 宽度
* @property {Number|String} height 高度
* @property {String} defaultIcon 默认的POI图标
* @property {Array} customIcons 自定义图标根据POI的type来区分
* @property {String|Number} latitude 中心纬度
* @property {String|Number} longitude 中心经度
* @property {Number} scale 地图缩放等级,部分情况下会自动设置,此参数会失效
* @property {Number} minScale 最小缩放等级
* @property {Number} maxScale 最大缩放等级
* @property {String|Number} layerStyle 个性化地图
* @property {Boolean} showCompass 是否显示指南针
* @property {Boolean} enableZoom 是否支持缩放
* @property {Boolean} enableScroll 是否支持拖动
* @property {Boolean} enableRotate 是否支持旋转
* @property {Number} rotate 旋转角度当enableRotate为true时才生效
* @property {Boolean} enableOverlooking 是否开启俯视
* @property {Boolean} enableSatellite 是否开启卫星图
* @property {Boolean} enableTraffic 是否开启实时路况
* @property {Boolean} enablePoi 是否展示地图的原生 POI 点
* @property {Boolean} enableBuilding 是否展示建筑物
* @property {Boolean} showLocation 显示带有方向的当前定位点
* @property {Array} polygons 多边形
* @property {Boolean} enableIndoorMap 是否展示室内地图
* @property {Function} poiTitleFormat 自定义poi标题的格式化函数
* @event {Function} mounted 组件加载完成触发(此时不一定有数据)
* @event {Function} load 数据加载完成事件
* @event {Function} markertap 点击标记点时触发
* @event {Function} labeltap 点击label时触发
* @event {Function} callouttap 点击标记点对应的气泡时触发
* @event {Function} controltap 点击控件时触发
* @event {Function} regionchange 视野发生变化时触发
* @event {Function} tap 点击地图时触发; App-nvue、微信小程序2.9支持返回经纬度
* @event {Function} updated 在地图渲染更新完成时触发
* @event {Function} anchorpointtap 点击定位标时触发
* @event {Function} native-poitap 点击地图原生POI点时触发
* @event {Function} poitap 点击自定义POI点时触发
*
*/
export default {
name: "unicloud-map",
emits: ["mounted", "load", "markertap", "labeltap","callouttap","controltap","regionchange","tap","updated","anchorpointtap","poitap","native-poitap"],
props: {
debug: {
type: Boolean,
default: false
},
collection:{
type: String,
default: "opendb-poi"
},
// 数据加载时机
loadtime:{
type: String,
default: "auto"
},
where: {
type: Object
},
poiMaximum: {
type: [String, Number],
default: 100
},
poiMaxDistance: {
type: Number
},
poiMinDistance: {
type: Number
},
width: {
type: [String, Number],
default: 600
},
height: {
type: [String, Number],
default: 600
},
// 默认的POI图标
defaultIcon:{
type: String,
default: "/static/location.png"
},
// 自定义图标根据POI的type来区分
customIcons:{
type: Array,
default: function(){
return []
}
},
// 纬度
latitude: {
type: [String, Number]
},
// 经度
longitude: {
type: [String, Number]
},
// 默认纬度
defaultLatitude: {
type: [String, Number],
default: 39.908823
},
// 默认经度
defaultLongitude: {
type: [String, Number],
default: 116.39747
},
scale: {
type: Number,
default: 16
},
minScale: {
type: Number,
default: 3
},
maxScale: {
type: Number,
default: 20
},
showCompass: {
type: Boolean,
default: false
},
enableZoom: {
type: Boolean,
default: true
},
enableScroll: {
type: Boolean,
default: true
},
enableRotate: {
type: Boolean,
default: false
},
rotate: {
type: Number,
default: 0
},
enableOverlooking: {
type: Boolean,
default: false
},
enableSatellite: {
type: Boolean,
default: false
},
enableTraffic: {
type: Boolean,
default: false
},
enablePoi: {
type: Boolean,
default: true
},
enableBuilding: {
type: Boolean,
default: true
},
showLocation: {
type: Boolean,
default: true
},
polygons: {
type: Array,
default: function() {
return []
}
},
enableIndoorMap: {
type: Boolean,
default: false
},
poiTitleFormat: {
type: Function,
}
},
data() {
return {
// 标记点
markers: [],
// 路线
polyline: [],
// 圆
circles: [],
// 控件
controls: [],
// 缩放视野以包含所有给定的坐标点
includePoints: [],
// 当前pois列表数据
pois:[],
needIncludePoints: true,
calcIncludePointsLastTime: 0
};
},
mounted() {
let { loadtime } = this;
if (loadtime === "auto") {
this.getCloudData();
}
this.$emit("mounted");
},
methods: {
// 提供给外部调用
async refresh(obj={}){
return this.getCloudData(obj);
},
// 获取云端数据
async getCloudData(obj={}){
let {
longitude: myLongitude,
latitude: myLatitude,
scale,
needIncludePoints
} = obj;
let {
collection,
defaultIcon,
longitudeCom: longitude,
latitudeCom: latitude,
showLocation,
} = this;
if (typeof myLongitude != "undefined" && typeof myLatitude != "undefined") {
longitude = myLongitude;
latitude = myLatitude;
}
if (this.debug) console.log('longitude, latitude: ', longitude, latitude)
let geoNearJson = {};
if (typeof this.poiMaxDistance === "number" && this.poiMaxDistance > 0) {
geoNearJson.maxDistance = this.poiMaxDistance;
}
if (typeof this.poiMinDistance === "number" && this.poiMinDistance > 0) {
geoNearJson.minDistance = this.poiMinDistance;
}
if (scale) {
delete geoNearJson.maxDistance;
}
let where = {
visible: true,
...Object.assign({}, this.where)
};
if (this.debug) console.log('geoNearJson: ', geoNearJson)
if (this.debug) console.log('where: ', where)
const db = uniCloud.database();
let res = await db.collection(collection).aggregate()
.geoNear({
...geoNearJson,
distanceField: 'distance', // 输出的每个记录中 distance 即是与给定点的距离
spherical: true,
near: new db.Geo.Point(longitude, latitude),
query: where,
key: 'location', // 若只有 location 一个地理位置索引的字段,则不需填
includeLocs: 'location', // 若只有 location 一个是地理位置,则不需填
})
.sort({
distance: 1,
})
.limit(Number(this.poiMaximum))
.end();
// 根据level手动排序
if (this.debug) console.log('res.result.data: ', res.result.data)
res.result.data.sort((a, b) => {
let { level: levelA = 0 } = a;
let { level: levelB = 0 } = b;
return levelA - levelB;
});
this.pois = res.result.data;
let markers = res.result.data.map((item, index) => {
let data = {
id: index,
latitude: item.location.coordinates[1],
longitude: item.location.coordinates[0],
iconPath: this._getIcon(item),
width: 30,
height: 30,
rotate: item.rotate || 0
};
if (item.title) {
data.title = this._getPoiTitle(item);
data.callout = {
content: this._getPoiTitle(item),
color: "#000000",
fontSize: 12,
borderRadius: 5,
borderWidth: 1,
borderColor: "#f8f8f8",
bgColor: "#ffffff",
padding: 4,
display: "ALWAYS",
textAlign: "center"
};
}
return data;
});
if (this.debug) console.log('markers: ', markers)
this.markers = markers;
if (this.needIncludePoints || needIncludePoints) {
this.calcIncludePoints();
}
let emitData = {
pois: res.result.data,
markers,
};
this.$emit("load", emitData);
// console.log('markers: ', markers)
// console.log('res: ', res.result.data)
},
// 计算一组坐标的边界
_calculateBounds(coordinates){
if (!Array.isArray(coordinates) || coordinates.length === 0) {
return [];
}
let minLongitude = coordinates[0].longitude;
let maxLongitude = coordinates[0].longitude;
let minLatitude = coordinates[0].latitude;
let maxLatitude = coordinates[0].latitude;
for (const coord of coordinates) {
const { longitude, latitude } = coord;
minLongitude = Math.min(minLongitude, longitude);
maxLongitude = Math.max(maxLongitude, longitude);
minLatitude = Math.min(minLatitude, latitude);
maxLatitude = Math.max(maxLatitude, latitude);
}
let k = 0.002; // 额外偏移0.002,使所有坐标都在一屏中显示
const southwest = { longitude: minLongitude-k, latitude: minLatitude-k };
const northeast = { longitude: maxLongitude+k, latitude: maxLatitude+k };
return [
southwest,
northeast
]
},
getMarkers(){
return this.markers;
},
setMarkers(markers){
this.markers = markers;
},
getPolyline(){
return this.polyline;
},
setPolyline(polyline){
this.polyline = polyline;
this.calcIncludePoints();
},
calcIncludePoints(){
let { polyline=[], markers=[], calcIncludePointsLastTime = 0 } = this;
// uni.showToast({
// title: `${Date.now() - calcIncludePointsLastTime}`,
// icon: "none"
// })
if (Date.now() - calcIncludePointsLastTime < 5000) {
return;
}
let points = [];
if (markers.length > 0) {
let list = markers.map((item, index) => {
return {
latitude: item.latitude,
longitude: item.longitude
}
});
points = points.concat(list);
}
if (polyline.length > 0) {
polyline.map((item, index) => {
points = points.concat(item.points);
});
}
if (points.length > 0) {
this.includePoints = this._calculateBounds(points);
} else {
this.includePoints = [
{
latitude: this.latitude,
longitude: this.longitude
}
];
}
},
getCircles(){
return this.circles;
},
setCircles(circles){
this.circles = circles;
},
getControls(){
return this.controls;
},
setControls(controls){
this.controls = controls;
},
_getPoiTitle(poi={}){
let { poiTitleFormat } = this;
if (typeof poiTitleFormat === "function") {
return poiTitleFormat(poi);
} else {
return poi.title;
}
},
_getIcon(obj={}){
let { type, icon } = obj;
if (icon) {
return icon;
}
let { customIcons=[], defaultIcon } = this;
let findItem = customIcons.find((item) => {
return item.type === type;
});
return findItem && findItem.icon ? findItem.icon : defaultIcon;
},
// 触发监听 - 点击poi
_emitPoi(type, e){
let markerId = e.detail.markerId;
let poi = this.pois[markerId];
e.poi = poi;
this.$emit(type, e);
this.$emit("poitap", e);
},
_markertap(e) {
if (this.debug) console.log('markertap: ', e)
this._emitPoi("markertap", e);
},
_callouttap(e) {
if (this.debug) console.log('callouttap: ', e)
this._emitPoi("callouttap", e);
},
_labeltap(e) {
if (this.debug) console.log('labeltap: ', e)
this.$emit("labeltap", e);
},
_controltap(e) {
if (this.debug) console.log('controltap: ', e)
this.$emit("controltap", e);
},
_regionchange(e) {
if (this.debug) console.log('regionchange: ', e)
this.$emit("regionchange", e);
if (e.detail.causedBy === "gesture") {
this.calcIncludePointsLastTime = Date.now();
}
if (e.detail.centerLocation) {
const getCloudData = () =>{
this.getCloudData({
latitude: e.detail.centerLocation.latitude,
longitude: e.detail.centerLocation.longitude,
scale: e.detail.scale
});
}
// 当用户手动拖动地图时5秒内不再自动变更地图可视范围
this.needIncludePoints = false;
throttle(getCloudData, 2000);
}
},
_tap(e) {
if (this.debug) console.log('tap: ', e)
this.$emit("tap", e);
},
_updated(e) {
if (this.debug) console.log('updated: ', e)
this.$emit("updated", e);
},
_anchorpointtap(e) {
if (this.debug) console.log('anchorpointtap: ', e)
this.$emit("anchorpointtap", e);
},
_poitap(e) {
if (this.debug) console.log('poitap: ', e)
this.$emit("native-poitap", e);
}
},
watch: {
whereWatchCom(){
// 需要重新查询
let { loadtime } = this;
if (loadtime !== "manual") {
let getCloudDataDebounce = () =>{
this.refresh({
needIncludePoints: true
});
}
debounce(getCloudDataDebounce, 200);
}
}
},
computed: {
whereWatchCom(){
let { where, collection } = this;
return JSON.stringify({ where, collection })
},
widthCom() {
let { width } = this;
return !isNaN(width) ? `${width}rpx` : width;
},
heightCom() {
let { height } = this;
return !isNaN(height) ? `${height}rpx` : height;
},
styleCom() {
let {
widthCom,
heightCom
} = this;
let style = "";
if (widthCom) {
style += `width:${widthCom};`;
}
if (heightCom) {
style += `height:${heightCom};`;
}
return style;
},
latitudeCom(){
let { latitude, defaultLatitude } = this;
return (latitude || latitude === 0) ? latitude : defaultLatitude;
},
longitudeCom(){
let { longitude, defaultLongitude } = this;
return (longitude || longitude === 0) ? longitude : defaultLongitude;
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,87 @@
{
"id": "unicloud-map",
"displayName": "unicloud-map 云端一体地图组件",
"version": "1.2.0",
"description": "主要用于显示数据库里的自定义POI渲染在地图上支持静态POI和动态POI",
"keywords": [
"unicloud-map",
"自定义POI"
],
"repository": "",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"type": "unicloud-template-page",
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "插件不采集任何数据",
"permissions": "无"
},
"npmurl": ""
},
"uni_modules": {
"dependencies": [
"uni-map-common"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "y"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y",
"app-harmony": "u",
"app-uvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "n",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "u",
"百度": "u",
"字节跳动": "u",
"QQ": "u",
"钉钉": "u",
"快手": "u",
"飞书": "u",
"京东": "u"
},
"快应用": {
"华为": "u",
"联盟": "u"
}
}
}
}
}

View File

@ -0,0 +1,9 @@
<!-- 此页面仅为发布插件时强制需要一个页面无其他作用 -->
<template>
</template>
<script>
</script>
<style>
</style>

View File

@ -0,0 +1,29 @@
# unicloud-map
unicloud-map是云端一体组件主要用于显示数据库里的自定义POI渲染在地图上。具体可以实现如下功能
1. 显示门店位置、景点位置、个人位置、车辆位置、活动举办地点等各种静态POI
2. 外卖软件显示外卖员实时配送路线
3. 打车软件显示司机到乘客上车点的实时路线
4. 更多基于自定义POI实现的功能
## 使用教程
> 文档地址:[uni-app](https://uniapp.dcloud.net.cn/uniCloud/unicloud-map.html)
> 文档地址:[uni-app x](https://uniapp.dcloud.net.cn/uniCloud/unicloud-map-x.html)
## bug反馈地址
> 加群:[uni-map交流群](https://im.dcloud.net.cn/#/?joinGroup=64d62b106823de10406ad72f)
## 运行效果图:
静态POI
![](https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/3707/409.png)
动态POI
![](https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/3707/408.png)

View File

@ -0,0 +1,22 @@
[
{
"IndexName": "location",
"MgoKeySchema": { "MgoIndexKeys": [{ "Name": "location", "Direction": "2dsphere" }], "MgoIsUnique": false }
},
{
"IndexName": "visible",
"MgoKeySchema": {"MgoIndexKeys": [{"Name": "visible","Direction": "1"}],"MgoIsUnique": false}
},
{
"IndexName": "province-city-district",
"MgoKeySchema": {"MgoIndexKeys": [{"Name": "province","Direction": "1"},{"Name": "city","Direction": "1"},{"Name": "district","Direction": "1"}],"MgoIsUnique": false}
},
{
"IndexName": "create_date",
"MgoKeySchema": {"MgoIndexKeys": [{"Name": "create_date","Direction": "1"}],"MgoIsUnique": false}
},
{
"IndexName": "category",
"MgoKeySchema": {"MgoIndexKeys": [{"Name": "category","Direction": "1"}],"MgoIsUnique": false}
}
]

View File

@ -0,0 +1,88 @@
{
"bsonType": "object",
"required": ["location", "title"],
"permission": {
"read": "doc.visible == true",
"create": false,
"update": false,
"delete": false
},
"properties": {
"_id": {
"description": "ID系统自动生成"
},
"visible": {
"title": "是否显示",
"bsonType": "bool",
"description": "为true代表前端clientDB可直接查询 false则clientDB不可以查询"
},
"category": {
"bsonType": "string",
"title": "分类",
"description": "用于区分显示在不同的场景地图下"
},
"type": {
"bsonType": "string",
"title": "类型",
"description": "POI类型可根据type自动匹配对应的icon支持直接输入中文"
},
"icon": {
"bsonType": "string",
"title": "图标",
"description": "支持https网络路径或本地绝对路径如果传了icon则不再根据type去匹配icon"
},
"rotate": {
"bsonType": "number",
"title": "图标角度",
"description": "POI图标的角度需保证0°的图片方向应朝左(西) 故可得90° 朝上(北) 180° 朝右(东) 270° 朝下(南)"
},
"level": {
"bsonType": "number",
"title": "图标显示的层级",
"description": "POI图标显示的层级越高越现在在上面"
},
"location": {
"title": "地理位置",
"bsonType": "object",
"description": "地理位置(包含经纬度)"
},
"title": {
"bsonType": "string",
"title": "名称",
"description": "名称"
},
"address": {
"bsonType": "string",
"title": "地址",
"description": "地址"
},
"tel": {
"bsonType": "string",
"title": "电话",
"description": "电话"
},
"province": {
"bsonType": "string",
"title": "省",
"description": "省"
},
"city": {
"bsonType": "string",
"title": "市",
"description": "市"
},
"district": {
"bsonType": "string",
"title": "区/县",
"description": "区/县"
},
"create_date": {
"title": "创建时间",
"bsonType": "timestamp",
"description": "创建时间",
"forceDefaultValue": {
"$env": "now"
}
}
}
}

View File

@ -0,0 +1,8 @@
{
"name": "database",
"origin-plugin-dev-name": "unicloud-map",
"origin-plugin-version": "1.2.0",
"plugin-dev-name": "unicloud-map",
"plugin-version": "1.2.0",
"description": ""
}