bug:订单详情问题修复

This commit is contained in:
jzp 2025-11-12 22:03:36 +08:00
parent bb958ce2fe
commit c4f85aa9dc
3 changed files with 179 additions and 83 deletions

View File

@ -1,46 +1,72 @@
export type PrDetailType = {
adminCategoryId1: 146
adminCategoryId2: 147
appCategoryId1: 48
appCategoryId2: 56
createTime: 1762760434000
defaultSort: 0
frontPage: 1
id: 73
isDelete: 0
isFlash: 0
isNew: 1
isTest: 0
mainImageUrl: 'https://heyuimage.ihzhy.com/prd/202511/9a77567562f87caf.jpg?key=xxxxxxx'
modifyTime: 1762760434000
realSaleCount: 0
showPromotionPrice: 0
showSaleCount: 0
showSalePrice: 0
status: 'online'
title: 'Akoya双珠海水戒指'
export type ProductDetailType = {
adminCategoryId1: number
adminCategoryId2: number
appCategoryId1: number
appCategoryId2: number
createTime: number
defaultSort: number
frontPage: number
id: number
isDelete: number
isFlash: number
isNew: number
isTest: number
mainImageUrl: string
modifyTime: number
realSaleCount: number
showPromotionPrice: number
showSaleCount: number
showSalePrice: number
status: string
title: string
vvSkuList: SkuType[]
vvProductDetailList: []
vvProductPropertyList: []
vvProductDetailList: ProductDetailImagesType[]
vvProductPropertyList: ProudictProductPropertyType[]
}
export type SkuType = {
createTime: number
id: 364
isDelete: 0
modifyTime: 1762760434000
productId: 73
promotionPrice: 0
showSaleCount: 0
stock: 0
id: number
isDelete: number
modifyTime: number
productId: number
promotionPrice: number
showSaleCount: number
stock: number
imageUrl: string
salePrice: number
vvSkuPropertyValueList: SkuPropretyType[]
}
export type SkuPropretyType = {
createTime: number
id: number
isDelete: number
modifyTime: number
productId: number
productPropertyName: string
productPropertyValue: string
skuId: number
}
export type ProductDetailImagesType = {
createTime: 1762760434000
detail: 'https://heyuimage.ihzhy.com/prd/202511/304182481ebfc9ab.jpg?key=xxxxxxx'
id: 187
isDelete: 0
modifyTime: 1762760434000
productId: 73
type: 1
createTime: number
detail: string
id: number
isDelete: number
modifyTime: number
productId: number
type: number
}
export type ProudictProductPropertyType = {
createTime: number
defaultSort: number
id: number
isDelete: number
modifyTime: number
productId: number
productPropertyName: string
vvProductPropertyList?: ProudictProductPropertyType[] // 如需树状结构可用此字段
vvProductPropertyValueList: ProudictProductPropertyType[] // 按你原写法保持不变
}

View File

@ -15,8 +15,8 @@ const service: AxiosInstance = axios.create({
timeout: 150000, // 请求超时时间
})
const noEncryptWhiteList = ['/common/protocol/content', '/portal/common/protocol/content', '/bl-log/web/log/add']
const noConsoleWhiteList = ['bl-log/web/log/add']
// const noEncryptWhiteList = ['/common/protocol/content', '/portal/common/protocol/content', '/bl-log/web/log/add']
// const noConsoleWhiteList = ['bl-log/web/log/add']
// request拦截器
service.interceptors.request.use(

View File

@ -6,7 +6,7 @@
<div class="cart-wrap">
<p class="flex justify-between" @click="showAddressList = true">
<van-icon name="location-o" class="mr-1" /><span class="max-w-12 mr-1">{{ curAddressData?.buyerName }}</span>
<b class="font- flex-1">{{ (curAddressData?.province ?? '') + (curAddressData?.city ?? '') + (curAddressData?.district ?? '') + (curAddressData?.detail?? '') }}</b
<b class="font- flex-1">{{ (curAddressData?.province ?? '') + (curAddressData?.city ?? '') + (curAddressData?.district ?? '') + (curAddressData?.detail ?? '') }}</b
><van-icon name="arrow" />
</p>
</div>
@ -19,6 +19,13 @@
</p>
<p>
<van-stepper v-model="formData.num" input-width=".4rem" button-size=".2rem" min="1" :max="curPageData.num" />
<span class="text-[10px] text-hex-cccccc ml-[5px]">
(最大可购买{{ curPageData?.num }})
</span>
<span class="text-[10px] text-hex-cccccc ml-[3px]">
{{ curPageData?.num > 0 ? "有货" : "缺货"}}
</span>
</p>
</div>
</div>
@ -28,11 +35,11 @@
<div class="cagetory">
<p
v-for="v in arr"
:key="v.propertyId"
:class="{ 'theme-border-color theme-color': v.propertyId === curPropertyIds.find((it) => it.productPropertyName === key).id }"
@click="onChooseProperty(v, key)"
:key="v.sid"
:class="[{ 'theme-border-color theme-color': v.label === curSelection[key] }, { disabled: v.disabled }]"
@click="!v.disabled && onChooseProperty(v, key)"
>
{{ v?.label }}
{{ v.label }} {{ v.num }}
</p>
</div>
</template>
@ -49,59 +56,109 @@
<script lang="ts" setup>
import api from '@/api'
import { requestPayment } from '@/utils/wx-minprogram'
import type { SkuType } from '@/api/types/shop'
const model = defineModel<boolean>()
const showAddressList = defineModel<boolean>('showAddressList')
const props = defineProps<{ list: Array<any>; curAddressData: Recordable<any>; scene: 'order' | 'cart' }>()
const skuData = ref<any>({}) // skukeyvalue{ : [{ ''}]}
const commodityData = ref<any>({}) // valueskuId(keyid)
const curPropertyIds = ref<any>([]) // id[{ id: 1, name: ''},{id: 2, name: ''}] name
const curPageData = ref<any>({ price: '', imageUrl: '', num: 0 }) //
const formData = ref<any>({ skuId: NaN, num: 1 })
type SkuOption = { sid: string; label: string; num: number; disabled?: boolean }
const skuData = ref<Record<string, SkuOption[]>>({}) // skukey
const commodityData = ref<Record<string, { id: number; imageUrl?: string; salePrice?: number; price?: number; num: number }>>({}) // sku
const categoryOrder = ref<string[]>([]) //
const curSelection = ref<Record<string, string>>({})
const curPageData = ref({ price: 0, imageUrl: '', num: 0 })
const formData = ref({ skuId: NaN, num: 1 })
// pathset
const buildSignature = (order: string[], selection: Record<string, string>) => order.map((name) => `${name}:${selection[name] ?? ''}`).join('|')
// id
const buildOptionSid = (vv: SkuType['vvSkuPropertyValueList'][number]) => `${vv.productPropertyName}:${vv.productPropertyValue}`
watch(model, (val) => {
if (val) {
const { result, commodityMap, firstShopIds } = handleGetSkuList(props.list)
skuData.value = result
commodityData.value = commodityMap
curPropertyIds.value = firstShopIds
const { pageData, skuId } = handleGetPageData(commodityData.value, curPropertyIds.value)
curPageData.value = pageData
formData.value = { skuId, num: 1 }
}
if (!val) return
const { result, commodityMap, firstSelection } = handleGetSkuList(props.list)
skuData.value = result
commodityData.value = commodityMap
curSelection.value = firstSelection
syncCurrentSku()
updateOptionDisabled()
})
const handleGetSkuList = (list: any[]) => {
const result = {} as any // sku
const commodityMap = {} //
let firstShopIds = []
list.forEach((item, i) => {
const commodityValue = { id: item.id, imageUrl: item.imageUrl, salePrice: item.salePrice, price: item.promotionPrice, num: item.stock }
let commodityPropertyId = ''
if (i === 0) {
firstShopIds = item.vvSkuPropertyValueList.map((child) => ({ id: child.id, productPropertyName: child.productPropertyName }))
}
item.vvSkuPropertyValueList.forEach((item2, index) => {
commodityPropertyId += item2.id + (index < item.vvSkuPropertyValueList.length - 1 ? '_' : '')
if (!result[item2.productPropertyName]) result[item2.productPropertyName] = []
if (result[item2.productPropertyName].some((child) => child.propertyId === item2.id)) return
result[item2.productPropertyName].push({ propertyId: item2.id, label: item2.productPropertyValue })
//
const updateOptionDisabled = () => {
Object.entries(skuData.value).forEach(([categoryName, options]) => {
options.forEach((opt) => {
const trialSelection = { ...curSelection.value, [categoryName]: opt.label }
const match = commodityData.value[buildSignature(categoryOrder.value, trialSelection)]
opt.disabled = !match || (match.num ?? 0) <= 0
})
})
}
const handleGetSkuList = (list: SkuType[]) => {
const result: Record<string, SkuOption[]> = {}
const commodityMap: Record<string, { id: number; imageUrl?: string; salePrice?: number; price?: number; num: number }> = {}
//
const order = list[0]?.vvSkuPropertyValueList?.map((vv) => vv.productPropertyName) || []
categoryOrder.value = order
let firstSelection: Record<string, string> = {}
list.forEach((sku, index) => {
//
if (index === 0) {
firstSelection = Object.fromEntries(sku.vvSkuPropertyValueList.map((vv) => [vv.productPropertyName, vv.productPropertyValue]))
}
//
const selection = Object.fromEntries(sku.vvSkuPropertyValueList.map((vv) => [vv.productPropertyName, vv.productPropertyValue]))
//
const comboKey = buildSignature(order, selection)
commodityMap[comboKey] = {
id: sku.id,
imageUrl: sku.imageUrl,
salePrice: sku.salePrice,
price: sku.promotionPrice,
num: sku.stock,
}
//
sku.vvSkuPropertyValueList.forEach((vv, idx) => {
if (!result[vv.productPropertyName]) result[vv.productPropertyName] = []
const bucket = result[vv.productPropertyName]
const sid = buildOptionSid(vv)
const existing = bucket.find((item) => item.sid === sid)
const isLeafProperty = idx === order.length - 1
if (existing) {
// SKU SKU
existing.num = isLeafProperty ? sku.stock : existing.num + sku.stock
} else {
bucket.push({
sid,
label: vv.productPropertyValue,
num: sku.stock,
disabled: isLeafProperty ? sku.stock <= 0 : false,
})
}
})
commodityMap[commodityPropertyId] = commodityValue
})
return { result, commodityMap, firstShopIds }
return { result, commodityMap, firstSelection }
}
const onChooseProperty = (v, categoryName) => {
curPropertyIds.value.find((it) => it.productPropertyName === categoryName).id = v.propertyId
const { pageData, skuId } = handleGetPageData(commodityData.value, curPropertyIds.value)
curPageData.value = pageData
formData.value = { skuId, num: 1 }
if (v.disabled) return
curSelection.value[categoryName] = v.label
syncCurrentSku()
updateOptionDisabled()
}
const handleGetPageData = (commodityData, curPropertyIds) => {
const data = commodityData[curPropertyIds.map((item) => item.id).join('_')]
return { pageData: { price: data?.price, imageUrl: data?.imageUrl, num: data?.num }, skuId: data?.id }
const syncCurrentSku = () => {
const data = commodityData.value[buildSignature(categoryOrder.value, curSelection.value)]
curPageData.value = { price: data?.price ?? 0, imageUrl: data?.imageUrl ?? '', num: data?.num ?? 0 }
formData.value = { skuId: data?.id ?? NaN, num: 1 }
}
const onSubmit = async () => {
@ -125,15 +182,18 @@ const onSubmit = async () => {
border-radius: 0.04rem;
overflow: hidden;
}
.cart-cagetory-wrap {
h4 {
font-weight: bold;
margin-top: 0.16rem;
margin-bottom: 0.12rem;
}
.cagetory {
display: flex;
flex-wrap: wrap;
& > p {
border: 1px solid #f2f2f2;
box-sizing: border-box;
@ -146,14 +206,24 @@ const onSubmit = async () => {
border-radius: 0.04rem;
margin-right: 0.08rem;
margin-bottom: 0.08rem;
&.theme-border-color {
background: #fff;
}
&.disabled {
background: #eee;
color: #bbb;
border-color: #eee;
pointer-events: none;
}
}
}
}
&.van-popup--bottom.van-popup--round {
border-radius: 0.08rem 0.08rem 0 0;
:deep(.van-popup__close-icon) {
top: 0.06rem;
font-size: 0.2rem;