react-cascader-popover
Version:
react级联选择器/支持多选/单选
474 lines (415 loc) • 11 kB
Markdown
# react-cascader-popover
react 级联选择器/支持多选/单选
src/Type 有完整演示
## Install
```bash
npm i react-cascader-popover
或者
yarn add react-cascader-popover
```
## demo
[https://react-cascader-popover-eyrp.vercel.app/](https://react-cascader-popover-eyrp.vercel.app/)
## 单选

```js
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;
```
## 多选

```js
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;
```
## 动态加载数据
```js
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
<table class="table table-bordered table-striped">
<tr>
<thead>
<tr>
<th >参数</th>
<th >类型</th>
<th >默认值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<th>value</th>
<th>string</th>
<th></th>
<th>选中的值</th>
</tr>
<tr>
<th>options</th>
<th>CascaderOption[]</th>
<th>[]</th>
<th>数据</th>
</tr>
<tr>
<th>open</th>
<th>boolean</th>
<th>false</th>
<th>是否弹出选择器</th>
</tr>
<tr>
<th>anchorEl</th>
<th>HTMLDivElement | null</th>
<th>null</th>
<th>HTMLDivElement元素 用于展示窗口的位置</th>
</tr>
<tr>
<th>multiple</th>
<th>boolean</th>
<th>false</th>
<th>是否开启多选</th>
</tr>
<tr>
<th>search</th>
<th>boolean</th>
<th>false</th>
<th>是否开启搜索</th>
</tr>
<tr>
<th>searchEmptyText</th>
<th>string</th>
<th>暂无数据</th>
<th>搜索为空提示</th>
</tr>
<tr>
<th>searchPlaceholder</th>
<th>string</th>
<th>请输入关键词</th>
<th>搜索框提示语</th>
</tr>
<tr>
<th>loadData</th>
<th> (value: CascaderOption) => Promise<CascaderOption[]></th>
<th></th>
<th>动态加载数据 options中要存在isLoad 配合 async await 使用</th>
</tr>
<tr>
<th>onChange</th>
<th> (value: CascaderOption | null, valueAll: CascaderOption[]) => void</th>
<th></th>
<th>点击后的事件</th>
</tr>
<tr>
<th>onClose</th>
<th> () => void</th>
<th></th>
<th>隐藏选择器事件</th>
</tr>
<tr>
<th>ref</th>
<th>CascaderRefProps</th>
<th></th>
<th>
<div>setValue: (value: string[]) => void; </div>
<div>clearValue: () => void; </div>
用于多选时使用
</th>
</tr>
</tbody>
</table>
### option
<table class="table table-bordered table-striped">
<tr>
<thead>
<tr>
<th >参数</th>
<th>类型</th>
<th >默认值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<th>label</th>
<th>string</th>
<th></th>
<th>展示的文字</th>
</tr>
<tr>
<th>value</th>
<th>string</th>
<th></th>
<th>对应的value</th>
</tr>
<tr>
<th>disabled</th>
<th>boolean</th>
<th>false</th>
<th>禁用</th>
</tr>
<tr>
<th>isLoad</th>
<th>boolean</th>
<th>false</th>
<th>true=有下级数据</th>
</tr>
<tr>
<th>children</th>
<th>Array</th>
<th></th>
<th>children</th>
</tr>
</tbody>
</table>
## 更新说明
1.0.6 更新项目
1.0.7 多选时展开列表默认展示第一个数据
1.1.0 增加输入框筛选 修复 onChange 触发问题
1.1.4 增加在多选搜索的时候可以选择多个
1.1.5 增加 loadData 动态加载数据逻辑, options 中加入禁用