当前位置: 首页 > 知识库问答 >
问题:

javascript - React 的弹框怎么实现(类 Antd 的 append 到 body)?

史英飙
2023-05-17

最近用react开发了个日期范围选择的自定义组件,效果图如下

image.png

页面滚动的时候就变成这样了

image.png

如何将自定义组件中的部分元素增加到body中?

我写的代码如下,请大神多多指教,最好附上同样功能的源码,谢谢!

import React, { useState, useEffect } from 'react'
import { Card, DatePicker, Input, message } from 'antd'
import { CalendarOutlined } from '@ant-design/icons'
import './index.less';

const { RangePicker } = DatePicker

export default function CustomDatePicker(props) {
    let tabIndex = 1;
    if (typeof props.tab !== 'undefined') {
        tabIndex = Number(props.tab)
    }

    const tabsList = [
        {
            desc: '昨天',
            val: 1,
            type: 'day',
        },
        {
            desc: '今天',
            val: 0,
            type: 'day',
        },
        {
            desc: '近7天',
            val: 7,
            type: 'near',
        },
        {
            desc: '近30天',
            val: 30,
            type: 'near',
        },
    ]

    const cycleList = [
        {
            desc: '自然日',
            type: 'day',
        },
        {
            desc: '自然周',
            type: 'week',
        },
        {
            desc: '自然月',
            type: 'month',
        },
        {
            desc: '自然年',
            type: 'year',
        },
        {
            desc: '自定义',
            type: 'custom',
        },
    ]

    const dateFormatStr = 'yyyy-MM-dd'

    const [tabCurrent, setTabCurrent] = useState(tabIndex)
    const [cycle, setCycle] = useState('day')
    const [cycleCurrent, setCycleCurrent] = useState(0)
    const [startDate, setStartDate] = useState()
    const [endDate, setEndDate] = useState()
    const [inputVal, setInputVal] = useState(dateFormat(new Date(), dateFormatStr))
    const [isShowSelect, setIsShowSelect] = useState(false)

    // 格式化时间
    function dateFormat(date, format) {
        if (!format) {
            format = dateFormatStr
        }
        date = new Date(date)
        var map = {
            M: date.getMonth() + 1, //月份
            d: date.getDate(), //日
            h: date.getHours(), //小时
            m: date.getMinutes(), //分
            s: date.getSeconds(), //秒
            q: Math.floor((date.getMonth() + 3) / 3), //季度
            S: date.getMilliseconds(),
            //毫秒
        }

        format = format.replace(/([yMdhmsqS])+/g, function (all, t) {
            var v = map[t]
            if (v !== undefined) {
                if (all.length > 1) {
                    v = '0' + v
                    v = v.substr(v.length - 2)
                }
                return v
            } else if (t === 'y') {
                return (date.getFullYear() + '').substr(4 - all.length)
            }
            return all
        })
        return format
    }

    useEffect(() => {
        if (tabCurrent > -1) {
            setDate(tabsList[tabCurrent])
        }
    }, [tabCurrent])

    const emitDate = (data) => {
        const { type, start, end } = data
        if (type === 'day') {
            setInputVal(start)
        } else {
            setInputVal(start + '/' + end)
        }
        setStartDate(start)
        setEndDate(end)
        setIsShowSelect(false)

        typeof props.onChange === 'function' && props.onChange(data)
    }

    const setDate = (data) => {
        const { val, type } = data
        const now = new Date().getTime()
        const day = 24 * 3600 * 1000

        setCycle(type)
        if (type === 'day') {
            const date = dateFormat(now - val * day, dateFormatStr)

            setCycleCurrent(0)
            emitDate({
                type: 'day',
                start: date,
                end: date,
            })
        } else {
            setCycleCurrent(4)
            emitDate({
                type: 'custom',
                start: dateFormat(now - (val + 1) * day, dateFormatStr),
                end: dateFormat(now - day, dateFormatStr),
            })
        }
    }

    const showDateSelect = () => {
        setIsShowSelect(!isShowSelect)
    }

    const clickTab = (index) => {
        if (tabCurrent !== index) {
            setTabCurrent(index)
        }
    }

    const TabsNode = () => {
        const list = tabsList.map((item, index) => {
            return (
                <span className={tabCurrent === index ? 'tab active' : 'tab'} key={index} onClick={() => clickTab(index)}>
                    {item.desc}
                </span>
            )
        })

        return (
            <div className="flex tabs">
                {list}
                <Input className="dateVal" readOnly suffix={<CalendarOutlined />} defaultValue={inputVal} onClick={() => showDateSelect()}></Input>
            </div>
        )
    }

    const changePicker = () => {
        const item = cycleList[cycleCurrent]
        const { type } = item

        if (type === 'custom') {
        } else {
        }
    }

    const CyClePicker = () => {
        const clickCyCle = (index) => {
            setCycleCurrent(index)
            changePicker()
        }

        const CycleView = () => {
            const list = cycleList.map((item, index) => {
                return (
                    <div key={index} className={cycleCurrent === index ? 'tab active' : 'tab'} onClick={() => clickCyCle(index)}>
                        {item.desc}
                    </div>
                )
            })

            return <div className="dateAreaTabs tabs">{list}</div>
        }

        const changeDate = (date, dateStr) => {
            const item = cycleList[cycleCurrent]
            let { type } = item

            setTabCurrent(-1)

            let start, end
            switch (type) {
                case 'day':
                    start = dateStr
                    end = dateStr
                    break
                case 'week':
                    start = dateFormat(date.startOf('week').valueOf())
                    end = dateFormat(date.endOf('week').valueOf())
                    break
                case 'month':
                    start = dateFormat(date.startOf('month').valueOf())
                    end = dateFormat(date.endOf('month').valueOf())
                    break
                case 'year':
                    start = dateFormat(date.startOf('year').valueOf())
                    end = dateFormat(date.endOf('year').valueOf())
                    break
                default:
                    start = dateStr[0]
                    end = dateStr[1]
                    if ((new Date(end).getTime() - new Date(start).getTime()) / 1000 / 3600 / 24 > 90) {
                        message.warning('最多选择90天')
                        return
                    }
                    if (start === end) {
                        type = 'day'
                    }
            }

            emitDate({
                type,
                start,
                end,
            })
        }

        const disabledDate = (current) => {
            const item = cycleList[cycleCurrent]
            const { type } = item
            switch (type) {
                case 'day':
                    return current && current.valueOf() > new Date().getTime()
                case 'week':
                    const now = new Date()
                    const day = now.getDay()
                    return current && current.valueOf() > new Date().getTime() - day * 24 * 3600 * 1000
                case 'month':
                    return current && current.endOf('month').valueOf() > new Date().getTime()
                case 'year':
                    return current && current.endOf('year').valueOf() > new Date().getTime()
                default:
                    return current && current.valueOf() > new Date().getTime() - 24 * 3600 * 1000
            }
        }

        const PickerView = () => {
            const item = cycleList[cycleCurrent]
            const { type } = item

            if (type === 'custom') {
                return (
                    <div className="picker" style={{ width: '550px' }}>
                        <RangePicker open style={{ opacity: 0 }} onChange={changeDate} disabledDate={disabledDate}></RangePicker>
                    </div>
                )
            } else {
                return (
                    <div className="picker">
                        <DatePicker open style={{ opacity: 0 }} picker={type} onChange={changeDate} disabledDate={disabledDate}></DatePicker>
                    </div>
                )
            }
        }

        if (isShowSelect) {
            return (
                <div
                    className="dateArea flex"
                    onClick={(e) => {
                        e.stopPropagation()
                    }}
                >
                    <CycleView />
                    <PickerView />
                </div>
            )
        } else {
            return <></>
        }
    }

    return (
        <Card className="customDate">
            <TabsNode />
            <CyClePicker />
        </Card>
    )
}

