✨ feat: 优化码单管理
This commit is contained in:
parent
e8f050bcbb
commit
dd3963be99
@ -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 }
|
||||
}
|
||||
|
||||
72
src/context/ContextShop/reducers.ts
Normal file
72
src/context/ContextShop/reducers.ts
Normal 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
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
})
|
||||
|
||||
33
src/pages/shopCar/components/colorCheckbox/index.tsx
Normal file
33
src/pages/shopCar/components/colorCheckbox/index.tsx
Normal 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)} />
|
||||
})
|
||||
28
src/pages/shopCar/components/main/index.module.scss
Normal file
28
src/pages/shopCar/components/main/index.module.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
242
src/pages/shopCar/components/main/index.tsx
Normal file
242
src/pages/shopCar/components/main/index.tsx
Normal 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>
|
||||
}
|
||||
23
src/pages/shopCar/components/noShop/index.module.scss
Normal file
23
src/pages/shopCar/components/noShop/index.module.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
13
src/pages/shopCar/components/noShop/index.tsx
Normal file
13
src/pages/shopCar/components/noShop/index.tsx
Normal 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>
|
||||
})
|
||||
82
src/pages/shopCar/components/operation/index.module.scss
Normal file
82
src/pages/shopCar/components/operation/index.module.scss
Normal 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);
|
||||
}
|
||||
52
src/pages/shopCar/components/operation/index.tsx
Normal file
52
src/pages/shopCar/components/operation/index.tsx
Normal 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>}
|
||||
</>
|
||||
})
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
}
|
||||
})
|
||||
|
||||
33
src/pages/shopCar/components/productCheckbox/index copy.tsx
Normal file
33
src/pages/shopCar/components/productCheckbox/index copy.tsx
Normal 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)} />
|
||||
})
|
||||
32
src/pages/shopCar/components/productCheckbox/index.tsx
Normal file
32
src/pages/shopCar/components/productCheckbox/index.tsx
Normal 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)} />
|
||||
})
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
</>
|
||||
}
|
||||
})
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
103
src/pages/shopCar/components/recommendProduct/index.tsx
Normal file
103
src/pages/shopCar/components/recommendProduct/index.tsx
Normal 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>
|
||||
})
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
108
src/pages/shopCar/components/recommendProductItem/index.tsx
Normal file
108
src/pages/shopCar/components/recommendProductItem/index.tsx
Normal 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>
|
||||
</>
|
||||
})
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
})
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
export default {
|
||||
navigationBarTitleText: '购物车',
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user