feat: 优化码单管理

This commit is contained in:
czm 2023-01-10 11:41:01 +08:00
parent e8f050bcbb
commit dd3963be99
29 changed files with 1418 additions and 196 deletions

View File

@ -1,17 +1,77 @@
import React, { useContext, useReducer } from 'react'
import { useDispatch } from 'react-redux'
import { CustomPrintCalculationApi } from '@/api/codeManage'
import type { CodeParam } from '@/reducers/codeData'
import { useSelector } from '@/reducers/hooks'
import type { saleModeType } from '@/common/enum'
interface params {
state: CodeParam
dispatch: React.Dispatch<any>
state: StateType
dispatch: React.Dispatch<Actions>
}
interface PropsType {
children: React.ReactNode
}
export interface ColorType {
estimate_amount: number
id: number
lab: { a: number; b: number; l: number }
length: number
product_color_code: string
product_color_id: number
product_color_name: string
rgb: { b: number; g: number; r: number }
roll: number
sale_mode: number
sale_mode_name: string
sale_offset: number
sale_price: number
standard_price: number
texture_url: string
weight_error: number
index_str: string
checked: boolean
has_screw_recommend: boolean
is_screw_recommend: boolean
}
export interface ProductType {
color_list: ColorType[]
product_code: string
product_id: number
product_name: string
index_str: string
checked: boolean
}
export interface StateType {
list: ProductType[]
modeNumber?: {
0: number
1: number
2: number
}
statistics?: {
product_number: number
color_number: number
roll_number: number
length_number: number
price_number: number
}
sale_mode?: saleModeType
checkList?: { [key in number]: boolean }
recommendSubmitStatus?: boolean
}
export interface ActionsMap {
setInitData: StateType
updateProduct: StateType
setRecommendSubmit: StateType
}
export type Actions = {
[Key in keyof ActionsMap]: {
type: Key
data: ActionsMap[Key]
}
}[keyof ActionsMap]
function createCtx<A extends {} | null>() {
const ctx = React.createContext<A | undefined>(undefined)
function useCtx() {
@ -21,17 +81,29 @@ function createCtx<A extends {} | null>() {
}
return [useCtx, ctx.Provider] as const
}
export const [useCurrenCode, CurrentProvider] = createCtx<params>()
export const [useCurrenShop, CurrentProvider] = createCtx<params>()
// https://dev.to/elisealcala/react-context-with-usereducer-and-typescript-4obm
export default (props: PropsType) => {
const initialState = { count: 0 }
function reducer(state, action) {
const initialState = {
list: [],
statistics: {
product_number: 0,
color_number: 0,
roll_number: 0,
length_number: 0,
price_number: 0,
},
recommendSubmitStatus: false,
}
function reducer(state: StateType, action: Actions) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 }
case 'decrement':
return { count: state.count - 1 }
case 'setInitData':
return onInitData(state, action)
case 'updateProduct':
return onUpdateData(state, action)
case 'setRecommendSubmit':
return { ...state, recommendSubmitStatus: action.data.recommendSubmitStatus }
default:
return state
}
@ -41,3 +113,37 @@ export default (props: PropsType) => {
<CurrentProvider value={{ state, dispatch }} children={props.children}></CurrentProvider>
)
}
// 初始化数据
const onInitData = (state: StateType, action: Actions) => {
const { data } = action
const statistics = { product_number: 0, color_number: 0, roll_number: 0, length_number: 0, price_number: 0 }
data.list?.map((item, index) => {
item.index_str = index.toString()
item.checked = false
item.color_list?.map((citem, cindex) => {
citem.index_str = `${index},${cindex}`
citem.checked = false
})
})
return { ...state, list: data.list, sale_mode: data.sale_mode, recommendSubmitStatus: data.recommendSubmitStatus, statistics }
}
// 更新数据
const onUpdateData = (state: StateType, action: Actions) => {
const { data } = action
const statistics = { product_number: 0, color_number: 0, roll_number: 0, length_number: 0, price_number: 0 }
data.list?.map((item, index) => {
item.color_list?.map((citem) => {
if (citem.checked) {
statistics.color_number += 1
statistics.price_number += citem.estimate_amount
statistics.roll_number += citem.roll
statistics.length_number += citem.length
}
})
if (item.checked) { statistics.product_number += 1 }
})
console.log('data:::', data.list)
return { ...state, list: data.list, statistics }
}

View File

@ -0,0 +1,72 @@
type ActionMap<M extends Record<string, any>> = {
[Key in keyof M]: M[Key] extends undefined
? {
type: Key
}
: {
type: Key
payload: M[Key]
}
}
export enum Types {
Create = 'CREATE_PRODUCT',
Delete = 'DELETE_PRODUCT',
Add = 'ADD_PRODUCT',
}
// Product
interface ProductType {
id: number
name: string
price: number
}
interface ProductPayload {
[Types.Create]: {
id: number
name: string
price: number
}
[Types.Delete]: {
id: number
}
}
export type ProductActions = ActionMap<ProductPayload>[keyof ActionMap<ProductPayload>]
export const productReducer = (state: ProductType[], action: ProductActions | ShoppingCartActions) => {
switch (action.type) {
case Types.Create:
return [
...state,
{
id: action.payload.id,
name: action.payload.name,
price: action.payload.price,
},
]
case Types.Delete:
return [
...state.filter(product => product.id !== action.payload.id),
]
default:
return state
}
}
interface ShoppingCartPayload {
[Types.Add]: undefined
}
export type ShoppingCartActions = ActionMap<ShoppingCartPayload>[keyof ActionMap<ShoppingCartPayload>]
export const shoppingCartReducer = (state: number, action: ProductActions | ShoppingCartActions) => {
switch (action.type) {
case Types.Add:
return state + 1
default:
return state
}
}

View File

@ -61,6 +61,6 @@ export default () => {
</View>
})}
</View>
<TimePickerPopup showTime={showTime} closePopup={handClose} onSelectDate={onSelectDate} />
<TimePickerPopup showTime={showTime} end={formData?.sale_end_time} start={formData?.sale_start_time} closePopup={handClose} onSelectDate={onSelectDate} />
</View>
}

View File

