Skip to content

Commit 036cc5b

Browse files
heiyexingsyb01094648
andauthored
feat: 新增区块-行政区划选择器 (#190)
* feat: 缩放器新增显示实时 zoom 数值功能 * docs: 文档showZoom添加默认值 * feat: 区块新增城市联级选择器 * fix: 区块修复城市联级选择器ts报错问题 * docs: 新增区块-行政区划选择器 --------- Co-authored-by: syb01094648 <[email protected]> Co-authored-by: yanxiong <[email protected]>
1 parent b9d6386 commit 036cc5b

File tree

3 files changed

+209
-0
lines changed

3 files changed

+209
-0
lines changed

docs/blocks/administrative-select.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
toc: false
3+
order: 3
4+
nav:
5+
title: 区块
6+
path: /blocks
7+
order: 4
8+
---
9+
10+
## 城市联级选择器
11+
12+
### 介绍
13+
14+
用于快速查找中国省/市/县行政区域并快速定位的控件,基于 Ant Design 中的 [Cascader](https://ant-design.antgroup.com/components/cascader-cn#api) 组件封装而成
15+
16+
### 代码演示
17+
18+
#### 默认示例
19+
20+
<code src="./administrative-select/demos/default.tsx" compact></code>
21+
22+
### API
23+
24+
| 参数 | 说明 | 类型 | 默认值 |
25+
| --- | --- | --- | --- |
26+
| autoFit | 是否控制地图自动平移到选项对应行政区域 | `boolean` | `true` |
27+
| enableBoundary | 是否在地图上展示行政区域边界 | `boolean` | `true` |
28+
| boundaryLayer | 边界线图层属性,可参考 [LineLayerProps](https://larkmap.antv.antgroup.com/components/layers/base-layers/line-layer#api) | `Omit<LineLayerProps, 'source'>` | `--` |
29+
30+
其他参数可以参照 [Ant Design 5.0 Cascader](https://ant-design.antgroup.com/components/cascader-cn#api)
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import type { LineLayerProps } from '@antv/larkmap';
2+
import { LineLayer, useScene } from '@antv/larkmap';
3+
import type { Feature, MultiLineString } from '@turf/turf';
4+
import { bbox, featureCollection, multiLineString } from '@turf/turf';
5+
import type { CascaderProps } from 'antd';
6+
import { Cascader, message } from 'antd';
7+
import React, { useEffect, useState } from 'react';
8+
9+
export interface AdministrativeSelectProps
10+
extends Omit<CascaderProps, 'options' | 'multiple'>,
11+
Partial<Pick<CascaderProps, 'value' | 'onChange'>> {
12+
/**
13+
* 是否平移
14+
*/
15+
autoFit?: boolean;
16+
/**
17+
* 是否显示边界
18+
*/
19+
enableBoundary?: boolean;
20+
/**
21+
* layer属性
22+
*/
23+
boundaryLayer?: Omit<LineLayerProps, 'source'>;
24+
}
25+
26+
/**
27+
* 将获取的行政区域雷彪转换为 Cascader 的 options
28+
* @param list
29+
* @returns
30+
*/
31+
const getCascadeData = (list: any[]) => {
32+
list.sort((a: { adcode: number }, b: { adcode: number }) => {
33+
return +a.adcode - +b.adcode;
34+
});
35+
if (list.length) {
36+
return list.map((item: any) => {
37+
const { name, districts, adcode } = item;
38+
return {
39+
adcode,
40+
value: adcode,
41+
label: name,
42+
children: getCascadeData(districts),
43+
};
44+
});
45+
} else {
46+
return [];
47+
}
48+
};
49+
50+
export const AdministrativeSelect: React.FC<AdministrativeSelectProps> = ({
51+
enableBoundary,
52+
autoFit,
53+
boundaryLayer,
54+
value: originValue,
55+
onChange,
56+
...props
57+
}) => {
58+
const [districtFeature, setDistrictFeature] = useState<Feature<MultiLineString> | null>(null);
59+
const scene = useScene();
60+
const [options, setOptions] = useState();
61+
const [value, setValue] = useState<AdministrativeSelectProps['value']>(originValue);
62+
63+
useEffect(() => {
64+
setValue(originValue);
65+
}, [originValue]);
66+
67+
useEffect(() => {
68+
fetch(
69+
'https://restapi.amap.com/v3/config/district?key=98d10f05a2da96697313a2ce35ebf1a2&keywords=中华人民共和国&subdistrict=3&extensions=base',
70+
)
71+
.then((res) => res.json())
72+
.then((res) => {
73+
setOptions(getCascadeData(res.districts[0].districts));
74+
});
75+
}, []);
76+
77+
// 当选项发生改变时,更新围栏数据
78+
useEffect(() => {
79+
if (value) {
80+
const name = value[value.length - 1];
81+
fetch(
82+
`https://restapi.amap.com/v3/config/district?keywords=${name}&subdistrict=0&key=98d10f05a2da96697313a2ce35ebf1a2&extensions=all`,
83+
)
84+
.then((res) => res.json())
85+
.then((res) => {
86+
if (res.status === '1' && res.districts?.length && scene) {
87+
const positions: number[][][] = [];
88+
res.districts.forEach((district: any) => {
89+
(district.polyline as string).split('|').forEach((chunk) => {
90+
positions.push(chunk.split(';').map((item) => item.split(',').map((num) => +num)));
91+
});
92+
});
93+
94+
const feature = multiLineString(positions);
95+
setDistrictFeature(feature);
96+
if (autoFit) {
97+
const [lng1, lat1, lng2, lat2] = bbox(feature);
98+
scene.fitBounds([
99+
[lng1, lat1],
100+
[lng2, lat2],
101+
]);
102+
}
103+
}
104+
})
105+
.catch(() => {
106+
message.error('围栏数据请求失败');
107+
});
108+
}
109+
// eslint-disable-next-line react-hooks/exhaustive-deps
110+
}, [value]);
111+
112+
return (
113+
<>
114+
<Cascader
115+
options={options}
116+
value={value}
117+
onChange={(newValue: string[], option: any) => {
118+
setValue(newValue);
119+
// @ts-ignore
120+
onChange?.(newValue, option);
121+
if (!value) {
122+
setDistrictFeature(null);
123+
}
124+
}}
125+
multiple={false}
126+
{...props}
127+
/>
128+
{enableBoundary && (
129+
<LineLayer
130+
source={{
131+
data: featureCollection(districtFeature ? [districtFeature] : []),
132+
}}
133+
{...boundaryLayer}
134+
/>
135+
)}
136+
</>
137+
);
138+
};
139+
140+
AdministrativeSelect.defaultProps = {
141+
placeholder: '可选择省/市/县',
142+
expandTrigger: 'hover',
143+
allowClear: true,
144+
changeOnSelect: true,
145+
enableBoundary: true,
146+
autoFit: true,
147+
showSearch: true,
148+
boundaryLayer: {
149+
shape: 'line',
150+
color: '#ff0000',
151+
size: 2,
152+
style: {
153+
opacity: 0.8,
154+
},
155+
},
156+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { LarkMapProps } from '@antv/larkmap';
2+
import { CustomControl, LarkMap } from '@antv/larkmap';
3+
import React from 'react';
4+
import { AdministrativeSelect } from '../administrative-select';
5+
6+
const config: LarkMapProps = {
7+
mapType: 'Gaode',
8+
mapOptions: {
9+
style: 'light',
10+
center: [120.210792, 30.246026],
11+
zoom: 9,
12+
},
13+
};
14+
15+
export default () => {
16+
return (
17+
<LarkMap {...config} style={{ height: '300px' }}>
18+
<CustomControl>
19+
<AdministrativeSelect style={{ width: 250 }} />
20+
</CustomControl>
21+
</LarkMap>
22+
);
23+
};

0 commit comments

Comments
 (0)