我们在写自定义表单的时候,首选明确功能
- 表单子元素的所有类型
- 表单子元素的增删,赋值
- 表单子元素如果是一个符合元素(多选 单选)那么这个元素的增删改
- 表单是在展示状态还是在可以修改状态
- 还有一点需要注意的是渲染列表的时候需要绑定key并且key需要唯一 ,因为react的diff算法,不然删除元素的时候就会出现不论删除的是哪个元素出现的结果都是删除最后一个元素,
表单子元素的所有类型
目前表单有四种类型 头像上传、输入框、单选按钮、多选按钮
export enum ConfigType {
IMG = 'IMG',
INPUT = 'INPUT',
SINGLE = 'SINGLE',
MULE = 'MULE',
}
function RenderConfig({ type, index, title, arr, changeItemValue, display }: any) {
const [editItem, setEditItem] = useState(false)
const [img, setImg] = useState(nurseItemImg)
const [input, setInput] = useState('')
const [radioValue, setRadioValue] = useState()
const [checkArr, setCheckArr] = useState<any>([])
const imgFinish = (img: any) => {
setImg(img)
changeItemValue({ type: EditType.CHANGEVALUE, index, changeValue: img })
}
const onInput = (e: any) => {
const value = e.target.value
changeItemValue({ type: EditType.CHANGEVALUE, index, changeValue: value })
}
const onRadioChange = (e: any) => {
console.log(e.target.value)
const value = e.target.value
setRadioValue(value)
changeItemValue({
type: EditType.CHANGEVALUE, index, changeValue: value
})
}
let allCheckBoxArr: any = []
if (arr && type == ConfigType.MULE) {
allCheckBoxArr = arr.map((item: any) => item.value)
}
const checkAll = checkArr.length == allCheckBoxArr.length
const onCheckAllChange: CheckboxProps['onChange'] = (e) => {
const arr = e.target.checked ? allCheckBoxArr : []
setCheckArr(arr);
changeItemValue({
type: EditType.CHANGEVALUE, index, changeValue: arr
})
};
const itemChange = (e: any, value: any) => {
console.log(e.target.checked)
const checked = e.target.checked
let arr = [...checkArr]
if (checked) {
arr.push(value)
} else {
arr = arr.filter((item) => item != value)
}
setCheckArr(arr)
changeItemValue({
type: EditType.CHANGEVALUE, index, changeValue: arr
})
}
switch (type) {
case ConfigType.IMG:
return <Card display={display} changeItemValue={changeItemValue} type={type} index={index} title={`${index + 1}.${title}(上传图片)`}>
<ImgUpload img={img} finish={(img: any) => {
imgFinish(img)
}} />
</Card>;
case ConfigType.INPUT:
return <Card display={display} changeItemValue={changeItemValue} type={type} index={index} title={`${index + 1}.${title}(输入)`}>
<Input onChange={(e) => {
onInput(e)
}} className='bg-[#F5F8FA]' placeholder='请输入文字' />
</Card>;
case ConfigType.SINGLE:
if (display) {
return <Card display={display} changeItemValue={changeItemValue} type={type} index={index} editItem={editItem} setEditItem={setEditItem} title={`${index + 1}.${title}(单选)`}>
<Radio.Group onChange={onRadioChange} value={radioValue}>
<Space className='w-full' direction="vertical">
{
arr ? arr.map((item: nurseSelectItemParam, itemIndex: number) => {
return (
<div className='relative flex'>
<div className='flex'>
<Radio className='pl-[5px]' value={item.value}>{item.value}</Radio>
</div>
</div>
)
}) : ''
}
</Space>
</Radio.Group>
</Card>;
} else {
return <Card changeItemValue={changeItemValue} type={type} index={index} editItem={editItem} setEditItem={setEditItem} title={`${index + 1}.${title}(单选)`}>
<Radio.Group onChange={onRadioChange} value={radioValue}>
<Space className='w-full' direction="vertical">
{
arr ? arr.map((item: nurseSelectItemParam, itemIndex: number) => {
return (
<div className='relative flex items-center justify-center'>
<input
key={item.id}
value={item.value}
onChange={(e) => {
const value = e.target.value
changeItemValue({ type: EditType.EDIT, value, index, itemIndex })
}}
className='w-full leading-5 text-base py-[8px] rounded-[10px] pl-[25px]' type="text" placeholder={`输入${nurseItemIncludeChild[0].text}选项`} />
<div className='flex justify-between items-center'>
<Radio className='absolute left-[5px]' value={item.value}></Radio>
{editItem ? <div className='flex absolute right-[5px]'>
{itemIndex == arr.length - 1 ?
<div onClick={() => {
changeItemValue({ type: EditType.ADDITEM, index, itemIndex })
}} className='mr-[10px]'>
<img src={nurseItemAdd} className='w-[16px]' alt="" />
</div> : ''}
<div onClick={() => {
changeItemValue({ type: EditType.DELETEITEM, index, itemIndex })
}}>
<img src={nurseItemDelete} className='w-[16px]' alt="" />
</div>
</div> : ''}
</div>
</div>
)
}) : ''
}
</Space>
</Radio.Group>
</Card>;
}
case ConfigType.MULE:
if (display) {
return <Card display={display} changeItemValue={changeItemValue} type={type} index={index} editItem={editItem} setEditItem={setEditItem} title={`${index + 1}.${title}(多选)`}>
<Space direction="vertical">
<Checkbox className='pl-[5px]' onChange={onCheckAllChange} checked={checkAll}>全选</Checkbox>
{
arr ? arr.map((item: nurseSelectItemParam, itemIndex: number) => {
return (
<div className='relative'>
<div className='flex pl-[5px]'>
<Checkbox onChange={(e) => { itemChange(e, item.value) }} className=' left-[5px]' checked={checkArr.includes(item.value)}>{item.value}</Checkbox>
</div>
</div>
)
}) : ''
}
</Space>
</Card>;
} else {
return <Card changeItemValue={changeItemValue} type={type} index={index} editItem={editItem} setEditItem={setEditItem} title={`${index + 1}.${title}(多选)`}>
<Space direction="vertical">
<Checkbox className='pl-[5px]' onChange={onCheckAllChange} checked={checkAll}>全选</Checkbox>
{
arr ? arr.map((item: nurseSelectItemParam, itemIndex: number) => {
return (
<div className='relative flex items-center justify-center'>
<input
key={item.id}
value={item.value}
onChange={(e) => {
const value = e.target.value
changeItemValue({ type: EditType.EDIT, value, index, itemIndex })
}}
className='w-full leading-5 text-base py-[8px] rounded-[10px] pl-[25px]' type="text" placeholder={`输入${nurseItemIncludeChild[1].text}选项`} />
<div className='flex justify-between items-center'>
<Checkbox onChange={(e) => { itemChange(e, item.value) }} className='absolute left-[5px]' checked={checkArr.includes(item.value)}></Checkbox>
{editItem ? <div className='flex absolute right-[5px]'>
{itemIndex == arr.length - 1 ?
<div onClick={() => {
changeItemValue({ type: EditType.ADDITEM, index, itemIndex })
}} className='mr-[10px]'>
<img src={nurseItemAdd} className='w-[16px]' alt="" />
</div> : ''}
<div onClick={() => {
changeItemValue({ type: EditType.DELETEITEM, index, itemIndex })
}}>
<img src={nurseItemDelete} className='w-[16px]' alt="" />
</div>
</div> : ''}
</div>
</div>
)
}) : ''
}
</Space>
</Card>;
}
default:
return null;
}
}
Card组件
interface cardParam {
children: any
title: string
changeItemValue: Function
index: number
type: string
editItem?: boolean
setEditItem?: Function
display?: boolean
}
function Card(props: cardParam) {
const { children, title, changeItemValue, type, index, editItem, setEditItem, display } = props
const deleteItems = () => {
changeItemValue({ index, type: EditType.DELETE })
}
const isMobile = useGetWindowSize()
// 展示表单的页面
if (props.hasOwnProperty("display") && display) {
if (isMobile) {
return <div className='p-[16px] pb-[15px] bg-[#fff] w-full m-auto rounded-[10px] mb-[16px]'>
<CommonTitle name={title} type='rect' />
<div className='flex flex-col ml-[9px] mb-[20px]'>
{children}
</div>
</div>
} else {
return <><CommonTitle name={title} type='rect' />
<div className='flex flex-col ml-[9px] mb-[20px]'>
{children}
</div>
</>
}
}
// 编辑的表单页面
else {
return <div className='p-[16px] bg-[#fff] w-full m-auto rounded-[10px] mb-[16px]'>
<div className='flex items-center justify-between font-bold mb-[13px] text-base'>
{title}
<div className='flex text-base text-[#0072EF] font-normal'>
{props.hasOwnProperty("editItem") ? <div onClick={() => {
console.log(editItem)
setEditItem && setEditItem(!editItem)
}} className='mr-[15px]'>编辑</div> : ''}
<div className='flex items-center' onClick={() => { deleteItems() }}>
<img src={nurseSelectItemDelete} className='w-[20px]' alt="" /></div>
</div>
</div>
<div className='flex flex-col'>
{children}
</div>
</div>
}
}
加载表单组件
interface nurseSelectItemParam {
value: string
id: number
// changeValue?: any
}
interface nurseConfigParam {
title: string
type: ConfigType
arr?: Array<nurseSelectItemParam>
id: number
changeValue: any
}
interface preViewConfigParam {
// deleteItem: Function
display: boolean
setNurseConfig: Function
nurseConfig: Array<nurseConfigParam>
}
export function PreViewConfig(props: preViewConfigParam) {
//
const changeItemValue = ({ type, index, itemIndex, value, changeValue }: any) => {
switch (type) {
case EditType.DELETE:
{
const newConfig = [...nurseConfig].filter((item, itemIndex) => itemIndex != index);
setNurseConfig(newConfig);
return;
}
case EditType.ADDITEM:
{
const newConfig = [...nurseConfig]
newConfig[index].arr?.push({ value: '', id: new Date().getTime() })
setNurseConfig(newConfig);
return;
}
case EditType.DELETEITEM:
{
const newConfig = [...nurseConfig]
let changeItem = newConfig[index]
if (changeItem.arr) {
changeItem.arr = changeItem.arr.filter((item, changeIndex) => changeIndex != itemIndex)
}
newConfig[index] = changeItem
console.log(newConfig, changeItem, index, itemIndex)
setNurseConfig(newConfig);
return
}
case EditType.EDIT: {
const newConfig = [...nurseConfig]
const changeArr = newConfig[index].arr
if (changeArr) {
changeArr[itemIndex].value = value
newConfig[index].arr = changeArr
setNurseConfig(newConfig);
}
return
}
case EditType.CHANGEVALUE: {
const newConfig = [...nurseConfig]
newConfig[index].changeValue = changeValue
setNurseConfig(newConfig);
return
}
case EditType.CHNAGESELETEVALUE: {
return
}
default:
return null
}
}
const { display, setNurseConfig, nurseConfig } = props
return (
<div className={`${display ? '' : 'bg-[#f4f5f6]'} `}>
{
nurseConfig.map((item, index) => {
return <RenderConfig display={display} changeItemValue={changeItemValue} key={item.id} type={item.type} index={index} title={item.title} arr={item.arr} />
})
}
</div>
)
}
页面挂载
<PreViewConfig display={false} nurseConfig={nurseConfig} setNurseConfig={setNurseConfig} />