@hzab/form-render-mobile 0.2.3 → 0.2.4
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/lib/index.js +1 -1
- package/package.json +1 -1
- package/src/components/LocationPicker/common/utils.ts +0 -4
- package/src/components/LocationPicker/components/ModalContent/index.less +48 -0
- package/src/components/LocationPicker/components/ModalContent/index.tsx +202 -0
- package/src/components/LocationPicker/components/PickerInfo/index.tsx +31 -26
- package/src/components/LocationPicker/index.less +1 -47
- package/src/components/LocationPicker/index.tsx +123 -189
- package/src/index.less +6 -4
package/package.json
CHANGED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
.location-picker-modal-content {
|
|
2
|
+
position: relative;
|
|
3
|
+
|
|
4
|
+
.location-picker-map {
|
|
5
|
+
position: relative;
|
|
6
|
+
width: 100%;
|
|
7
|
+
flex: 1;
|
|
8
|
+
.a-map-container {
|
|
9
|
+
height: inherit;
|
|
10
|
+
.amap-logo,
|
|
11
|
+
.amap-copyright {
|
|
12
|
+
z-index: 0;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.location-picker-center-icon {
|
|
17
|
+
position: absolute;
|
|
18
|
+
top: 50%;
|
|
19
|
+
left: 50%;
|
|
20
|
+
transform: translate(-50%, -100%);
|
|
21
|
+
font-size: 32px;
|
|
22
|
+
color: #1890ff;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.spin-loading-wrap {
|
|
27
|
+
position: absolute;
|
|
28
|
+
top: 0;
|
|
29
|
+
left: 0;
|
|
30
|
+
right: 0;
|
|
31
|
+
bottom: 0;
|
|
32
|
+
z-index: 9;
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
background-color: rgba(0, 0, 0, 0.5);
|
|
37
|
+
.spin-loading {
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/* 水平样式 */
|
|
41
|
+
&.location-modal-content-layout-hor {
|
|
42
|
+
display: flex;
|
|
43
|
+
|
|
44
|
+
.location-picker-info {
|
|
45
|
+
width: 50vw;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { SpinLoading, Toast } from "antd-mobile";
|
|
3
|
+
import { LocationFill } from "antd-mobile-icons";
|
|
4
|
+
import { useField } from "@formily/react";
|
|
5
|
+
import { debounce } from "lodash";
|
|
6
|
+
|
|
7
|
+
import PickerInfo from "../PickerInfo";
|
|
8
|
+
import AMapCom from "../../Map/AMap";
|
|
9
|
+
import Notice from "../Notice";
|
|
10
|
+
import MapSearch from "../MapSearch";
|
|
11
|
+
|
|
12
|
+
import { MapUtils } from "../../Map/AMap/common/utils";
|
|
13
|
+
import { getAddress } from "../../servers";
|
|
14
|
+
import { getPropsValue } from "../../common/utils";
|
|
15
|
+
|
|
16
|
+
import "./index.less";
|
|
17
|
+
|
|
18
|
+
export const ModalContent = (props) => {
|
|
19
|
+
const {
|
|
20
|
+
/**
|
|
21
|
+
* 选择器和地图的布局
|
|
22
|
+
* 水平 hor
|
|
23
|
+
* 垂直 ver
|
|
24
|
+
*/
|
|
25
|
+
layout = "ver",
|
|
26
|
+
// 是否允许搜索
|
|
27
|
+
hasSearch = true,
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
isObjectRes
|
|
31
|
+
true: 把所有数据存放在一个对象中(该对象和其他表单项平级),对象 key 为当前项的 name
|
|
32
|
+
false: 所有数据打平放到当前的 data 中,和其他表单项平级
|
|
33
|
+
*/
|
|
34
|
+
isObjectRes = true,
|
|
35
|
+
/**
|
|
36
|
+
* 打开地图时是否根据经纬度自动修正已填的地址
|
|
37
|
+
*/
|
|
38
|
+
isAutoFixAddr = false,
|
|
39
|
+
/**
|
|
40
|
+
* 改变经纬度等数据的触发模式
|
|
41
|
+
* 移动地图 move
|
|
42
|
+
* 点击地图 click
|
|
43
|
+
*/
|
|
44
|
+
changeMode = "move",
|
|
45
|
+
lonKey = "longitude",
|
|
46
|
+
latKey = "latitude",
|
|
47
|
+
addrKey = "address",
|
|
48
|
+
value,
|
|
49
|
+
// 搜索框是否自动搜索
|
|
50
|
+
isAutoSearch = true,
|
|
51
|
+
//
|
|
52
|
+
visible,
|
|
53
|
+
defaultLocation,
|
|
54
|
+
} = props;
|
|
55
|
+
|
|
56
|
+
const field: any = useField();
|
|
57
|
+
|
|
58
|
+
const mapUtilsRef = useRef<MapUtils>();
|
|
59
|
+
|
|
60
|
+
// 数据格式转为内部格式,方便存取
|
|
61
|
+
const formatVal = useMemo(
|
|
62
|
+
() =>
|
|
63
|
+
getPropsValue(value, field, {
|
|
64
|
+
isObjectRes,
|
|
65
|
+
lonKey,
|
|
66
|
+
latKey,
|
|
67
|
+
addrKey,
|
|
68
|
+
}),
|
|
69
|
+
[isObjectRes, lonKey, latKey, addrKey, value, field.data],
|
|
70
|
+
);
|
|
71
|
+
const [loading, setLoading] = useState(false);
|
|
72
|
+
const [addrLoading, setAddrLoading] = useState(false);
|
|
73
|
+
// 地图选点组件选中的值
|
|
74
|
+
const [pickInfo, setPickInfo] = useState(formatVal);
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
if (visible) {
|
|
78
|
+
let _lon = defaultLocation.lon;
|
|
79
|
+
let _lat = defaultLocation.lat;
|
|
80
|
+
if (formatVal.lon && formatVal.lat) {
|
|
81
|
+
_lon = formatVal.lon;
|
|
82
|
+
_lat = formatVal.lat;
|
|
83
|
+
}
|
|
84
|
+
// 解决关闭弹窗之后点位不居中的问题
|
|
85
|
+
if (window.AMap && window.AMap.LngLat) {
|
|
86
|
+
setMapCenter(_lon, _lat);
|
|
87
|
+
}
|
|
88
|
+
setPickPoint(_lon, _lat, { isAutoFixAddr: false });
|
|
89
|
+
}
|
|
90
|
+
}, [visible, formatVal, changeMode]);
|
|
91
|
+
|
|
92
|
+
function mapInit({ map, mapUtils }) {
|
|
93
|
+
mapUtilsRef.current = mapUtils;
|
|
94
|
+
let _lon = defaultLocation.lon;
|
|
95
|
+
let _lat = defaultLocation.lat;
|
|
96
|
+
if (pickInfo.lon && pickInfo.lat) {
|
|
97
|
+
_lon = pickInfo.lon;
|
|
98
|
+
_lat = pickInfo.lat;
|
|
99
|
+
}
|
|
100
|
+
setPickPoint(_lon, _lat, { isAutoFixAddr });
|
|
101
|
+
|
|
102
|
+
setMapCenter(_lon, _lat);
|
|
103
|
+
setLoading(false);
|
|
104
|
+
|
|
105
|
+
if (changeMode === "click") {
|
|
106
|
+
mapUtilsRef.current?.setPickerMarker(_lon, _lat);
|
|
107
|
+
// 点击选中点位的情况
|
|
108
|
+
mapUtilsRef.current.on("click", function (ev) {
|
|
109
|
+
const { lng: evLon, lat: evLat } = ev.lnglat || {};
|
|
110
|
+
setPickPoint(evLon, evLat, { changeCenter: true });
|
|
111
|
+
});
|
|
112
|
+
} else {
|
|
113
|
+
// 移动地图
|
|
114
|
+
mapUtilsRef.current.on(
|
|
115
|
+
"touchend",
|
|
116
|
+
debounce(function (ev) {
|
|
117
|
+
let currentCenter = mapUtilsRef.current?.getCenter();
|
|
118
|
+
const { lng: centerLon, lat: centerLat } = currentCenter || {};
|
|
119
|
+
// 移动选中点位的情况
|
|
120
|
+
let _lon = centerLon;
|
|
121
|
+
let _lat = centerLat;
|
|
122
|
+
setPickPoint(_lon, _lat);
|
|
123
|
+
}, 1000),
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 设置地图中心点
|
|
130
|
+
* @param lon
|
|
131
|
+
* @param lat
|
|
132
|
+
*/
|
|
133
|
+
function setMapCenter(lon, lat) {
|
|
134
|
+
mapUtilsRef.current?.setCenter(lon, lat);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function setPickPoint(_lon, _lat, opt?: any) {
|
|
138
|
+
const { isAutoFixAddr: _isAutoFixAddr = isAutoFixAddr || true, changeCenter } = opt || {};
|
|
139
|
+
const lon = _lon || pickInfo.lon;
|
|
140
|
+
const lat = _lat || pickInfo.lat;
|
|
141
|
+
if (changeMode === "click") {
|
|
142
|
+
mapUtilsRef.current?.setPickerMarker(_lon, _lat);
|
|
143
|
+
}
|
|
144
|
+
if (changeCenter) {
|
|
145
|
+
setMapCenter(_lon, _lat);
|
|
146
|
+
}
|
|
147
|
+
const addr = _isAutoFixAddr || !pickInfo.addr ? await getAddr(lon, lat) : pickInfo.addr;
|
|
148
|
+
const res = { ...pickInfo, lon, lat, addr };
|
|
149
|
+
setPickInfo(res);
|
|
150
|
+
props.setPickInfo && props.setPickInfo(res);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function getAddr(_lng, _lat) {
|
|
154
|
+
return new Promise((resolve, reject) => {
|
|
155
|
+
if (!window.AMap) {
|
|
156
|
+
reject();
|
|
157
|
+
return new Error("window.AMap is not defined");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
setAddrLoading(true);
|
|
161
|
+
getAddress(_lng, _lat)
|
|
162
|
+
.then((addr) => {
|
|
163
|
+
setAddrLoading(false);
|
|
164
|
+
setPickInfo((_p) => ({ ..._p, addr }));
|
|
165
|
+
resolve(addr);
|
|
166
|
+
})
|
|
167
|
+
.catch((err) => {
|
|
168
|
+
reject(err);
|
|
169
|
+
Toast.show({ icon: "fail", content: err });
|
|
170
|
+
setAddrLoading(false);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return (
|
|
176
|
+
<div className={`location-picker-modal-content location-modal-content-layout-${layout}`}>
|
|
177
|
+
{/* 点击/移动 地图选中的数据 */}
|
|
178
|
+
<PickerInfo
|
|
179
|
+
pickInfo={pickInfo}
|
|
180
|
+
setPickInfo={setPickInfo}
|
|
181
|
+
setPoint={setPickPoint}
|
|
182
|
+
addrLoading={addrLoading}
|
|
183
|
+
defaultLocation={defaultLocation}
|
|
184
|
+
/>
|
|
185
|
+
|
|
186
|
+
<div className="location-picker-map">
|
|
187
|
+
{/* 关闭弹窗之后清除搜索内容 */}
|
|
188
|
+
{hasSearch && visible ? <MapSearch setPoint={setPickPoint} isAutoSearch={isAutoSearch} /> : null}
|
|
189
|
+
<AMapCom init={mapInit} loading={loading} style={{ height: "66vh" }} />
|
|
190
|
+
{!loading && changeMode !== "click" && <LocationFill className="location-picker-center-icon" />}
|
|
191
|
+
<Notice changeMode={changeMode} />
|
|
192
|
+
</div>
|
|
193
|
+
{loading && (
|
|
194
|
+
<div className="spin-loading-wrap">
|
|
195
|
+
<SpinLoading className="spin-loading" />
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export default ModalContent;
|
|
@@ -3,33 +3,21 @@ import { Input, DotLoading } from "antd-mobile";
|
|
|
3
3
|
import "./index.less";
|
|
4
4
|
|
|
5
5
|
export const PickerInfo = (props) => {
|
|
6
|
-
const { pickInfo, setPickInfo, setPoint, addrLoading,
|
|
6
|
+
const { pickInfo, setPickInfo, setPoint, addrLoading, defaultLocation } = props;
|
|
7
7
|
|
|
8
8
|
function onPILonChange(e) {
|
|
9
|
-
const val =
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
...info,
|
|
14
|
-
lon: val,
|
|
15
|
-
center: [val, info.lat],
|
|
16
|
-
};
|
|
17
|
-
return res;
|
|
18
|
-
});
|
|
9
|
+
const val = e?.target?.value ?? e;
|
|
10
|
+
const res: { lon; lat; addr } = { ...pickInfo };
|
|
11
|
+
res.lon = val;
|
|
12
|
+
setPickInfo(res);
|
|
19
13
|
return res;
|
|
20
14
|
}
|
|
21
15
|
|
|
22
16
|
function onPILatChange(e) {
|
|
23
|
-
const val =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
...info,
|
|
28
|
-
lat: val,
|
|
29
|
-
center: [info.lon, val],
|
|
30
|
-
};
|
|
31
|
-
return res;
|
|
32
|
-
});
|
|
17
|
+
const val = e?.target?.value ?? e;
|
|
18
|
+
const res: { lon; lat; addr } = { ...pickInfo };
|
|
19
|
+
res.lat = val;
|
|
20
|
+
setPickInfo(res);
|
|
33
21
|
return res;
|
|
34
22
|
}
|
|
35
23
|
|
|
@@ -39,7 +27,7 @@ export const PickerInfo = (props) => {
|
|
|
39
27
|
*/
|
|
40
28
|
function onPILonBlur(e) {
|
|
41
29
|
const res = onPILonChange(e);
|
|
42
|
-
setPoint(res.lon, res.lat);
|
|
30
|
+
setPoint && setPoint(res?.lon || defaultLocation.lon, res?.lat || defaultLocation.lat, { changeCenter: true });
|
|
43
31
|
}
|
|
44
32
|
|
|
45
33
|
/**
|
|
@@ -48,19 +36,36 @@ export const PickerInfo = (props) => {
|
|
|
48
36
|
*/
|
|
49
37
|
function onPILatBlur(e) {
|
|
50
38
|
const res = onPILatChange(e);
|
|
51
|
-
setPoint(res.lon, res.lat);
|
|
39
|
+
setPoint && setPoint(res?.lon || defaultLocation.lon, res?.lat || defaultLocation.lat, { changeCenter: true });
|
|
52
40
|
}
|
|
53
41
|
|
|
54
42
|
return (
|
|
55
43
|
<div className="location-picker-info">
|
|
56
44
|
<div className="picker-info-lon">
|
|
57
45
|
<div className="picker-info-label">经度:</div>
|
|
58
|
-
<Input
|
|
46
|
+
<Input
|
|
47
|
+
className="picker-info-input"
|
|
48
|
+
disabled={addrLoading}
|
|
49
|
+
max={180}
|
|
50
|
+
min={-180}
|
|
51
|
+
value={pickInfo.lon}
|
|
52
|
+
onChange={onPILonChange}
|
|
53
|
+
onBlur={onPILonBlur}
|
|
54
|
+
onEnterPress={onPILonBlur}
|
|
55
|
+
/>
|
|
59
56
|
</div>
|
|
60
57
|
<div className="picker-info-lat">
|
|
61
58
|
<div className="picker-info-label">纬度:</div>
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
<Input
|
|
60
|
+
className="picker-info-input"
|
|
61
|
+
disabled={addrLoading}
|
|
62
|
+
max={90}
|
|
63
|
+
min={-90}
|
|
64
|
+
value={pickInfo.lat}
|
|
65
|
+
onChange={onPILatChange}
|
|
66
|
+
onBlur={onPILatBlur}
|
|
67
|
+
onEnterPress={onPILatBlur}
|
|
68
|
+
/>
|
|
64
69
|
</div>
|
|
65
70
|
<div className="picker-info-addr">
|
|
66
71
|
<div className="picker-info-label">地址:</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
}
|
|
3
3
|
|
|
4
4
|
.location-picker-popup {
|
|
5
|
+
font-size: 4vw;
|
|
5
6
|
.location-popup-content {
|
|
6
7
|
position: relative;
|
|
7
8
|
}
|
|
@@ -15,52 +16,5 @@
|
|
|
15
16
|
align-items: center;
|
|
16
17
|
border-bottom: 1px solid #f2f2f2;
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
-
.location-picker-map {
|
|
20
|
-
position: relative;
|
|
21
|
-
width: 100%;
|
|
22
|
-
flex: 1;
|
|
23
|
-
display: flex;
|
|
24
|
-
justify-content: center;
|
|
25
|
-
.a-map-container {
|
|
26
|
-
height: inherit;
|
|
27
|
-
.amap-logo,
|
|
28
|
-
.amap-copyright {
|
|
29
|
-
z-index: 0;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.location-picker-notice-bar {
|
|
34
|
-
position: absolute;
|
|
35
|
-
top: 0;
|
|
36
|
-
left: 0;
|
|
37
|
-
width: 100vw;
|
|
38
|
-
--background-color: rgba(0, 0, 0, 0.6);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.location-picker-center-icon {
|
|
42
|
-
position: absolute;
|
|
43
|
-
top: 50%;
|
|
44
|
-
left: 50%;
|
|
45
|
-
transform: translate(-50%, -100%);
|
|
46
|
-
font-size: 32px;
|
|
47
|
-
color: #1890ff;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
.spin-loading-wrap {
|
|
52
|
-
position: absolute;
|
|
53
|
-
top: 0;
|
|
54
|
-
left: 0;
|
|
55
|
-
right: 0;
|
|
56
|
-
bottom: 0;
|
|
57
|
-
z-index: 9;
|
|
58
|
-
display: flex;
|
|
59
|
-
align-items: center;
|
|
60
|
-
justify-content: center;
|
|
61
|
-
background-color: rgba(0, 0, 0, 0.3);
|
|
62
|
-
.spin-loading {
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
19
|
}
|
|
66
20
|
}
|