基于react-vant封装表单组件

基于react-vant封装表单基本常用组件


react-vant 组件库地址

一、基础组件


1. LocationPicker(位置级联)

效果演示:



组件代码
import React, {useEffect, useRef, useState} from 'react';
import {Cell, Empty, Loading, Picker, Popup} from "react-vant";
import PropType from "prop-types";
import '@/common/style/common.less'
import emptyPic from '@/common/images/empty.png'
import {_arrayFilterEmpty, _isEmpty} from "../../../common/js/utils";


/**
 * 默认值
 */
LocationPicker.defaultProps = {
    isLoaded: false,
    columns: [],
    defaultValues: [],
}

/**
 * 类型和必填校验
 */
LocationPicker.propTypes = {
    defaultValues: PropType.array.isRequired,
    isLoaded: PropType.bool.isRequired
}

function LocationPicker(props) {

    //数据源
    const columns = props.columns;
    //是否必填
    const required = props.required;
    //数据是否已加载
    const isLoaded = props.isLoaded;
    //默认展示数据
    const defaultValues = props.defaultValues;
    //组件数据变化函数
    const onLineChange = props.onLineChange;


    const [showPopup, setShowPopup] = useState(false);
    const [lineValue, setLineValue] = useState('');

    const picker = useRef(null);


    /**
     * 选中值
     * @param val
     * @returns {*[]}
     */
    const handleSelected = (val) => {

        if (val.constructor !== Array) {
            console.error('选中数据格式错误,请处理')
        }
        let textList = []
        let codeList = []
        val.forEach((el) => {
            textList.push(el['text'])
            codeList.push(el['value'])
        })
        textList = _arrayFilterEmpty(textList);
        codeList = _arrayFilterEmpty(codeList);

        //展示数据
        setLineValue(textList.join('-'))
        //向父组件传递数据
        onLineChange(textList, codeList)

    }
    /**
     * 设置默认选中
     * @param defaultArr
     */
    const setDefaultValues = (defaultArr) => {
        picker && picker.current && picker.current.setValues(defaultArr)
    };

    /**
     * 弹出pop
     * @returns {Promise<void>}
     */
    const showPickerPop = async () => {
        await setShowPopup(true)
        setDefaultValues(defaultValues)
    }
    /**
     * 隐藏pop
     * @returns {Promise<void>}
     */
    const hidePickerPop = async () => {
        await setShowPopup(false)
    }

    /**
     * 监听默认值变化
     */
    useEffect(() => {
        setLineValue(defaultValues.join('-'))
    }, [defaultValues]);


    return (
        <div>
            <Cell title="地址" isLink size="large" value={lineValue} required={required} onClick={() =>
                showPickerPop()
            }/>

            <Popup
                visible={showPopup}
                position="bottom"
                style={{height: '45%'}}
                safeAreaInsetBottom
                onClose={() => setShowPopup(false)}
            >

                {!isLoaded && <Loading type="ball" className='content-loading' vertical>加载中...</Loading>}

                {
                    isLoaded && columns.length <= 0 &&
                    <Empty
                        className="custom-image"
                        image={emptyPic}
                        description="暂无数据"
                    />

                }
                {
                    isLoaded && columns.length > 0 &&
                    <Picker
                        columns={columns}
                        ref={picker}
                        onConfirm={async (val) => {
                            handleSelected(val);
                            await hidePickerPop();
                        }}
                        onCancel={async (val) => {
                            handleSelected([]);
                            await hidePickerPop();
                        }}

                    />
                }

            </Popup>

        </div>
    );
}


export default LocationPicker;

页面state 设置:

    //线路基础数据
    const [columns,setColumns] = useState([]);
    const [isLineLoaded, setLineLoaded] = useState(false);
    //当前选择的线路lineList  线路编码lineCodeList
    const [lineList, setLineList] = useState([]);
    const [lineCodeList, setLineCodeList] = useState([]);
    useEffect(() => {
        setTimeout(async () => {
           await setLineLoaded(true)
           await setColumns( [
                {
                    "text": '江苏',
                    "children": [
                        {
                            "text": '苏州',
                            "children": [
                                {
                                    "text": '姑苏区',
                                    value: "001"
                                },
                                {
                                    "text": '吴中区'
                                }
                            ]
                        },
                        {
                            "text": '扬州',
                            "children": [
                                {
                                    "text": '广陵区'
                                },
                                {
                                    "text": '邗江区'
                                }
                            ]
                        }
                    ]
                },
                {
                    "text": '浙江',
                    value: "002",
                    "children": [
                        {
                            "text": '杭州',
                            "children": [
                                {
                                    "text": '西湖区'
                                },
                                {
                                    "text": '余杭区'
                                }
                            ]
                        },
                        {
                            "text": '温州',
                            "children": [
                                "",
                                {
                                    "text": '鹿城区'
                                },
                                {
                                    "text": '瓯海区'
                                }
                            ]
                        }
                    ]
                }
            ])

        }, 5000)
    }, [])

