2021-07-12 11-52-28.gif
一般大部分組件都會有對應(yīng)的地區(qū)選擇組件菱鸥,類似于 Ant Design Mobile 中的地區(qū)選擇。當然抚垄,如果你們的產(chǎn)品和設(shè)計沒有過多的要求蜕窿,完全可以直接用,而且穩(wěn)定性也很好呆馁。
然而我就沒這么幸運~ ??
?? 產(chǎn)品需求
本片的代碼組件只適用于此種產(chǎn)品需求
省份 城市 區(qū)域 為3個接口
//獲取省份
export async function getProvinces() {
return request('/user/provinces', {
method: 'GET',
});
}
//根據(jù)省份id獲取城市
export function getCitys(provinceId) {
return request(`/user/province/cities/${provinceId}`, {
method: 'GET',
});
}
//根據(jù)城市id獲取區(qū)域
export function getDistricts(cityId) {
return request(`/user/city/districts/${cityId}`, {
method: 'GET',
});
}
?? 插件 Swiper
為了想要自己實現(xiàn)這種滑動拖拽的效果桐经,自己也查閱了很多文章,最后選用了 swiper浙滤。
你可能會懷疑次询,這不是做輪播圖的嗎,這也能搞瓷叫?屯吊!
沒錯,它確實能搞摹菠,swiper 中的 網(wǎng)格分布 就屬于這種的,大家也可以先去了解下盒卸。
npm i swiper
你可以跟著官方說的搞,但是我這邊并沒有引入它所說的 import 'swiper/swiper.scss';
因為這里根本也就用不到次氨。也可能會發(fā)現(xiàn)我的寫法和官方提供的有點不一致蔽介,有需要的可以在我代碼上改造。
??注意:多個 swiper 的創(chuàng)建方法,可以添加不同的 id
或者后墜不同的 class
new Swiper('#swiper1');
new Swiper('#swiper2');
new Swiper('#swiper3');
...
<div className="swiper-container" id="swiper1"></div>
<div className="swiper-container" id="swiper2"></div>
<div className="swiper-container" id="swiper3"></div>
?? 拓展組件
本品文章只會給大家提供基礎(chǔ)的地區(qū)選擇組件,可能代碼有點不完美虹蓄,但是學會了也就能自己隨便搞了犀呼,后續(xù)也希望大家提出寶貴意見和代碼優(yōu)化。
這是我們產(chǎn)品最終的地區(qū)選擇效果圖薇组,大家也能基于這個自行擴展~
2021-07-13 17-30-41.gif
?? 組件使用
import AreaSelection from '@/components/AreaSelection';
...
//show 組件的顯示狀態(tài)
//isShow 子組件控制組件的顯示方法
//value 默認回顯數(shù)據(jù) 格式為['上海','上海市','靜安區(qū)']
//onChange 返回數(shù)據(jù) [{name:'上海',id:600003},{name:'上海市',id:6003},{name:'靜安區(qū)',id:1036}]
<AreaSelection
show={顯示狀態(tài)}
value={回顯數(shù)據(jù)}
isShow={(val) => {
//顯示方法
}}
onChange={(val) => {
//返回數(shù)據(jù)
}}
/>
?? 組件代碼
import React, { Component } from 'react';
import { getProvinces, getCitys, getDistricts } from '@/api/user';
import styles from './index.less';
import Swiper from 'swiper';
//根據(jù)地區(qū)名字篩選下標
function fliterIndex(n, list) {
let index = 0;
let id = null;
let name = null;
list.map((val, v) => {
if (n === val.name) {
index = v;
id = val.id;
name = val.name;
}
});
return { index: index, id: id, name: name };
}
//地區(qū)選擇組件
export default class AreaSelection extends Component {
state = {
value: this.props.value,
activeIndex: [], //回顯時候的默認數(shù)據(jù)
provinces: [], //省份
citys: [], //城市
districts: [], //區(qū)縣
};
initArea = async () => {
//異步方法 獲取省份 - 根據(jù)省份查城市 - 根據(jù)城市查區(qū)域
//因為選中的數(shù)據(jù)為 [北京市,北京市,東城區(qū)] ,所以回顯數(shù)據(jù)時要根據(jù)名稱去匹配對應(yīng)的id外臂,name和index
const { value } = this.state;
let arr = [];
if (value[0]) {
//獲取省份
await getProvinces().then((res) => {
if (res.data) {
arr.push(fliterIndex(value[0], res.data));
}
});
//獲取城市
await getCitys(arr[0].id).then((res) => {
if (res.data) {
arr.push(fliterIndex(value[1], res.data));
}
});
//獲取區(qū)域
await getDistricts(arr[1].id).then((res) => {
if (res.data) {
arr.push(fliterIndex(value[2], res.data));
}
});
this.setState({
activeIndex: [...arr],
});
}
const self = this;
//定義通用的swiper配置
const options = {
direction: 'vertical',
speed: 300, //切換速度
spaceBetween: 0, //間距
height: 123, // slide 高度,必要,否則會有滑動偏差問題
slidesPerView: 3, //網(wǎng)格分布3個 https://www.swiper.com.cn/api/grid/24.html
centeredSlides: true, //居中
resistance: true, //邊緣抵抗
observer: true, //修改swiper自己或子元素時律胀,自動初始化swiper 不加有滑動失效問題
observeParents: true, //修改swiper的父元素時宋光,自動初始化swiper 不加有滑動失效問題
};
//獲取省份
getProvinces().then((res) => {
if (res.data) {
this.setState({
provinces: res.data,
});
new Swiper('#swiper1', provinces);
}
});
const val = self.state.activeIndex;
//省份配置
const provinces = {
...options,
initialSlide: val[0] && val[0].index, //默認顯示的下標位置
on: {
//初始化時候執(zhí)行 只執(zhí)行一次
init: function () {
let { provinces, value } = self.state;
let { name, id } = provinces[this.activeIndex];
let arr = [...value];
arr[0] = { name: name, id: id };
self.setState({ value: arr });
getCitys(id).then((res) => {
self.setState({
citys: res.data,
});
new Swiper('#swiper2', citys);
});
},
//切換的時候執(zhí)行
slideChange: function () {
let { provinces, value } = self.state;
let { name, id } = provinces[this.activeIndex];
let arr = [...value];
arr[0] = { name: name, id: id };
self.setState({ value: arr });
setTimeout(() => {
getCitys(id).then((res) => {
self.setState({
citys: res.data,
});
new Swiper('#swiper2', citys);
});
}, 0);
},
},
};
//城市配置
const citys = {
...options,
initialSlide: val[1] && val[1].index,
on: {
init: function () {
let { citys, value } = self.state;
let { name, id } = citys[this.activeIndex];
let arr = [...value];
arr[1] = { name: name, id: id };
self.setState({ value: arr });
getDistricts(id).then((res) => {
self.setState({
districts: res.data,
});
new Swiper('#swiper3', districts);
});
},
slideChange: function () {
let { citys, value } = self.state;
let { name, id } = citys[this.activeIndex];
let arr = [...value];
arr[1] = { name: name, id: id };
self.setState({ value: arr });
setTimeout(() => {
getDistricts(id).then((res) => {
self.setState({
districts: res.data,
});
new Swiper('#swiper3', districts);
});
}, 0);
},
},
};
//地區(qū)配置
const districts = {
...options,
initialSlide: val[2] && val[2].index,
on: {
init: function () {
let { districts, value } = self.state;
let { name, id } = districts[this.activeIndex];
let arr = [...value];
arr[2] = { name: name, id: id };
self.setState({ value: arr });
},
slideChange: function () {
let { districts, value } = self.state;
let { name, id } = districts[this.activeIndex];
let arr = [...value];
arr[2] = { name: name, id: id };
self.setState({ value: arr });
},
},
};
};
componentDidMount() {
this.initArea();
}
render() {
const { value, provinces, citys, districts } = this.state;
return (
<>
<div className={styles.page}>
<div
className={styles.cover}
onClick={() => {
this.props.isShow(false);
}}
></div>
<div id="AreaSelection" className={styles.AreaSelection}>
{/* 此處可加入標題頭 */}
<div className={styles.selectArr}>
{/* 選中橫條框 */}
<div
className={styles.containerCover}
onClick={() => {
this.props.isShow(false);
}}
></div>
<div className={styles.container}>
<div className="swiper-container" id="swiper1">
<div className="swiper-wrapper">
{provinces.map((pro, p) => (
<div key={p} className="swiper-slide">
{pro.name}
</div>
))}
</div>
</div>
</div>
<div className={styles.container}>
<div className="swiper-container" id="swiper2">
<div className="swiper-wrapper">
{citys.map((cit, c) => (
<div key={c} className="swiper-slide">
{cit.name}
</div>
))}
</div>
</div>
</div>
<div className={styles.container}>
<div className="swiper-container" id="swiper3">
<div className="swiper-wrapper">
{districts.map((cou, c) => (
<div key={c} className="swiper-slide">
{cou.name}
</div>
))}
</div>
</div>
</div>
</div>
</div>
<div className={styles.bottom}>
<div
className="commonPrimaryBotton"
style={{ width: '13.38rem', fontSize: '.88rem' }}
onClick={() => {
this.props.onChange(value);
this.props.isShow(false);
console.log(value);
}}
>
確定
</div>
</div>
</div>
</>
);
}
}
樣式引用
因為這里有針對 swiper 樣式的修改,所以只能在全局樣式里做了調(diào)整
//swiper 樣式
#AreaSelection {
.swiper-slide {
width: auto;
display: flex;
justify-content: center;
align-items: center;
color: #888888;
}
.swiper-slide-active {
color: #2d353a;
font-size: 1rem;
}
}
//組件引用的樣式
import styles from './index.less';
.page {
width: 100vw;
height: 100vh;
position: fixed;
z-index: 100;
left: 0;
top: 0;
}
.cover {
position: absolute;
width: 100%;
height: 100%;
background-color: black;
opacity: 0.5;
top: 0;
left: 0;
}
.bottom {
position: absolute;
bottom: 0;
width: 100%;
left: 0;
z-index: 10;
min-height: 3.6rem;
background-color: white;
padding: 0.5rem 1rem;
box-shadow: 0 0 5px #f3f3f3;
}
@slideHeight: 40px;
//地區(qū)選擇樣式
.AreaSelection {
// background-color: white;
width: 100%;
position: absolute;
bottom: 0;
left: 0;
z-index: 10;
// padding-bottom: 4rem;
background-color: white;
animation: showUp 0.3s ease both 1;
.selectArr {
display: flex;
position: relative;
overflow: hidden;
}
.containerCover {
width: 100%;
height: @slideHeight;
border: 1px solid #f3f3f3;
border-left: none;
border-right: none;
position: absolute;
left: 0;
top: @slideHeight;
z-index: 9;
pointer-events: none;
}
.container {
flex-grow: 1;
flex-basis: 0; //解決 flex-grow 內(nèi)容撐開問題
text-align: center;
background-color: white;
height: 14rem;
}
}
@keyframes showUp {
0% {
opacity: 0;
transform: translateY(100%);
}
100% {
opacity: 1;
transform: translateY(0);
}
}