示例
image.pngimage.png
使用插件:swiper
一、创建 AddressSelectModal文件夹,并分别创建 addressInfo.tsx 、 index.less、index.tsx
index.tsx
import React, { useEffect, useState } from "react";
import styles from "./index.less";
import { addressInfo } from "./addressInfo";
import closeIcon from "@/assets/icons/closeIcon.png";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/swiper.less";
interface AddressProps {
prefixCls?: string;
closeModel?: () => void;
getAddress?: (arg1: { city?: string; district?: string; streetName?: string }) => void;
addressTitle?: string;
hierarchy?: number; //层级,表示该地址有几层
visible?: boolean;
}
interface AddressListType {
id: number | string;
code?: string;
name: string;
engName?: string;
isLetter?: boolean;
}
interface titleTab {
name: string;
isCurrentSelected: boolean;
title: string;
id: number | string;
}
const Address: React.FC<AddressProps> = ({
prefixCls,
closeModel,
getAddress,
addressTitle,
hierarchy = 3,
visible = false,
}) => {
const [addressSwipe, setAddressSwipe] = useState<any>(null);
let tabObj = {
name: "",
isCurrentSelected: true,
title: "",
id: -999,
};
const [selectOne, setSelectOne] = useState<titleTab>({ ...tabObj });
const [selectTwo, setSelectTwo] = useState<titleTab>({ ...tabObj });
const [selectThree, setSelectThree] = useState<titleTab>({ ...tabObj });
const [selectOneList, setSelectOneList] = useState<AddressListType[] | null>();
const [selectTwoList, setSelectTwoList] = useState<AddressListType[] | null>();
const [selectThreeList, setSelectThreeList] = useState<AddressListType[] | null>();
const [selectOneListLetter, setSelectOneListLetter] = useState<string[] | null>();
const [selectTwoListLetter, setSelectTwoListLetter] = useState<string[] | null>();
const [selectThreeListLetter, setSelectThreeListLetter] = useState<string[] | null>();
useEffect(() => {
let handleResult = handleSelectList(addressInfo);
setSelectOneList(handleResult.resultTabList);
setSelectOneListLetter(handleResult.letterList);
initTab();
}, []);
const initTab = () => {
let defaultTabObj = {
name: "",
isCurrentSelected: false,
id: -999,
};
let obj1 = Object.assign({ ...defaultTabObj }, { isCurrentSelected: true, title: "选择省" });
let obj2 = Object.assign({ ...defaultTabObj }, { title: "选择市" });
let obj3 = Object.assign({ ...defaultTabObj }, { title: "选择区" });
setSelectOne(obj1);
setSelectTwo(obj2);
setSelectThree(obj3);
setSelectTwoList([]);
setSelectThreeList([]);
setSelectTwoListLetter([]);
setSelectThreeListLetter([]);
};
const changeTab = (num: number) => {
let obj = {
isCurrentSelected: false,
};
let titleTabObj: titleTab = {
name: "",
isCurrentSelected: false,
title: "",
id: -999,
};
let obj1 = { ...titleTabObj },
obj2 = { ...titleTabObj },
obj3 = { ...titleTabObj };
if (num === 1) {
obj1 = Object.assign({ ...selectOne }, { isCurrentSelected: true });
obj2 = Object.assign({ ...selectTwo }, { ...obj });
obj3 = Object.assign({ ...selectThree }, { ...obj });
} else if (num === 2) {
obj1 = Object.assign({ ...selectOne }, { ...obj });
obj2 = Object.assign({ ...selectTwo }, { isCurrentSelected: true });
obj3 = Object.assign({ ...selectThree }, { ...obj });
} else if (num === 3) {
obj1 = Object.assign({ ...selectOne }, { ...obj });
obj2 = Object.assign({ ...selectTwo }, { ...obj });
obj3 = Object.assign({ ...selectThree }, { isCurrentSelected: true });
}
setSelectOne(obj1);
setSelectTwo(obj2);
setSelectThree(obj3);
addressSwipe.slideTo(num - 1);
};
const handleSelectList = (tabList: AddressListType[]): { resultTabList: AddressListType[]; letterList: string[] } => {
let currentTabList: AddressListType[] = JSON.parse(JSON.stringify(tabList));
let resultTabList: AddressListType[] = [];
let letterObj: any = {};
currentTabList.sort(function(a: { name: string }, b: { name: string }) {
if (a.name > b.name) {
return 1;
} else if (a.name === b.name) {
return 0;
} else {
return -1;
}
});
currentTabList.map((item: AddressListType) => {
let obj: AddressListType = {
id: item.id,
code: item.code || "",
name: item.name || "",
engName: item.engName || "",
isLetter: false,
};
let firstLetter = item.name ? item.name[0] : "";
if (firstLetter && !letterObj[firstLetter]) {
letterObj[firstLetter] = 1;
resultTabList.push({
name: firstLetter,
id: item.id + firstLetter,
isLetter: true,
});
}
resultTabList.push(obj);
});
let letterList = Object.keys(letterObj);
return { resultTabList, letterList };
};
const getSelectedChildrenList = (num: number, currentId: number | string) => {
if (num === 1) {
let currentSelectTwoList: AddressListType[] = [];
for (let item of addressInfo) {
if (item.id === currentId) {
currentSelectTwoList = JSON.parse(JSON.stringify(item.city));
break;
}
}
let handleResult = handleSelectList(currentSelectTwoList);
setSelectTwoList(handleResult.resultTabList);
setSelectTwoListLetter(handleResult.letterList);
} else if (num === 2) {
let currentSelectThreeList: AddressListType[] = [];
let parentList = [];
for (let item of addressInfo) {
if (item.id === selectOne.id) {
parentList = JSON.parse(JSON.stringify(item.city));
break;
}
}
for (let item of parentList) {
if (item.id === currentId) {
currentSelectThreeList = item.country;
break;
}
}
console.log(selectOne, currentSelectThreeList, "currentSelectThreeList");
let handleResult = handleSelectList(currentSelectThreeList);
setSelectThreeList(handleResult.resultTabList);
setSelectThreeListLetter(handleResult.letterList);
}
};
const checkItem = (num: number, item: AddressListType) => {
if (item.isLetter) true;
if (num < hierarchy) {
getSelectedChildrenList(num, item.id);
}
let defaultTabObj = {
name: "",
isCurrentSelected: false,
id: -999,
};
switch (num) {
case 1:
{
let obj1 = Object.assign(
{ ...selectOne },
{
name: item.name,
isCurrentSelected: false,
id: item.id,
},
);
setSelectOne({ ...obj1 });
let obj2 = Object.assign({ ...selectTwo }, { ...defaultTabObj }, { isCurrentSelected: true });
setSelectTwo({ ...obj2 });
let obj3 = Object.assign({ ...selectThree }, { ...defaultTabObj });
setSelectThree({ ...obj3 });
setSelectThreeList([]);
}
break;
case 2:
{
let obj2 = Object.assign(selectTwo, {
name: item.name,
isCurrentSelected: false,
id: item.id,
});
setSelectTwo({ ...obj2 });
let obj3 = Object.assign({ ...selectThree }, { ...defaultTabObj }, { isCurrentSelected: true });
setSelectThree({ ...obj3 });
}
break;
case 3:
{
setSelectThree(
Object.assign(selectThree, {
name: item.name,
isCurrentSelected: true,
id: item.id,
}),
);
}
break;
}
if (hierarchy === num) {
// 关闭窗口
getAddress && getAddress({ city: selectOne.name, district: selectTwo.name, streetName: selectThree.name });
handleClose();
} else {
setTimeout(() => {
addressSwipe.slideTo(num);
}, 0);
}
};
const getTopTab = (tabIndex: number, selectTab: titleTab) => {
return (
<span
className={`${selectTab.isCurrentSelected ? styles.currentTab : ""}`}
onClick={() => {
changeTab(tabIndex);
}}
>
{selectTab.name ? selectTab.name : selectTab.title}
</span>
);
};
const getSwiperContent = (currentTab: number, tabList: AddressListType[], letterList: string[], tab: titleTab) => {
return (
<div className={`${styles[prefixCls + "-wrapper-tab"]}`}>
<div className={styles.listContent}>
{(tabList || []).map(item => {
return (
<div
key={item.id}
className={`${item.isLetter ? styles.letter : styles.item} ${
item.name === tab.name ? styles.checked : ""
}`}
id={item.isLetter ? currentTab + "-selected-" + item.name : undefined}
onClick={() => {
checkItem(currentTab, item);
}}
>
{item.name}
</div>
);
})}
</div>
<div className={styles.rightLetter}>
{(letterList || []).map((item, index) => {
return (
<span
key={index}
onClick={() => {
const id = document.getElementById(currentTab + "-selected-" + item);
if (id) {
id.scrollIntoView({ block: "start", behavior: "auto" });
}
}}
>
{item}
</span>
);
})}
</div>
</div>
);
};
const handleClose = () => {
closeModel && closeModel();
initTab();
};
return (
<div className={`${styles[prefixCls || ""]} ${visible ? "" : styles[prefixCls + "-hidden"]}`}>
<div className={`${styles[prefixCls + "-content"]}`}>
<div className={`${styles[prefixCls + "-wrapper"]}`}>
<div className={`${styles[prefixCls + "-title"]}`}>
<div className={`${styles[prefixCls + "-title-left"]}`}>{addressTitle || "请选择您的现居地址"}</div>
<div onClick={handleClose}>
<img src={closeIcon} className={`${styles[prefixCls + "-title-close"]}`} />
</div>
<div className={`${styles[prefixCls + "-title-sub"]}`}>
{getTopTab(1, selectOne)}
{selectTwo.name || selectTwo.isCurrentSelected || (selectTwoList || []).length > 0
? getTopTab(2, selectTwo)
: null}
{selectThree.name || selectThree.isCurrentSelected || (selectThreeList || []).length > 0
? getTopTab(3, selectThree)
: null}
</div>
</div>
<div className={`${styles[prefixCls + "-select"]}`}>
<Swiper
spaceBetween={0}
slidesPerView={1}
onSlideChange={() => {
console.log("onSlideChange");
}}
onSwiper={swiper => {
setAddressSwipe(swiper);
}}
observeParents={true}
observer={true}
>
<SwiperSlide>
{getSwiperContent(1, selectOneList || [], selectOneListLetter || [], selectOne)}
</SwiperSlide>
{selectTwoList && selectTwoList.length > 0 ? (
<SwiperSlide>{getSwiperContent(2, selectTwoList, selectTwoListLetter || [], selectTwo)}</SwiperSlide>
) : null}
{selectThreeList && selectThreeList.length > 0 ? (
<SwiperSlide>
{getSwiperContent(3, selectThreeList, selectThreeListLetter || [], selectThree)}
</SwiperSlide>
) : null}
</Swiper>
</div>
</div>
</div>
</div>
);
};
Address.defaultProps = {
prefixCls: "s-address",
closeModel: () => {},
getAddress: () => {},
hierarchy: 3,
visible: false,
};
export default Address;
index.less
@bc-white: #fff;
@primaryColor: #3682ff;
@prefixCls: s-address;
.@{prefixCls} {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
z-index: 999;
background: rgba(0,0,0,0.7);
.@{prefixCls}-content {
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 540 * @vw;
background: @bc-white;
.@{prefixCls}-wrapper{
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
.@{prefixCls}-title{
display: flex;
flex-direction: row;
align-items: flex-start;
flex-wrap: wrap;
height: 98 * @vw;
.@{prefixCls}-title-left{
width: 303 * @vw;
height: 25 * @vw;
color: #333333;
font-size: 18 * @vw;
font-weight: 500;
margin-top: 20 * @vw;
margin-left: 25 * @vw;
line-height: 25 * @vw;
}
.@{prefixCls}-title-close{
width: 15 * @vw;
height: 15 * @vw;
margin-top: 12 * @vw;
margin-left: 20 * @vw;
}
.@{prefixCls}-title-sub {
display: flex;
flex-direction: row;
width: 100%;
padding-left: 25 * @vw;
span{
height: 22 * @vw;
color: @primaryColor;
font-size: 16 * @vw;
font-weight: 400;
margin-right: 20 * @vw;
line-height: 22 * @vw;
}
span.currentTab{
color: #E3302F;
}
}
}
.@{prefixCls}-select {
flex: 1;
height: 442 * @vw;
:global{
.swiper-container{
height: 100%;
overflow-y: auto;
}
}
.@{prefixCls}-wrapper-tab{
position: relative;
height: 100%;
.listContent{
height: 100%;
overflow-y: auto;
.letter{
height: 26 * @vw;
padding: 0 25 * @vw;
background: #F9F9FE;
line-height: 26 * @vw;
}
.item{
height: 58 * @vw;
margin: 0 15 * @vw;
padding: 0 10 * @vw;
line-height: 58 * @vw;
color: #333;
font-size: 16 * @vw;
font-weight: 400;
border-top: 1px solid #EBEBF1;
}
.letter + .item {
border-top: 0;
}
.checked{
color: @primaryColor;
}
}
.rightLetter{
position: absolute;
top: 0;
right: 0;
max-height: 442 * @vw;
display: flex;
flex-direction: column;
// justify-content: space-between;
align-items: center;
text-align: center;
width: 17 * @vw;
span{
line-height: 20 * @vw;
}
}
}
}
}
}
}
.@{prefixCls}.@{prefixCls}-hidden {
display: none;
height: 0;
width: 0;
overflow: hidden;
}
addressInfo.tsx ,数据量较大,请前往github获取(https://github.com/XiaoYouEr/Address/blob/main/AddressSelectModal/addressInfo.tsx)
示例
index.less
.selectBox{
border: 1px solid #dcdcdc;
height: 60 * @vw;
}
index.tsx
import React, { useState } from "react";
import styles from "./index.less";
import Address from '../AddressSelectModal';
export default function IndexPage() {
const [address, setAddress] = useState("");
const [city, setCity] = useState(props.city || "");
const [district, setDistrict] = useState(props.district || "");
const [streetName, setStreetName] = useState(props.streetName || "");
const [addressVisible, setAddressVisible] = useState(false);
const handleAddress = (result: { city?: string; district?: string; streetName?: string }) => {
setAddress([result.city || "", result.district || "", result.streetName || ""].join(" "));
setCity(result.city || "");
setDistrict(result.district || "");
setStreetName(result.streetName || "");
};
return (
<div>
点击选择
<div className={styles.selectBox} onClick={() => {setAddressVisible(true)}}>
{currentValue ? currentValue : "请选择您的地址"}
</div>
<Address hierarchy={3} getAddress={handleAddress} closeModel={() => {setAddressVisible(false)}} visible={addressVisible}/>
</div>
);
}
说明: 该地址使用了泰国地址,可根据需要更换为所需要国家的地址;地址选择框请自行封装,此处只是示例代码,未做样式处理
github地址: https://github.com/XiaoYouEr/Address.git
网友评论