✨ feat(销售统计): 时间选择器组件

This commit is contained in:
Haiyi
2022-09-30 10:00:08 +08:00
parent 82c87b75d9
commit b878c5ea9d
23 changed files with 526 additions and 51 deletions
@@ -0,0 +1,6 @@
.grid {
padding: 24px 40px;
display: grid;
grid-gap: 24px 24px;
grid-template-columns: 1fr 1fr;
}
@@ -0,0 +1,68 @@
import { FC, useEffect, useMemo, useState } from 'react'
import DropDownItem from '../dropDown-item'
import FilterButton from '../filterButton'
import { EnumMarketingDepartmentApi } from '@/api/index'
import { View } from '@tarojs/components'
import styles from './index.module.scss'
type ChangedValue = string | number
interface SelectSaleTypeProps {
onChange?: (value: ChangedValue) => void
}
type EnumList = {
id: number
code: string
name: string
}
// 营销部门
const SelectSaleType: FC<SelectSaleTypeProps> = props => {
const selectName = '营销部门'
const { onChange } = props
console.log(props)
const { fetchData } = EnumMarketingDepartmentApi()
const getData = async () => {
const res = await fetchData()
setEnumList([{ id: -1, code: '', name: '全部' }, ...res.data.list])
}
const [enumList, setEnumList] = useState<EnumList[]>([])
useEffect(() => {
getData()
}, [])
const [currentValue, setCurrentValue] = useState<ChangedValue>(-1)
const handleClick = (value: ChangedValue) => {
setCurrentValue(value)
onChange?.(value)
}
const displayTitle = useMemo(() => {
if (currentValue === -1) {
return selectName
}
return !!enumList.length ? enumList.filter(option => option.id === currentValue)?.[0]?.name : selectName
}, [enumList, currentValue])
return (
<DropDownItem title={displayTitle} value={currentValue} activeColor='#337fff'>
<View className={styles.grid}>
{!!enumList.length &&
enumList.map((item: EnumList) => {
return (
<FilterButton isActive={item.id === currentValue} onClick={() => handleClick(item.id)}>
{item.name}
</FilterButton>
)
})}
</View>
</DropDownItem>
)
}
export default SelectSaleType
@@ -0,0 +1,6 @@
.grid {
padding: 24px 40px;
display: grid;
grid-gap: 24px 24px;
grid-template-columns: 1fr 1fr;
}
+67
View File
@@ -0,0 +1,67 @@
import { FC, useEffect, useMemo, useState } from 'react'
import DropDownItem from '../dropDown-item'
import FilterButton from '../filterButton'
import { EnumSalesTypeApi } from '@/api/index'
import { View } from '@tarojs/components'
import styles from './index.module.scss'
type ChangedValue = string | number
interface SelectSaleTypeProps {
onChange?: (value: ChangedValue) => void
defaultValue?: string
}
type EnumList = {
id: number
code: string
name: string
}
// 销售类型
const SelectSaleType: FC<SelectSaleTypeProps> = props => {
const selectName = '销售类型'
const { onChange } = props
console.log(props)
const { fetchData } = EnumSalesTypeApi()
const getData = async () => {
const res = await fetchData()
setEnumList([{ id: -1, code: '', name: '全部' }, ...res.data.list])
}
const [enumList, setEnumList] = useState<EnumList[]>([])
useEffect(() => {
getData()
}, [])
const [currentValue, setCurrentValue] = useState<ChangedValue>(-1)
const handleClick = (value: ChangedValue) => {
setCurrentValue(value)
onChange?.(value)
}
const displayTitle = useMemo(() => {
if (currentValue === -1) {
return selectName
}
return !!enumList.length ? enumList.filter(option => option.id === currentValue)?.[0]?.name : selectName
}, [enumList, currentValue])
return (
<DropDownItem title={displayTitle} value={currentValue} activeColor='#337fff'>
<View className={styles.grid}>
{!!enumList.length &&
enumList.map((item: EnumList) => {
return (
<FilterButton isActive={item.id === currentValue} onClick={() => handleClick(item.id)}>
{item.name}
</FilterButton>
)
})}
</View>
</DropDownItem>
)
}
export default SelectSaleType
@@ -0,0 +1,13 @@
.grid {
padding: 24px 40px;
display: grid;
grid-gap: 24px 24px;
grid-template-columns: 1fr 1fr 1fr;
}
.customFilterTime{
grid-column-start: span 3;
display: flex;
flex-flow: row nowrap;
justify-content: center;
align-items: center;
}
+113
View File
@@ -0,0 +1,113 @@
import { View, Text } from '@tarojs/components'
import dayjs from 'dayjs'
import { FC, useState } from 'react'
import DropDownItem from '../dropDown-item'
import FilterButton from '../filterButton'
import IconFont from '../iconfont/iconfont'
import TimePicker from '../timePicker'
import styles from './index.module.scss'
type ChangedValue = string | number
interface SelectSaleTypeProps {
onChange?: (value: ChangedValue) => void
}
const FilterTimeOptions = {
'0': {
name: '全部',
date_min: undefined,
date_max: undefined,
},
'1': {
name: '今天',
date_min: `${dayjs(new Date())
.add(0, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
date_max: `${dayjs(new Date())
.add(1, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
},
'2': {
name: '昨日',
date_min: `${dayjs(new Date())
.add(-1, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
date_max: `${dayjs(new Date())
.add(0, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
},
'3': {
name: '近7日',
date_min: `${dayjs(new Date())
.add(-7, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
date_max: `${dayjs(new Date())
.add(0, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
},
'4': {
name: '近30日',
date_min: `${dayjs(new Date())
.add(-30, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
date_max: `${dayjs(new Date())
.add(0, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
},
'5': {
name: '近90日',
date_min: `${dayjs(new Date())
.add(-90, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
date_max: `${dayjs(new Date())
.add(0, 'day')
.format('YYYY-MM-DD')} 00:00:00`,
},
} as const
type Key = keyof typeof FilterTimeOptions
type Value = (typeof FilterTimeOptions)[Key]
const SelectTimePicker: FC<SelectSaleTypeProps> = props => {
const { onChange: change } = props
const [currentValue, setCurrentValue] = useState('0')
const [currentDate, setCurrentDate] = useState({
start: new Date(),
end: new Date(),
})
const onSelectDate = time => {
console.log(time)
change?.(time)
}
const handleClick = (key: Key) => {
setCurrentValue(key)
}
return (
<DropDownItem title='查询日期' direction='down' value={currentValue} activeColor='#337fff'>
<View className={styles.grid} style={{ paddingBottom: '24rpx' }}>
{Object.entries(FilterTimeOptions).map(([key, value]) => {
return (
<FilterButton isActive={key === currentValue} onClick={() => handleClick(key as Key)}>
{value.name}
</FilterButton>
)
})}
<FilterButton customClassName={styles.customFilterTime} isActive={'6' === currentValue} onClick={() => handleClick('6' as Key)}>
<Text>自定义时间</Text>
<IconFont name='icon-chakanquanbukehu' color={'6' === currentValue ? '#3983ff' : '#7f7f7f'}></IconFont>
</FilterButton>
</View>
<View style={{ paddingBottom: '24rpx' }}>
<TimePicker start={currentDate.start} end={currentDate.end} onSelectDate={onSelectDate}></TimePicker>
</View>
</DropDownItem>
)
}
export default SelectTimePicker
@@ -1,7 +1,7 @@
.dropDownItem{
.dropDownItem {
width: 100%;
&--title{
&--title {
position: relative;
z-index: 2000;
background-color: white;
@@ -10,9 +10,14 @@
justify-content: center;
align-items: center;
padding: 24px 0;
&--text{
color: #8c8c8c;
font-size: 28px;
&--text {
display: block;
margin-right: 12px;
}
}
&Options {
padding: 24px 40px;
}
}
+60 -25
View File
@@ -1,10 +1,10 @@
import { View, Text } from '@tarojs/components'
import { useCallback, useEffect, useMemo, useRef, useState, memo } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import styles from './index.module.scss'
import classnames from 'classnames'
import Iconfont, { IconNames } from '../iconfont/iconfont'
import Iconfont from '../iconfont/iconfont'
import Popup from '../popup'
import FilterButton from '../filterButton'
import Cell from '../cell'
import Taro from '@tarojs/taro'
// 弹窗选择向上弹窗还是向下弹窗
type Direction = 'up' | 'down'
// 配置 菜单可选项
@@ -14,42 +14,42 @@ export type DropDownOptions = {
}
interface DropDownEvent {
change?: (value: DropDownOptions['value']) => void // value 变化时触发
change?: (value: DropDownOptions['value']) => void // value 变化时触发
}
interface PropsType extends DropDownEvent{
interface PropsType extends DropDownEvent {
direction?: Direction
title: string // 已选中的菜单标题
options?: DropDownOptions[]
value?: number | string // 当前选中的值
title?: string
value: number | string // 当前选中的值
children?: React.ReactNode
activeColor?: string
showOverlay?: boolean
customClassName?: string
customStyle?: React.CSSProperties
}
export default (props: PropsType) => {
const { children, direction = 'down', title, value, options, change, activeColor } = props
const { children, direction = 'down', title = '', value, options = [], change, activeColor, showOverlay = true } = props
const [showPopup, setShowPopup] = useState(false)
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})=>{
return (
<FilterButton isActive={currentValue === value} onClick={() => handleClickOption(value)}>
{text}
</FilterButton>
)
return options?.map(({ text, value }) => {
currentValue === value && setText(text)
return <Cell title={text} desc='' isLink onClick={() => handleClickOption(value)}></Cell>
})
}, [value])
const getIconName = () => {
if(direction === 'up'){
if (direction === 'up') {
return showPopup ? 'icon-zhankai1' : 'icon-shouqi1'
}
// down
@@ -64,23 +64,58 @@ export default (props: PropsType) => {
setShowPopup(false)
}
const [overlayOffsetTop, setOverlayOffsetTop] = useState('unset')
// 获取遮罩层的样式
const getOverlayStyle = (): React.CSSProperties => {
return { position: '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 (
<View className={styles.dropDownItem}>
<View className={styles.dropDownItem} id='DropDownItem'>
<View className={styles['dropDownItem--title']} onClick={handleClickTitle}>
<Text className={styles['dropDownItem--title--text']} style={{ color: activeColor }}>
{title}
<Text className={styles['dropDownItem--title--text']} style={showPopup ? { color: activeColor } : {}}>
{title ? title : text}
</Text>
<Iconfont name={getIconName()} size={20} color={value !== options![0].value ? activeColor : '#333'}></Iconfont>
<Iconfont name={getIconName()} size={20} color={showPopup ? activeColor : '#7f7f7f'}></Iconfont>
</View>
<Popup
onClose={handleClosePopup}
showOverLay={showOverlay}
show={showPopup}
showTitle={false}
safeAreaInsetBottom={false}
customStyle={{ position: 'absolute', top: 'unset' }}
overlayStyle={{ position: 'absolute', top: 'unset' }}
customStyle={getCustomStyle}
overlayStyle={getOverlayStyle()}
position={direction === 'down' ? 'top' : 'bottom'}>
{children ? children : defaultOptions}
{children ? children : <View className={styles.dropDownItemOptions}>{defaultOptions}</View>}
</Popup>
</View>
)
+4 -1
View File
@@ -15,6 +15,9 @@ $am-ms: 200ms;
&_active {
opacity: 1;
}
&--hidden {
background: transparent;
}
}
.drawer_main {
@@ -32,7 +35,7 @@ $am-ms: 200ms;
z-index: 1000;
visibility: hidden;
transition: visibility $am-ms ease-in-out;
overflow: hidden;
.drawer_container {
display: flex;
flex-direction: column;
+7 -2
View File
@@ -22,6 +22,7 @@ export interface Params extends PopupEvent {
customStyle?: React.CSSProperties
overlayStyle?: React.CSSProperties
safeAreaInsetBottom?: boolean // 是否为iphoneX提供小黑条适配
showOverLay?: boolean // 是否显示遮罩层
}
export default memo(
({
@@ -36,6 +37,7 @@ export default memo(
animationEnd,
customStyle,
safeAreaInsetBottom = true,
showOverLay = true,
overlayStyle,
}: Params) => {
const animationTime = useRef<any>(null)
@@ -60,9 +62,12 @@ export default memo(
<>
<View className={style.drawer_main}>
<View catchMove={true} className={classnames(style.drawer, show ? style.drawer_active : '')} style={customStyle}>
<View className={classnames(style.drawer_mask, { [style.drawer_mask_active]: show })} onClick={onClose} style={overlayStyle}></View>
<View
style={{ position: `${isFixed == true ? 'fixed' : 'absolute'}`, }}
className={classnames(style.drawer_mask, { [style.drawer_mask_active]: show, [style['drawer_mask--hidden']]: !showOverLay })}
onClick={onClose}
style={overlayStyle}></View>
<View
style={{ position: `${isFixed == true ? 'fixed' : 'absolute'}` }}
className={classnames(style.drawer_container, style['drawer_container_' + position], { [style.drawer_container_active]: show })}
onClick={e => e.stopPropagation()}>
{showTitle && <View className={style.drawer_container_title}>{title}</View>}
+3 -5
View File
@@ -7,15 +7,13 @@ import dayjs from 'dayjs'
type DateArg = string | number | Date
interface Props {
showTime: boolean
closePopup?: () => void
end?: DateArg
start?: DateArg
onSelectDate?: (any) => void
}
export default memo((props: Props) => {
let { showTime = false, closePopup, start = '', end = '', onSelectDate } = props
let { start = '', end = '', onSelectDate } = props
const [time, setTime] = useState<any>({})
const handTime = (e) => {
@@ -33,7 +31,7 @@ export default memo((props: Props) => {
return (
<Popup title={'选择时间'} show={showTime} onClose={() => closePopup?.()}>
<>
<View className='time-box'>
<AtCalendar
isMultiSelect
@@ -48,6 +46,6 @@ export default memo((props: Props) => {
<View className='sure-box' onClick={() => onSelectDate?.(time)}>
确认
</View>
</Popup>
</>
)
})
+19
View File
@@ -0,0 +1,19 @@
.time-box {
padding: 40px;
}
.sure-box {
// padding: 16px 102px 30px 102px;
margin-left: 102px;
margin-right: 102px;
height: 80px;
background: #337FFF;
border-radius: 44px;
font-size: 28px;
font-weight: 500;
color: #FFFFFF;
text-align: center;
line-height: 80px;
}
+24
View File
@@ -0,0 +1,24 @@
import Popup from '@/components/popup'
import { memo } from 'react'
import './index.scss'
import TimePicker from '../timePicker'
type DateArg = string | number | Date
interface Props {
showTime: boolean
closePopup?: () => void
end?: DateArg
start?: DateArg
onSelectDate?: (any) => void
}
export default memo((props: Props) => {
let { showTime = false, closePopup, start = '', end = '', onSelectDate } = props
return (
<Popup title={'选择时间'} show={showTime} onClose={() => closePopup?.()}>
<TimePicker start={start} end={end} onSelectDate={onSelectDate}></TimePicker>
</Popup>
)
})