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

View File

@ -1,4 +1,6 @@
// 关于登录
export { LoginApi } from './login/index'
export {
productabsorbcontrast,
SelectProductListApi,
@ -9,8 +11,10 @@ export {
FindColorListApi,
} from './product/index'
// 关于购物页面
export { ShoppingCartUpdateApi, ShoppingCartDeleteApi, ShoppingCartListApi } from './shopping/index'
// 关于发货列表
export {
DeliverNoticeOrderList,
DeliverNoticeOrder,
@ -21,6 +25,7 @@ export {
EnumSaleorderStatus,
} from './delivery/index'
// 关于提货列表
export {
EnumTakeGoodsOrderStatus,
EnumTakeGoodsOrderTypeList,
@ -32,6 +37,9 @@ export {
GenBarCodeOrQrCode,
} from './takeDelivery/index'
// 关于销售统计
export { EnumMarketingDepartmentApi, EnumSalesTypeApi, SalesmanRankApi, ProductRankApi, PurchaserRankApi, SupplierRankApi, CensusApi } from './statistic/index'
import { useRequest } from '@/use/useHttp'
/**
*

23
src/api/statistic/enum.ts Normal file
View File

@ -0,0 +1,23 @@
import { useRequest } from "@/use/useHttp"
/**
*
* @returns
*/
export const EnumSalesTypeApi = () => {
return useRequest({
url: `/v1/mp/enum/saleType`,
method: 'get',
})
}
/**
*
* @returns
*/
export const EnumMarketingDepartmentApi = () => {
return useRequest({
url: `/v1/mp/enum/saleDepartment/list`,
method: "get"
})
}

View File

@ -0,0 +1,2 @@
export { EnumSalesTypeApi, EnumMarketingDepartmentApi } from './enum'
export { CensusApi, ProductRankApi, PurchaserRankApi, SupplierRankApi, SalesmanRankApi } from './saleStatistic'

View File

@ -0,0 +1,56 @@
import { useRequest } from '@/use/useHttp'
/**
*
* @returns
*/
export const CensusApi = () => {
return useRequest({
url: `/v1/mp/saleOrderDataForm`,
method: "get"
})
}
/**
*
* @returns
*/
export const ProductRankApi = () => {
return useRequest({
url: `/v1/mp/saleOrderDataForm/product/list`,
method: 'get',
})
}
/**
*
* @returns
*/
export const PurchaserRankApi = () => {
return useRequest({
url: `/v1/mp/saleOrderDataForm/purchaser/list`,
method: 'get',
})
}
/**
*
* @returns
*/
export const SupplierRankApi = () => {
return useRequest({
url: `/v1/mp/saleOrderDataForm/supplier/list`,
method: 'get',
})
}
/**
*
* @returns
*/
export const SalesmanRankApi = () => {
return useRequest({
url: `/v1/mp/saleOrderDataForm/saleUser/list`,
method: 'get',
})
}

View File

@ -1,4 +1,4 @@
export const BASE_URL = CURRENT_BASE_URL
// export const BASE_URL = CURRENT_BASE_URL
// export const BASE_URL = `http://192.168.0.75:50001/lymarket`
// export const BASE_URL = `http://192.168.0.89:50001/lymarket`
// export const BASE_URL = `http://10.0.0.5:50001/lymarket`
@ -12,7 +12,7 @@ export const BASE_URL = CURRENT_BASE_URL
// export const BASE_URL = `https://dev.zzfzyc.com/lymarket` // 开发环境
// export const BASE_URL = `https://www.zzfzyc.com/lymarket` // 正式环境
// export const BASE_URL = `http://192.168.1.5:40001/lymarket` // 王霞
// export const BASE_URL = `http://192.168.1.7:50002/lymarket` // 添
export const BASE_URL = `http://192.168.1.7:50002/lymarket` // 添
// export const BASE_URL = `http://192.168.1.42:50001/lymarket` // 杰
// export const BASE_URL = `http://192.168.1.95:40001/lymarket` // 华

View File

@ -0,0 +1,6 @@
.grid {
padding: 24px 40px;
display: grid;
grid-gap: 24px 24px;
grid-template-columns: 1fr 1fr;
}

View File

@ -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

View File

@ -0,0 +1,6 @@
.grid {
padding: 24px 40px;
display: grid;
grid-gap: 24px 24px;
grid-template-columns: 1fr 1fr;
}

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

View File

@ -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;
}

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

View File

@ -10,9 +10,14 @@
justify-content: center;
align-items: center;
padding: 24px 0;
color: #8c8c8c;
font-size: 28px;
&--text {
display: block;
margin-right: 12px;
}
}
&Options {
padding: 24px 40px;
}
}

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'
// 配置 菜单可选项
@ -19,32 +19,32 @@ interface 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>
)
currentValue === value && setText(text)
return <Cell title={text} desc='' isLink onClick={() => handleClickOption(value)}></Cell>
})
}, [value])
@ -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>
)

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;

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>}

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>
</>
)
})

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;
}

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>
)
})