@ -9,6 +9,7 @@
width: 100%;
box-sizing: border-box;
z-index: 999;
box-shadow: 0px -5px 20px -8px rgba(0, 0, 0, 0.06);
.select_text {
padding: 0 32px 0 16px;
font-size: 28px;
@ -39,4 +40,7 @@
text-align: center;
line-height: 80px;
}
.del {
background-color: #f44761ff;
}
}

View File

@ -1,18 +1,62 @@
import { Text, View } from '@tarojs/components'
import { memo, useEffect, useMemo, useState } from 'react'
import classNames from 'classnames'
import styles from './index.module.scss'
import MCheckbox from '@/components/checkbox'
import type { StateType } from '@/context/ContextShop'
import { useCurrenShop } from '@/context/ContextShop'
import { formatPriceDiv } from '@/common/fotmat'
import type { saleModeType } from '@/common/enum'
export default () => {
return <View className={styles.Settlement_btn}>
<MCheckbox />
<Text className={styles.select_text}></Text>
<View className={styles.price_count}>
<View className={styles.price}>
<Text>:</Text>
<Text>3564.00</Text>
</View>
<View className={styles.count}>1,1,4</View>
</View>
<View className={styles.btn}></View>
</View>
interface ParamType {
onSelect?: (val: boolean) => void
onBtnClick?: (val: boolean) => void
model: boolean
}
type IndexType = {
onSelect?: (val: boolean) => void
statistics?: StateType['statistics']
sale_mode?: saleModeType
}&ParamType
export default (props: ParamType) => {
const { onSelect, model, onBtnClick } = props
const { state, dispatch } = useCurrenShop()
return <Index onSelect={onSelect} statistics={state.statistics} model={model} onBtnClick={onBtnClick} sale_mode={state.sale_mode} />
}
const Index = memo((props: IndexType) => {
const { onSelect, statistics, model, sale_mode } = props
const [status, setStatus] = useState(false)
const getSelect = (status) => {
setStatus(() => status)
onSelect?.(status)
}
useEffect(() => {
setStatus(false)
}, [sale_mode])
const count = useMemo(() => {
return sale_mode === 0
? `已选${statistics?.product_number}种面料,${statistics?.color_number}个颜色,共${statistics?.roll_number}`
: `已选${statistics?.product_number}种面料,${statistics?.color_number}个颜色,共${(statistics?.length_number || 0) / 100}`
}, [statistics])
const onBtnClick = () => {
props.onBtnClick?.(model)
}
return <View className={styles.Settlement_btn}>
<MCheckbox status={status} onSelect={() => getSelect(true)} onClose={() => getSelect(false)} />
<Text className={styles.select_text} ></Text>
<View className={styles.price_count}>
{!model && <>
<View className={styles.price}>
<Text>:</Text>
<Text>{formatPriceDiv(statistics?.price_number)}</Text>
</View>
<View className={styles.count}>{count}</View>
</>}
</View>
<View onClick={onBtnClick} className={classNames(styles.btn, model && styles.del)}>{!model ? '结算' : '删除'}</View>
</View>
})

View File

@ -0,0 +1,33 @@
import { memo, useCallback } from 'react'
import MCheckbox from '@/components/checkbox'
import type { ColorType, ProductType } from '@/context/ContextShop'
import { useCurrenShop } from '@/context/ContextShop'
interface ParamType {
colorItem: ColorType
}
interface IndexType {
onSelect?: (val: boolean) => void
checkStatus: boolean
}
export default memo((props: ParamType) => {
const { colorItem } = props
const { state, dispatch } = useCurrenShop()
const indexs = colorItem.index_str.split(',')
const onSelect = useCallback((status) => {
state.list[indexs[0]].color_list[indexs[1]].checked = status
const tf = state.list[indexs[0]].color_list.some(item => item.checked)
state.list[indexs[0]].checked = tf
dispatch({ type: 'updateProduct', data: { list: state.list } })
}, [state.list[indexs[0]].color_list[indexs[1]].checked])
return <Index checkStatus={state.list[indexs[0]].color_list[indexs[1]].checked} onSelect={onSelect} />
})
const Index = memo((props: IndexType) => {
const { checkStatus, onSelect } = props
const setSelect = (status) => {
onSelect?.(status)
}
return <MCheckbox status={checkStatus} onSelect={() => setSelect(true)} onClose={() => setSelect(false)} />
})

View File

@ -0,0 +1,28 @@
.shop_main {
min-height: 100vh;
background-color: #f8f8f8;
display: flex;
flex-direction: column;
.shop_con {
// flex: 1;
box-sizing: border-box;
height: calc(100vh - 300px);
overflow: hidden;
}
.scroll_view {
padding: 0 24px;
box-sizing: border-box;
height: 100%;
}
.shop_header {
background-color: #fff;
position: relative;
z-index: 1500;
.search_title {
padding: 0 24px;
}
}
.loading_card {
height: 70vh;
}
}

View File

@ -0,0 +1,242 @@
import { ScrollView, Text, View } from '@tarojs/components'
import Taro, { useDidShow } from '@tarojs/taro'
import React, { useCallback, useEffect, useMemo, useRef, useState, useTransition } from 'react'
import ProductBlock from '../productBlock'
import BottomBtn from '../bottomBtn'
import Operation from '../operation'
import type { listType } from '../search'
import Search from '../search'
import NoShop from '../noShop'
import styles from './index.module.scss'
import { DelShoppingCartApi, GetProductColorApi, GetShoppingCartV2Api } from '@/api/shopCart'
import type { ProductType } from '@/context/ContextShop'
import { useCurrenShop } from '@/context/ContextShop'
import type { saleModeType } from '@/common/enum'
import LoadingCard from '@/components/loadingCard'
import { alert, goLink } from '@/common/common'
import InfiniteScroll from '@/components/infiniteScroll'
import { dataLoadingStatus, throttle } from '@/common/util'
import { ApplyOrderAccessApi, GetAdminUserInfoApi } from '@/api/user'
import { setParam } from '@/common/system'
import type { SalesManDialogRef } from '@/components/bindSalesManDialog'
import BindSalesManDialog from '@/components/bindSalesManDialog'
import { useSelector } from '@/reducers/hooks'
import OrganizationNameModal from '@/components/organizationNameModal'
import useLogin from '@/use/useLogin'
import { companyDetailApi, companyUpdateApi } from '@/api/company'
interface FilterType {
abstract_sort_key: -1|1|2|-2
sale_mode: saleModeType
}
export default () => {
const { state, dispatch } = useCurrenShop()
const [filter, setFilter] = useState<FilterType>({
abstract_sort_key: -1,
sale_mode: 0,
})
const userInfo = useSelector(state => state.userInfo)
const [isPending, startTransition] = useTransition()
const { fetchData: getShoppingCartFetchData, state: shopState } = GetShoppingCartV2Api()
const getShoppingCart = async() => {
const res = await getShoppingCartFetchData(filter)
if (res.success) {
startTransition(() => {
dispatch({ type: 'setInitData', data: { list: JSON.parse(JSON.stringify(res?.data?.product_list)), sale_mode: filter.sale_mode, recommendSubmitStatus: false } })
})
}
}
const [modeNumber, setModeNumber] = useState({
bulk_number: 0,
length_length: 0,
weight_number: 0,
})
const { fetchData: productSkuFetchData } = GetProductColorApi()
const getProductColorNum = async() => {
const res = await productSkuFetchData()
if (res.success) {
setModeNumber(res.data)
}
}
const initStatus = useRef(false)
useDidShow(() => {
getProductColorNum()
initStatus.current && getShoppingCart()
initStatus.current = true
})
const onSelect = useCallback((status) => {
state.list?.map((item) => {
item.checked = status
item.color_list?.map((citem) => {
citem.checked = status
})
})
dispatch({ type: 'updateProduct', data: { list: state.list } })
}, [state.list])
useEffect(() => {
getShoppingCart()
}, [filter])
useEffect(() => {
console.log('state.recommendSubmitStatus::', state.recommendSubmitStatus)
if (state.recommendSubmitStatus) {
getProductColorNum()
getShoppingCart()
}
}, [state.recommendSubmitStatus])
const onSortChange = useCallback((val) => {
setFilter(e => ({ ...e, abstract_sort_key: val.value }))
}, [])
const getSelect = useCallback((val: listType) => {
setFilter(e => ({ ...e, sale_mode: val.value }))
}, [])
const logadingStatus = useMemo(() => {
return shopState.loading || isPending
}, [shopState.loading, isPending])
const [settingStatus, setSettingStatus] = useState(false)
const onChangeSetting = useCallback((val) => {
setSettingStatus(() => val)
}, [])
const { fetchData: delShopFetchData } = DelShoppingCartApi()
const onBtnClick = (model) => {
if (model) {
delShop()
}
else {
orderDetail()
}
}
const bindSalesManDialogRef = useRef<SalesManDialogRef | null>(null)
const [showModal, setShowModal] = useState(false)
// 去结算
const { fetchData: FetchData } = GetAdminUserInfoApi()
const { fetchData: applyOrderAccessFetchData } = ApplyOrderAccessApi()
const orderDetail = throttle(async() => {
const res = await FetchData()
if (res.data.order_access_status !== 3) {
if (res.data.order_access_status == 1) { applyOrderAccessFetchData() }
bindSalesManDialogRef.current?.handleChange(true)
return
}
// 检测是否修改过组织昵称
if (userInfo.adminUserInfo.first_change_name) {
setShowModal(true)
return
}
const SelectIds = getSelectIds()
if (SelectIds.length == 0) {
alert.error('请选择面料')
}
else {
const ids = SelectIds.join('-')
setParam({ ids, sale_mode: state.sale_mode }) // 临时存储
goLink('/pages/order/comfirm')
}
}, 500)
// 删除商品
const delShop = () => {
Taro.showModal({
content: '删除所选商品?',
async success(res) {
if (res.confirm) {
const ids = getSelectIds()
console.log('ids:::', ids)
const res = await delShopFetchData({ id: ids })
if (res.success) {
getShoppingCart()
getProductColorNum()
}
}
else if (res.cancel) {
console.log('用户点击取消')
}
},
})
}
// 弹出修改组织昵称弹窗
const handleBindSalesManSuccess = () => {
// 检测是否修改过组织昵称
if (userInfo.adminUserInfo.first_change_name) {
setShowModal(true)
}
}
// 获取面料选中的id
const getSelectIds = () => {
const ids: number[] = []
state.list.map((item) => {
item.color_list.map((citem) => {
citem.checked && ids.push(citem.id)
})
})
return ids
}
const handleClose = useCallback(() => {
setShowModal(false)
}, [])
const handleShowChange = useCallback((val: boolean) => {
setShowModal(val)
}, [])
const { getAdminUserInfo } = useLogin()
const { fetchData: saveFetch } = companyUpdateApi()
const { fetchData: getCompanyFetch } = companyDetailApi()
const handleOrganizationNameModalConfirm = useCallback(async(text: string) => {
const params = await getCompanyFetch()
const result = await saveFetch({
...params.data,
company_name: text,
})
if (result.success) {
getAdminUserInfo()
handleClose()
alert.success('保存成功')
}
else {
alert.none(result.msg)
}
}, [])
return <View className={styles.shop_main}>
<View className={styles.shop_header}>
<Operation onSelect={onSortChange} onChange={onChangeSetting} />
<View className={styles.search_title}>
<Search numberObj={modeNumber} defaultIndex={filter.sale_mode} onSelect={getSelect} />
</View>
</View>
<View className={styles.shop_con}>
{logadingStatus && <View className={styles.loading_card}><LoadingCard /></View>}
{(!logadingStatus && state.list && state.list.length > 0) && <ScrollView scrollY className={styles.scroll_view}>
{state.list?.map(item => (<ProductBlock productItem={item} key={item.product_id} />))}
<View style={{ height: '100rpx' }}></View>
</ScrollView>}
{(!logadingStatus && !state.list) && <NoShop />}
</View>
<BottomBtn onSelect={onSelect} model={settingStatus} onBtnClick={onBtnClick} />
<View>
<BindSalesManDialog ref={bindSalesManDialogRef} onSuccess={handleBindSalesManSuccess} />
</View>
<View>
<OrganizationNameModal showModal={showModal} onClose={handleClose} onShowModalChange={handleShowChange} onConfirm={handleOrganizationNameModalConfirm} />
</View>
<View className="common_safe_area_y"></View>
</View>
}

View File

@ -0,0 +1,23 @@
.empty {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.title {
color: $color_font_two;
font-size: $font_size_min;
}
.btn {
width: 222px;
height: 68px;
background-color: $color_main;
font-size: $font_size;
text-align: center;
line-height: 68px;
margin-top: 42px;
color: #fff;
border-radius: 50px;
}
}

View File

@ -0,0 +1,13 @@
import { View } from '@tarojs/components'
import { memo } from 'react'
import styles from './index.module.scss'
import { goLink } from '@/common/common'
export default memo(() => {
return <View className={styles.empty} onClick={() => goLink('/pages/index/index', {}, 'switchTab')}>
<View className={styles.title}></View>
<View className={styles.btn} >
</View>
</View>
})

View File

@ -0,0 +1,82 @@
.shop_header_sort_main {
width: 100%;
background-color: #fff;
position: relative;
z-index: 1500;
padding: 0 24px;
box-sizing: border-box;
}
.shop_header_sort {
height: 88px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28px;
z-index: 1500;
position: relative;
background-color: #fff;
.sort {
display: flex;
align-items: center;
background-color: rgb(245, 245, 245);
height: 55px;
padding: 0 24px;
box-sizing: border-box;
border-radius: 55px;
font-size: 25px;
color: #337fffff;
}
.edit {
display: flex;
align-items: center;
text {
margin-left: 10px;
}
}
}
.sort_list {
position: absolute;
left: 0;
top: 0;
display: grid;
grid-template-columns: repeat(3, auto);
justify-content: space-between;
grid-row-gap: 16px;
width: 100%;
border-radius: 0px 0px 16px 16px;
background-color: #fff;
z-index: 1300;
padding: 24px;
padding-top: 100px;
box-sizing: border-box;
transition: all 0.3s ease-in-out;
transform: translateY(-100%);
.item_sort {
width: 223px;
height: 68px;
border-radius: 34px;
background: #e9e9e9;
color: rgba(0, 0, 0, 0.6);
font-size: 28px;
text-align: center;
line-height: 68px;
}
.search_item_select {
background-color: #cde5ff;
color: #4581ff;
border: 1px solid #4581ff;
box-sizing: border-box;
}
}
.sort_list_selected {
transform: translateY(0%);
}
.mask {
position: fixed;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
z-index: 1000;
background-color: rgba(0, 0, 0, 0.6);
}

View File

@ -0,0 +1,52 @@
import { Text, View } from '@tarojs/components'
import { memo, useState } from 'react'
import classNames from 'classnames'
import styles from './index.module.scss'
import IconFont from '@/components/iconfont/iconfont'
interface SortType { value: -1|1|-2|2; title: string }
interface ParamType {
onSelect: (val: SortType) => void
onChange: (val: boolean) => void
}
export default memo((props: ParamType) => {
const { onSelect } = props
const [open, setOpen] = useState(false)
const sort_data: SortType[] = [
{ value: -1, title: '添加时间降序' },
{ value: 1, title: '添加时间升序' },
{ value: -2, title: '面料编号降序' },
{ value: 2, title: '面料编号升序' },
]
const [selectData, setSelectData] = useState<SortType>({
value: -1, title: '添加时间降序',
})
const getSelect = (item) => {
setOpen(false)
onSelect(item)
setSelectData(item)
}
const [status, setStatus] = useState(false)
const onChange = () => {
setStatus(!status)
props.onChange?.(!status)
}
return <><View className={styles.shop_header_sort_main}>
<View className={styles.shop_header_sort}>
<View className={styles.sort} onClick={() => setOpen(true)}>
<IconFont name="icon-paixu1" size={30} color="#337FFFFF" />
<Text>{selectData.title}</Text>
<IconFont name="icon-zhankai" size={30} color="#337FFFFF" />
</View>
<View className={styles.edit} onClick={onChange}>
<IconFont name="icon-guanli" size={43} color={`${status ? '#337FFFFF' : '#000000FF'}`} />
<Text style={{ color: `${status ? '#337FFFFF' : '#000000FF'}` }}>{status ? '取消' : '管理'}</Text>
</View>
</View>
<View className={classNames(styles.sort_list, open && styles.sort_list_selected)}>
{sort_data.map(item => <View key={item.value} onClick={() => getSelect(item)} className={classNames(styles.item_sort, (selectData.value === item.value) && styles.search_item_select)}>{item.title}</View>)}
</View>
</View>
{open && <View className={styles.mask} onClick={() => setOpen(false)}></View>}
</>
})

View File

@ -14,11 +14,23 @@
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #ccc;
// border-bottom: 1px solid rgba(0, 0, 0, 0.1);
height: 100%;
position: relative;
&::before {
content: '';
height: 1px;
width: 100%;
position: absolute;
background-color: rgba(0, 0, 0, 0.1);
bottom: 0;
left: 0;
transform: scaleY(0.5);
}
.model {
display: flex;
align-items: center;
color: rgba(0, 0, 0, 0.8);
.label {
width: 60px;
height: 28px;

View File

@ -1,24 +1,51 @@
import { Text, View } from '@tarojs/components'
import { CustomWrapper, Text, View } from '@tarojs/components'
import { memo, useCallback, useEffect, useMemo } from 'react'
import ProductItem from '../productItem'
import Checkbox from '../productCheckbox'
import styles from './index.module.scss'
import MCheckbox from '@/components/checkbox'
import type { ProductType } from '@/context/ContextShop'
import { useCurrenShop } from '@/context/ContextShop'
import { formatHashTag } from '@/common/fotmat'
import type { saleModeType } from '@/common/enum'
export default () => {
interface ParamType {
productItem: ProductType
}
type IndexType = {
onSelect?: (val: boolean) => void
sale_mode: saleModeType
} & ParamType
export default memo((props: ParamType) => {
const { productItem } = props
// const { state, dispatch } = useCurrenShop()
useEffect(() => {
console.log('productItem66:::', productItem)
}, [productItem])
return <Index productItem={productItem} sale_mode={0} />
})
const Index = memo((props: IndexType) => {
const { productItem, onSelect, sale_mode } = props
const setSelect = (status) => {
onSelect?.(status)
}
const sale_mode_name = useMemo(() => {
const data = ['大货', '剪版', '删剪']
return data[sale_mode]
}, [sale_mode])
console.log('productItem22::', productItem)
return <View className={styles.shop_product}>
<View className={styles.shop_product_title}>
<MCheckbox />
<Checkbox productItem={productItem} />
<View className={styles.title_desc}>
<View className={styles.model}>
<Text className={styles.name}>0681# 26s全棉平纹</Text>
<Text className={styles.label}></Text>
<Text className={styles.name}>{formatHashTag(productItem?.product_code, productItem?.product_name)}</Text>
<Text className={styles.label}>{sale_mode_name}</Text>
</View>
<View className={styles.unit}>大货单位:</View>
</View>
</View>
<ProductItem />
<ProductItem />
<ProductItem />
<ProductItem />
{productItem?.color_list?.map(item => <ProductItem key={item.product_color_id} colorItem={item} />)}
</View>
}
})

View File

@ -0,0 +1,33 @@
import { memo, useCallback } from 'react'
import MCheckbox from '@/components/checkbox'
import type { ProductType } from '@/context/ContextShop'
import { useCurrenShop } from '@/context/ContextShop'
interface ParamType {
productItem: ProductType
}
type IndexType = {
onSelect?: (val: boolean) => void
} & ParamType
export default memo((props: ParamType) => {
const { productItem } = props
const { state, dispatch } = useCurrenShop()
const index = productItem.index_str.split(',')[0]
const onSelect = useCallback((status) => {
state.list[index] = { ...state.list[index], checked: status }
state.list[index].color_list?.map((citem, cindex) => {
state.list[index].color_list[cindex] = { ...state.list[index].color_list[cindex], checked: status }
})
dispatch({ type: 'updateProduct', data: { list: state.list } })
}, [state.list[index]])
return <Index productItem={state.list[index]} onSelect={onSelect} />
})
const Index = memo((props: IndexType) => {
const { productItem, onSelect } = props
const setSelect = (status) => {
onSelect?.(status)
}
return <MCheckbox status={productItem.checked} onSelect={() => setSelect(true)} onClose={() => setSelect(false)} />
})

View File

@ -0,0 +1,32 @@
import { memo, useCallback } from 'react'
import MCheckbox from '@/components/checkbox'
import type { ProductType } from '@/context/ContextShop'
import { useCurrenShop } from '@/context/ContextShop'
interface ParamType {
productItem: ProductType
}
interface IndexType {
onSelect?: (val: boolean) => void
checkStatus: boolean
}
export default memo((props: ParamType) => {
const { productItem } = props
const { state, dispatch } = useCurrenShop()
const index = productItem.index_str.split(',')[0]
const onSelect = useCallback((status) => {
state.list[index].checked = status
state.list[index].color_list?.map((citem, cindex) => citem.checked = status)
dispatch({ type: 'updateProduct', data: { list: state.list } })
}, [state.list[index].checked])
return <Index checkStatus={state.list[index].checked} onSelect={onSelect} />
})
const Index = memo((props: IndexType) => {
const { checkStatus, onSelect } = props
const setSelect = (status) => {
onSelect?.(status)
}
return <MCheckbox status={checkStatus} onSelect={() => setSelect(true)} onClose={() => setSelect(false)} />
})

View File

@ -1,48 +1,102 @@
.shop_product_item_con {
padding-bottom: 20px;
position: relative;
&:nth-child(n + 3) {
&::before {
content: '';
height: 1px;
width: 100%;
position: absolute;
background-color: rgba(0, 0, 0, 0.1);
top: 0;
left: 0;
transform: scaleY(0.5);
}
}
.shop_product_item {
display: flex;
padding: 24px 0;
align-items: center;
height: 166px;
.item_con {
display: flex;
margin-left: 20px;
flex: 1;
height: 100%;
align-items: center;
position: relative;
.img {
width: 118px;
height: 118px;
}
.item_name_price_count {
flex: 1;
margin-left: 32px;
margin-left: 24px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 118px;
.name_price {
display: flex;
justify-content: space-between;
font-size: 28px;
text {
&:nth-child(2) {
color: red;
.price {
text {
&:nth-child(1) {
color: #999999;
text-decoration: line-through;
}
&:nth-child(2) {
color: #f41a39ff;
margin-left: 10px;
}
}
}
}
.count {
width: 201px;
align-self: flex-end;
.count_con {
width: 100%;
display: flex;
justify-content: space-between;
.recommend_title {
border-radius: 4px;
font-size: 24px;
display: flex;
align-items: center;
text {
text-align: center;
&:nth-child(1) {
width: 61px;
height: 34px;
background: #fee5cd;
color: #522b0fff;
font-weight: 700;
border-radius: 4px 0px 0px 4px;
}
&:nth-child(2) {
width: 112px;
height: 34px;
background: #fcf4e9;
color: #c57c26ff;
border-radius: 0px 4px 4px 0px;
}
}
}
.count {
width: 201px;
}
}
}
}
}
.recommended {
height: 60px;
height: 82px;
padding: 0 20px 0 50px;
background-color: #ccc;
background: #f6f6f6;
border-radius: 10px;
display: flex;
justify-content: space-between;
font-size: 25px;
font-size: 24px;
align-items: center;
color: rgba(0, 0, 0, 0.8);
.icon {
transform: rotate(-90deg);
}

View File

@ -1,36 +1,122 @@
import { Text, View } from '@tarojs/components'
import { CustomWrapper, Text, View } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { memo, useCallback, useEffect, useMemo } from 'react'
import Big from 'big.js'
import ColorCheckbox from '../colorCheckbox'
import RecommendProduct from '../recommendProduct'
import styles from './index.module.scss'
import MCheckbox from '@/components/checkbox'
import LabAndImg from '@/components/LabAndImg'
import Counter from '@/components/counter'
import IconFont from '@/components/iconfont/iconfont'
import type { ColorType } from '@/context/ContextShop'
import { useCurrenShop } from '@/context/ContextShop'
import { formatPriceDiv } from '@/common/fotmat'
import { GetShoppingCartApi, UpdateShoppingCartApi } from '@/api/shopCart'
import { debounce } from '@/common/util'
import { SALE_MODE_SETTING } from '@/common/enum'
export default () => {
interface ColorItemType {
colorItem: ColorType
}
type IndexItemType = {
onSelect?: (val: boolean) => void
onChangeNum?: (val: { id: number; length: number; roll: number }) => void
onSubmitSuccess?: () => void
}&ColorItemType
export default memo((props: ColorItemType) => {
const { fetchData: updateShoppingCartFetchData } = UpdateShoppingCartApi()
const { fetchData: getShoppingCartFetchData } = GetShoppingCartApi()
const { colorItem } = props
const { state, dispatch } = useCurrenShop()
const indexs = colorItem.index_str.split(',')
const colorItemNew = state.list[indexs[0]].color_list[indexs[1]]
const onChangeNum = useCallback(async(val) => {
await updateShoppingCartFetchData(val)
const res = await getShoppingCartFetchData({ id: colorItem.id })
if (res.success) {
state.list[indexs[0]].color_list[indexs[1]] = { ...state.list[indexs[0]].color_list[indexs[1]], ...res.data.color_list[0] }
dispatch({ type: 'updateProduct', data: { list: state.list } })
}
}, [colorItemNew])
const onSubmitSuccess = useCallback(() => {
Taro.showToast({
title: '添加成功',
icon: 'success',
duration: 1000,
success: () => {
dispatch({ type: 'setRecommendSubmit', data: { list: state.list, recommendSubmitStatus: true } })
},
})
}, [])
return <Index colorItem={state.list[indexs[0]].color_list[indexs[1]]} onChangeNum={onChangeNum} onSubmitSuccess={onSubmitSuccess} />
})
const Index = memo((props: IndexItemType) => {
const { colorItem, onChangeNum, onSubmitSuccess } = props
const rga = useMemo(() => {
return { lab: colorItem.lab, rgb: colorItem.rgb, texture_url: colorItem.texture_url, title: colorItem.product_color_code }
}, [colorItem])
const getCount = debounce(async(num) => {
const data = { id: colorItem.id, length: 0, roll: 0 }
if (colorItem.sale_mode === 0) {
data.roll = num
}
else {
data.length = Big(num).times(100).toNumber()
}
onChangeNum?.(data)
}, 300)
const selectList = SALE_MODE_SETTING
const saleSetting = useMemo(() => {
return selectList[colorItem.sale_mode]
}, [colorItem])
console.log('colorItem::', colorItem)
return <>
<View className={styles.shop_product_item_con}>
<View className={styles.shop_product_item}>
<MCheckbox />
<ColorCheckbox colorItem={colorItem} />
<View className={styles.item_con}>
<View className={styles.img}>
<LabAndImg value={{}}></LabAndImg>
<LabAndImg value={rga}></LabAndImg>
</View>
<View className={styles.item_name_price_count}>
<View className={styles.name_price}>
<Text>001# </Text>
<Text>37.50/kg</Text>
<Text>{colorItem.product_color_code + colorItem.product_color_name}</Text>
<View className={styles.price}>
<Text>¥{formatPriceDiv(colorItem.sale_price)}</Text>
<Text>¥{formatPriceDiv(colorItem.standard_price)}/kg</Text>
</View>
</View>
<View className={styles.count}>
<Counter defaultNum={1} />
<View className={styles.count_con}>
<View className={styles.recommend_title}>
{colorItem.is_screw_recommend
&& <>
<Text></Text>
<Text></Text>
</>
}
</View>
<View className={styles.count}>
<CustomWrapper>
<Counter
onClickBtn={getCount}
minNum={saleSetting.minNum}
maxNum={saleSetting.maxNum}
onBlue={getCount}
defaultNum={colorItem.sale_mode === 0 ? colorItem.roll : colorItem.length / 100}
unit={colorItem.sale_mode === 0 ? '条' : '米'}
digits={saleSetting.digits}
/>
</CustomWrapper>
</View>
</View>
</View>
</View>
</View>
<View className={styles.recommended}>
<Text></Text>
<View className={styles.icon}>
<IconFont name="icon-xiala" size={30} />
</View>
</View>
{colorItem.has_screw_recommend && <RecommendProduct shopId={colorItem.id} submitSuccess={onSubmitSuccess} />}
</View>
</>
}
})

View File

@ -0,0 +1,59 @@
.recommended_main {
.recommended {
height: 82px;
padding: 0 20px 0 50px;
background: #f6f6f6;
border-radius: 10px;
display: flex;
justify-content: space-between;
font-size: 24px;
align-items: center;
color: rgba(0, 0, 0, 0.8);
.icon {
transform: rotate(-90deg);
}
}
.recommended_open {
width: 654px;
height: 502px;
position: fixed;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-color: #fff;
z-index: 1600;
border-radius: 16px;
padding: 32px;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: flex-start;
.search {
padding-bottom: 22px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.onSubmit {
width: 590px;
height: 80px;
background: #337fff;
border-radius: 16px;
text-align: center;
line-height: 80px;
color: #fff;
font-size: 28px;
margin-top: 60px;
justify-self: end;
}
}
.mask {
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vh;
background: rgba(0, 0, 0, 0.6);
z-index: 1500;
}
}

View File

@ -0,0 +1,103 @@
import { Text, View } from '@tarojs/components'
import { memo, useCallback, useEffect, useRef, useState } from 'react'
import Big from 'big.js'
import Search from '../search'
import type { ItemType } from '../recommendProductItem'
import RecommendProductItem from '../recommendProductItem'
import styles from './index.module.scss'
import IconFont from '@/components/iconfont/iconfont'
import { AddShoppingCartApi, GetScrewProductApi } from '@/api/shopCart'
import type { listType, saleModeType } from '@/common/enum'
import { SALE_MODE_SETTING } from '@/common/enum'
interface ParamType {
shopId: number
submitSuccess?: () => void
}
type DataType = ItemType
interface submitType {
color_list: { product_color_id: number; roll: number; length: number }[]
sale_mode: saleModeType
is_screw_recommend: boolean
}
export default memo((props: ParamType) => {
const selectList = SALE_MODE_SETTING
const { shopId, submitSuccess } = props
const [filter, setFilter] = useState({
id: 0,
sale_mode: 0,
})
const submitData = useRef<submitType>({
sale_mode: 0,
color_list: [],
is_screw_recommend: true,
})
useEffect(() => {
(filter.id !== shopId) && setFilter(e => ({ ...e, id: shopId }))
}, [shopId])
const [productData, setProductData] = useState<DataType>()
const { fetchData: getScrewProductFetchData } = GetScrewProductApi()
const getScrewProduct = async() => {
const res = await getScrewProductFetchData({ id: filter.id, sale_mode: filter.sale_mode })
if (res.success) {
res.data = {
...res.data,
sale_mode_type: filter.sale_mode,
}
onSubmitData(res.data.defaultNum, res.data.screw_color_id)
setProductData(res.data)
}
}
const [showOpen, setShowOpen] = useState(false)
useEffect(() => {
showOpen && getScrewProduct()
}, [showOpen, filter])
const getSelect = useCallback((val: listType) => {
submitData.current.sale_mode = val.value
setFilter(e => ({ ...e, sale_mode: val.value }))
}, [])
const onChangeNum = useCallback((num) => {
onSubmitData(num, productData?.screw_color_id)
}, [productData])
// 整理提交数据
const onSubmitData = (num = 0, product_color_id = 0) => {
const data = { product_color_id, length: 0, roll: 0 }
if (filter.sale_mode === 0) {
data.roll = num
}
else {
data.length = Big(num).times(100).toNumber()
}
submitData.current.color_list = [data]
}
const { fetchData: addShoppingCartfetchData } = AddShoppingCartApi()
const onSubmit = useCallback(async() => {
const res = await addShoppingCartfetchData(submitData.current)
if (res.success) {
submitSuccess?.()
}
}, [])
return <View className={styles.recommended_main}>
<View className={styles.recommended} onClick={() => setShowOpen(true)}>
<Text></Text>
<View className={styles.icon}>
<IconFont name="icon-xiala" size={30} />
</View>
</View>
{showOpen && <View className={styles.recommended_open}>
<View className={styles.search}>
<Search onSelect={getSelect} numberStatus={false} defaultIndex={filter.sale_mode} />
</View>
<RecommendProductItem colorItem={productData!} onChangeNum={onChangeNum} />
<View className={styles.onSubmit} onClick={onSubmit}></View>
</View>}
{showOpen && <View catchMove className={styles.mask} onClick={() => setShowOpen(false)}></View>}
</View>
})

View File

@ -0,0 +1,101 @@
.shop_product_item_con {
padding-bottom: 20px;
position: relative;
&:nth-child(n + 3) {
&::before {
content: '';
height: 1px;
width: 100%;
position: absolute;
background-color: rgba(0, 0, 0, 0.1);
top: 0;
left: 0;
transform: scaleY(0.5);
}
}
.shop_product_item {
display: flex;
flex-direction: column;
height: 166px;
.title_desc {
flex: 1;
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
position: relative;
padding: 24px 0;
box-sizing: border-box;
.model {
display: flex;
align-items: center;
color: rgba(0, 0, 0, 0.8);
.label {
width: 60px;
height: 28px;
font-size: 20px;
background-color: #4581ff;
color: #fff;
text-align: center;
align-items: center;
border-radius: 10px;
margin-left: 16px;
}
}
}
.item_con {
display: flex;
flex: 1;
height: 100%;
align-items: center;
position: relative;
.img {
width: 118px;
height: 118px;
}
.item_name_price_count {
flex: 1;
margin-left: 24px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 118px;
.name_price {
display: flex;
justify-content: space-between;
font-size: 28px;
.price {
text {
&:nth-child(1) {
color: #999999;
text-decoration: line-through;
}
&:nth-child(2) {
color: #f41a39ff;
margin-left: 10px;
}
}
}
}
.count {
width: 201px;
align-self: flex-end;
}
}
}
}
.recommended {
height: 82px;
padding: 0 20px 0 50px;
background: #f6f6f6;
border-radius: 10px;
display: flex;
justify-content: space-between;
font-size: 24px;
align-items: center;
color: rgba(0, 0, 0, 0.8);
.icon {
transform: rotate(-90deg);
}
}
}

View File

@ -0,0 +1,108 @@
import { CustomWrapper, Text, View } from '@tarojs/components'
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
import ColorCheckbox from '../colorCheckbox'
import RecommendProduct from '../recommendProduct'
import styles from './index.module.scss'
import LabAndImg from '@/components/LabAndImg'
import Counter from '@/components/counter'
import { formatHashTag, formatPriceDiv } from '@/common/fotmat'
import { debounce } from '@/common/util'
import type { listType } from '@/common/enum'
import { SALE_MODE_SETTING, saleModeType } from '@/common/enum'
export interface ItemType {
lab: { l: number; a: number; b: number }
rgb: { r: number; g: number; b: number }
sale_offset: number
sale_price: number
screw_code: string
screw_color_code: string
screw_color_id: number
screw_color_name: string
screw_id: number
screw_name: string
standard_price: number
texture_url: string
weight_error: number
sale_mode_type: 0|1|2
sale_mode: '大货'|'剪板'|'散剪'
unit: '条'|'米'
eunit: 'kg'|'m'
step: number
digits: number
minNum: number
maxNum: number
defaultNum: number
}
export interface ProductType {
colorItem: ItemType
onChangeNum?: (val: number) => void
}
export default memo((props: ProductType) => {
return <Index {...props} />
})
const Index = memo((props: ProductType) => {
const { colorItem, onChangeNum } = props
const rga = useMemo(() => {
return { lab: colorItem?.lab, rgb: colorItem?.rgb, texture_url: colorItem?.texture_url, title: colorItem?.screw_color_code }
}, [colorItem])
const getCount = debounce((num: number) => {
setDefaultNum(num)
}, 300)
const selectList: listType[] = SALE_MODE_SETTING
const saleSetting: listType = useMemo(() => {
console.log('colorItem123123::', colorItem)
return colorItem && selectList[colorItem.sale_mode_type]
}, [colorItem?.sale_mode_type])
const [defaultNum, setDefaultNum] = useState(0)
useEffect(() => {
if (colorItem) {
setDefaultNum(selectList[colorItem.sale_mode_type].defaultNum)
}
}, [colorItem?.sale_mode_type])
useEffect(() => {
onChangeNum?.(defaultNum)
}, [defaultNum])
return <>
<View className={styles.shop_product_item_con}>
<View className={styles.shop_product_item}>
<View className={styles.title_desc}>
<View className={styles.model}>
<Text className={styles.name}>{formatHashTag(colorItem?.screw_code, colorItem?.screw_name)}</Text>
<Text className={styles.label}>{saleSetting?.title}</Text>
</View>
</View>
<View className={styles.item_con}>
<View className={styles.img}>
<LabAndImg value={rga}></LabAndImg>
</View>
<View className={styles.item_name_price_count}>
<View className={styles.name_price}>
<Text>{colorItem?.screw_color_code + colorItem?.screw_color_name}</Text>
<View className={styles.price}>
<Text>¥{formatPriceDiv(colorItem?.sale_price)}</Text>
<Text>¥{formatPriceDiv(colorItem?.standard_price)}/kg</Text>
</View>
</View>
<View className={styles.count}>
<CustomWrapper>
<Counter
onClickBtn={getCount}
minNum={saleSetting?.minNum}
maxNum={saleSetting?.maxNum}
onBlue={getCount}
defaultNum={defaultNum}
unit={saleSetting?.unit}
digits={saleSetting?.digits}
/>
{/* <Counter onClickBtn={getCount} maxNum={colorItem?.maxNum} minNum={colorItem?.minNum} onBlue={getCount} defaultNum={colorItem?.defaultNum} unit={colorItem?.sale_mode_type === 0 ? '条' : '米'} /> */}
</CustomWrapper>
</View>
</View>
</View>
</View>
</View>
</>
})

View File

@ -1,14 +1,20 @@
.shop_search {
display: flex;
justify-content: space-between;
display: grid;
grid-template-columns: repeat(3, auto);
grid-column-gap: 16px;
justify-items: stretch;
height: 100px;
align-items: center;
// padding: 0 24px;
box-sizing: border-box;
.search_item {
width: 100%;
height: 68px;
width: 224px;
flex: 1;
border-radius: 68px;
text-align: center;
line-height: 68px;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
background-color: #f8f8f8;
color: rgba(0, 0, 0, 0.8);

View File

@ -1,23 +1,32 @@
import { Text, View } from '@tarojs/components'
import { memo, useEffect, useState } from 'react'
import { memo, useEffect, useMemo, useState } from 'react'
import classNames from 'classnames'
import styles from './index.module.scss'
import type { StateType } from '@/context/ContextShop'
import type { listType } from '@/common/enum'
import { SALE_MODE_SETTING } from '@/common/enum'
interface Params {
defaultIndex?: number
onSelect?: (val: number) => void
onSelect?: (val: listType) => void
numberObj?: {
'bulk_number': number
'length_length': number
'weight_number': number }
numberStatus?: boolean
}
export default memo((props: Params) => {
const selectList = [
{ value: 0, title: '大货', unit: '条', eunit: 'kg', step: 1, digits: 0, minNum: 1, maxNum: 100000, defaultNum: 1 },
{ value: 1, title: '剪板', unit: '米', eunit: 'm', step: 1, digits: 2, minNum: 0.5, maxNum: 9.99, defaultNum: 1 },
{ value: 2, title: '散剪', unit: '米', eunit: 'kg', step: 1, digits: 2, minNum: 3, maxNum: 100000, defaultNum: 3 },
]
const { defaultIndex = 0, onSelect } = props
const { numberStatus = true } = props
const selectList = SALE_MODE_SETTING
const { defaultIndex = 0, onSelect, numberObj } = props
const [index, setIndex] = useState(0)
useEffect(() => {
defaultIndex !== index && setIndex(defaultIndex)
}, [defaultIndex])
const getName = (item) => {
return item.title + (numberStatus ? `${numberObj?.[item.field]}` : '')
}
return <View className={styles.shop_search}>
{selectList?.map(item => <View key={item.value} onClick={() => onSelect?.(item.value)} className={styles.search_item}>{item.title}</View>)}
{selectList?.map(item => <View key={item.value} onClick={() => onSelect?.(item)} className={classNames(styles.search_item, index === item.value && styles.search_item_select)}>{getName(item)}</View>)}
</View>
})

View File

@ -0,0 +1,3 @@
export default {
navigationBarTitleText: '购物车',
}

View File

@ -1,36 +0,0 @@
.shop_main {
min-height: 100vh;
background-color: #f8f8f8;
.shop_header {
padding: 0 24px;
background-color: #fff;
.shop_header_sort {
height: 88px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28px;
.sort {
display: flex;
align-items: center;
background-color: rgb(245, 245, 245);
height: 55px;
padding: 0 24px;
box-sizing: border-box;
border-radius: 55px;
font-size: 25px;
}
.edit {
display: flex;
align-items: center;
text {
margin-left: 10px;
}
}
}
}
.shop_con {
padding: 0 24px;
padding-bottom: 150px;
}
}

View File

@ -1,82 +1,8 @@
import { Text, View } from '@tarojs/components'
import React, { ReactHTMLElement, memo, useEffect, useRef, useState } from 'react'
import styles from './index.module.scss'
import Search from './components/search'
import ProductBlock from './components/productBlock'
import BottomBtn from './components/bottomBtn'
import Screwed from './components/screwed'
import IconFont from '@/components/iconfont/iconfont'
import Main from './components/main'
import ContextShop from '@/context/ContextShop'
export default () => {
useEffect(() => {
initData()
}, [])
const [data, setData] = useState<any[]>([])
const initData = () => {
const product_list: any = []
new Array(10).fill('').map((item, index) => {
const product: any = {
product_code: `100${index}`,
product_id: index + 1,
product_name: '26s全棉平纹',
color_list: [],
}
new Array(130).fill('').map((citem, cindex) => {
product.color_list.push({
estimate_amount: 100,
estimate_weight: 100,
id: 0,
lab: {
a: 0,
b: 0,
l: 0,
},
length: 0,
product_code: '00',
product_color_code: `00${cindex}}`,
product_color_id: cindex + 1,
product_color_name: '环保黑',
product_id: 0,
product_name: 'string',
rgb: {
b: 0,
g: 0,
r: 0,
},
roll: 10,
sale_mode: 0,
sale_mode_name: '大货',
sale_offset: 500,
sale_price: 500,
standard_price: 500,
texture_url: '',
weight_error: 500,
})
})
product_list.push(product)
})
setData(product_list)
}
return <View className={styles.shop_main}>
<View className={styles.shop_header}>
<View className={styles.shop_header_sort}>
<View className={styles.sort}>
<IconFont name="icon-paixu" size={30} />
<Text></Text>
</View>
<View className={styles.edit}>
<IconFont name="icon-guanli" size={43} />
<Text></Text>
</View>
</View>
<Search />
</View>
<View className={styles.shop_con}>
{data?.map((item, index) => <ProductBlock key={index} />)}
</View>
<BottomBtn />
{/* <Screwed /> */}
<View className="common_safe_area_y"></View>
</View>
return <ContextShop>
<Main />
</ContextShop>
}