@hzab/form-render-mobile 0.2.2 → 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.
@@ -1,20 +1,11 @@
1
1
  .location-picker {
2
- .location-value-box {
3
- .location-value-head-box {
4
- display: flex;
5
- .location-lon-lat {
6
- flex: 1;
7
- }
8
- }
9
-
10
- .location-btn {
11
- padding: 4px 0;
12
- padding-left: 5px;
13
- }
14
- }
15
2
  }
16
3
 
17
4
  .location-picker-popup {
5
+ font-size: 4vw;
6
+ .location-popup-content {
7
+ position: relative;
8
+ }
18
9
  .adm-popup-body {
19
10
  display: flex;
20
11
  flex-direction: column;
@@ -25,69 +16,5 @@
25
16
  align-items: center;
26
17
  border-bottom: 1px solid #f2f2f2;
27
18
  }
28
-
29
- .picker-info {
30
- padding: 2vw 4vw;
31
- .picker-info-lon,
32
- .picker-info-lat {
33
- display: inline-block;
34
- }
35
- .picker-info-lon {
36
- margin-right: 4vw;
37
- }
38
- .picker-info-lat {
39
- }
40
- .picker-info-addr {
41
- margin-top: 2vw;
42
- line-height: 1.6;
43
- }
44
- }
45
-
46
- .location-picker-map {
47
- position: relative;
48
- width: 100%;
49
- flex: 1;
50
- display: flex;
51
- justify-content: center;
52
- .a-map-container {
53
- height: inherit;
54
- .amap-logo,
55
- .amap-copyright {
56
- z-index: 0;
57
- }
58
- }
59
-
60
- .location-picker-notice-bar {
61
- position: absolute;
62
- bottom: 0;
63
- left: 0;
64
- width: 100vw;
65
- --background-color: rgba(0, 0, 0, 1);
66
- }
67
-
68
- .location-picker-center-icon {
69
- position: absolute;
70
- top: 50%;
71
- left: 50%;
72
- transform: translate(-50%, -100%);
73
- font-size: 32px;
74
- color: #1890ff;
75
- }
76
- }
77
-
78
- .spin-loading-wrap {
79
- position: absolute;
80
- top: 0;
81
- left: 0;
82
- right: 0;
83
- bottom: 0;
84
- z-index: 9;
85
- display: flex;
86
- align-items: center;
87
- justify-content: center;
88
- background-color: rgba(0, 0, 0, 0.5);
89
- .spin-loading {
90
- }
91
- }
92
19
  }
93
20
  }
@@ -1,31 +1,28 @@
1
- import { useEffect, useMemo, useRef, useState } from "react";
2
- import { Popup, Button, SpinLoading, DotLoading, NoticeBar } from "antd-mobile";
1
+ import { useRef, useState } from "react";
2
+ import { Popup, Button } from "antd-mobile";
3
3
  import { useField, useFieldSchema } from "@formily/react";
4
- import { LocationFill, ExclamationCircleOutline } from "antd-mobile-icons";
5
- import { debounce } from "lodash";
6
4
 
7
- import AMapCom from "./Map/AMap";
8
- import { getCurrentPosition } from "../../common/location-utils";
9
- import MapSearch from "./components/MapSearch";
5
+ import ResInfo from "./components/ResInfo";
6
+ import ModalContent from "./components/ModalContent";
10
7
 
11
- import { getAddress } from "./servers/index";
12
- import { getPropsValue, getParentValue } from "./common/utils";
8
+ import { MapUtils } from "./Map/AMap/common/utils";
9
+ import { getCurrentPosition } from "../../common/location-utils";
10
+ import { getParentValue } from "./common/utils";
13
11
 
14
12
  import "./index.less";
15
13
 