View File

@ -13,7 +13,7 @@ import ItemList from './components/ItemList'
import DeliveryStatusList from './components/DeliveryStatusList'
import Popup from '@/components/popup'
import DeliveryFilter, { SearchField } from './components/Filter'
import TimePicker from '@/components/timePicker'
import TimePickerPopup from '@/components/timePickerPopup'
type SearchData = {
delivery_notice_order_no?: string // 发货单号
@ -196,7 +196,7 @@ const Delivery: FC = () => {
<Popup show={showFilter} title='筛选列表' onClose={handlePopupClose}>
<DeliveryFilter onSearchTime={handleSearchTime} onConfirm={handleConfirm} onReset={handleReset} />
</Popup>
<TimePicker start={start} end={end} showTime={showTime} closePopup={handClose} onSelectDate={(e) => handTime(e)}></TimePicker>
<TimePickerPopup start={start} end={end} showTime={showTime} closePopup={handClose} onSelectDate={(e) => handTime(e)}></TimePickerPopup>
</View>
)
}

View File

@ -12,7 +12,7 @@ import { formatDateTime, formatHashTag, formatImgUrl, formatPriceDiv, formatWeig
import {
mpcashManagementOrderlist
} from "@/api/newCollection"
import TimePicker from "@/components/timePicker"
import TimePickerPopup from '@/components/timePickerPopup'
import dayjs from 'dayjs'
import IconFont from '@/components/iconfont/iconfont'
@ -294,13 +294,13 @@ export default () => {
</View>
</View>
</Popup>
<TimePicker
<TimePickerPopup
start={start}
end={end}
showTime={showTime}
closePopup={handClose}
onSelectDate={(e) => handTime(e)}
></TimePicker>
></TimePickerPopup>
</>
)
}

View File

@ -15,7 +15,7 @@ import {
mpenumreturnType
} from "@/api/refound"
import Tabs from "./components/tabs"
import TimePicker from "@/components/timePicker"
import TimePickerPopup from '@/components/timePickerPopup'
import dayjs from 'dayjs'
import IconFont from '@/components/iconfont/iconfont'
export default () => {
@ -395,13 +395,13 @@ export default () => {
{/* <View className={styles.areaBox}></View> */}
</View>
</Popup>
<TimePicker
<TimePickerPopup
start={start}
end={end}
showTime={showTime}
closePopup={handClose}
onSelectDate={(e) => handTime(e)}
></TimePicker>
></TimePickerPopup>
</>
)
}

View File

@ -1,5 +1,12 @@
import AtCalendar from '@/components/calendar'
import DropDownItem, { DropDownOptions } from '@/components/dropDown-item'
import SelectMarketingDepartment from '@/components/SelectMarketingDepartment'
import SelectSaleType from '@/components/SelectSaleType'
import SelectTimePicker from '@/components/SelectTimePicker'
import TimePicker from '@/components/timePicker'
import { View } from '@tarojs/components'
import classnames from 'classnames'
import dayjs from 'dayjs'
import { useState } from 'react'
import styles from './index.module.scss'
@ -17,13 +24,30 @@ const saleStatistic = () => {
text: 'name2',
value: 2,
},
{
text: 'name3',
value: 3,
},
])
const onChangeTimePicker = (value) => {
console.log(value)
}
const onChangeSaleType = (saleType) => {
console.log(saleType)
}
const onChangeDepartment = (department) => {
console.log(department)
}
return (
<View className={styles.saleStatistic}>
<View className={styles['saleStatistic--filterBar']}>
<View>sdflkajsfdlk</View>
<DropDownItem title='name' activeColor='#337fff' options={options}></DropDownItem>
<SelectSaleType onChange={onChangeSaleType}></SelectSaleType>
<SelectMarketingDepartment onChange={onChangeDepartment}></SelectMarketingDepartment>
<SelectTimePicker onChange={onChangeTimePicker}></SelectTimePicker>
</View>
</View>
)

View File

@ -13,7 +13,7 @@ import ItemList from './components/ItemList'
import DeliveryStatusList from './components/DeliveryStatusList'
import Popup from '@/components/popup'
import DeliveryFilter, { SearchField } from './components/Filter'
import TimePicker from '@/components/timePicker'
import TimePickerPopup from '@/components/timePickerPopup'
type SearchData = {
take_goods_order_no?: string // 提货单号
@ -191,7 +191,7 @@ const Delivery: FC = () => {
<Popup show={showFilter} title='筛选列表' onClose={handlePopupClose}>
<DeliveryFilter onSearchTime={handleSearchTime} onConfirm={handleConfirm} onReset={handleReset} />
</Popup>
<TimePicker start={start} end={end} showTime={showTime} closePopup={handClose} onSelectDate={(e) => handTime(e)}></TimePicker>
<TimePickerPopup start={start} end={end} showTime={showTime} closePopup={handClose} onSelectDate={(e) => handTime(e)}></TimePickerPopup>
</View>
)
}