/** * 注意:需要在父节点设置 position: relative; */ import { Text, View } from '@tarojs/components' import Taro from '@tarojs/taro' import type { Ref } from 'react' import React, { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react' import classNames from 'classnames' import Iconfont from '../iconfont/iconfont' import Popup from '../popup' import Cell from '../cell' import styles from './index.module.scss' // 弹窗选择向上弹窗还是向下弹窗 type Direction = 'up' | 'down' // 配置 菜单可选项 export interface DropDownOptions { text: string value: number } interface DropDownEvent { change?: (value: DropDownOptions['value']) => void // value 变化时触发 onCloseOverlay?: () => void } export interface DropDownItemRef { show: boolean closePopup: () => void showPopup: () => void } interface PropsType extends DropDownEvent { direction?: Direction options?: DropDownOptions[] title?: string value?: number | string // 当前选中的值 children?: React.ReactNode activeColor?: string showOverlay?: boolean customClassName?: string customStyle?: React.CSSProperties hasBottomBtn?: boolean // 底部有按钮不允许点击蒙层关闭 titleSlot?: React.ReactNode } const DropDownItem = (props: PropsType, ref: Ref) => { const { children, titleSlot, direction = 'down', title = '', value, options = [], change, activeColor, showOverlay = true, hasBottomBtn = false, customClassName, customStyle, onCloseOverlay } = props const [showPopup, setShowPopup] = useState(false) useImperativeHandle(ref, () => ({ show: showPopup, // 暴露出方法给有需要的用 closePopup() { setShowPopup(false) }, showPopup() { setShowPopup(true) }, }), [showPopup]) const handleClickOption = (value: DropDownOptions['value']) => { change?.(value) } const [text, setText] = useState(options?.[0]?.text || '') const defaultOptions = useMemo(() => { const currentValue = value return options?.map(({ text, value }, key) => { currentValue === value && setText(text) return handleClickOption(value)}> }) }, [value]) const getIconName = () => { if (direction === 'up') { return showPopup ? 'icon-zhankai1' : 'icon-shouqi1' } // down return showPopup ? 'icon-shouqi1' : 'icon-zhankai1' } const handleClickTitle = () => { console.log('handleClickTitle', showPopup) onCloseOverlay?.() setShowPopup(prev => !prev) } const handleClosePopup = () => { if (hasBottomBtn) { return } setShowPopup(false) onCloseOverlay?.() } const [overlayOffsetTop, setOverlayOffsetTop] = useState('unset') // 获取遮罩层的样式 const getOverlayStyle = (): React.CSSProperties => { return { position: !showOverlay ? 'fixed' : 'absolute', top: 0 } } // 获取整个页面的完整高度 const [scrollHeight, setScrollHeight] = useState(0) useEffect(() => { const query = Taro.createSelectorQuery() query.select('#DropDownItem').boundingClientRect() query.selectViewport().scrollOffset() query.exec((res) => { console.log('res==>', res) setScrollHeight(res[1].scrollHeight) if (direction === 'down') { setOverlayOffsetTop(`${res[0].bottom}px`) } else { setOverlayOffsetTop(`${res[0].top}px`) } }) }, []) // 获取样式 const getCustomStyle: React.CSSProperties = useMemo(() => { if (direction === 'up') { return { position: 'absolute', top: 0, height: overlayOffsetTop } } else { return { position: 'absolute', top: overlayOffsetTop, height: `calc(${`${scrollHeight}px`} - ${overlayOffsetTop})` } } }, [overlayOffsetTop]) return ( { titleSlot || <> {title || text} } {/* showPopup 为true才显示children 是因为真机环境下 children内的字体有穿透的情况 */} {showPopup && (children || {defaultOptions})} ) } export default forwardRef(DropDownItem)