组件引用

    <LocationPicker
                required
                columns={columns}
                defaultValues={lineList}
                isLoaded={isLineLoaded}
                onLineChange={(valArr, codeArr) => {
                    setLineList(valArr);
                    setLineCodeList(codeArr)
                }}/>

2. RadioList(单选)、SingleSelector (表单联动单选popup)

效果演示:





RadioList组件代码

import {Radio, Cell} from "react-vant";


RadioList.defaultProps = {
    isCancel: true,
    onChange: () => {

    }
}

function RadioList(props) {

    //list-数据源  isCancel-可取消  onChange-change函数  defaultValue-默认选中值
    const {list, isCancel, onChange, value} = props;

    /**
     * 处理选中--可取消
     */
    function handleSelected(selectedVal) {
        let val = value === selectedVal && isCancel ? '' : selectedVal;
        onChange(val);
    }

    return (
        <Radio.Group value={value}>
            <Cell.Group>
                {
                    list.map((_item, _idx, _arr) => {
                        return <Cell
                            title={_item.text}
                            key={_idx}
                            rightIcon={
                                <Radio name={_item.value}/>
                            }
                            onClick={() => {
                                handleSelected(_item.value)
                            }}
                        />
                    })
                }

            </Cell.Group>
        </Radio.Group>
    );
}

export default RadioList;

SingleSelector 组件代码

import React, {useEffect, useState} from 'react';
import {Cell, Empty, Loading, Radio, Popup} from "react-vant";
import emptyPic from '@/common/images/empty.png'
import RadioList from "../RadioList";

/**
 *  单选popup框
 */

SingleSelector.defaultProps = {
    isLoaded: false,
    list: [],
    defaultValue: '',
    label: '单元格',
}

function SingleSelector(props) {


    //isLoaded 异步数据是否已相应 list - 数据源头   onChange-回调 defaultValue 默认选中值
    const {label, required, isLoaded, list, onChange, value} = props;

    const [text, setText] = useState('');
    const [show, setShowPopup] = useState(false);

    useEffect(() => {

        initCell(value);

    }, [value])


    /**
     * 渲染cell
     * @param val
     */
    function initCell(val) {
        setText(getTextByVal(val));
    }

    function getTextByVal(val) {
        let obj = list.find((_item) => {
            return _item['value'] === val;
        })
        return obj ? obj['text'] : '';
    }

    function showPopup() {
        setShowPopup(true);
    }

    function hidePopup() {
        setShowPopup(false);
    }


    /**
     * 获取渲染结点
     * @returns {JSX.Element}
     */
    function getRender() {

        if (!isLoaded) {
            return <Loading type="ball" className='content-loading' vertical>加载中...</Loading>
        } else if (list.length === 0) {

            return <Empty className="custom-image" image={emptyPic} description="暂无数据"/>
        } else {

            return (
                <RadioList
                    list={list}
                    value={value}
                    onChange={(val) => {
                        const text = getTextByVal(val);
                        onChange(val, text);
                    }}/>
            )
        }
    }

    return (
        <div>

            <Cell title={label} isLink size="large" value={text} required={required} onClick={() =>
                showPopup()
            }/>

            <Popup
                visible={show}
                position="bottom"
                style={{height: '45%'}}
                safeAreaInsetBottom
                onClose={() => hidePopup()
                }
            >

                {getRender()}

            </Popup>
        </div>
    );
}

export default SingleSelector;

页面引用:

import React, {useState} from 'react';
import Header from "@/components/base/Header";
import SingleSelector from "@/components/base/SingleSelector";

