From de1bf858ed8b96f284081fa523bfc432e1bd6fce Mon Sep 17 00:00:00 2001 From: DCloud_LXH <283700113@qq.com> Date: Tue, 27 Jul 2021 16:14:21 +0800 Subject: [PATCH] feat(App): map --- package.json | 2 +- .../src/service/context/createMapContext.ts | 44 +- packages/uni-app-plus/src/platform/index.ts | 15 +- .../src/service/api/context/operateMap.ts | 17 + .../service/api/context/operateVideoPlayer.ts | 16 + .../src/view/components/map/index.tsx | 489 +++++++++++++++++- .../view/framework/dom/components/UniMap.ts | 11 +- packages/uni-app-plus/style/map.css | 31 ++ .../src/service/api/context/operateMap.ts | 6 +- yarn.lock | 8 +- 10 files changed, 609 insertions(+), 30 deletions(-) create mode 100644 packages/uni-app-plus/src/service/api/context/operateMap.ts create mode 100644 packages/uni-app-plus/src/service/api/context/operateVideoPlayer.ts create mode 100644 packages/uni-app-plus/style/map.css diff --git a/package.json b/package.json index d8f7d0fb9..495e1c224 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ }, "devDependencies": { "@babel/preset-env": "^7.14.7", - "@dcloudio/types": "^2.3.3", + "@dcloudio/types": "^2.3.5", "@jest/types": "^27.0.2", "@microsoft/api-extractor": "^7.13.2", "@rollup/plugin-alias": "^3.1.1", diff --git a/packages/uni-api/src/service/context/createMapContext.ts b/packages/uni-api/src/service/context/createMapContext.ts index dc4f10d93..19b4c0c37 100644 --- a/packages/uni-api/src/service/context/createMapContext.ts +++ b/packages/uni-api/src/service/context/createMapContext.ts @@ -7,6 +7,30 @@ import { CreateMapContextProtocol, } from '../../protocols/context/context' +const operateMapCallback = ( + options: { fail?: Function; success?: Function; complete?: Function }, + res: { errMsg: string; [propName: string]: any } +) => { + const errMsg = res.errMsg || '' + if (new RegExp('\\:\\s*fail').test(errMsg)) { + options.fail && options.fail(res) + } else { + options.success && options.success(res) + } + options.complete && options.complete(res) +} + +const operateMapWrap = ( + id: string, + pageId: number, + type: string, + options?: Data +) => { + operateMap(id, pageId, type, options, (res) => { + options && operateMapCallback(options, res) + }) +} + export class MapContext implements UniApp.MapContext { private id: string private pageId: number @@ -15,22 +39,27 @@ export class MapContext implements UniApp.MapContext { this.pageId = pageId } getCenterLocation(options: any) { - operateMap(this.id, this.pageId, 'getCenterLocation', options) + operateMapWrap(this.id, this.pageId, 'getCenterLocation', options) } moveToLocation() { - operateMap(this.id, this.pageId, 'moveToLocation') + operateMapWrap(this.id, this.pageId, 'moveToLocation') } getScale(options: any) { - operateMap(this.id, this.pageId, 'getScale', options) + operateMapWrap(this.id, this.pageId, 'getScale', options) } getRegion(options: any) { - operateMap(this.id, this.pageId, 'getRegion', options) + operateMapWrap(this.id, this.pageId, 'getRegion', options) } includePoints(options: any) { - operateMap(this.id, this.pageId, 'includePoints', options) + operateMapWrap(this.id, this.pageId, 'includePoints', options) } translateMarker(options: any) { - operateMap(this.id, this.pageId, 'translateMarker', options) + operateMapWrap(this.id, this.pageId, 'translateMarker', options) + } + $getAppMap() { + if (__PLATFORM__ === 'app') { + return plus.maps.getMapById(this.pageId + '-map-' + this.id) + } } addCustomLayer() {} removeCustomLayer() {} @@ -41,8 +70,7 @@ export class MapContext implements UniApp.MapContext { addMarkers() {} removeMarkers() {} moveAlong() {} - openMapAp() {} - $getAppMap() {} + openMapApp() {} } export const createMapContext = defineSyncApi( diff --git a/packages/uni-app-plus/src/platform/index.ts b/packages/uni-app-plus/src/platform/index.ts index 5b943b179..1aff2779f 100644 --- a/packages/uni-app-plus/src/platform/index.ts +++ b/packages/uni-app-plus/src/platform/index.ts @@ -8,19 +8,8 @@ import { export { getBaseSystemInfo } from '../service/api/base/getBaseSystemInfo' export { requestComponentInfo } from '../service/api/ui/requestComponentInfo' export { getRealPath } from './getRealPath' - -export function operateVideoPlayer( - videoId: string, - pageId: number, - type: string, - data?: unknown -) {} -export function operateMap( - id: string, - pageId: number, - type: string, - data?: unknown -) {} +export { operateVideoPlayer } from '../service/api/context/operateVideoPlayer' +export { operateMap } from '../service/api/context/operateMap' export function addIntersectionObserver( args: AddIntersectionObserverArgs, diff --git a/packages/uni-app-plus/src/service/api/context/operateMap.ts b/packages/uni-app-plus/src/service/api/context/operateMap.ts new file mode 100644 index 000000000..bd8fc168c --- /dev/null +++ b/packages/uni-app-plus/src/service/api/context/operateMap.ts @@ -0,0 +1,17 @@ +export function operateMap( + id: string, + pageId: number, + type: string, + data?: unknown, + operateMapCallback?: (res: any) => void +) { + UniServiceJSBridge.invokeViewMethod( + 'map.' + id, + { + type, + data, + }, + pageId, + operateMapCallback + ) +} diff --git a/packages/uni-app-plus/src/service/api/context/operateVideoPlayer.ts b/packages/uni-app-plus/src/service/api/context/operateVideoPlayer.ts new file mode 100644 index 000000000..dac17b663 --- /dev/null +++ b/packages/uni-app-plus/src/service/api/context/operateVideoPlayer.ts @@ -0,0 +1,16 @@ +export function operateVideoPlayer( + videoId: string, + pageId: number, + type: string, + data?: unknown +) { + UniServiceJSBridge.invokeViewMethod( + 'video.' + videoId, + { + videoId, + type, + data, + }, + pageId + ) +} diff --git a/packages/uni-app-plus/src/view/components/map/index.tsx b/packages/uni-app-plus/src/view/components/map/index.tsx index dc3b998d8..2b4f1f720 100644 --- a/packages/uni-app-plus/src/view/components/map/index.tsx +++ b/packages/uni-app-plus/src/view/components/map/index.tsx @@ -1,5 +1,492 @@ -import { defineBuiltInComponent } from '@dcloudio/uni-components' +import { + Ref, + ref, + watch, + onBeforeUnmount, + computed, + ExtractPropTypes, +} from 'vue' +import { extend } from '@vue/shared' +import { + defineBuiltInComponent, + useCustomEvent, + CustomEventTrigger, + EmitEvent, + useSubscribe, + useContextInfo, +} from '@dcloudio/uni-components' +import { useNativeAttrs, useNative } from '../../../helpers/useNative' +import { getCurrentPageId } from '@dcloudio/uni-core' +import { getRealPath } from '../../../platform/getRealPath' + +interface Coordinate { + latitude: number + longitude: number +} +type Coordinates = { + coord: Coordinate +} +const convertCoordinates = ( + lng: number, + lat: number, + callback: (res: Coordinates) => void +) => { + callback({ + coord: { + latitude: lat, + longitude: lng, + }, + }) +} + +function parseHex(color: string) { + if (color.indexOf('#') !== 0) { + return { + color, + opacity: 1, + } + } + const opacity = color.substr(7, 2) + return { + color: color.substr(0, 7), + opacity: opacity ? Number('0x' + opacity) / 255 : 1, + } +} + +interface Marker extends PlusMapsMarker { + id: number + latitude: number + longitude: number + iconPath: string + callout?: { content: string } + label?: { content: string } +} +interface Line extends PlusMapsPolyline { + points: Array + color?: string + width?: number +} +interface Circle extends PlusMapsCircle, Coordinate { + radius: number + color?: string + fillColor?: string + strokeWidth?: number +} +type Markers = Array +type Lines = Array +type Circles = Array +type Control = { + id?: number + position: Data + iconPath: string + clickable: boolean +} +interface Map extends PlusMapsMap { + __markers__: Markers + __markers_map__: Record + __lines__: Lines + __circles__: Circles +} + +const props = { + id: { + type: String, + default: '', + }, + latitude: { + type: [Number, String], + default: '', + }, + longitude: { + type: [Number, String], + default: '', + }, + scale: { + type: [String, Number], + default: 16, + }, + markers: { + type: Array, + default() { + return [] + }, + }, + polyline: { + type: Array, + default() { + return [] + }, + }, + circles: { + type: Array, + default() { + return [] + }, + }, + controls: { + type: Array, + default() { + return [] + }, + }, +} +type Props = ExtractPropTypes export default /*#__PURE__*/ defineBuiltInComponent({ name: 'Map', + props, + emits: ['click', 'regionchange', 'controltap', 'markertap', 'callouttap'], + setup(props, { emit }) { + const rootRef: Ref = ref(null) + const trigger = useCustomEvent>(rootRef, emit) + const containerRef: Ref = ref(null) + const attrs = useNativeAttrs(props, ['id']) + const { position, hidden, onParentReady } = useNative(containerRef) + + let map: Map | undefined + + const { _addMarkers, _addMapLines, _addMapCircles, _setMap } = + useMapMethods(props, trigger) + + onParentReady(() => { + map = extend( + plus.maps.create( + getCurrentPageId() + '-map-' + (props.id || Date.now()), + Object.assign({}, attrs.value, position) + ), + { + __markers__: [], + __markers_map__: {}, + __lines__: [], + __circles__: [], + } + ) + map.setZoom(parseInt(String(props.scale))) + plus.webview.currentWebview().append(map as any) + if (hidden.value) { + map.hide() + } + map.onclick = (e) => { + trigger('click', {} as Event, e) + } + map.onstatuschanged = (e) => { + trigger('regionchange', {} as Event, {}) + } + _setMap(map) + _addMarkers(props.markers as Markers) + _addMapLines(props.polyline as Lines) + _addMapCircles(props.circles as Circles) + + watch( + () => attrs.value, + (attrs) => map && map.setStyles(attrs as any), + { deep: true } + ) + watch( + () => position, + (position) => map && map.setStyles(position), + { deep: true } + ) + watch( + () => hidden.value, + (val) => { + map && map[val ? 'hide' : 'show']() + } + ) + watch( + () => props.scale, + (val) => { + map && map.setZoom(parseInt(String(val))) + } + ) + watch( + [() => props.latitude, () => props.longitude], + ([latitude, longitude]) => { + map && + map.setStyles({ + center: new plus.maps.Point!(Number(latitude), Number(longitude)), + }) + } + ) + watch( + () => props.markers, + (val) => { + _addMarkers(val as Markers, true) + } + ) + watch( + () => props.polyline, + (val) => { + _addMapLines(val as Lines) + } + ) + watch( + () => props.circles, + (val) => { + _addMapCircles(val as Circles) + } + ) + }) + + const mapControls = computed(() => + (props.controls as Array).map((control) => { + const position = { position: 'absolute' } + ;['top', 'left', 'width', 'height'].forEach((key) => { + if (control.position[key]) { + ;(position as any)[key] = control.position[key] + 'px' + } + }) + return { + id: control.id, + iconPath: getRealPath(control.iconPath), + position: position, + } + }) + ) + + onBeforeUnmount(() => { + if (map) { + map.close() + } + }) + + return () => { + return ( + +
+ {mapControls.value.map((control, index) => ( + + trigger('controltap', {} as Event, { controlId: control.id }) + } + /> + ))} +
+ + ) + } + }, }) + +type Callback = (res: any) => void +function useMapMethods(props: Props, trigger: CustomEventTrigger) { + let map: Map + function moveToLocation( + resolve: Callback, + { + longitude, + latitude, + }: { longitude?: Props['longitude']; latitude?: Props['latitude'] } = {} + ) { + if (!map) return + map.setCenter( + // @ts-expect-error + new plus.maps.Point( + Number(longitude || props.longitude), + Number(latitude || props.latitude) + ) + ) + resolve({ + errMsg: 'moveToLocation:ok', + }) + } + function getCenterLocation(resolve: Callback) { + if (!map) return + map.getCurrentCenter((state, point) => { + resolve({ + longitude: point.getLng(), + latitude: point.getLat(), + errMsg: 'getCenterLocation:ok', + }) + }) + } + function getRegion(resolve: Callback) { + if (!map) return + const rect = map.getBounds() + resolve({ + southwest: rect.getSouthWest(), + northeast: rect.getNorthEast(), // 5plus API 名字写错了 + errMsg: 'getRegion:ok', + }) + } + function getScale(resolve: Callback) { + if (!map) return + resolve({ + scale: map.getZoom(), + errMsg: 'getScale:ok', + }) + } + + function _addMarker(marker: Marker) { + if (!map) return + const { + id, + // title, + latitude, + longitude, + iconPath, + // width, + // height, + // rotate, + // alpha, + callout, + label, + } = marker + convertCoordinates(longitude, latitude, (res) => { + const { latitude, longitude } = res.coord + const nativeMarker = new plus.maps.Marker!( + new plus.maps.Point!(longitude, latitude) + ) + if (iconPath) { + nativeMarker.setIcon(getRealPath(iconPath)) + } + if (label && label.content) { + nativeMarker.setLabel(label.content as string) + } + let nativeBubble: PlusMapsBubble | undefined = undefined + if (callout && callout.content) { + nativeBubble = new plus.maps.Bubble!(callout.content) + } + if (nativeBubble) { + nativeMarker.setBubble(nativeBubble) + } + if (id || id === 0) { + nativeMarker.onclick = (e) => { + trigger('markertap', {} as Event, { + markerId: id, + }) + } + if (nativeBubble) { + nativeBubble.onclick = () => { + trigger('callouttap', {} as Event, { + markerId: id, + }) + } + } + } + map.addOverlay(nativeMarker as unknown as PlusMapsOverlay) + // 此处5+文档中PlusMapsMarker对象只有方法,没有属性 + // @ts-expect-error + map.__markers__.push(nativeMarker) + map.__markers_map__[id + ''] = nativeMarker + }) + } + function _clearMarkers() { + if (!map) return + const markers = map.__markers__ + markers.forEach((marker) => { + map.removeOverlay(marker as unknown as PlusMapsOverlay) + }) + map.__markers__ = [] + map.__markers_map__ = {} + } + function _addMarkers(markers: Markers, clear?: boolean) { + if (clear) { + _clearMarkers() + } + markers.forEach((marker) => { + _addMarker(marker) + }) + } + function _addMapLines(lines: Lines) { + if (!map) return + if (map.__lines__.length > 0) { + map.__lines__.forEach((circle) => { + map.removeOverlay(circle as unknown as PlusMapsOverlay) + }) + map.__lines__ = [] + } + + lines.forEach((line: Line) => { + const { + color, + width, + // dottedLine, + // arrowLine, + // arrowIconPath, + // borderColor, + // borderWidth + } = line + const points = line.points.map( + (point) => new plus.maps.Point!(point.longitude, point.latitude) + ) + const polyline = new plus.maps.Polyline!(points) + if (color) { + const strokeStyle = parseHex(color) + polyline.setStrokeColor(strokeStyle.color) + polyline.setStrokeOpacity(strokeStyle.opacity) + } + if (width) { + polyline.setLineWidth(width) + } + map.addOverlay(polyline as unknown as PlusMapsOverlay) + // 此处5+文档中PlusMapsPolyline对象只有方法,没有属性 + // @ts-expect-error + map.__lines__.push(polyline) + }) + } + function _addMapCircles(circles: Circles) { + if (!map) return + if (map.__circles__.length > 0) { + map.__circles__.forEach((circle) => { + map.removeOverlay(circle as unknown as PlusMapsOverlay) + }) + map.__circles__ = [] + } + + circles.forEach((circle) => { + const { latitude, longitude, color, fillColor, radius, strokeWidth } = + circle + const nativeCircle = new plus.maps.Circle!( + new plus.maps.Point!(longitude, latitude), + radius + ) + if (color) { + const strokeStyle = parseHex(color) + nativeCircle.setStrokeColor(strokeStyle.color) + nativeCircle.setStrokeOpacity(strokeStyle.opacity) + } + if (fillColor) { + const fillStyle = parseHex(fillColor) + nativeCircle.setFillColor(fillStyle.color) + nativeCircle.setFillOpacity(fillStyle.opacity) + } + if (strokeWidth) { + nativeCircle.setLineWidth(strokeWidth) + } + map.addOverlay(nativeCircle as unknown as PlusMapsOverlay) + // 此处5+文档中PlusMapsCircle对象只有方法,没有属性 + // @ts-expect-error + map.__circles__.push(nativeCircle) + }) + } + + const methods = { + moveToLocation, + getCenterLocation, + getRegion, + getScale, + } + type Method = keyof typeof methods + + useSubscribe( + (type, data: any, resolve) => { + methods[type as Method] && methods[type as Method](resolve, data) + }, + useContextInfo(), + true + ) + + return { + _addMarkers, + _addMapLines, + _addMapCircles, + _setMap(_map: Map) { + map = _map + }, + } +} diff --git a/packages/uni-app-plus/src/view/framework/dom/components/UniMap.ts b/packages/uni-app-plus/src/view/framework/dom/components/UniMap.ts index 7c750686e..8c78ce862 100644 --- a/packages/uni-app-plus/src/view/framework/dom/components/UniMap.ts +++ b/packages/uni-app-plus/src/view/framework/dom/components/UniMap.ts @@ -1,4 +1,5 @@ import { UniNodeJSON } from '@dcloudio/uni-shared' +import '../../../../../style/map.css' import Map from '../../../components/map' import { UniComponent } from './UniComponent' @@ -10,6 +11,14 @@ export class UniMap extends UniComponent { refNodeId: number, nodeJson: Partial ) { - super(id, 'uni-map', Map, parentNodeId, refNodeId, nodeJson) + super( + id, + 'uni-map', + Map, + parentNodeId, + refNodeId, + nodeJson, + '.uni-map-slot' + ) } } diff --git a/packages/uni-app-plus/style/map.css b/packages/uni-app-plus/style/map.css new file mode 100644 index 000000000..768e85688 --- /dev/null +++ b/packages/uni-app-plus/style/map.css @@ -0,0 +1,31 @@ +uni-map { + width: 300px; + height: 225px; + display: inline-block; + line-height: 0; + overflow: hidden; + position: relative; +} + +uni-map[hidden] { + display: none; +} + +.uni-map-container { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + overflow: hidden; + background-color: black; +} + +.uni-map-slot { + position: absolute; + top: 0; + width: 100%; + height: 100%; + overflow: hidden; + pointer-events: none; +} \ No newline at end of file diff --git a/packages/uni-h5/src/service/api/context/operateMap.ts b/packages/uni-h5/src/service/api/context/operateMap.ts index dc3b50b72..bd8fc168c 100644 --- a/packages/uni-h5/src/service/api/context/operateMap.ts +++ b/packages/uni-h5/src/service/api/context/operateMap.ts @@ -2,7 +2,8 @@ export function operateMap( id: string, pageId: number, type: string, - data?: unknown + data?: unknown, + operateMapCallback?: (res: any) => void ) { UniServiceJSBridge.invokeViewMethod( 'map.' + id, @@ -10,6 +11,7 @@ export function operateMap( type, data, }, - pageId + pageId, + operateMapCallback ) } diff --git a/yarn.lock b/yarn.lock index c70949ae0..59ff606f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -922,10 +922,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@dcloudio/types@^2.3.3": - version "2.3.4" - resolved "https://registry.yarnpkg.com/@dcloudio/types/-/types-2.3.4.tgz#24368f30ba9cb865d454fc183caa74f9920bb7ec" - integrity sha512-G86VT8YmlBdeN3Fzsq8QeI47AuKoOBSLkpBWBBilxC032p+lBx7ry/aY5ynriIjuqSVKUYvxA3LJ1DmXXXASmw== +"@dcloudio/types@^2.3.5": + version "2.3.5" + resolved "https://registry.nlark.com/@dcloudio/types/download/@dcloudio/types-2.3.5.tgz#01d1f4880dd6289b34e35792227bda28dd75493e" + integrity sha1-AdH0iA3WKJs041eSInvaKN11ST4= "@eslint/eslintrc@^0.4.3": version "0.4.3" -- GitLab