Skip to content

zrx-888/react-cascader-popover

Repository files navigation

react-cascader-popover

react 级联选择器/支持多选/单选

src/Type 有完整演示

Install

 npm   i react-cascader-popover
或者
 yarn add react-cascader-popover

demo

https://react-cascader-popover-eyrp.vercel.app/

单选

import { useRef, useState } from "react";
import { province } from "../data";
import {
  Cascader,
  CascaderOption,
  CascaderRefProps,
} from "react-cascader-popover";

function Default() {
  const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);

  const [valueAllPath, setValueAllPath] = useState<CascaderOption[]>([]);

  const [value, setValue] = useState("120103");

  const cascaderRef = useRef<CascaderRefProps>(null);

  // 点击展开
  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
    setAnchorEl(event.currentTarget);
  };
  // change 事件
  const handleChange = (
    value: CascaderOption | null,
    valueAll: CascaderOption[]
  ) => {
    setValue(value ? value.value : "");
    setValueAllPath(valueAll);
  };
  // 清空选中
  const handleClear = () => {
    setValueAllPath([]);
    // 两种都可以清空
    // setValue("");
    cascaderRef.current?.clearValue();
  };

  // 设置选中
  const handleSet = () => {
    setValue("11010333555");
  };

  const open = Boolean(anchorEl);
  return (
    <>
      <div style={{ width: "500px" }}>
        <div className="cascader">
          <div className="cascader_input_box" onClick={handleClick}>
            {valueAllPath.length ? (
              <div className="cascader_input">
                {valueAllPath.map((e) => e.label).join(" - ")}
              </div>
            ) : (
              <div className="placeholder">请选择</div>
            )}
          </div>
        </div>
      </div>

      <Cascader
        ref={cascaderRef}
        value={value}
        open={open}
        anchorEl={anchorEl}
        options={province}
        onClose={() => setAnchorEl(null)}
        onChange={handleChange}
      />
    </>
  );
}

export default Default;

多选

  import { useRef, useState } from "react";
import {
  Cascader,
  CascaderOption,
  CascaderRefProps,
} from "react-cascader-popover";
import { province } from "../data";

function Multiple() {
  const value = "130102";
  const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);
  const [valueAll, setValueAll] = useState<CascaderOption[]>([]);
  const cascaderRef = useRef<CascaderRefProps>(null);

  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
    setAnchorEl(event.currentTarget);
  };
  const handleChange = (
    _: CascaderOption | null,
    valueAll: CascaderOption[]
  ) => {
    console.log(valueAll);

    setValueAll(valueAll);
  };
  const handleClear = () => {
    // cascaderRef.current?.setValue([]);
    cascaderRef.current?.clearValue();
  };
  const handleSetValue = () => {
    const data = [
      {
        value: "120101",
        label: "和平区",
      },
      {
        value: "120102",
        label: "河东区",
      },
    ];
    cascaderRef.current?.setValue(data.map((e) => e.value));
  };

  const open = Boolean(anchorEl);
  return (
    <>
      <div style={{ width: "500px" }}>
        <h3>多选 </h3>
        {valueAll.length ? (
          <>
            <button onClick={handleClear}>清空VALUE</button>
            <div>
              当前选中【{valueAll.map((e) => JSON.stringify(e) + ",")}</div>
          </>
        ) : (
          <button onClick={handleSetValue}>设置VALUE选中</button>
        )}

        <div className="cascader">
          <div className="cascader_input_box" onClick={handleClick}>
            {valueAll.length ? (
              <div className="cascader_input">
                {valueAll.map((e) => e.label).join(" , ")}
              </div>
            ) : (
              <div className="placeholder">请选择</div>
            )}
          </div>
        </div>
      </div>

      <Cascader
        ref={cascaderRef}
        value={value}
        open={open}
        anchorEl={anchorEl}
        multiple
        options={province}
        onClose={() => setAnchorEl(null)}
        onChange={handleChange}
      />
    </>
  );
}

export default Multiple;

动态加载数据

import { useRef, useState } from "react";
import {
  Cascader,
  CascaderOption,
  CascaderRefProps,
} from "react-cascader-popover";

