@heycar/heycars-map 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/business-components/BusinessRecomendPlaceMap/BusinessRecomendPlaceMap.d.ts +0 -1
- package/dist/business-components/PickupPoints/PickupPoints.d.ts +2 -1
- package/dist/components/Gmap/Gmap.css.d.ts +1 -0
- package/dist/components/Gmap/Gmap.d.ts +2 -0
- package/dist/components/MapProvider/MapProvider.d.ts +2 -0
- package/dist/hooks/useMapLngLatToVw.d.ts +10 -0
- package/dist/hooks/useMapPlace.d.ts +2 -1
- package/dist/hooks/useMapRecomendPlace.d.ts +4 -1
- package/dist/hooks/usePlacesLabelDirection.d.ts +9 -0
- package/dist/hooks/useWaitTimer.d.ts +12 -0
- package/dist/index.cjs +53 -53
- package/dist/index.js +1286 -1172
- package/dist/style.css +1 -1
- package/dist/utils/transform.d.ts +2 -0
- package/package.json +1 -1
- package/src/App.tsx +1 -1
- package/src/Demo/DemoBusinessRecomendPlace.tsx +8 -4
- package/src/business-components/AbsoluteAddressBox/AbsoluteAddressBox.tsx +1 -1
- package/src/business-components/BusinessRecomendPlaceMap/BusinessRecomendPlaceMap.tsx +42 -16
- package/src/business-components/PickupPoints/PickupPoints.tsx +24 -11
- package/src/business-components/PlaceCircle/PlaceCircle.css.ts +7 -2
- package/src/components/Gmap/Gmap.css.ts +5 -0
- package/src/components/Gmap/Gmap.ts +8 -10
- package/src/components/MapProvider/MapProvider.tsx +4 -2
- package/src/hooks/useGeoLocation.ts +3 -2
- package/src/hooks/useMapDrag.ts +0 -1
- package/src/hooks/useMapFitView.ts +0 -1
- package/src/hooks/useMapLngLatToVw.ts +31 -0
- package/src/hooks/useMapPlace.ts +15 -7
- package/src/hooks/useMapRecomendPlace.ts +17 -2
- package/src/hooks/usePlacesLabelDirection.ts +61 -0
- package/src/hooks/useWaitTimer.ts +25 -0
- package/src/hooks/useWalkingRoute.ts +0 -1
- package/src/utils/transform.ts +8 -0
- package/todo.md +1 -0
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.n8tgem0{pointer-events:none;position:absolute;bottom:50%;left:0;right:0;margin-left:auto;margin-right:auto;display:flex;flex-direction:column;align-items:center;z-index:1}.n8tgem1{pointer-events:auto;padding:2.67vw 4.27vw;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw;display:flex;align-items:center}.n8tgem2{padding-right:2.67vw;max-width:41.33vw}.n8tgem3{display:-webkit-box;font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.n8tgem4{margin-top:.53vw;font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#fff;line-height:4.27vw;opacity:.8}.n8tgem5{width:1.758vw;height:2.93vw}.n8tgem6{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw}.n8tgem7{width:6.9355vw;height:10.67vw}.rgf09w0{width:7.1045vw;height:10.93vw;display:flex}._15rq2xn0{display:flex;flex-direction:column;align-items:center;margin-bottom:-1.6vw}._15rq2xn1{padding:2.13vw 4.5vw 2.67vw 4.27vw;display:flex;align-items:center;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw}._15rq2xn2{font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;white-space:nowrap}._15rq2xn3{font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#eafaf8;line-height:4.27vw;white-space:nowrap;opacity:.8}._15rq2xn4{margin-left:3.2vw;width:1.44vw;height:2.4vw}._15rq2xn5{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw;z-index:1}._15rq2xn6{margin-top:-1.5vw;width:3.2vw;height:3.2vw}._1d8ur7t0
|
|
1
|
+
.n8tgem0{pointer-events:none;position:absolute;bottom:50%;left:0;right:0;margin-left:auto;margin-right:auto;display:flex;flex-direction:column;align-items:center;z-index:1}.n8tgem1{pointer-events:auto;padding:2.67vw 4.27vw;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw;display:flex;align-items:center}.n8tgem2{padding-right:2.67vw;max-width:41.33vw}.n8tgem3{display:-webkit-box;font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.n8tgem4{margin-top:.53vw;font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#fff;line-height:4.27vw;opacity:.8}.n8tgem5{width:1.758vw;height:2.93vw}.n8tgem6{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw}.n8tgem7{width:6.9355vw;height:10.67vw}.rgf09w0{width:7.1045vw;height:10.93vw;display:flex}._15rq2xn0{display:flex;flex-direction:column;align-items:center;margin-bottom:-1.6vw}._15rq2xn1{padding:2.13vw 4.5vw 2.67vw 4.27vw;display:flex;align-items:center;background:linear-gradient(90deg,#79AFFF 0%,#5C8DFF 32%,#507FFF 75%,#4471FF 100%);border-radius:3.2vw}._15rq2xn2{font-size:3.47vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#fff;line-height:4.8vw;white-space:nowrap}._15rq2xn3{font-size:2.93vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#eafaf8;line-height:4.27vw;white-space:nowrap;opacity:.8}._15rq2xn4{margin-left:3.2vw;width:1.44vw;height:2.4vw}._15rq2xn5{width:.8vw;height:4vw;background:#4C80FF;border-radius:0vw 0vw 26.67vw 26.67vw;z-index:1}._15rq2xn6{margin-top:-1.5vw;width:3.2vw;height:3.2vw}._1d8ur7t0{position:relative}._7anfuo0{height:inherit;width:inherit}._7anfuo1{position:relative}._4a4ovk0{margin-bottom:-.4vw;display:flex;flex-direction:column;align-items:center}._4a4ovk1{padding:1.07vw 2.4vw 1.6vw 2.67vw;background:#FFFFFF;box-shadow:0 .53vw 2.4vw #3f3f3f33;border-radius:2.13vw}._4a4ovk2{padding:2.67vw 2.67vw 2.4vw 3.2vw;background:#FFFFFF;box-shadow:0 0 2.67vw #4e556c26;border-radius:4.8vw;font-size:3.2vw;font-family:PingFangSC-Medium,PingFang SC;font-weight:500;color:#1e1e1e;line-height:4.53vw;white-space:nowrap}._4a4ovk3{font-size:3.2vw;font-family:PingFangSC-Medium,PingFang SC;font-weight:500;color:#1e1e1e;line-height:4.53vw;white-space:nowrap}._4a4ovk4{font-size:2.4vw;font-family:PingFangSC-Regular,PingFang SC;font-weight:400;color:#7e7e7e;line-height:3.47vw;white-space:nowrap}._4a4ovk5{color:#4471ff;font-family:PingFangSC-Medium,PingFang SC;font-weight:500}._4a4ovk6{margin-top:1.33vw;width:6.192vw;height:7.998vw}._4a4ovk7{margin-top:2.4vw;width:6.192vw;height:7.998vw}._1l6offo0{display:flex}._1l6offo1{width:4.53vw;height:4.53vw;margin-bottom:-2.265vw}._1l6offo2{width:16.27vw;height:16.27vw;margin-bottom:-8.135vw}.fhyw80{display:flex;align-items:center}.fhyw81{flex-direction:row-reverse}.fhyw83{margin-right:-2.265vw}.fhyw84{margin-left:-2.265vw}.fhyw85{margin-bottom:-2.265vw}.fhyw86{margin-right:calc(50% - 2.265vw)}.fhyw87{margin-left:calc(50% - 2.265vw)}.fhyw88{display:flex;width:4.53vw;height:4.53vw}.fhyw89{opacity:0}.fhyw8b{display:-webkit-box;font-size:3.2vw;font-family:PingFangSC-Semibold,PingFang SC;font-weight:600;color:#243e66;line-height:4.53vw;width:max-content;max-width:32vw;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}._65j3sr1{margin-top:1.87vw;margin-bottom:-5.5vw;width:6vw;height:11vw}@keyframes rrvp0y0{0%{transform:scale(0);opacity:1}33.33%{transform:scale(.5);opacity:1}66.66%{transform:scale(1);opacity:0}to{transform:scale(1);opacity:0}}@keyframes rrvp0y1{0%{transform:scale(0);opacity:0}33.33%{transform:scale(0);opacity:1}66.66%{transform:scale(.5);opacity:1}to{transform:scale(1);opacity:0}}.rrvp0y2{display:flex;margin-bottom:-30.935vw;width:61.87vw;height:61.87vw;animation-name:rrvp0y0;animation-duration:3s;animation-iteration-count:infinite;animation-timing-function:linear}.rrvp0y3{animation-name:rrvp0y1}
|
|
@@ -13,3 +13,5 @@ export declare const language2vectorMapForeign: (lang: "en" | "zh") => VectorMap
|
|
|
13
13
|
export declare const place2point: (place: Place) => Point;
|
|
14
14
|
export declare const pipeAsync: <P extends any[]>(fn?: ((...args: P) => any) | undefined) => (...args: P) => void;
|
|
15
15
|
export declare const geocoderResult2cityName: (value: google.maps.GeocoderResult) => string | undefined;
|
|
16
|
+
export declare const isPlaceEqual: (p1: Place, p2?: Place) => boolean;
|
|
17
|
+
export declare const isPointEqual: (p1: Point, p2?: Point) => boolean;
|
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -42,8 +42,13 @@ export const DemoBusinessRecomendPlace = defineSetup(function DemoBusinessRecome
|
|
|
42
42
|
const { lat, lng } = place;
|
|
43
43
|
console.log("getRecomendPlace context?.source = ", context?.source);
|
|
44
44
|
return [
|
|
45
|
-
{
|
|
46
|
-
|
|
45
|
+
{
|
|
46
|
+
lat: lat + 0.1001,
|
|
47
|
+
lng: lng + 0.0001,
|
|
48
|
+
name: "Blessing Koko Dayc are Blessing Koko which is a very long name",
|
|
49
|
+
},
|
|
50
|
+
{ lat: lat + 0.0002, lng: lng + 0.0003, name: "place name 2" },
|
|
51
|
+
{ lat: lat + 0.0003, lng: lng + 0.0002, name: "place name 3" },
|
|
47
52
|
];
|
|
48
53
|
};
|
|
49
54
|
const {
|
|
@@ -64,10 +69,9 @@ export const DemoBusinessRecomendPlace = defineSetup(function DemoBusinessRecome
|
|
|
64
69
|
<SomeAutoComplete cityName="Shinjuku-ku"></SomeAutoComplete>
|
|
65
70
|
<BusinessRecomendPlaceMap
|
|
66
71
|
class={css.demo}
|
|
67
|
-
geoErrorTitle={"定位获取失败\n请手动设置上车点"}
|
|
68
72
|
geoLoadingTitle={"正在读取GPS位置信息..."}
|
|
69
73
|
unavailableTitle={"当前区域暂未开通服务"}
|
|
70
|
-
noRecomendDescription={"
|
|
74
|
+
noRecomendDescription={""}
|
|
71
75
|
recomendDescription={"最佳上车地点"}
|
|
72
76
|
geoDefaultPosition={[139.728287, 35.661007]}
|
|
73
77
|
getRecomendPlace={getRecomendPlace}
|
|
@@ -39,7 +39,7 @@ export const AbsoluteAddressBox = defineSetup(function AbsoluteAddressBox(
|
|
|
39
39
|
<div class={css.absoluteAddressBox} onClick={handleClick}>
|
|
40
40
|
<div class={css.boxTextLayout}>
|
|
41
41
|
<MultilineTitle class={css.boxTitle} value={title} />
|
|
42
|
-
{description && <div class={css.boxDescription}>{description}</div>}
|
|
42
|
+
{!!description && <div class={css.boxDescription}>{description}</div>}
|
|
43
43
|
</div>
|
|
44
44
|
<img class={css.arrowRight} src={imgArrowRight} />
|
|
45
45
|
</div>
|
|
@@ -8,13 +8,16 @@ import { MapLogProps, useMapLog } from "../../hooks/useMapLog";
|
|
|
8
8
|
import type { UseMapPlaceProps } from "../../hooks/useMapPlace";
|
|
9
9
|
import { useMapRecomendPlace, UseMapRecomendPlaceProps } from "../../hooks/useMapRecomendPlace";
|
|
10
10
|
import { useMapSupplier } from "../../hooks/useMapSupplier";
|
|
11
|
+
import { useWaitTimer } from "../../hooks/useWaitTimer";
|
|
11
12
|
import { defineLagecySetup } from "../../types/helper";
|
|
12
13
|
import type { Place, Point } from "../../types/interface";
|
|
14
|
+
import { isPlaceEqual } from "../../utils/transform";
|
|
13
15
|
import { AbsoluteAddressBox, AbsoluteAddressBoxProps } from "../AbsoluteAddressBox";
|
|
14
16
|
import { PassengerCircle } from "../PassengerCircle";
|
|
15
17
|
import { PickupPoints } from "../PickupPoints";
|
|
16
18
|
|
|
17
19
|
const RECOMMEND_PLACE_LIMIT = 30;
|
|
20
|
+
const MAP_DRAG_END_WAIT_ANIMATION = 500;
|
|
18
21
|
|
|
19
22
|
export interface CenterPlaceSource {
|
|
20
23
|
source: "default" | "geo" | "drag" | "recomend" | "api";
|
|
@@ -24,7 +27,6 @@ export interface BusinessRecomendPlaceMapProps
|
|
|
24
27
|
Pick<UseMapRecomendPlaceProps<CenterPlaceSource>, "getRecomendPlace">,
|
|
25
28
|
MapLogProps {
|
|
26
29
|
geoLoadingTitle: string;
|
|
27
|
-
geoErrorTitle: string;
|
|
28
30
|
unavailableTitle: string;
|
|
29
31
|
recomendDescription: string;
|
|
30
32
|
noRecomendDescription: string;
|
|
@@ -45,7 +47,8 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
45
47
|
useMapLog(props, "BusinessRecomendPlaceMap");
|
|
46
48
|
const { geoDefaultPosition, getRecomendPlace, mapContext } = props;
|
|
47
49
|
const { mapRef, setMap } = useHeycarMap();
|
|
48
|
-
const { readyPromise } = useMapSupplier();
|
|
50
|
+
const { readyPromise, supplier } = useMapSupplier();
|
|
51
|
+
const { timerStart, timerStop } = useWaitTimer({ interval: MAP_DRAG_END_WAIT_ANIMATION });
|
|
49
52
|
const centerPlace = reactive<Place>({
|
|
50
53
|
lng: geoDefaultPosition?.[0] ?? 0,
|
|
51
54
|
lat: geoDefaultPosition?.[1] ?? 0,
|
|
@@ -99,6 +102,7 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
99
102
|
mapRef,
|
|
100
103
|
onChange: (point) => {
|
|
101
104
|
centerSource.source = "drag";
|
|
105
|
+
timerStart();
|
|
102
106
|
updatePlace(point);
|
|
103
107
|
emit("changeByDrag", point);
|
|
104
108
|
},
|
|
@@ -110,6 +114,7 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
110
114
|
updateRecomandPlace,
|
|
111
115
|
placeCandidates,
|
|
112
116
|
availableRef,
|
|
117
|
+
isElectedRef,
|
|
113
118
|
} = useMapRecomendPlace({
|
|
114
119
|
pointRef: centerPoint,
|
|
115
120
|
context: centerSource,
|
|
@@ -133,7 +138,8 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
133
138
|
}
|
|
134
139
|
emit("changePlace", place);
|
|
135
140
|
},
|
|
136
|
-
onChange: (place) => {
|
|
141
|
+
onChange: async (place) => {
|
|
142
|
+
await timerStop.promise;
|
|
137
143
|
centerSource.source = "recomend";
|
|
138
144
|
Object.assign(centerPlace, { ...place });
|
|
139
145
|
emit("changeRecomandPlace", place);
|
|
@@ -151,35 +157,55 @@ export const BusinessRecomendPlaceMap = defineLagecySetup(
|
|
|
151
157
|
return () => {
|
|
152
158
|
const title = geoLoading.value
|
|
153
159
|
? props.geoLoadingTitle
|
|
154
|
-
: geoError.value
|
|
155
|
-
? props.geoErrorTitle
|
|
156
160
|
: availableRef.value
|
|
157
161
|
? centerPlace.name
|
|
158
162
|
: props.unavailableTitle;
|
|
159
163
|
const description =
|
|
160
|
-
!availableRef.value ||
|
|
164
|
+
!availableRef.value || geoLoading.value
|
|
161
165
|
? undefined
|
|
162
|
-
:
|
|
166
|
+
: isElectedRef.value
|
|
163
167
|
? props.recomendDescription
|
|
164
168
|
: props.noRecomendDescription;
|
|
165
169
|
return (
|
|
166
|
-
<HeycarMap
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
<HeycarMap
|
|
171
|
+
center={centerPoint.value}
|
|
172
|
+
zoom={18}
|
|
173
|
+
mapRef={setMap}
|
|
174
|
+
renderOverlay={() => {
|
|
175
|
+
return supplier === "gmap" ? (
|
|
176
|
+
<AbsoluteAddressBox
|
|
177
|
+
title={title}
|
|
178
|
+
description={description}
|
|
179
|
+
type={isDragging.value ? "locator" : "box"}
|
|
180
|
+
log={props.log}
|
|
181
|
+
onClick={() => emit("clickLocator")}
|
|
182
|
+
/>
|
|
183
|
+
) : null;
|
|
184
|
+
}}
|
|
185
|
+
log={props.log}
|
|
186
|
+
>
|
|
187
|
+
{!geoError.value && <PassengerCircle position={geoPosition.value} />}
|
|
188
|
+
<PickupPoints
|
|
189
|
+
places={placeCandidates.value}
|
|
190
|
+
renderLabel={(place) => (isPlaceEqual(place, centerPlace) ? undefined : place.name)}
|
|
191
|
+
onClick={setCenterByPlace}
|
|
173
192
|
log={props.log}
|
|
174
|
-
onClick={() => emit("clickLocator")}
|
|
175
193
|
/>
|
|
194
|
+
{supplier === "amap" && (
|
|
195
|
+
<AbsoluteAddressBox
|
|
196
|
+
title={title}
|
|
197
|
+
description={description}
|
|
198
|
+
type={isDragging.value ? "locator" : "box"}
|
|
199
|
+
log={props.log}
|
|
200
|
+
onClick={() => emit("clickLocator")}
|
|
201
|
+
/>
|
|
202
|
+
)}
|
|
176
203
|
</HeycarMap>
|
|
177
204
|
);
|
|
178
205
|
};
|
|
179
206
|
}
|
|
180
207
|
).props([
|
|
181
208
|
"log",
|
|
182
|
-
"geoErrorTitle",
|
|
183
209
|
"geoLoadingTitle",
|
|
184
210
|
"unavailableTitle",
|
|
185
211
|
"recomendDescription",
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { reactive, toRef } from "vue-demi";
|
|
1
2
|
import { MapLogProps, useMapLog } from "../../hooks/useMapLog";
|
|
3
|
+
import { usePointsLabelDirection } from "../../hooks/usePlacesLabelDirection";
|
|
2
4
|
import { defineSetup } from "../../types/helper";
|
|
3
5
|
import type { Place } from "../../types/interface";
|
|
4
6
|
import { place2point } from "../../utils/transform";
|
|
@@ -6,20 +8,31 @@ import { PlaceCircle } from "../PlaceCircle";
|
|
|
6
8
|
|
|
7
9
|
export interface PickupPointsProps extends MapLogProps {
|
|
8
10
|
places: Place[];
|
|
11
|
+
renderLabel?: (place: Place) => string | undefined;
|
|
9
12
|
onClick?: (value: Place) => any;
|
|
10
13
|
}
|
|
11
14
|
export const PickupPoints = defineSetup(function PickupPoints(props: PickupPointsProps, { emit }) {
|
|
12
15
|
useMapLog(props, "PickupPoints");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<PlaceCircle
|
|
17
|
-
position={place2point(place)}
|
|
18
|
-
label={place.name}
|
|
19
|
-
textAlign="left"
|
|
20
|
-
onClick={() => emit("click", place)}
|
|
21
|
-
/>
|
|
22
|
-
))}
|
|
23
|
-
</div>
|
|
16
|
+
const { renderLabel } = props;
|
|
17
|
+
const directions = usePointsLabelDirection(
|
|
18
|
+
reactive({ places: toRef(props, "places"), heightLimit: 9, widthLimit: 32 })
|
|
24
19
|
);
|
|
20
|
+
return () => {
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
{props.places.map((place, idx) => {
|
|
24
|
+
const direction = directions.value[idx];
|
|
25
|
+
const label = renderLabel ? renderLabel(place) : place.name;
|
|
26
|
+
return (
|
|
27
|
+
<PlaceCircle
|
|
28
|
+
position={place2point(place)}
|
|
29
|
+
label={label}
|
|
30
|
+
textAlign={direction}
|
|
31
|
+
onClick={() => emit("click", place)}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
})}
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
25
38
|
});
|
|
@@ -62,10 +62,15 @@ export const placeIcon = recipe({
|
|
|
62
62
|
});
|
|
63
63
|
|
|
64
64
|
export const label = style({
|
|
65
|
-
|
|
65
|
+
display: "-webkit-box",
|
|
66
|
+
fontSize: "3.2vw",
|
|
66
67
|
fontFamily: "PingFangSC-Semibold, PingFang SC",
|
|
67
68
|
fontWeight: 600,
|
|
68
69
|
color: "#243E66",
|
|
69
70
|
lineHeight: "4.53vw",
|
|
70
|
-
|
|
71
|
+
width: "max-content",
|
|
72
|
+
maxWidth: "32vw",
|
|
73
|
+
WebkitLineClamp: 2,
|
|
74
|
+
WebkitBoxOrient: "vertical",
|
|
75
|
+
overflow: "hidden",
|
|
71
76
|
});
|
|
@@ -2,7 +2,7 @@ import { computed, h, shallowRef, watchPostEffect } from "vue-demi";
|
|
|
2
2
|
import type { SetMap } from "../../hooks/useHeycarMap";
|
|
3
3
|
import { provideGmap } from "../../hooks/useMap";
|
|
4
4
|
import { useMapEventSource } from "../../hooks/useMapEventSource";
|
|
5
|
-
import { defineSetup } from "../../types/helper";
|
|
5
|
+
import { defineSetup, VueNode } from "../../types/helper";
|
|
6
6
|
import type { MapEventHandler } from "../../types/mapHelper";
|
|
7
7
|
import {
|
|
8
8
|
watchNoneImmediatePostEffectForDeepOption,
|
|
@@ -14,12 +14,13 @@ export interface GmapProps extends google.maps.MapOptions {
|
|
|
14
14
|
mapRef?: SetMap<google.maps.Map>;
|
|
15
15
|
onDragStart?: MapEventHandler<google.maps.Map>;
|
|
16
16
|
onDragEnd?: MapEventHandler<google.maps.Map>;
|
|
17
|
+
renderOverlay?: () => VueNode;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export const Gmap = defineSetup(function Gmap(props: GmapProps, { slots, emit }) {
|
|
20
21
|
const setMap = props.mapRef;
|
|
21
22
|
const options = computed(() => {
|
|
22
|
-
const { mapRef, onDragStart, onDragEnd, ...options } = props;
|
|
23
|
+
const { renderOverlay, mapRef, onDragStart, onDragEnd, ...options } = props;
|
|
23
24
|
return options;
|
|
24
25
|
});
|
|
25
26
|
const defaultOptions = options.value;
|
|
@@ -38,13 +39,6 @@ export const Gmap = defineSetup(function Gmap(props: GmapProps, { slots, emit })
|
|
|
38
39
|
mapRef.value = map!;
|
|
39
40
|
setMap?.(map!);
|
|
40
41
|
});
|
|
41
|
-
// watch(
|
|
42
|
-
// () => options.value.zoom,
|
|
43
|
-
// (zoom) => {
|
|
44
|
-
// if (zoom) mapRef.value?.setZoom(zoom);
|
|
45
|
-
// },
|
|
46
|
-
// { flush: "post" }
|
|
47
|
-
// );
|
|
48
42
|
watchNoneImmediatePostEffectForDeepOption(
|
|
49
43
|
() => options.value,
|
|
50
44
|
(options) => {
|
|
@@ -52,5 +46,9 @@ export const Gmap = defineSetup(function Gmap(props: GmapProps, { slots, emit })
|
|
|
52
46
|
}
|
|
53
47
|
);
|
|
54
48
|
watchPostEffectForGMapEvent(mapRef, props, handleDrag, ["onDragStart", "onDragEnd"]);
|
|
55
|
-
return () =>
|
|
49
|
+
return () =>
|
|
50
|
+
h("div", { class: css.gmapLayout }, [
|
|
51
|
+
props.renderOverlay?.(),
|
|
52
|
+
h("div", { ref: elementRef, class: css.gmap }, slots.default?.()),
|
|
53
|
+
]);
|
|
56
54
|
});
|
|
@@ -29,6 +29,7 @@ export interface HeycarMapProps
|
|
|
29
29
|
Pick<AmapProps, "center" | "zoom">,
|
|
30
30
|
MapLogProps {
|
|
31
31
|
mapRef?: SetMap<AMap.Map> | SetMap<google.maps.Map>;
|
|
32
|
+
renderOverlay?: GmapProps["renderOverlay"];
|
|
32
33
|
onDragEnd?: (value: [number, number]) => any;
|
|
33
34
|
}
|
|
34
35
|
export const HeycarMap = defineLagecySetup(function HeycarMap(
|
|
@@ -47,7 +48,7 @@ export const HeycarMap = defineLagecySetup(function HeycarMap(
|
|
|
47
48
|
};
|
|
48
49
|
return () => {
|
|
49
50
|
const { supplier, gmapId, language = "zh", status } = payload;
|
|
50
|
-
const { mapRef: setMap, center, zoom } = props;
|
|
51
|
+
const { mapRef: setMap, center, zoom, renderOverlay } = props;
|
|
51
52
|
if (status === Status.LOADING) return slots.loading?.();
|
|
52
53
|
if (status === Status.FAILURE) return slots.fallback?.();
|
|
53
54
|
const children = slots.default?.();
|
|
@@ -59,6 +60,7 @@ export const HeycarMap = defineLagecySetup(function HeycarMap(
|
|
|
59
60
|
mapId={gmapId}
|
|
60
61
|
center={center ? vec2lnglat(center) : undefined}
|
|
61
62
|
zoom={zoom}
|
|
63
|
+
renderOverlay={renderOverlay}
|
|
62
64
|
onDragEnd={gmapDebouncedEmit}
|
|
63
65
|
>
|
|
64
66
|
{children}
|
|
@@ -78,4 +80,4 @@ export const HeycarMap = defineLagecySetup(function HeycarMap(
|
|
|
78
80
|
);
|
|
79
81
|
}
|
|
80
82
|
};
|
|
81
|
-
}).props(["center", "fallback", "loading", "mapRef", "zoom"]);
|
|
83
|
+
}).props(["center", "fallback", "loading", "mapRef", "zoom", "renderOverlay"]);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ref, watch } from "vue-demi";
|
|
2
2
|
import type { Point } from "../types/interface";
|
|
3
3
|
|
|
4
|
+
const GEO_TIMEOUT = 5000;
|
|
5
|
+
|
|
4
6
|
export interface UseGeoLocationProps {
|
|
5
7
|
geoDefaultPosition?: Point;
|
|
6
8
|
onChange?: (value: { position: Point; coordinate: GeolocationCoordinates }) => any;
|
|
@@ -52,8 +54,7 @@ export const useGeoLocation = (props?: UseGeoLocationProps) => {
|
|
|
52
54
|
onError?.(error);
|
|
53
55
|
console.log("errorRef = ", errorRef);
|
|
54
56
|
},
|
|
55
|
-
|
|
56
|
-
{ timeout: 3000, maximumAge: 0, enableHighAccuracy: false }
|
|
57
|
+
{ timeout: GEO_TIMEOUT, maximumAge: 0, enableHighAccuracy: false }
|
|
57
58
|
);
|
|
58
59
|
onCleanup(() => navigator.geolocation.clearWatch(watchId));
|
|
59
60
|
},
|
package/src/hooks/useMapDrag.ts
CHANGED
|
@@ -23,7 +23,6 @@ export const useAmapDrag = (props: UseMapDragProps<AMap.Map>) => {
|
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
const emitDragEnd: SetupContextEmitHandler<Pick<AmapProps, "onDragEnd">> = (_, { target }) => {
|
|
26
|
-
console.log("on DragEnd ------");
|
|
27
26
|
isDragging.value = false;
|
|
28
27
|
const { lng, lat } = target.getCenter();
|
|
29
28
|
centerRef.value = [lng, lat];
|
|
@@ -32,7 +32,6 @@ export const useAmapFitView = (props: UseMapFitViewProps<AMap.Map>) => {
|
|
|
32
32
|
if (!map) return;
|
|
33
33
|
const vw = window.innerWidth / 100;
|
|
34
34
|
const [top, right, bottom, left] = props.padding ?? [0, 0, 0, 0];
|
|
35
|
-
console.log([...overlayGroup]);
|
|
36
35
|
map.setFitView([...overlayGroup], false, [top * vw, bottom * vw, left * vw, right * vw]);
|
|
37
36
|
};
|
|
38
37
|
const registerFitVeiw: RegisterOverlay<AmapOverlay> = {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Point } from "../types/interface";
|
|
2
|
+
import { useAmap, useGmap } from "./useMap";
|
|
3
|
+
import { useMapSupplier } from "./useMapSupplier";
|
|
4
|
+
|
|
5
|
+
export const useAmapLngLatToVw = () => {
|
|
6
|
+
const amap = useAmap();
|
|
7
|
+
const apiMapLngLatToVw = (point: Point) => {
|
|
8
|
+
return amap?.value?.lngLatToContainer(point).toArray() as [number, number];
|
|
9
|
+
};
|
|
10
|
+
return { apiMapLngLatToVw };
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const useGmapLngLatToVw = () => {
|
|
14
|
+
const gmap = useGmap();
|
|
15
|
+
const apiMapLngLatToVw = (point: Point) => {
|
|
16
|
+
const [lng, lat] = point;
|
|
17
|
+
const projection = gmap?.value?.getProjection();
|
|
18
|
+
const worldCoordinate = projection?.fromLatLngToPoint({ lng, lat });
|
|
19
|
+
const x = worldCoordinate?.x ?? 0;
|
|
20
|
+
const y = worldCoordinate?.y ?? 0;
|
|
21
|
+
const zoom = gmap?.value?.getZoom() ?? 0;
|
|
22
|
+
const scale = 2 ** zoom;
|
|
23
|
+
return [x * scale, y * scale] as [number, number];
|
|
24
|
+
};
|
|
25
|
+
return { apiMapLngLatToVw };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const useMapLngLatToVw = () => {
|
|
29
|
+
const { supplier } = useMapSupplier();
|
|
30
|
+
return supplier === "gmap" ? useGmapLngLatToVw() : useAmapLngLatToVw();
|
|
31
|
+
};
|
package/src/hooks/useMapPlace.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { useUpdate } from "./useUdate";
|
|
|
8
8
|
export interface UseMapPlaceOnChangeCityProps {
|
|
9
9
|
cityName?: string;
|
|
10
10
|
cityParentName?: string;
|
|
11
|
+
countryName?: string;
|
|
11
12
|
}
|
|
12
13
|
export interface UseMapPlaceProps {
|
|
13
14
|
pointRef: Ref<Point>;
|
|
@@ -33,7 +34,7 @@ export const useAmapPlace = (props: UseMapPlaceProps) => {
|
|
|
33
34
|
const { addressComponent } = (result as AMap.ReGeocoderResult).regeocode;
|
|
34
35
|
const cityName = addressComponent.city ? addressComponent.city : addressComponent.province;
|
|
35
36
|
const cityParentName = addressComponent.province;
|
|
36
|
-
onChangeCity?.({ cityName, cityParentName });
|
|
37
|
+
onChangeCity?.({ cityName, cityParentName, countryName: "" });
|
|
37
38
|
});
|
|
38
39
|
};
|
|
39
40
|
const updatePlace = (value: Point) => {
|
|
@@ -64,7 +65,7 @@ export const useAmapPlace = (props: UseMapPlaceProps) => {
|
|
|
64
65
|
place.lng = lng;
|
|
65
66
|
place.lat = lat;
|
|
66
67
|
onChange?.({ lng, lat, name });
|
|
67
|
-
onChangeCity?.({ cityName, cityParentName });
|
|
68
|
+
onChangeCity?.({ cityName, cityParentName, countryName: "" });
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
71
|
|
|
@@ -82,14 +83,20 @@ export const useAmapPlace = (props: UseMapPlaceProps) => {
|
|
|
82
83
|
};
|
|
83
84
|
|
|
84
85
|
export const useGmapPlace = (props: UseMapPlaceProps) => {
|
|
85
|
-
const {
|
|
86
|
+
const { onChange, onChangeCity } = props;
|
|
87
|
+
const defaultPoint: Point = [...props.pointRef.value];
|
|
88
|
+
const pointRef = ref<Point>(defaultPoint);
|
|
86
89
|
const { readyPromise } = useMapSupplier();
|
|
87
|
-
const { idx: placeKey, update
|
|
90
|
+
const { idx: placeKey, update } = useUpdate();
|
|
88
91
|
const place = reactive<Place>({
|
|
89
92
|
lng: pointRef.value[0],
|
|
90
93
|
lat: pointRef.value[1],
|
|
91
94
|
name: "",
|
|
92
95
|
});
|
|
96
|
+
const updatePlace = (value: Point) => {
|
|
97
|
+
pointRef.value = value;
|
|
98
|
+
update();
|
|
99
|
+
};
|
|
93
100
|
const setPlace = (value: Place) => {
|
|
94
101
|
place.lng = value.lng;
|
|
95
102
|
place.lat = value.lat;
|
|
@@ -101,7 +108,8 @@ export const useGmapPlace = (props: UseMapPlaceProps) => {
|
|
|
101
108
|
const { results } = await geocoder.geocode({ language: LANGUAGE, location: { lng, lat } });
|
|
102
109
|
const cityName = geocoderResult2cityName(results[0]);
|
|
103
110
|
const cityParentName = results.slice(-2)[0].address_components[0].long_name;
|
|
104
|
-
|
|
111
|
+
const countryName = results.slice(-1)[0].address_components[0].long_name;
|
|
112
|
+
onChangeCity?.({ cityName, cityParentName, countryName });
|
|
105
113
|
};
|
|
106
114
|
watch(
|
|
107
115
|
() => placeKey.value,
|
|
@@ -113,12 +121,12 @@ export const useGmapPlace = (props: UseMapPlaceProps) => {
|
|
|
113
121
|
const name = results[0].formatted_address;
|
|
114
122
|
const cityName = geocoderResult2cityName(results[0]);
|
|
115
123
|
const cityParentName = results.slice(-2)[0].address_components[0].long_name;
|
|
116
|
-
|
|
124
|
+
const countryName = results.slice(-1)[0].address_components[0].long_name;
|
|
117
125
|
place.lng = lng;
|
|
118
126
|
place.lat = lat;
|
|
119
127
|
place.name = name;
|
|
120
128
|
onChange?.({ lng, lat, name });
|
|
121
|
-
onChangeCity?.({ cityName, cityParentName });
|
|
129
|
+
onChangeCity?.({ cityName, cityParentName, countryName });
|
|
122
130
|
}
|
|
123
131
|
);
|
|
124
132
|
return { place, updatePlace, setPlace, updateCity };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { reactive, ref, Ref, watch } from "vue-demi";
|
|
1
|
+
import { computed, reactive, ref, Ref, watch } from "vue-demi";
|
|
2
2
|
import type { Place, Point } from "../types/interface";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { isPointEqual, place2point } from "../utils/transform";
|
|
4
5
|
import { useAmapPlace, useGmapPlace, UseMapPlaceProps } from "./useMapPlace";
|
|
5
6
|
import { useMapSupplier } from "./useMapSupplier";
|
|
6
7
|
import { useUpdate } from "./useUdate";
|
|
@@ -52,6 +53,12 @@ export const useAmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
52
53
|
onChangeCity,
|
|
53
54
|
});
|
|
54
55
|
const recomendPlace = reactive<Place>({ ...place });
|
|
56
|
+
const isElectedRef = computed(() => {
|
|
57
|
+
const point = pointRef.value;
|
|
58
|
+
return placeCandidatesRef.value.some((candidate) =>
|
|
59
|
+
isPointEqual(place2point(candidate), point)
|
|
60
|
+
);
|
|
61
|
+
});
|
|
55
62
|
watch(
|
|
56
63
|
() => recomendPlaceKey.value,
|
|
57
64
|
async () => {
|
|
@@ -77,6 +84,7 @@ export const useAmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
77
84
|
recomendPlace,
|
|
78
85
|
placeCandidates: placeCandidatesRef,
|
|
79
86
|
availableRef,
|
|
87
|
+
isElectedRef,
|
|
80
88
|
updateRecomandPlace,
|
|
81
89
|
updatePlace,
|
|
82
90
|
setPlace,
|
|
@@ -97,6 +105,12 @@ export const useGmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
97
105
|
onChangeCity,
|
|
98
106
|
});
|
|
99
107
|
const recomendPlace = reactive<Place>({ ...place });
|
|
108
|
+
const isElectedRef = computed(() => {
|
|
109
|
+
const point = pointRef.value;
|
|
110
|
+
return placeCandidatesRef.value.some((candidate) =>
|
|
111
|
+
isPointEqual(place2point(candidate), point)
|
|
112
|
+
);
|
|
113
|
+
});
|
|
100
114
|
watch(
|
|
101
115
|
() => recomendPlaceKey.value,
|
|
102
116
|
async () => {
|
|
@@ -122,6 +136,7 @@ export const useGmapRecomendPlace = <C>(props: UseMapRecomendPlaceProps<C>) => {
|
|
|
122
136
|
recomendPlace,
|
|
123
137
|
placeCandidates: placeCandidatesRef,
|
|
124
138
|
availableRef,
|
|
139
|
+
isElectedRef,
|
|
125
140
|
updateRecomandPlace,
|
|
126
141
|
updatePlace,
|
|
127
142
|
setPlace,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ref } from "vue-demi";
|
|
2
|
+
import type { Place, Point } from "../types/interface";
|
|
3
|
+
import { watchEffectForDeepOption } from "../utils/compare";
|
|
4
|
+
import { place2point } from "../utils/transform";
|
|
5
|
+
import { useMapLngLatToVw } from "./useMapLngLatToVw";
|
|
6
|
+
|
|
7
|
+
type LabelDirection = "left" | "right";
|
|
8
|
+
const reverse = (direction: LabelDirection) => (direction === "left" ? "right" : "left");
|
|
9
|
+
|
|
10
|
+
export interface UsePointsLabelDirectionProps {
|
|
11
|
+
places: Place[];
|
|
12
|
+
widthLimit: number;
|
|
13
|
+
heightLimit: number;
|
|
14
|
+
}
|
|
15
|
+
export const usePointsLabelDirection = (props: UsePointsLabelDirectionProps) => {
|
|
16
|
+
const { widthLimit, heightLimit } = props;
|
|
17
|
+
const { apiMapLngLatToVw } = useMapLngLatToVw();
|
|
18
|
+
const diffVw = (from: Point, to: Point) => {
|
|
19
|
+
const vw = 100 / window.innerWidth;
|
|
20
|
+
const [x1, y1] = apiMapLngLatToVw(from);
|
|
21
|
+
const [x2, y2] = apiMapLngLatToVw(to);
|
|
22
|
+
return [(x2 - x1) * vw, (y2 - y1) * vw];
|
|
23
|
+
};
|
|
24
|
+
const directionsRef = ref<LabelDirection[]>(Array(props.places.length).fill("right"));
|
|
25
|
+
watchEffectForDeepOption(
|
|
26
|
+
() => props.places.map(place2point),
|
|
27
|
+
(points) => {
|
|
28
|
+
const sortedPoints = points
|
|
29
|
+
.map((point, index) => ({ point, index }))
|
|
30
|
+
.sort(({ point: [, lat1] }, { point: [, lat2] }) =>
|
|
31
|
+
lat1 < lat2 ? 1 : lat1 > lat2 ? -1 : 0
|
|
32
|
+
);
|
|
33
|
+
const len = points.length;
|
|
34
|
+
const result: LabelDirection[] = Array(len);
|
|
35
|
+
let prevDirection: LabelDirection = "right";
|
|
36
|
+
let isLimited = false;
|
|
37
|
+
for (let idx = 0; idx < len; idx++) {
|
|
38
|
+
const { index } = sortedPoints[idx];
|
|
39
|
+
if (idx === len - 1) {
|
|
40
|
+
result[index] = isLimited ? reverse(prevDirection) : "right";
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (idx === 0) {
|
|
44
|
+
const [dx, dy] = diffVw(sortedPoints[idx].point, sortedPoints[idx + 1].point);
|
|
45
|
+
isLimited = dx > -widthLimit && dx < widthLimit && dy > -heightLimit && dy < heightLimit;
|
|
46
|
+
const direction: LabelDirection = !isLimited ? "right" : dx > 0 ? "left" : "right";
|
|
47
|
+
result[index] = direction;
|
|
48
|
+
prevDirection = direction;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const [dx, dy] = diffVw(sortedPoints[idx].point, sortedPoints[idx + 1].point);
|
|
52
|
+
const direction: LabelDirection = isLimited ? reverse(prevDirection) : "right";
|
|
53
|
+
result[index] = direction;
|
|
54
|
+
prevDirection = direction;
|
|
55
|
+
isLimited = dx > -widthLimit && dx < widthLimit && dy > -heightLimit && dy < heightLimit;
|
|
56
|
+
}
|
|
57
|
+
directionsRef.value = result;
|
|
58
|
+
}
|
|
59
|
+
);
|
|
60
|
+
return directionsRef;
|
|
61
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
interface TimerStop {
|
|
2
|
+
promise: Promise<void>;
|
|
3
|
+
resolve?: () => void;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export interface UseWaitTimerProps {
|
|
7
|
+
interval: number;
|
|
8
|
+
}
|
|
9
|
+
export const useWaitTimer = (props: UseWaitTimerProps) => {
|
|
10
|
+
const { interval } = props;
|
|
11
|
+
let timer: number | undefined = undefined;
|
|
12
|
+
const timerStop: TimerStop = {
|
|
13
|
+
promise: Promise.resolve(),
|
|
14
|
+
resolve: undefined,
|
|
15
|
+
};
|
|
16
|
+
const timerStart = () => {
|
|
17
|
+
if (timer) clearTimeout(timer);
|
|
18
|
+
timerStop.resolve?.();
|
|
19
|
+
timerStop.promise = new Promise((resolve) => {
|
|
20
|
+
timerStop.resolve = resolve;
|
|
21
|
+
timer = setTimeout(resolve, interval);
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
return { timerStart, timerStop };
|
|
25
|
+
};
|