function Index(props) {
    const headConfig = {
        title: '测试页'
    }
    //调度数据
    const [reportList, setReportList] = useState([{text: '南京', value: "00001"}, {
        text: '北京',
        value: "00002"
    }, {text: '上海', value: "00003"}]);
    const [isReportLoaded, setReportLoaded] = useState(false);
    const [selectedReport, setSelectedReport] = useState('00001');
    setTimeout(() => {
        setReportLoaded(true);
    }, 2000)
    return (
        <div className='page-container page-background'>
            <Header {...headConfig}>

            </Header>
            <SingleSelector
                label='城市'
                required
                isLoaded={isReportLoaded}
                list={reportList}
                value={selectedReport}
                onChange={(val, text) => {
                    console.log(val, text)
                    setSelectedReport(val);
                }}

            />
        </div>

    );
}

export default Index;


3. CheckboxList(多选)、MultipleSelector(表单联动多选Popup)

效果演示:



组件CheckboxList代码
import {Checkbox, Cell} from "react-vant";

CheckboxList.defaultProps = {
    defaultValue: [],
    list: [],
    onChange: () => {
    }
}

function CheckboxList(props) {


    //list - 数据源 defaultValue-默认选中值 onChange-父组件回调函数
    const {list, value, onChange} = props;

    /**
     * 选中 与 取消选中处理
     * @param name
     */
    const toggle = (name) => {
        const cellCheck = JSON.parse(JSON.stringify(value));
        const newValue = cellCheck.includes(name) ? cellCheck.filter((el) => el !== name) : [...cellCheck, name];
        onChange(newValue);
    };

    return (
        <Checkbox.Group value={value}>
            <Cell.Group>
                {
                    list.map((_item, _idx) => {
                        return <Cell
                            clickable
                            title={_item['text']}
                            key={_idx}
                            onClick={() => {
                                toggle(_item['value'])
                            }}
                            rightIcon={<Checkbox name={_item['value']}/>}
                        />
                    })
                }
            </Cell.Group>
        </Checkbox.Group>
    );

}

export default CheckboxList;

组件MultipleSelector代码

import React, {useEffect, useState} from 'react';
import {Cell, Empty, Loading, Radio, Popup} from "react-vant";
import emptyPic from '@/common/images/empty.png'
import CheckboxList from "../CheckboxList";

/**
 * 默认值
 */
MultipleSelector.defaultProps = {
    isLoaded: false,
    list: [],
    value: [],
    label: '单元格'
}

function MultipleSelector(props) {

    //isLoaded 异步数据是否已相应 list - 数据源头   onChange-回调 value 默认选中值
    const {label, required, isLoaded, list, onChange, value} = props;

    const [selectedText, setSelectedText] = useState('');
    const [show, setShowPopup] = useState(false);

    useEffect(() => {

        initCell(value);

    }, [value])


    /**
     * 渲染cell
     * @param val
     */
    function initCell(val) {
        setSelectedText(getTextByVal(val));
    }

    function getTextByVal(val) {

        let textList = [];
        list.forEach((_item) => {
            if (val.includes(_item['value'])) {
                textList.push(_item['text'])
            }
        })
        return textList.join(',')
    }

    function showPopup() {
        setShowPopup(true);
    }

    function hidePopup() {
        setShowPopup(false);
    }


    /**
     * 获取渲染结点
     * @returns {JSX.Element}
     */
    function getRender() {

        if (!isLoaded) {
            return <Loading type="ball" className='content-loading' vertical>加载中...</Loading>
        } else if (list.length === 0) {

            return <Empty className="custom-image" image={emptyPic} description="暂无数据"/>
        } else {

            return (
                <CheckboxList list={list}
                              value={value}
                              onChange={(val) => {
                                  initCell(val)
                                  onChange(val);

                              }}/>
            )
        }
    }

    return (
        <div>

            <Cell title={label} isLink size="large" value={selectedText} required={required} onClick={() =>
                showPopup()
            }/>


            <Popup
                visible={show}
                position="bottom"
                style={{height: '45%'}}
                safeAreaInsetBottom
                onClose={() => hidePopup()
                }
            >

                {getRender()}

            </Popup>
        </div>
    );
}

export default MultipleSelector;


页面引用:

import React, {useState} from 'react';
import Header from "@/components/base/Header";
import MultipleSelector from "@/components/base/MultipleSelector";