function Default() {
  const options = [
    {
      value: "120000",
      label: "天津市",
      isLoad: true,
    },
    {
      value: "110000",
      label: "北京市",
      disabled: true,
      isLoad: true,
    },
  ];

  const [anchorEl, setAnchorEl] = useState<HTMLDivElement | null>(null);
  const [valueAllPath, setValueAllPath] = useState<CascaderOption[]>([]);
  const [value, setValue] = useState("");
  const [num, setNum] = useState(0);
  const [valueItem, setValueItem] = useState<CascaderOption | null>(null);
  const cascaderRef = useRef<CascaderRefProps>(null);

  const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
    setAnchorEl(event.currentTarget);
  };
  const handleChange = (
    value: CascaderOption | null,
    valueAll: CascaderOption[]
  ) => {
    console.log(value, valueAll);

    setValueItem(value);
    // setValue(value ? value.value : "");
    setValueAllPath(valueAll);
  };

  // 模拟接口返回数据
  const getServiceData = (item: CascaderOption) => {
    return new Promise<CascaderOption[]>((resolve) => {
      setTimeout(() => {
        const count = num + 1;
        setNum(count);

        resolve([
          {
            label: `${item.label}-1 `,
            value: item.value + 1,
            isLoad: count >= 2 ? false : true,
          },
          {
            label: `${item.label}-2 `,
            value: item.value + 222,
            isLoad: count >= 2 ? false : true,
          },
        ]);
      }, 1000);
    });
  };

  const loadData = async (item: CascaderOption) => {
    const data = await getServiceData(item);
    return data;
  };

  const open = Boolean(anchorEl);
  return (
    <>
      <div style={{ width: "500px" }}>
        <h3>动态加载 </h3>
        <h6>value:{value}</h6>
        {valueItem ? (
          <h6>
            valueItem:{valueItem.value}/{valueItem.label}
          </h6>
        ) : (
          <></>
        )}
        {valueAllPath.length ? (
          <h6>全路径:{valueAllPath.map((e) => e.label).join(" - ")}</h6>
        ) : (
          <></>
        )}
        <div className="cascader">
          <div className="cascader_input_box" onClick={handleClick}>
            {valueAllPath.length ? (
              <div className="cascader_input">
                {valueAllPath.map((e) => e.label).join(" - ")}
              </div>
            ) : (
              <div className="placeholder">请选择</div>
            )}
          </div>
        </div>
      </div>

      <Cascader
        search
        ref={cascaderRef}
        value={value}
        open={open}
        anchorEl={anchorEl}
        options={options}
        onClose={() => setAnchorEl(null)}
        onChange={handleChange}
        loadData={loadData}
      />
    </>
  );
}

export default Default;

API

props

参数 类型 默认值 描述
value string 选中的值
options CascaderOption[] [] 数据
open boolean false 是否弹出选择器
anchorEl HTMLDivElement | null null HTMLDivElement元素 用于展示窗口的位置
multiple boolean false 是否开启多选
search boolean false 是否开启搜索
searchEmptyText string 暂无数据 搜索为空提示
searchPlaceholder string 请输入关键词 搜索框提示语
loadData (value: CascaderOption) => Promise<CascaderOption[]> 动态加载数据 options中要存在isLoad 配合 async await 使用
onChange (value: CascaderOption | null, valueAll: CascaderOption[]) => void 点击后的事件
onClose () => void 隐藏选择器事件
ref CascaderRefProps
setValue: (value: string[]) => void;
clearValue: () => void;
用于多选时使用

option

参数 类型 默认值 描述
label string 展示的文字
value string 对应的value
disabled boolean false 禁用
isLoad boolean false true=有下级数据
children Array children

更新说明

1.0.6 更新项目

1.0.7 多选时展开列表默认展示第一个数据

1.1.0 增加输入框筛选 修复 onChange 触发问题

1.1.4 增加在多选搜索的时候可以选择多个

1.1.5 增加 loadData 动态加载数据逻辑, options 中加入禁用

About

react级联选择器/支持多选/单选

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages