289 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Search from '@/components/search'
import { View } from '@tarojs/components'
import Taro, { useDidShow } from '@tarojs/taro'
import { FC, memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useTransition } from 'react'
import styles from './index.module.scss'
import classnames from 'classnames'
import IconText from '@/components/iconText'
import InfiniteScroll from '@/components/infiniteScroll'
import ItemList from './components/shoppingCartItem/index'
import { formatPriceDiv } from '@/common/format'
import BottomSettleBar from './components/bottomSettleBar'
import BottomEditBar from './components/bottomEditBar'
import { ShoppingCartDeleteApi, ShoppingCartListApi } from '@/api/index'
import { dataLoadingStatus, debounce, getFilterData } from '@/common/util'
import { ShoppingProvider } from './components/shoppingCart/index'
import { Goods, ShoppingDispatchType, useShoppingDispatch, useShoppingState } from './context'
import { alert, goLink, isEmptyObject } from '@/common/common'
import LoadingCard from '@/components/loadingCard'
export const Shopping: FC = memo(() => {
// 计算总的预估金额
const handleTriggerCheckbox = ({ colorStore, currentCheckedPurchaserId, setSelectedAmount }) => {
const targetGoodsKind = colorStore?.[currentCheckedPurchaserId]?.['goodsKind']
console.log('handleTriggerCheckbox==>', colorStore)
if (targetGoodsKind) {
const result = Object.values(targetGoodsKind).reduce((prev: number, value: Goods) => {
if (value.checked) {
return prev + Number(formatPriceDiv(value.estimate_amount))
}
return prev
}, 0) as number
console.log('result==>', result)
// 同步修改上下文的 预估金额
setSelectedAmount(result)
} else {
setSelectedAmount(0)
}
}
return (
<ShoppingProvider onTriggerCheckbox={handleTriggerCheckbox}>
<ShoppingCartContainer />
</ShoppingProvider>
)
})
interface InternalContainer {}
const ShoppingCartContainer: FC<InternalContainer> = () => {
let isFirst = useRef(true)
const { isManageStatus, selectedAmount, currentCheckedPurchaserId, currentCheckedSaleMode, colorStore } = useShoppingState()
const dispatch = useShoppingDispatch()
// 管理
const onStartToManage = () => {
dispatch({ type: ShoppingDispatchType.UPDATE_MANAGE_STATUS, data: !isManageStatus })
}
const listHeightRef = useRef('auto')
const { fetchData, state } = ShoppingCartListApi()
const [searchOptions, setSearchOptions] = useState({
short_name_or_phone: '',
})
useDidShow(() => {
if (!isFirst.current) {
fetchData(searchOptions)
}
})
console.log('Rerender component: ShoppingCartContainer')
useEffect(() => {
console.log('useEffect fetchData')
if (!isEmptyObject(getFilterData(searchOptions))) {
fetchData(searchOptions)
}
}, [searchOptions])
// 输入了搜索关键字
const getSearchData = useCallback(e => {
setSearchOptions(prev => ({ ...prev, short_name_or_phone: e }))
}, [])
const [shoppingCartData, setShoppingCartData] = useState<{ list: ShoppingCartData[]; total: number }>({ list: [], total: 0 })
//数据加载状态
const statusMore = useMemo(() => {
const status = dataLoadingStatus({ list: shoppingCartData.list, total: shoppingCartData.total, status: state.loading })
console.log('status==>', status)
return status
}, [shoppingCartData, state])
// useLayoutEffect 执行在DOM更新之后浏览器绘制之前 如果放在 useEffect 里面会产生多一次不必要的回流和重绘,可能会引起视图闪现
useLayoutEffect(() => {
;(async () => {
console.log('useLayoutEffect')
await fetchData()
isFirst.current = false
let query = Taro.createSelectorQuery()
console.log('query', query)
query.select('#shoppingContainer').boundingClientRect()
query.select('#topBar').boundingClientRect()
query.select('#bottomBar').boundingClientRect()
query.exec(res => {
console.log('res==>', res)
const containerHeight = res[0].height
const topBarHeight = res[1].height
const bottomBarHeight = res[2].height
const listHeight = containerHeight - topBarHeight - bottomBarHeight
listHeightRef.current = listHeight + 'px'
})
})()
}, [])
const [isPending, startTransition] = useTransition()
useEffect(() => {
startTransition(() => {
setShoppingCartData({ list: state.data, total: state.data.length })
})
}, [state])
// 结算
const handleSettleAccount = debounce(() => {
const targetGoodsKind = colorStore?.[currentCheckedPurchaserId]?.['goodsKind']
if (!targetGoodsKind) return Taro.showToast({ title: '请先选择客户', icon: 'error' })
const checkedGoodsKind = Object.values(targetGoodsKind).reduce((prev, item: Goods) => {
if (item.checked) {
return [...prev, item.id]
}
return prev
}, [])
if (checkedGoodsKind.length === 0) return Taro.showToast({ title: '请先选择商品', icon: 'error' })
goLink('/pages/submitOrder/index', {
purchaser_id: currentCheckedPurchaserId,
sale_mode: currentCheckedSaleMode,
shopping_cart_product_color_list: JSON.stringify(checkedGoodsKind),
purchaser_name: shoppingCartData?.list.find(item => item.purchaser_id === currentCheckedPurchaserId)?.purchaser_name,
})
}, 400)
const { fetchData: deleteApi } = ShoppingCartDeleteApi()
// 批量某个客户的删除商品
const handleDelete = async () => {
const targetGoodsKind = colorStore?.[currentCheckedPurchaserId]?.['goodsKind']
let checked: Goods[] = []
if (targetGoodsKind) {
checked = Object.values(targetGoodsKind).filter((item: Goods) => item.checked)
if (checked.length === 0) {
return Taro.showToast({ title: '请选择商品', icon: 'error' })
}
}
console.log('checked==>', checked)
Taro.showModal({
title: '要删除这些商品吗?',
success: async function(res) {
if (res.confirm) {
const res = await deleteApi({
ids: checked.map(item => item.id).join(','),
})
if (res.success) {
alert.success('删除成功')
fetchData(searchOptions)
} else {
alert.none(res.msg)
}
}
},
})
}
// 全选
const handleSelectAllCheckbox = (isSelectAll: boolean) => {
console.log('handleSelectAllCheckbox', isSelectAll)
const targetGoodsKind = colorStore?.[currentCheckedPurchaserId]?.['goodsKind']
if (!targetGoodsKind) return Taro.showToast({ title: '请先选择客户', icon: 'error' })
console.log('targetGoodsKind==>', targetGoodsKind)
const tempObject = {}
Object.entries(targetGoodsKind).forEach(([key, value]) => {
tempObject[key] = {
...value,
checked: isSelectAll,
}
})
console.log('tempObject==>', tempObject)
dispatch({
type: ShoppingDispatchType.UPDATE_CHANGED_CHECKBOX,
data: {
purchaserId: currentCheckedPurchaserId,
goodsKind: tempObject,
},
})
// setChangedCheckbox({
// purchaserId: currentCheckedPurchaserId,
// goodsKind: tempObject,
// })
}
// 加载刷新数据
const [refreshStatus, setRefreshStatus] = useState(false)
// 下拉刷新
const handleRefresh = async () => {
setRefreshStatus(true)
const res = await fetchData(searchOptions)
if (res.success) {
console.log('请求 成功')
setRefreshStatus(false)
Taro.showToast({ title: '刷新成功', icon: 'success' })
} else {
console.log('请求 失败')
Taro.showToast({ title: '刷新失败', icon: 'error' })
setRefreshStatus(false)
}
}
return (
<View className={classnames('flex-col', styles.shopping)} id='shoppingContainer'>
<View className={styles['shopping--topBar']} id='topBar'>
<Search placeholder='请输入客户名称' showBtn={false} changeOnSearch={getSearchData} debounceTime={300}>
<View className={styles.flexBox} onClick={onStartToManage}>
{isManageStatus ? (
<IconText svg iconName='icon-guanlidingdan' text='取消' color='#4581ff' customClass={styles['icon--manage--cancel']} />
) : (
<IconText svg iconName='icon-guanlidingdan' text='管理' />
)}
</View>
</Search>
</View>
<View className={classnames('flex-item', 'flex-col', styles['shopping--context'])}>
<View className={classnames(styles.shopping__list__container, 'flex-item')} style={{ height: listHeightRef.current }}>
<InfiniteScroll statusMore={statusMore} refresherEnabled={true} selfOnRefresherRefresh={handleRefresh} refresherTriggered={refreshStatus}>
{isPending ? (
<LoadingCard />
) : (
!!shoppingCartData?.list?.length &&
shoppingCartData?.list?.map((item, index) => {
return <ItemList itemData={item} key={index}></ItemList>
})
)}
</InfiniteScroll>
</View>
</View>
<View id='bottomBar'>
{isManageStatus ? (
<BottomEditBar
disabled={currentCheckedPurchaserId < 0}
onDelete={handleDelete}
onSelectCheckbox={isAll => handleSelectAllCheckbox(isAll)}></BottomEditBar>
) : (
<BottomSettleBar onSettleAccount={handleSettleAccount} amount={selectedAmount}></BottomSettleBar>
)}
</View>
</View>
)
}
// interface ScrollListPropType {
// height: string
// }
// const ScrollList = memo<ScrollListPropType>((props) => {
// const { height } = props
// const [shoppingCartData, setShoppingCartData] = useState<{ list: ShoppingCartData[]; total: number }>({ list: [], total: 0 })
// //数据加载状态
// const statusMore = useMemo(() => {
// return dataLoadingStatus({ list: shoppingCartData.list, total: shoppingCartData.total, status: state.loading })
// }, [shoppingCartData, state])
// return (
// <View className={classnames('flex-item', 'flex-col', styles['shopping--context'])}>
// <View className={classnames(styles.shopping__list__container, 'flex-item')} style={{ height: height }}>
// <InfiniteScroll statusMore={statusMore} refresherEnabled={true} selfOnRefresherRefresh={handleRefresh} refresherTriggered={refreshStatus}>
// {!!shoppingCartData?.list?.length &&
// shoppingCartData?.list?.map((item, index) => {
// return <ItemList itemData={item} key={index}></ItemList>
// })}
// </InfiniteScroll>
// </View>
// </View>
// )
// })
export default Shopping