16
- const defaultLngLat = {
17
- lon: 120.160217,
18
- lat: 30.243861,
14
+ const defaultLocation = {
15
+ lon: 120.168893,
16
+ lat: 30.225404,
17
+ addr: "浙江省杭州市上城区南星街道杭州西湖风景名胜区",
19
18
  };
20
19
 
21
- declare var AMap: any;
22
-
23
20
  // 获取当前经纬度
24
21
  getCurrentPosition()
25
22
  .then((res) => {
26
23
  const { coords = {} } = res;
27
- defaultLngLat.lon = coords.longitude;
28
- defaultLngLat.lat = coords.latitude;
24
+ defaultLocation.lon = coords.longitude;
25
+ defaultLocation.lat = coords.latitude;
29
26
  })
30
27
  .catch((e) => {
31
28
  console.warn("Error getCurrentPosition e: ", e);
@@ -33,6 +30,18 @@ getCurrentPosition()
33
30
 
34
31
  export function LocationPicker(props) {
35
32
  const {
33
+ /**
34
+ * 地图显示类型:
35
+ * 弹窗 dialog
36
+ * 直接显示 show
37
+ */
38
+ mode = "dialog",
39
+ /**
40
+ * 选择器和地图的布局
41
+ * 水平 hor
42
+ * 垂直 ver
43
+ */
44
+ layout = "ver",
36
45
  // 是否允许搜索
37
46
  hasSearch = true,
38
47
  /**
@@ -48,8 +57,8 @@ export function LocationPicker(props) {
48
57
  isAutoFixAddr = false,
49
58
  /**
50
59
  * 改变经纬度等数据的触发模式
51
- * 移动地图:move
52
- * 点击地图:click
60
+ * 移动地图 move
61
+ * 点击地图 click
53
62
  */
54
63
  changeMode = "move",
55
64
  lonKey = "longitude",
@@ -62,143 +71,15 @@ export function LocationPicker(props) {
62
71
  const field: any = useField();
63
72
  const fieldSchema = useFieldSchema();
64
73
 
65
- const mapRef: any = useRef();
66
- const markerRef: any = useRef();
74
+ const mapUtilsRef = useRef<MapUtils>();
75
+ const pickInfoRef = useRef(defaultLocation);
67
76
 
68
77
  const [loading, setLoading] = useState(false);
69
78
  const [visible, setVisible] = useState(false);
70
- // 数据格式转为内部格式,方便存取
71
- const opt = {
72
- isObjectRes,
73
- lonKey,
74
- latKey,
75
- addrKey,
76
- };
77
-
78
- const formatVal = useMemo(
79
- () => getPropsValue(value, field, opt),
80
- [isObjectRes, lonKey, latKey, addrKey, value, field.data],
81
- );
82
-
83
- // 表单实际的值
84
- const [resInfo, setResInfo] = useState(formatVal);
85
- // 地图选点组件选中的值
86
- const [pickInfo, setPickInfo] = useState(formatVal);
87
- const [addrLoading, setAddrLoading] = useState(false);
88
-
89
- useEffect(() => {
90
- if (visible) {
91
- let _lon = defaultLngLat.lon;
92
- let _lat = defaultLngLat.lat;
93
- if (resInfo.lon && resInfo.lat) {
94
- _lon = resInfo.lon;
95
- _lat = resInfo.lat;
96
- }
97
- // 解决关闭弹窗之后点位不居中的问题
98
- if (window.AMap && window.AMap.LngLat) {
99
- const position = new window.AMap.LngLat(_lon, _lat);
100
- mapRef.current?.setCenter(position);
101
- }
102
- setPoint(_lon, _lat, { isAutoFixAddr: false });
103
- }
104
- }, [visible, resInfo, changeMode]);
105
-
106
- useEffect(() => {
107
- setResInfo((_res) => {
108
- const { lon: _lon, lat: _lat, addr: _addr } = formatVal;
109
- if ((_lon && _lon != _res.lon) || (_lat && _lat != _res.lat)) {
110
- setPoint(_lon, _lat, { isAutoFixAddr });
111
- }
112
- return formatVal;
113
- });
114
- setPickInfo(formatVal);
115
- }, [formatVal, isAutoFixAddr]);
116
-
117
- function mapInit(_map, AMap) {
118
- mapRef.current = _map;
119
- setLoading(false);
120
- let _lon = defaultLngLat.lon;
121
- let _lat = defaultLngLat.lat;
122
- if (pickInfo.lon && pickInfo.lat) {
123
- _lon = pickInfo.lon;
124
- _lat = pickInfo.lat;
125
- }
126
- setPoint(_lon, _lat, { isAutoFixAddr });
127
-
128
- if (changeMode === "click") {
129
- setMarker(_lon, _lat);
130
- // 点击选中点位的情况
131
- mapRef.current.on("click", function (ev) {
132
- const { lng: evLon, lat: evLat } = ev.lnglat || {};
133
- setPoint(evLon, evLat, { changeCenter: true });
134
- });
135
- } else {
136
- // 移动地图
137
- mapRef.current.on(
138
- "moveend",
139
- debounce(function (ev) {
140
- let currentCenter = mapRef.current.getCenter();
141
- const { lng: centerLon, lat: centerLat } = currentCenter || {};
142
- // 移动选中点位的情况
143
- let _lon = centerLon;
144
- let _lat = centerLat;
145
- setPoint(_lon, _lat);
146
- }, 500),
147
- );
148
- }
149
- }
150
-
151
- function setMarker(_lon, _lat) {
152
- if (changeMode !== "click") {
153
- return;
154
- }
155
- if (window.AMap && mapRef.current) {
156
- const position = new AMap.LngLat(_lon, _lat);
157
- // 创建 Marker 或修改位置
158
- if (markerRef.current) {
159
- markerRef.current.setPosition(position);
160
- } else {
161
- markerRef.current = new AMap.Marker({
162
- position,
163
- });
164
- mapRef.current?.add(markerRef.current);
165
- }
166
- }
167
- }
168
-
169
- function setPoint(_lon, _lat, opt?: any) {
170
- const { isAutoFixAddr: _isAutoFixAddr = isAutoFixAddr || true, changeCenter } = opt || {};
171
- const lon = _lon || pickInfo.lon;
172
- const lat = _lat || pickInfo.lat;
173
- (_isAutoFixAddr || !pickInfo.addr) && getAddr(lon, lat);
174
- const res = { ...pickInfo, lon, lat, center: [lon, lat] };
175
- setPickInfo(res);
176
- setMarker(_lon, _lat);
177
- if (changeCenter) {
178
- const position = new AMap.LngLat(_lon, _lat);
179
- // 设置中心点
180
- mapRef.current.setCenter(position);
181
- }
182
- }
183
-
184
- function getAddr(_lng, _lat) {
185
- if (!window.AMap) {
186
- return "";
187
- }
188
-
189
- setAddrLoading(true);
190
- getAddress(_lng, _lat)
191
- .then((addr) => {
192
- setAddrLoading(false);
193
- setPickInfo((_p) => ({ ..._p, addr }));
194
- })
195
- .catch((err) => {
196
- setAddrLoading(false);
197
- });
198
- }
199
79
 
200
80
  function onShow() {
201
- !mapRef.current && setLoading(true);
81
+ // 等待地图加载完毕
82
+ !mapUtilsRef.current && setVisible(true);
202
83
  setVisible(true);
203
84
  }
204
85
 
@@ -206,91 +87,121 @@ export function LocationPicker(props) {
206
87
  setVisible(false);
207
88
  }
208
89
 
209
- function onOk() {
210
- const res = {
211
- [lonKey]: pickInfo.lon,
212
- [latKey]: pickInfo.lat,
213
- [addrKey]: pickInfo.addr,
90
+ function onChange(res = pickInfoRef.current) {
91
+ const info = res || pickInfoRef.current;
92
+ const _res = {
93
+ [lonKey]: info.lon,
94
+ [latKey]: info.lat,
95
+ [addrKey]: info.addr,
214
96
  };
215
- props.onChange && props.onChange(res);
97
+ props.onChange && props.onChange(_res);
216
98
  if (isObjectRes === false) {
217
99
  // 只有一层的情况
218
100
  if (!field.parent) {
219
- field.form.setValues(res);
101
+ field.form.setValues(_res);
220
102
  } else if (field.parent && fieldSchema.parent) {
221
103
  // 嵌套在 ArrayBase: ArrayTable ArrayCard 等 里面的情况
222
104
  const parentVal = getParentValue(field);
223
- Object.keys(res).forEach((key) => {
224
- parentVal[key] = res[key];
105
+ Object.keys(_res).forEach((key) => {
106
+ parentVal[key] = _res[key];
225
107
  });
226
108
  }
227
109
  }
110
+ }
228
111
 
112
+ function onOk() {
113
+ onChange();
229
114
  onClose();
230
115
  }
231
116
 
117
+ function setResInfo(res) {
118
+ setPickInfo(res);
119
+ onChange(res);
120
+ }
121
+
122
+ function setPickInfo(res) {
123
+ pickInfoRef.current = res;
124
+ }
125
+
126
+ const _props = {
127
+ /**
128
+ * 地图显示类型:
129
+ * 弹窗 dialog
130
+ * 直接显示 show
131
+ */
132
+ mode: mode ?? "dialog",
133
+ /**
134
+ * 选择器和地图的布局
135
+ * 水平 hor
136
+ * 垂直 ver
137
+ */
138
+ layout: layout ?? "ver",
139
+ // 是否允许搜索
140
+ hasSearch: hasSearch ?? true,
141
+ /**
142
+ *
143
+ isObjectRes
144
+ true: 把所有数据存放在一个对象中(该对象和其他表单项平级),对象 key 为当前项的 name
145
+ false: 所有数据打平放到当前的 data 中,和其他表单项平级
146
+ */
147
+ isObjectRes: isObjectRes ?? true,
148
+ /**
149
+ * 打开地图时是否根据经纬度自动修正已填的地址
150
+ */
151
+ isAutoFixAddr: isAutoFixAddr ?? false,
152
+ /**
153
+ * 改变经纬度等数据的触发模式
154
+ * 移动地图:move
155
+ * 点击地图:click
156
+ */
157
+ changeMode: changeMode ?? "move",
158
+ lonKey: lonKey ?? "longitude",
159
+ latKey: latKey ?? "latitude",
160
+ addrKey: addrKey ?? "address",
161
+ value,
162
+ // 搜索框是否自动搜索
163
+ isAutoSearch: isAutoSearch ?? true,
164
+ defaultLocation,
165
+ };
166
+
232
167
  return (
233
168
  <div className="location-picker">
234
- <div className="location-value-box">
235
- <div className="location-value-head-box">
236
- <div className="location-lon-lat">
237
- <div>经度:{resInfo.lon} </div>
238
- <div>纬度:{resInfo.lat}</div>
239
- </div>
240
- <Button className="location-btn" color="primary" fill="none" onClick={onShow}>
241
- <LocationFill />
242
- 选点
243
- </Button>
244
- </div>
245
- <div>地址:{resInfo.addr}</div>
246
- </div>
247
- <Popup
248
- maskClassName="location-picker-popup-mask"
249
- className="location-picker-popup"
250
- onMaskClick={onClose}
251
- onClose={onClose}
252
- bodyStyle={{
253
- minHeight: "80vh",
254
- maxHeight: "100vh",
255
- }}
256
- visible={visible}
257
- >
258
- <div className="location-popup-header">
259
- <Button fill="none" onClick={onClose}>
260
- 取消
261
- </Button>
262
- <Button color="primary" fill="none" onClick={onOk} loading={addrLoading || loading}>
263
- 确认
264
- </Button>
265
- </div>
266
- <div className="picker-info">
267
- <div className="picker-info-lon">经度:{pickInfo.lon} </div>
268
- <div className="picker-info-lat">纬度:{pickInfo.lat}</div>
269
- <div className="picker-info-addr">地址:{addrLoading ? <DotLoading /> : pickInfo.addr}</div>
270
- </div>
271
- {/* 关闭弹窗之后清除搜索内容 */}
272
- {hasSearch && visible ? <MapSearch setPoint={setPoint} isAutoSearch={isAutoSearch} /> : null}
273
- <div className="location-picker-map">
274
- <AMapCom
275
- init={mapInit}
276
- loading={loading}
277
- center={pickInfo.center ? pickInfo.center : [defaultLngLat.lon, defaultLngLat.lat]}
278
- style={{ height: "68vh" }}
279
- />
280
- <NoticeBar
281
- className="location-picker-notice-bar"
282
- content={changeMode === "click" ? "点击地图上的位置,选择目标地址" : "拖拽移动地图,中心标点为目标地址"}
283
- color="default"
284
- icon={<ExclamationCircleOutline />}
285
- />
286
- {!loading && changeMode !== "click" && <LocationFill className="location-picker-center-icon" />}
287
- </div>
288
- {loading && (
289
- <div className="spin-loading-wrap">
290
- <SpinLoading className="spin-loading" color="primary" />
291
- </div>
292
- )}
293
- </Popup>
169
+ {mode === "show" ? (
170
+ <ModalContent {..._props} visible={visible} setPickInfo={setResInfo} layout={layout} />
171
+ ) : (
172
+ <>
173
+ {/* 弹窗 */}
174
+ {/* 有弹窗时,显示表单选中的结果 */}
175
+ <ResInfo {..._props} onShow={onShow} />
176
+ <Popup
177
+ maskClassName="location-picker-popup-mask"
178
+ className="location-picker-popup"
179
+ onMaskClick={onClose}
180
+ onClose={onClose}
181
+ bodyStyle={{
182
+ minHeight: "78vh",
183
+ maxHeight: "100vh",
184
+ }}
185
+ visible={visible}
186
+ >
187
+ <div className="location-popup-header">
188
+ <Button fill="none" onClick={onClose}>
189
+ 取消
190
+ </Button>
191
+ <Button color="primary" fill="none" onClick={onOk} loading={loading}>
192
+ 确认
193
+ </Button>
194
+ </div>
195
+ <ModalContent
196
+ {..._props}
197
+ visible={visible}
198
+ setPickInfo={setPickInfo}
199
+ layout={layout}
200
+ setLoading={setLoading}
201
+ />
202
+ </Popup>
203
+ </>
204
+ )}
294
205
  </div>
295
206
  );
296
207
  }
@@ -1,5 +1,9 @@
1
- import { Toast } from "antd-mobile";
2
-
1
+ /**
2
+ * 根据经纬度获取定义的地址
3
+ * @param lon
4
+ * @param lat
5
+ * @returns
6
+ */
3
7
  export function getAddress(lon, lat) {
4
8
  return new Promise((resolve, reject) => {
5
9
  if (!window.AMap) {
@@ -15,7 +19,7 @@ export function getAddress(lon, lat) {
15
19
  } else if (result === "USER_DAILY_QUERY_OVER_LIMIT") {
16
20
  // 超出限制,使用 webServer 进行请求
17
21
  if (!window._AMapLoaderTemp.serverKey) {
18
- Toast.show({ icon: "fail", content: "超出使用限制,请联系管理员" });
22
+ reject(new Error("超出使用限制,请联系管理员"));
19
23
  return;
20
24
  }
21
25
  fetch(
@@ -31,7 +35,8 @@ export function getAddress(lon, lat) {
31
35
  if (res.status === "1") {
32
36
  resolve(res.regeocode?.formatted_address);
33
37
  } else {
34
- Toast.show({ icon: "fail", content: res.info });
38
+ console.warn("Warn getAddress fetch: ", res);
39
+ reject(new Error(res.info));
35
40
  }
36
41
  })
37
42
  .catch(reject);
@@ -42,6 +47,12 @@ export function getAddress(lon, lat) {
42
47
  });
43
48
  }
44
49
 
50
+ /**
51
+ * 获取搜索提示
52
+ * @param search
53
+ * @param cityCode
54
+ * @returns
55
+ */
45
56
  export function getSearchTips(search, cityCode = "0571") {
46
57
  return new Promise((resolve, reject) => {
47
58
  if (!window.AMap) {
@@ -70,7 +81,7 @@ export function getSearchTips(search, cityCode = "0571") {
70
81
  } else if (result === "USER_DAILY_QUERY_OVER_LIMIT") {
71
82
  // 超出限制,使用 webServer 进行请求
72
83
  if (!window._AMapLoaderTemp.serverKey) {
73
- Toast.show({ icon: "fail", content: "超出使用限制,请联系管理员" });
84
+ reject(new Error("超出使用限制,请联系管理员"));
74
85
  return;
75
86
  }
76
87
  fetch(
@@ -97,7 +108,8 @@ export function getSearchTips(search, cityCode = "0571") {
97
108
  }),
98
109
  );
99
110
  } else {
100
- Toast.show({ icon: "fail", content: res.info });
111
+ console.warn("Warn getSearchTips fetch: ", res);
112
+ reject(new Error(res.info));
101
113
  }
102
114
  })
103
115
  .catch(reject);
package/src/index.less CHANGED
@@ -1,4 +1,8 @@
1
1
  .form-render {
2
+ font-size: 4vw;
3
+ .adm-formily-item {
4
+ font-size: 4vw;
5
+ }
2
6
  .adm-list-body {
3
7
  background-color: unset;
4
8
  .adm-card-body {
@@ -34,7 +38,8 @@
34
38
  }
35
39
  }
36
40
 
37
- & .adm-radio, & .adm-checkbox {
41
+ & .adm-radio,
42
+ & .adm-checkbox {
38
43
  padding-right: 10px;
39
44
  }
40
45
  }
@@ -1,17 +0,0 @@
1
- function getCurrentPosition(config) {
2
- return new Promise((resolve, reject) => {
3
- navigator.geolocation.getCurrentPosition(
4
- (position) => {
5
- resolve(position);
6
- },
7
- reject,
8
- {
9
- enableHighAccuracy: true,
10
- timeout: 5000,
11
- ...config,
12
- },
13
- );
14
- });
15
- }
16
-
17
- export default getCurrentPosition;
@@ -1,53 +0,0 @@
1
- import AMapLoader from "@/components/AMap/common/loader.js";
2
-
3
- if (!window._AMapLoaderTemp) {
4
- window._AMapLoaderTemp = {};
5
- }
6
-
7
- /**
8
- * 根据经纬度获取地址
9
- * @param {Array} lngLatArr
10
- * @returns
11
- */
12
- export function getAddress(lngLatArr) {
13
- return new Promise((resolve, reject) => {
14
- if (!lngLatArr) {
15
- reject("参数有误");
16
- return;
17
- }
18
- return AMapLoader({
19
- key: window._AMapLoaderTemp.key,
20
- securityJsCode: window._AMapLoaderTemp.securityJsCode,
21
- }).then((AMap) => {
22
- const geocoder = new AMap.Geocoder({});
23
-
24
- geocoder.getAddress(lngLatArr, function (state, result) {
25
- if (state === "complete" && result.regeocode) {
26
- resolve(result.regeocode.formattedAddress);
27
- } else {
28
- reject("根据经纬度查询地址失败");
29
- console.error("根据经纬度查询地址失败");
30
- }
31
- });
32
- });
33
- });
34
- }
35
-
36
- export function setKey(key, securityJsCode) {
37
- window._AMapLoaderTemp.key = key;
38
- window._AMapLoaderTemp.securityJsCode = securityJsCode;
39
- }
40
-
41
- export function setServerKey(key, securityJsCode) {
42
- window._AMapLoaderTemp.serverKey = key;
43
- window._AMapLoaderTemp.serverSecurityJsCode = securityJsCode;
44
- }
45
-
46
- export function setSecurityJsCode(securityJsCode) {
47
- window._AMapLoaderTemp.securityJsCode = securityJsCode;
48
- window._AMapSecurityConfig = {
49
- securityJsCode: securityJsCode,
50
- };
51
- }
52
-
53
- export { AMapLoader };