2023-10-11 11:27:47 +08:00
|
|
|
|
<template>
|
2023-11-14 17:21:03 +08:00
|
|
|
|
<Popup
|
|
|
|
|
ref="popupRef"
|
|
|
|
|
:showCloseable="false"
|
|
|
|
|
@close="emit('close')"
|
2023-10-11 11:27:47 +08:00
|
|
|
|
>
|
|
|
|
|
<view
|
2023-11-14 17:21:03 +08:00
|
|
|
|
class="goodAttrSelect"
|
2023-10-11 11:27:47 +08:00
|
|
|
|
>
|
2023-11-14 17:21:03 +08:00
|
|
|
|
<view
|
|
|
|
|
class="goodAttrSelect-goods"
|
|
|
|
|
v-if="curAttr"
|
|
|
|
|
>
|
|
|
|
|
<uv-image
|
|
|
|
|
class="attr-image"
|
|
|
|
|
:src="curAttr.image || detailData.storeInfo.image"
|
|
|
|
|
width="150rpx"
|
|
|
|
|
height="150rpx"
|
|
|
|
|
></uv-image>
|
|
|
|
|
<view class="attr-info">
|
|
|
|
|
<view class="name">
|
|
|
|
|
{{ storeInfo.storeName }}
|
|
|
|
|
</view>
|
|
|
|
|
<view class="attr-info-bottom">
|
|
|
|
|
<view class="price">¥{{ curAttr.price }}</view>
|
|
|
|
|
<view class="stock">库存:{{ curAttr.stock }}</view>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
2023-10-11 11:27:47 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
<div class="line"></div>
|
2023-11-14 17:21:03 +08:00
|
|
|
|
<view
|
|
|
|
|
class="goodAttrSelect-attr row"
|
|
|
|
|
v-if="curAttr"
|
|
|
|
|
>
|
2023-10-11 11:27:47 +08:00
|
|
|
|
<view class="goodAttrSelect-attr-title">
|
|
|
|
|
数量
|
|
|
|
|
</view>
|
|
|
|
|
<view class="goodAttrSelect-attr-content">
|
2023-11-14 17:21:03 +08:00
|
|
|
|
<!-- cart number -->
|
|
|
|
|
<view
|
|
|
|
|
class="cart-num flex flex-ai__center flex-jc__sb"
|
|
|
|
|
@click.stop=""
|
|
|
|
|
>
|
|
|
|
|
<view
|
|
|
|
|
class="button"
|
|
|
|
|
@click="handleCartNumberChange(curAttr,'minus')"
|
|
|
|
|
>
|
|
|
|
|
<uv-icon
|
|
|
|
|
name="minus"
|
|
|
|
|
color="#333"
|
|
|
|
|
size="12"
|
|
|
|
|
></uv-icon>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="input">
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
inputmode="numeric"
|
|
|
|
|
v-model="storeNum"
|
|
|
|
|
@blur="(e)=>handleCartNumberInputChange(e,curAttr)"
|
|
|
|
|
>
|
|
|
|
|
</view>
|
|
|
|
|
<view
|
|
|
|
|
class="button"
|
|
|
|
|
@click="handleCartNumberChange(curAttr,'plus')"
|
|
|
|
|
>
|
|
|
|
|
<uv-icon
|
|
|
|
|
name="plus"
|
|
|
|
|
color="#333"
|
|
|
|
|
size="12"
|
|
|
|
|
></uv-icon>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<!-- <uv-number-box
|
|
|
|
|
v-model="storeNum"
|
|
|
|
|
min="1"
|
|
|
|
|
:max="curAttr.stock"
|
|
|
|
|
></uv-number-box>-->
|
2023-10-11 11:27:47 +08:00
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<div class="line"></div>
|
|
|
|
|
|
|
|
|
|
<view
|
2023-11-14 17:21:03 +08:00
|
|
|
|
class="goodAttrSelect-attr"
|
|
|
|
|
v-for="(item, index) in productAttr"
|
|
|
|
|
:key="index"
|
2023-10-11 11:27:47 +08:00
|
|
|
|
>
|
|
|
|
|
<view class="goodAttrSelect-attr-title">
|
|
|
|
|
{{ item.attrName }}
|
|
|
|
|
</view>
|
|
|
|
|
<view class="goodAttrSelect-attr-content">
|
2023-11-14 17:21:03 +08:00
|
|
|
|
<space wrap="warp">
|
2023-10-11 11:27:47 +08:00
|
|
|
|
<view
|
2023-11-14 17:21:03 +08:00
|
|
|
|
:class="{ attr: true, check: selectedAttr[index] === attr }"
|
|
|
|
|
v-for="(attr, attrIndex) in item.attrValueArr"
|
|
|
|
|
:key="attrIndex"
|
|
|
|
|
@tap="handleSelectAttr(index, attr)"
|
|
|
|
|
>{{ attr }}
|
|
|
|
|
</view>
|
2023-10-11 11:27:47 +08:00
|
|
|
|
</space>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
|
|
|
|
<view class="goodAttrSelect-action">
|
|
|
|
|
<uv-button
|
2023-11-14 17:21:03 +08:00
|
|
|
|
round
|
|
|
|
|
block
|
|
|
|
|
type="primary"
|
|
|
|
|
@tap="handleSubmit"
|
2023-10-11 11:27:47 +08:00
|
|
|
|
>
|
|
|
|
|
确定
|
|
|
|
|
</uv-button>
|
|
|
|
|
</view>
|
|
|
|
|
</view>
|
2023-11-14 17:21:03 +08:00
|
|
|
|
</Popup>
|
2023-10-11 11:27:47 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2023-11-14 17:21:03 +08:00
|
|
|
|
import { ref, unref } from 'vue';
|
|
|
|
|
import { getProductDetail } from '@/api/product'
|
|
|
|
|
import { useInterface } from "@/hooks/useInterface";
|
|
|
|
|
import Popup from '@/components/Popup/index.vue';
|
2023-10-11 11:27:47 +08:00
|
|
|
|
|
|
|
|
|
const props = defineProps(["id"])
|
|
|
|
|
|
2023-11-14 17:21:03 +08:00
|
|
|
|
const emit = defineEmits(['onSelect', 'submit', 'close'])
|
2023-10-11 11:27:47 +08:00
|
|
|
|
|
|
|
|
|
const popupRef = ref()
|
2023-11-14 17:21:03 +08:00
|
|
|
|
const selectedAttr = ref([])
|
2023-10-11 11:27:47 +08:00
|
|
|
|
const visible = ref(false)
|
|
|
|
|
const detailData = ref(null)
|
|
|
|
|
const storeInfo = ref(null)
|
|
|
|
|
const productAttr = ref(null)
|
|
|
|
|
const productValue = ref(null)
|
|
|
|
|
const storeNum = ref(1)
|
2023-11-14 17:21:03 +08:00
|
|
|
|
const curAttr = ref(null)
|
|
|
|
|
const defaultSelectAttrStr = ref(undefined)
|
2023-10-11 11:27:47 +08:00
|
|
|
|
|
|
|
|
|
const selectAttrPanel = ref(false)
|
|
|
|
|
|
|
|
|
|
const handleGetDetail = async (id) => {
|
|
|
|
|
const detail = await getProductDetail(id)
|
|
|
|
|
if (detail) {
|
|
|
|
|
detailData.value = detail
|
|
|
|
|
storeInfo.value = detail.storeInfo
|
|
|
|
|
productAttr.value = detail.productAttr
|
|
|
|
|
productValue.value = detail.productValue
|
2023-11-14 17:21:03 +08:00
|
|
|
|
if (!defaultSelectAttrStr.value) {
|
|
|
|
|
// 设置默认选中
|
|
|
|
|
let attr = []
|
|
|
|
|
detail.productAttr.forEach((item, i) => {
|
|
|
|
|
attr[i] = item.attrValueArr[0]
|
|
|
|
|
})
|
|
|
|
|
selectedAttr.value = attr
|
|
|
|
|
let selectedAttrStr = selectedAttr.value.join(',')
|
|
|
|
|
curAttr.value = productValue.value[selectedAttrStr]
|
|
|
|
|
} else {
|
|
|
|
|
selectedAttr.value = unref(defaultSelectAttrStr).split(',')
|
|
|
|
|
curAttr.value = productValue.value[defaultSelectAttrStr.value]
|
|
|
|
|
}
|
2023-10-11 11:27:47 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 17:21:03 +08:00
|
|
|
|
const handleSelectAttr = (index, attr) => {
|
|
|
|
|
selectedAttr.value[index] = attr
|
|
|
|
|
let selectedAttrStr = selectedAttr.value.join(',')
|
|
|
|
|
curAttr.value = productValue.value[selectedAttrStr]
|
2023-10-11 11:27:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleSubmit = () => {
|
2023-11-14 17:21:03 +08:00
|
|
|
|
// let value = []
|
|
|
|
|
// productAttr.value.map(item => {
|
|
|
|
|
// value.push(selectedAttr.value[item.attrName] || '')
|
|
|
|
|
// })
|
|
|
|
|
|
|
|
|
|
// if (value.includes('')) {
|
|
|
|
|
// uni.showToast({
|
|
|
|
|
// title: '请选择规格',
|
|
|
|
|
// icon: 'none',
|
|
|
|
|
// duration: 2000,
|
|
|
|
|
// })
|
|
|
|
|
// return
|
|
|
|
|
// }
|
|
|
|
|
let selectedAttrStr = selectedAttr.value.join(',')
|
|
|
|
|
emit('submit', {
|
|
|
|
|
store: productValue.value[selectedAttrStr],
|
|
|
|
|
num: storeNum.value
|
2023-10-11 11:27:47 +08:00
|
|
|
|
})
|
|
|
|
|
emit('select', {
|
2023-11-14 17:21:03 +08:00
|
|
|
|
store: productValue.value[selectedAttrStr],
|
2023-10-11 11:27:47 +08:00
|
|
|
|
num: storeNum.value
|
|
|
|
|
})
|
2023-11-14 17:21:03 +08:00
|
|
|
|
emit('onSelect', {
|
|
|
|
|
store: productValue.value[selectedAttrStr],
|
|
|
|
|
num: storeNum.value
|
|
|
|
|
})
|
|
|
|
|
|
2023-10-11 11:27:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 17:21:03 +08:00
|
|
|
|
const {toast} = useInterface()
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户手动输入改变数量
|
|
|
|
|
* @param e
|
|
|
|
|
* @param item
|
|
|
|
|
* @returns {*}
|
|
|
|
|
*/
|
|
|
|
|
function handleCartNumberInputChange(e, item) {
|
|
|
|
|
const value = Number(e.detail.value)
|
|
|
|
|
if (value <= 0) {
|
|
|
|
|
storeNum.value = 1
|
|
|
|
|
toast({title: '至少选一件哦~'})
|
|
|
|
|
}
|
|
|
|
|
if (value > item.stock) {
|
|
|
|
|
storeNum.value = item.stock
|
|
|
|
|
toast({title: '超出库存啦~'})
|
|
|
|
|
}
|
|
|
|
|
storeNum.value = value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 用户点击购物车+-改变数量
|
|
|
|
|
* @param item
|
|
|
|
|
* @param type
|
|
|
|
|
* @returns {*}
|
|
|
|
|
*/
|
|
|
|
|
function handleCartNumberChange(item, type = 'plus') {
|
|
|
|
|
if (type === 'plus') {
|
|
|
|
|
// +
|
|
|
|
|
if (storeNum.value + 1 > item.stock) return toast({title: '超出库存啦~'})
|
|
|
|
|
storeNum.value += 1
|
|
|
|
|
} else {
|
|
|
|
|
// -
|
|
|
|
|
if (storeNum.value - 1 <= 0) return toast({title: '至少选一件哦~'})
|
|
|
|
|
storeNum.value -= 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const open = (cartNum = 1, selectAttrStr = undefined) => {
|
|
|
|
|
defaultSelectAttrStr.value = selectAttrStr
|
|
|
|
|
storeNum.value = cartNum
|
2023-10-11 11:27:47 +08:00
|
|
|
|
handleGetDetail(props.id)
|
2023-11-14 17:21:03 +08:00
|
|
|
|
popupRef.value.show()
|
2023-10-11 11:27:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const close = () => {
|
|
|
|
|
popupRef.value.close()
|
2023-11-14 17:21:03 +08:00
|
|
|
|
emit('close')
|
2023-10-11 11:27:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
defineExpose({
|
|
|
|
|
open,
|
|
|
|
|
close
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
2023-11-14 17:21:03 +08:00
|
|
|
|
<style lang="scss">
|
2023-10-11 11:27:47 +08:00
|
|
|
|
.goodAttrSelect {
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
|
|
|
|
&-goods {
|
2023-11-14 17:21:03 +08:00
|
|
|
|
padding: 40rpx 30rpx;
|
|
|
|
|
display: flex;
|
|
|
|
|
|
|
|
|
|
.attr-image {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.attr-info {
|
|
|
|
|
flex: 1;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
margin-left: 30rpx;
|
|
|
|
|
.name {
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
line-height: 40rpx;
|
|
|
|
|
color: #333333;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-bottom {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
|
|
|
|
.price {
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
line-height: 42rpx;
|
|
|
|
|
color: #333333;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stock {
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
line-height: 42rpx;
|
|
|
|
|
color: #666666;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-10-11 11:27:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-action {
|
|
|
|
|
padding: 20rpx 20rpx;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-attr {
|
|
|
|
|
padding: 40rpx 30rpx;
|
|
|
|
|
|
|
|
|
|
&.row {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
|
|
|
|
.goodAttrSelect-attr-title {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&-title {
|
|
|
|
|
margin-bottom: 30rpx;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-14 17:21:03 +08:00
|
|
|
|
&-content {
|
|
|
|
|
}
|
2023-10-11 11:27:47 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.line {
|
|
|
|
|
height: 1rpx;
|
|
|
|
|
background: #E6E6E6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.attr {
|
|
|
|
|
height: 68rpx;
|
|
|
|
|
border: 1rpx solid #333333;
|
|
|
|
|
padding: 0 20rpx;
|
|
|
|
|
font-size: 28rpx;
|
2023-11-14 17:21:03 +08:00
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
align-items: center;
|
2023-10-11 11:27:47 +08:00
|
|
|
|
|
|
|
|
|
&.check {
|
|
|
|
|
background: #333333;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-14 17:21:03 +08:00
|
|
|
|
|
|
|
|
|
.cart-num {
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
|
|
|
|
.input {
|
|
|
|
|
width: 120rpx;
|
|
|
|
|
|
|
|
|
|
input {
|
|
|
|
|
width: 100%;
|
|
|
|
|
text-align: center;
|
|
|
|
|
color: #333;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.button {
|
|
|
|
|
font-size: 32rpx;
|
|
|
|
|
width: 34rpx;
|
|
|
|
|
aspect-ratio: 1/1;
|
|
|
|
|
border-radius: 5rpx;
|
|
|
|
|
border: 2rpx solid #cccccc;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
transition: all .3s;
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
scale: 1.2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-11 11:27:47 +08:00
|
|
|
|
</style>
|