2022-12-09 18:55:22 +08:00

349 lines
14 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 Taro, { Events, useDidShow } from '@tarojs/taro'
import { Text, View } from '@tarojs/components'
import type { ITouchEvent } from '@tarojs/components'
import classnames from 'classnames'
import type { SetStateAction } from 'react'
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import Product, { ProductItem } from './components/productItem'
import styles from './index.module.scss'
import MoveCollectionPopup from './components/moveCollectionPopup'
import InfiniteScroll from '@/components/infiniteScroll'
import MCheckbox from '@/components/checkbox/index'
import Search from '@/components/searchBar'
import { dataLoadingStatus, debounce, getFilterData } from '@/common/util'
import { alert, goLink } from '@/common/common'
import { DelFavoriteProductApi, FavoriteListApi } from '@/api/favorite'
import useLogin from '@/use/useLogin'
import IconText from '@/components/iconText'
import LayoutBlock from '@/components/layoutBlock'
import IconFont from '@/components/iconfont/iconfont'
import NormalButton from '@/components/normalButton'
import Empty from '@/components/empty'
import { COLLECTION_EMPTY_IMAGE } from '@/common/constant'
// 消息机制
export const collectionEvents = new Events()
// 勾选map
export type CheckedMap = Record<number, Record<number, boolean>>
interface CollectionItemPropsType {
selectId: number
item?: any
isManageStatus: boolean
checkedMap: CheckedMap
onCheckedChange: (id: number, item: CollectionItemPropsType['item'], checked: boolean) => void
filterChecked: (currentId: number, filter?: () => boolean) => number[]
}
const CollectionItem = (props: CollectionItemPropsType) => {
const {
item, isManageStatus, selectId, onCheckedChange, filterChecked, checkedMap,
} = props
// 控制下拉上拉
const [isOpen, setOpen] = useState(false)
const changeOpenCon = () => {
// 更新当前选中的收藏夹
collectionEvents.trigger('updateCurrentSelection', item.id)
setOpen(e => !e)
}
// 编辑
const handleEditCollection = (e: ITouchEvent, item: any) => {
e.stopPropagation()
const editData = {
id: item.id,
name: item.name,
remark: item.remark,
is_top: item.is_top,
}
goLink('/pages/collection/collectionDetail/index', { title: '编辑收藏夹', isEdit: true, editData: JSON.stringify(editData) })
}
useEffect(() => {
console.log('selectId && item.id', selectId, item.id, isManageStatus)
if (selectId !== item.id && isManageStatus) {
setOpen(false)
}
}, [isManageStatus, selectId])
return <LayoutBlock circle customClassName={styles.layout} onClick={changeOpenCon}>
<View className={styles.class_item}>
<View key={item.id} className={styles.class_title}>
<View className={styles.class_prefix}>
<View className={styles.class_prefix_top}>
<View className={styles.title}>{item.name}
<Text className={styles.fg}></Text>
<Text className={styles.num}>{item.product_color_list?.length || 0}</Text>
<Text className={styles.fg}></Text>
</View>
{/* is_top === 3 代表该收藏夹是默认收藏夹 */}
{
item.is_top !== 3 && <View onClick={e => handleEditCollection(e, item)}>
<IconFont name="icon-bianji" size={36}></IconFont>
</View>
}
</View>
<View className={styles.class_prefix_bottom}>
<Text>{item.remark}</Text>
</View>
</View>
<IconFont name="icon-shangla" size={36} customClassName={classnames(isOpen ? styles.iconfont_show : styles.iconfont_hide)}></IconFont>
<IconFont name="icon-xiala" size={36} customClassName={classnames(!isOpen ? styles.iconfont_show : styles.iconfont_hide)}></IconFont>
</View>
<View className={styles.class_con} style={isOpen ? { maxHeight: `${10 * 260}rpx` } : { maxHeight: 0 }} >
<View className={styles.products_list}>
{item.product_color_list?.map((subItem: any) => {
return <ProductItem checked={checkedMap?.[item.id]?.[subItem.product_id]} onCheckedChange={onCheckedChange} filterChecked={filterChecked} metaDataId={item.id} event={collectionEvents} item={subItem} key={subItem.product_id} openCheckBox={isManageStatus} />
})}
</View>
</View>
</View>
</LayoutBlock>
}
const CollectionItemWithMemo = memo(CollectionItem)
/**
* *******************
* * 收藏夹页面本体 *
* *******************
*/
const Collection = () => {
useLogin()
// 获取搜索数据
const [searchData, setSearchData] = useState('')
// 是否首次进入
const isFirst = useRef(true)
const onSearch = useCallback(debounce((e) => {
setSearchData(() => e)
}, 400), [])
// 获取列表
const { fetchData: fetchDataList, state: favoriteState } = FavoriteListApi()
const [favoriteData, setFavoriteData] = useState<{ list: any[]; total: number }>({ list: [], total: 0 })
// 获取列表
const getFavoriteList = async() => {
const res = await fetchDataList(getFilterData({ name: searchData }))
setFavoriteData({ list: res.data.list, total: res.data.total })
}
// 当前选中的收藏夹
const [currentSelection, setCurrentSelection] = useState<number>(0)
// 更新当前选中的收藏夹的id
const updateCurrentSelection = (current) => {
console.log('updateCurrentSelection current', current)
setCurrentSelection(current)
}
// ---------勾选逻辑---------
const [checkedMap, setCheckedMap] = useState<CheckedMap>({})
const onCheckedChange = (id: number, itemId: number, checked: boolean) => {
console.log('onCheckedChange', itemId, checked)
const newCheckedMap = Object.assign({}, checkedMap, {
[id]: {
...checkedMap[id],
[itemId]: checked,
},
})
setCheckedMap(newCheckedMap)
}
// 过滤符合勾选条件的数据
const filterChecked = (id: number, filter?: () => boolean) => {
if (!id) { return }
if (!checkedMap[id]) {
checkedMap[id] = {}
}
console.log('filterChecked', checkedMap[id])
const checkedSelections = Object.entries(checkedMap[id])
// 通过这个filter 筛选出所有checked状态为true的项
const filteredWithId = checkedSelections.filter(entries => Boolean(entries[1])).map(([checkedId]) => Number(checkedId))
const currentCollectionItem = favoriteData.list.find(item => item.id === id)
console.log(filteredWithId, currentCollectionItem)
const filtered = currentCollectionItem.product_color_list?.filter(({ product_id }) => filteredWithId.includes(product_id)) || []
return filter ? filtered.filter(filter) : filtered
}
// ---------勾选逻辑---------
useDidShow(() => {
getFavoriteList()
isFirst.current = false
})
useEffect(() => {
if (!isFirst.current) {
getFavoriteList()
}
}, [searchData, isFirst.current])
const contentHeightWithBottomBar = useRef<string>('auto')
const [contentStyleObject, setContentStyleObject] = useState<React.CSSProperties>({ height: 'auto' })
// 获取显示底部全选栏时 content的高度
const handleLayout = () => {
const query = Taro.createSelectorQuery()
query.select('#bottomBar').boundingClientRect()
query.select('#scrollContent').boundingClientRect()
query.exec((res) => {
console.log('res', res)
const bottomBarHeight = res[0].height
const contentHeight = res[1].height
contentHeightWithBottomBar.current = `${contentHeight - bottomBarHeight}px`
})
}
useEffect(() => {
handleLayout()
}, [])
// 消息机制
useEffect(() => {
collectionEvents.on('updateCurrentSelection', updateCurrentSelection)
return () => {
collectionEvents.off('updateCurrentSelection', updateCurrentSelection)
}
})
const multipleSelection = useMemo(() => {
const selections = filterChecked(currentSelection)
console.log('selections', selections)
if (selections && selections.length !== 0) {
return selections.map(item => item.product_id)
}
else {
return []
}
}, [checkedMap, filterChecked])
// 取消收藏
const { fetchData: delFavoriteProductFetchData } = DelFavoriteProductApi()
const onDelCollection = async() => {
if (multipleSelection.length == 0) { return alert.none('请选择要取消面料') }
const showModalRes = await Taro.showModal({
title: '是否要取消收藏',
confirmText: '是',
cancelText: '否',
})
if (showModalRes.confirm) {
const res = await delFavoriteProductFetchData({ favorite_id: currentSelection, product_id: multipleSelection })
if (res.success) {
getFavoriteList()
alert.none('已取消收藏')
}
}
}
const [isManageStatus, setManageStatus] = useState(false)
// const [isSelectAll, setSelectAll] = useState(false)
// 全选
const isSelectAll = useMemo(() => {
const product_color_list = favoriteData.list.find(item => item.id === currentSelection)?.product_color_list || []
return product_color_list.length !== 0 && filterChecked(currentSelection).length === product_color_list.length
}, [currentSelection, favoriteData.list, filterChecked])
// 全选和反选的函数
const onCheckedAllChange = (checkedAll: boolean) => {
const product_color_list = favoriteData.list.find(item => item.id === currentSelection).product_color_list
const newCheckedMap: CheckedMap = JSON.parse(JSON.stringify((checkedMap)))
product_color_list.forEach((item) => {
newCheckedMap[currentSelection][item.product_id] = checkedAll
})
console.log('newCheckedMap==+>', newCheckedMap)
setCheckedMap(newCheckedMap)
}
// 点击管理
const onStartToManage = () => {
setManageStatus((isManageStatus) => {
// 控制动画效果
if (isManageStatus) {
setContentStyleObject({ height: 'auto', flex: '1 1 auto' })
}
else {
setContentStyleObject({
flex: 'unset',
height: `${contentHeightWithBottomBar.current}`,
})
}
return !isManageStatus
})
}
// 新建收藏夹
const handleAddNewCollection = () => {
goLink('/pages/collection/collectionDetail/index', { title: '新建收藏夹', isEdit: false, editData: null })
}
const [showMovePopup, setShowMovePopup] = useState(false)
const closeMovePopup = useCallback(() => {
setShowMovePopup(false)
}, [])
// 移动到
const handleMultipleMove = () => {
setShowMovePopup(true)
}
// 删除
const handleMultipleDelete = () => {
onDelCollection()
}
// 数据加载状态
const statusMore = useMemo(() => {
return dataLoadingStatus({ list: favoriteData.list, total: favoriteData.total, status: favoriteState.loading })
}, [favoriteState, favoriteData])
return (
<View className={styles.collection_main}>
<View className={styles.collection_content} >
<View className={styles.search}>
<Search showBtn={false} changeOnSearch={onSearch} placeholder="请输入面料关键词" >
<View className={styles.flexBox} onClick={onStartToManage}>
{isManageStatus
? (
<IconText svg iconName="icon-guanli" text="退出" color="#4581ff" customClass={styles['icon--manage--cancel']} />
)
: (
<IconText svg iconName="icon-guanli" text="管理" />
)}
</View>
</Search>
</View>
<LayoutBlock circle customClassName={styles.layout} onClick={handleAddNewCollection}>
<View className={styles.layoutBlock}>
<IconFont customClassName={styles.icon} name="icon-shoucangjia" size={60} ></IconFont>
<Text className={styles.content}></Text>
</View>
</LayoutBlock>
<View className={styles.scrollContent} id="scrollContent" style={contentStyleObject}>
<InfiniteScroll
statusMore={statusMore}
emptySlot={<Empty picUrl={COLLECTION_EMPTY_IMAGE} text="暂无数据" />}
>
{favoriteData.list?.map((item: any, key) =>
(
<CollectionItemWithMemo checkedMap={checkedMap} onCheckedChange={onCheckedChange} filterChecked={filterChecked} selectId={currentSelection} key={key} item={item} isManageStatus={isManageStatus} />
),
)}
</InfiniteScroll>
</View>
</View>
<View className={classnames(styles.bottomBar, isManageStatus ? styles.showBottomBar : '')} id="bottomBar">
<View className={styles.bottomLeft}>
<MCheckbox
status={isSelectAll}
round
size="small"
onSelect={() => onCheckedAllChange(true)}
onClose={() => onCheckedAllChange(false)}
customClassName={styles.multipleCheckbox}
customTextClass={styles.multipleCheckbox__text}
>
</MCheckbox>
</View>
<View className={styles.bottomRight}>
<NormalButton type="danger" plain round size="normal" onClick={handleMultipleMove} customStyles={{ padding: '0 50rpx', margin: '0 40rpx' }}>
<Text style={{ fontSize: '30rpx' }}></Text>
</NormalButton>
<NormalButton type="danger" round size="normal" onClick={handleMultipleDelete} customStyles={{ padding: '0 60rpx' }}>
<Text style={{ fontSize: '30rpx' }}></Text>
</NormalButton>
</View>
</View>
<MoveCollectionPopup onSuccess={getFavoriteList} collectionList={favoriteData.list} show={showMovePopup} onClose={closeMovePopup} ids={multipleSelection} sourceId={currentSelection} />
</View>
)
}
export default Collection