废话文学:一年没怎么更新过文章了,在忙着荒野求生(换了work+摸鱼)。但是,最近在工作的时候看到设计稿里的集成功能熟悉又陌生(其实就是包含了组件,然后组件又达不到这个要求),然后出现的频率还挺高的,先记录一下(二开倒是没什么难度,也不复杂,就是会浪费点开发时间),这里只针对Antd封装(版本^5.2.3),找个时间发个包,提高开发效率。
设计稿需求效果
解析:就是对CheckBox组件和CheckGroup的封装复用,具体功能还是根据需求文档来吧
组件代码(ts)
import { Button, Checkbox, Col, Popover, Row } from 'antd'
import styles from "./index.less"
import React, { useEffect, useState } from 'react'
import { CheckboxValueType } from 'antd/es/checkbox/Group';
import {
SettingOutlined
} from '@ant-design/icons';
interface boxProps {
boxOptions?: { label: string, value: CheckboxValueType }[] //所有指标
defaultCheckedList: CheckboxValueType[] //上次选择之后记录的指标
onFinished: (e: CheckboxValueType[]) => void //确认
type: "link" | "button",
isTitle?: boolean //是否有副标题
listOptions?: {
title: string;
valueOptions: {
label: string;
value: CheckboxValueType;
}[];
}[] //标题+value入参 istitle为true的时候传这个 不传boxOptions
}
export default function CheckBoxGroup({ defaultCheckedList, boxOptions = [], onFinished, type = "button", isTitle = false, listOptions = [] }: boxProps) {
const [open, setOpen] = useState(false)
const [checkedList, setCheckedList] = useState<CheckboxValueType[]>(defaultCheckedList);
const [allOptions, setAllOptions] = useState<CheckboxValueType[]>([])
const Operation = {
"button": < Button onClick={() => setOpen(true)} id='groupCheckbox' type='primary' style={{ width: "113px", fontSize: "12px", padding: "4px 7px" }}> 选择数据指标({checkedList.length})</Button >,
"link": <Button onClick={() => setOpen(true)} id='groupCheckbox' type='link' style={{ width: "113px", fontSize: "14px", padding: "4px 7px" }} icon={<SettingOutlined />}>列表配置</Button>
}
useEffect(() => {
// 是否有副标题
if (isTitle) {
let new_allOptions: any = []
listOptions.forEach(item => {
item.valueOptions.forEach((ele) => {
new_allOptions.push(ele.value)
})
})
setAllOptions(new_allOptions)
} else {
// allOptions
let new_allOptions: any[] = []
boxOptions.map(item => new_allOptions.push(item.value))
setAllOptions(new_allOptions)
}
}, [])
const checkAll = allOptions.length === checkedList.length;
const indeterminate = checkedList.length > 0 && checkedList.length < allOptions.length;
const onGroupChange = (list: CheckboxValueType[]) => {
setCheckedList(list);
}
const onCheckAllChange = (e: any) => {
setCheckedList(e.target.checked ? allOptions : []);
};
// 确认
const onConfirm = () => {
// console.log(checkedList);
setOpen(false)
onFinished(checkedList)
}
// 取消
const onCancel = () => {
setCheckedList(defaultCheckedList)
setOpen(false)
}
return (
<Popover
placement="bottomRight"
trigger={"click"}
overlayClassName={styles.checkPopover}
destroyTooltipOnHide={true}
open={open}
title={
<div style={{ display: "flex", alignItems: "center", marginBottom: 0, fontWeight: "normal" }}>
<Checkbox indeterminate={indeterminate} onChange={onCheckAllChange} checked={checkAll}>
全选
</Checkbox >
<div style={{ color: "#c2c2c2" }}>(<span style={{ color: "#666FFF" }}>{checkedList.length}</span> / {allOptions.length}已选)</div>
<Button type='link' onClick={() => setCheckedList([])}>重置</Button>
</div>
}
content={
<>
<Checkbox.Group value={checkedList} style={{ width: '100%' }} onChange={onGroupChange}>
{!isTitle
?
<Row>
{(boxOptions || []).map(item => {
return <Col span={8} style={{ marginTop: "8px" }}>
<Checkbox value={item.value}>{item.label}</Checkbox>
</Col>
})}
</Row>
:
listOptions.map(item => {
return <div style={{ display: "flex", width: "100%" }}>
<div style={{ width: "70px", marginTop: "8px" }}>{item.title}:</div>
<Row style={{ width: "calc(100% - 70px)" }}>
{(item.valueOptions || []).map(ele => {
return <Col span={8} style={{ marginTop: "8px" }}>
<Checkbox value={ele.value}>{ele.label}</Checkbox>
</Col>
})}
</Row>
</div>
})
}
</Checkbox.Group>
<div style={{ direction: "rtl", marginTop: "10px" }}>
<Button type='primary' style={{ marginLeft: 10 }} onClick={() => onConfirm()}>确定</Button>
<Button onClick={() => onCancel()}>取消</Button>
</div>
</>
}
arrow={false} >
{Operation[type]}
</Popover >
)
}
less样式代码
.checkPopover {
height: auto;
width: 436px;
background-color: #ffffff;
border: 1px solid rgba(229, 229, 229, 0.3);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.15);
:global {
.ant-popover-title {
border-bottom: 1px solid #e5e5e5;
}
}
}
组件的使用
封装了一个type属性,来切换触发的形态变体,可以根据需求再加自己想要的样式
设置了两种入参,因为传入的类型有点出入加上ts类型处理比较麻烦,就没做统一入参,在需要子标题区分选择时,isTitle为true,入参用listOptions,默认为false,入参为boxOptions
完成回调的结果,我只保留的value(具体保留什么格式可以根据服务端入参来写,value无非就是需要再处理),复杂处理建议放在外级处理,如果入参也需要区分标题入参,那就在onConfirm里筛选或者onfinished去筛选也行,这个可以根据不同需求大佬们自己改动。
入参示例 1
const allOptions = [
{ label: "A", value: "A" },
{ label: "B", value: "B" },
{ label: "C", value: "C" },
{ label: "D", value: "D" },
{ label: "E", value: "E" },
{ label: "F", value: "F" },
]; //所有指标
const defaultCheckedList = ['A', 'B', 'C']; //上次选择之后记录的指标
<CheckBoxGroup
type="link"
boxOptions={allOptions}
defaultCheckedList={defaultCheckedList}
onFinished={(e) => {
console.log(e);
}}
/>
入参示例 2
const list = [
{
title: "经营概览", valueOptions: [
{ label: "A", value: "A" },
{ label: "B", value: "B" },
{ label: "C", value: "C" },
{ label: "G", value: "G" }
]
},
{
title: "园区详情", valueOptions: [
{ label: "D", value: "D" },
{ label: "E", value: "E" },
{ label: "F", value: "F" },
]
}
] //所有指标
const defaultCheckedList = ['A', 'B', 'C']; //上次选择之后记录的指标
<CheckBoxGroup
type="link"
isTitle={true}
listOptions={list}
defaultCheckedList={defaultCheckedList}
onFinished={(e) => {
console.log(e);
}}
/>