index.less

.customDate {
    background-color: #fcfcfc;
    position: relative;
    width: 720px;
    .flex {
        display: flex;
    }
    .tabs {
        align-items: center;
        .tab {
            width: 80px;
            height: 30px;
            line-height: 30px;
            text-align: center;
            border: 1px solid #e8eaec;
            border-radius: 15px;
            margin-right: 12px;
            box-sizing: border-box;
            cursor: pointer;
            &.active {
                color: #fff;
                background: #027aff;
                border: 1px solid transparent;
            }
        }
        .dateVal {
            width: 280px;
        }
    }
    .dateArea {
        height: 320px;
        overflow-y: hidden;
        position: absolute;
        top: 80px;
        right: 30px;
        z-index: 1;
        background: #fff;
        padding: 20px;
        &Tabs {
            text-align: center;
            margin-right: 20px;
            .tab {
                margin: 12px 0;
            }
        }
        .picker {
            width: 280px;
            position: relative;
            top: -30px;
        }
    }
}

共有1个答案

鲁城
2023-05-17

通过React的createPortal方法 就能实现了

 类似资料:
  • React Antd 的机制是什么鬼?输入框怎么没值? 为什么输入以后 再打印 navCreateName 是空?晕了什么情况

  • 本文向大家介绍javascript实现无法关闭的弹框,包括了javascript实现无法关闭的弹框的使用技巧和注意事项,需要的朋友参考一下 大家都见过某网页中的恶意广告,你关闭了又出来了!为何,JS来告诉你 HTML CSS JS 以上就是本文的全部内容,希望对大家有所帮助,同时也希望多多支持呐喊教程!

  • 像这种卡券的缺口请问如何实现,如果背景是纯色直接定位个纯色上去也就没啥问题,但这种背景渐变的显然不行,请巨佬贴个demo给我学习一下谢谢

  • 本文向大家介绍基于JavaScript实现弹出框效果,包括了基于JavaScript实现弹出框效果的使用技巧和注意事项,需要的朋友参考一下 弹出框在网站页面中是必不可少的一部分,今天借助呐喊教程平台给大家分享使用js实现简单的弹出框效果,本文写不好,还请见谅!   首先我们来分析弹出框的部件.简单弹出框分为头,内容,尾部. 头部中有标题和关闭按钮,内容就可以图文,媒体,iframe,flash等等

  • 本文向大家介绍怎么实现移动端的边框0.5px?相关面试题,主要包含被问及怎么实现移动端的边框0.5px?时的应答技巧和注意事项,需要的朋友参考一下 一种是通过transform中的scale 一种是通过meta viewport中设置init-scale为0.5 一种是设置hr 一种是基于背景渐变实现

  • 本文向大家介绍React+Antd+Redux实现待办事件的方法,包括了React+Antd+Redux实现待办事件的方法的使用技巧和注意事项,需要的朋友参考一下 之前也是写过一篇关于Redux的文章,来简单理解一下Redux,以及该如何使用。今天我就来分享一个也是入门级别的,React+Redux+antd来实现简单的待办事件。同时也讲讲自己对Redux的理解。先来看一张图吧: 我们简单的比喻来