MENU

Taro + TS 封装pick组件 实现省市县三级联动

• November 10, 2021 • Read: 203 • Web Program

直接贴代码,省市级数据来自自定义的仓库

https://cdn.xiaohuwei.cn/ljJOZeGd7tG3SHuPJIEVzgahBn-O

index.tsx

import { View, PickerView, PickerViewColumn, Text } from "@tarojs/components";
import "./index.scss";
import React, { FC, useEffect, useState } from "react";
import Row from "@/component/Row";
import { useStores } from "@/store";
import { observer } from "mobx-react-lite";
import classNames from "classnames";

interface AddressPickerProps {
  //父组件接值回调
  onSure?: (address: [string, string][]) => void;
  //组件初始值
  defaultValue?: [string, string][];
}

const AddressPicker: FC<AddressPickerProps> = (props) => {
  //接受一个回调的通知函数 和组件初始值
  const { onSure, defaultValue } = props;
  //连接仓库
  const { userStore } = useStores();
  const address = userStore.address;
  //选定之后显示给用户的文本
  const [adressText, setAdressText] = useState("");
  //是否打开选择面板
  const [pickerShow, setPickerShow] = useState(false);
  //关闭面板函数
  const onClosd = () => setPickerShow(false);
  //地市级下标
  const [state, setState] = useState([0, 0, 0]);
  //监听三列滑动改变
  const cityChange = (e) => {
    if (e.detail.value[0] !== state[0]) {
      //省份发生改变
      return setState([e.detail.value[0], 0, 0]);
    }
    if (e.detail.value[1] !== state[1]) {
      //市级发生改变
      return setState([state[0], e.detail.value[1], 0]);
    }
    setState(e.detail.value);
  };
  //省市级数组 格式 [['100001',''北京],[‘100002’,'天津']]
  const currentProvinces = Object.entries(address[86]);
  //当前地级市数组
  const currentCitys = Object.entries(address[currentProvinces[state[0]][0]]);
  //当前县数组
  const currentAreas = Object.entries(address[currentCitys[state[1]][0]]);
  //返回最终结果
  const handleSure = () => {
    const tempAreaInfo = [
      currentProvinces[state[0]],
      currentCitys[state[1]],
      currentAreas[state[2]],
    ];
    //关闭弹框
    onClosd();
    //设置显示文字
    setAdressText(tempAreaInfo.map((item) => item[1]).join(""));
    //通知调用的组件 吧最终结果返回
    onSure && onSure(tempAreaInfo);
  };
  //classNames 动态声明
  const labelClasses = classNames("address-picker-primary", {
    "address-picker-active": !!adressText,
  });
  const pickerClasses = classNames("address-picker-container", {
    "show address-picker-container-active": pickerShow,
  });

  //完成初始化赋值
  useEffect(() => {
    if (defaultValue) {
      //如果传入初始值
      const provinceCode = defaultValue[0][0]; //省级key
      const cityCode = defaultValue[1][0]; //市级key
      const areaCode = defaultValue[2][0]; //省级key
      //省级下标
      const currentProvincesIndex = currentProvinces.findIndex(
        (v) => v[0] === provinceCode
      );
      //市级下标
      const currentCityIndex = Object.entries(address[provinceCode]).findIndex(
        (v) => v[0] === cityCode
      );
      //县级下标
      const currentAreaIndex = Object.entries(address[cityCode]).findIndex(
        (v) => v[0] === areaCode
      );
      //对齐下标
      setState([currentProvincesIndex, currentCityIndex, currentAreaIndex]);
      //对齐展示文字
      setAdressText(defaultValue.map((item) => item[1]).join(""));
    }
  }, []);

  return (
    <View>
      <Row onClick={() => setPickerShow(true)} showRightArrow title="所在地区">
        <Text className={labelClasses}>{adressText || "请选择地区"}</Text>
      </Row>
      <View onClick={() => onClosd()} className={pickerClasses}>
        <View
          onClick={(event) => event.stopPropagation()}
          className="picker-content"
        >
          <View className="dialog-header">
            <View onClick={() => onClosd()} className="dialog-button cancel">
              取消
            </View>
            <View className="dialog-title">请选择地区</View>
            <View className="dialog-button" onClick={handleSure}>
              确定
            </View>
          </View>
          <PickerView
            onChange={cityChange}
            value={state}
            className="picker-view-wrap"
          >
            <PickerViewColumn>
              {currentProvinces.map((province, index) => {
                return (
                  <View className="picker-item" key={index}>
                    {province[1]}
                  </View>
                );
              })}
            </PickerViewColumn>
            <PickerViewColumn>
              {currentCitys.map((city, index) => {
                return (
                  <View className="picker-item" key={index}>
                    {city[1]}
                  </View>
                );
              })}
            </PickerViewColumn>
            <PickerViewColumn>
              {currentAreas.map((area, index) => {
                return (
                  <View className="picker-item" key={index}>
                    {area[1]}
                  </View>
                );
              })}
            </PickerViewColumn>
          </PickerView>
        </View>
      </View>
    </View>
  );
};

