349 lines
14 KiB
TypeScript
349 lines
14 KiB
TypeScript
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
|