function Index(props) {
    const headConfig = {
        title: '测试页'
    }
    //调度数据
    const [majorList, setMajorList] = useState([{text: '计算机', value: "1"}, {text: '软件工程', value: "2"}
        , {text: '生物', value: "3"}
        , {text: '数学', value: "4"}
    ])
    const [isMajorLoaded, setMajorLoaded] = useState(false);
    const [selectedMajor, setSelectedMajor] = useState(['1']);
    setTimeout(() => {
        setMajorLoaded(true);
    }, 2000)
    return (
        <div className='page-container page-background'>
            <Header {...headConfig}>

            </Header>
            <MultipleSelector
                label='专业'
                required
                isLoaded={isMajorLoaded}
                list={majorList}
                value={selectedMajor}
                onChange={(val) => {
                    setSelectedMajor(val);

                }}
            />
        </div>

    );
}

export default Index;



4. DatePicker(日期)

效果演示:



组件DatePicker代码
import React, {useEffect, useState} from 'react';
import {Cell, Popup, DatetimePicker} from "react-vant";
import {formatDate} from "../../../common/js/utils";

/**
 *  单选popup框
 */

DatePicker.defaultProps = {
    value: formatDate(new Date()),
    dateType: 'datetime',
    label: '单元格',
    required: true,
    onChange: () => {
    }
}

function DatePicker(props) {

    // onChange-回调 value 选中值
    const {label, required, onChange, value, dateType} = props;

    const [text, setText] = useState('');
    const [show, setShow] = useState(false);

    useEffect(() => {

        initCell(value);

    }, [value])


    /**
     * 渲染cell
     */
    function initCell(val) {
        setText(val);
    }


    function showPopup() {
        setShow(true);
    }

    function hidePopup() {
        setShow(false);
    }


    return (
        <div>

            <Cell title={label} isLink size="large"
                  value={text} required={required} onClick={() =>
                showPopup()
            }/>


            <Popup
                visible={show}
                position="bottom"
                style={{height: '45%'}}
                safeAreaInsetBottom
                round

                onClose={() => hidePopup()
                }
            >

                <DatetimePicker
                    type={dateType}
                    minDate={new Date(2020, 0, 1)}
                    value={new Date(value)}
                    onCancel={() => {
                        onChange('');
                        hidePopup()
                    }}
                    onConfirm={(value) => {
                        onChange(formatDate(value));
                        hidePopup()
                    }}
                />

            </Popup>
        </div>
    );
}

export default DatePicker;

页面引用

import React, {useState} from 'react';
import Header from "@/components/base/Header";
import DatePicker from "@/components/base/DatePicker";
import {formatDate} from "@/common/js/utils";

function Index(props) {
    const headConfig = {
        title: '测试页'
    }
    //调度数据
    const [occurTime, setOccurTime] = useState(formatDate(new Date()))

    return (
        <div className='page-container page-background'>
            <Header {...headConfig}>

            </Header>
            <DatePicker
                label='故障发生时间'
                value={occurTime}
                onChange={(val) => {
                    setOccurTime(val);
                }}
            />
        </div>

    );
}

export default Index;

5. Header(标题栏)

效果演示:



组件Header 代码
import {NavBar} from 'react-vant';
import PropType from 'prop-types'
import {useNavigate} from "react-router-dom";


const Header = (props) => {

    const {title, showLeftBtn, leftText, rightText, leftClick, rightClick} = props;
    const navigate = useNavigate();

    return (
        <NavBar
            title={title}
            leftArrow={showLeftBtn}
            leftText={leftText}
            fixed
            placeholder
            safeAreaInsetTop
            rightText={rightText}
            onClickLeft={() => leftClick ? leftClick() : navigate(-1)}
            onClickRight={() => rightClick && rightClick()}
        />
    );

}
//props 类型 必填限制
Header.propTypes = {
    title: PropType.string.isRequired,
    leftClick: PropType.func,
    rightClick: PropType.func,
}

//props 默认值
Header.defaultProps = {
    title: '标题',    //导航标题
    showLeftBtn: true, //左侧按钮
    leftText: '',
    rightText: false,// false 不显示右侧按钮 , 其他ReactNode节点 , 则渲染
}

export default Header;

页面引用

import React, {useState} from 'react';
import Header from "@/components/base/Header";

import {Toast} from "react-vant";

function Index(props) {
    const headConfig = {
        title: '测试页',
        rightText: '筛选',
        rightClick:()=>{
            Toast('右侧按钮')
        }
    }

    return (
        <div className='page-container page-background'>
            <Header {...headConfig}>

            </Header>

        </div>

    );
}

export default Index;


总结

每天记录一点,从小小菜鸟变小菜鸟!!!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hui-1018

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值