export default observer(AddressPicker);

index.scss

@keyframes back {
    0% {
        background: rgba(255, 255, 255, 0);
        opacity: 0;
    }
    100% {
        background: rgba(0, 0, 0, 0.7);
        opacity: 1;
    }
}
@keyframes up
{
  0% { transform: translateY(600px);opacity: 0; }
  100% { transform: translateY(0);opacity: 1; }
}


.address-picker-container::after{
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    background: rgba(0, 0, 0, 1);
    opacity: 0;
    transition: opacity .3s ease-in-out;
}
.address-picker-container-active::after{

    opacity: 0.7;
// animation-fill-mode: forwards;
}

.address-picker-container {

    width: 100%;
    height: 100vh;
    display: flex;
    z-index: 12;
    // background: rgba(0, 0, 0, 0.7);
    flex-direction: column;
    justify-content: center;
    align-items: center;
    position: fixed;
    bottom: 0px;
    left: 0px;
    visibility: hidden;
    &.show {
        visibility: visible;
        .picker-content {
            transform: translateY(0);
            opacity: 1;
         //   transform: translateY(200%);
          // transition: all .4s ease-in-out;
            
            //animation: up .3s  ease-in-out forwards;

        }
    }
}

.picker-content {
    border-top-left-radius: 32px;
    border-top-right-radius: 32px;
    position: absolute;
    height: 660px;
    z-index: 999;
    width: 100%;
    bottom: 0;
    background-color: #fff;
    transition: all .3s ease-in-out;
    transform: translateY(600px);
    opacity: 0; 
    .picker-view-wrap {
        width: 100%;
        height: 500px;
        margin-top: -50px;
    }
}

.picker-item {
    line-height: 70px;
    font-size: 30px;
    text-align: center;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.dialog-header {
    width: 100%;
    // background: #ededed;
    display: flex;
    justify-content: space-between;
    align-items: center;
    border-radius: 30px;

    .dialog-title {
        
color: #2E2E2E;

font-size: 34px;
    }

    .dialog-button {
        display: inline-block;
        text-align: center;
        font-size: 34px;
        color: #846B57;
        padding: 50px;
        &.cancel {
            color: #999999;
        }
    }
}
.address-picker-primary{
padding-right: 22px;
color: #999999;
}
.address-picker-active{
    color: #262626;
}

开始使用

import AddressPicker from "@/component/SelectCity";

        <AddressPicker
          defaultValue={[
            ["340000", "安徽省"],
            ["340700", "铜陵市"],
            ["340705", "铜官区"],
          ]}
          onSure={(e) => console.log(e)}
        />
//defaultValue可不传

用到的省市级数据源

Archives QR Code
QR Code for this page
Tipping QR Code