<template>
|
<div class="wrapper">
|
<u-popup class="popup" :value="buyMask" :height="setup.height" closeable :mode="setup.mode" :border-radius="setup.radius" @input="handlePopupInput" @close="closeMask()">
|
<!-- 商品 -->
|
<view class="goods-box bottom">
|
<view class="goods-header">
|
<view class="goods-img">
|
<!-- <u-image width="200rpx" border-radius="20" class="uimage" height="200rpx" :src="selectedSpecImg ? selectedSpecImg : goodsDetail.thumbnail"></u-image> -->
|
</view>
|
<view class="goods-skus">
|
<!-- 有活动商品价格 -->
|
<view class="goods-price" v-if="goodsDetail.promotionPrice && ((isGroup && buyType === 'PINTUAN') || !isGroup)">
|
<span v-if="goodsDetail.promotionPrice && !pointDetail">
|
¥
|
<span class="goods-price-promotionShow goods-price-bigshow">{{ $options.filters.goodsFormatPrice(goodsDetail.promotionPrice)[0] }}</span>
|
.{{ $options.filters.goodsFormatPrice(goodsDetail.promotionPrice)[1] }}
|
</span>
|
<span v-if="pointDetail.points">
|
<span class="goods-price-promotionShow goods-price-bigshow">{{ pointDetail.points }}</span>
|
积分
|
</span>
|
<div class="promotion-box">
|
¥
|
<span class="goods-price-bigshow">{{ $options.filters.goodsFormatPrice(goodsDetail.price)[0] }}</span>
|
.{{ $options.filters.goodsFormatPrice(goodsDetail.price)[1] }}
|
</div>
|
</view>
|
<!-- 正常商品的价格 -->
|
<view v-else>
|
<!-- 批发价格 -->
|
<div class="price-row flex" v-if="goodsDetail.salesModel === 'WHOLESALE'">
|
<div class="goods-price" v-for="(item, index) in wholesaleList" :key="index">
|
<span>
|
¥
|
<span class="goods-price-bigshow">{{ $options.filters.goodsFormatPrice(item.price)[0] }}</span>
|
.{{ $options.filters.goodsFormatPrice(item.price)[1] }}
|
</span>
|
<span class="wholesale-item">{{ item.num }}{{ goodsDetail.goodsUnit }}</span>
|
</div>
|
</div>
|
<div class="goods-price" v-else>
|
<span>
|
¥
|
<span class="goods-price-bigshow">{{ $options.filters.goodsFormatPrice(goodsDetail.price)[0] }}</span>
|
.{{ $options.filters.goodsFormatPrice(goodsDetail.price)[1] }}
|
</span>
|
</div>
|
</view>
|
<view class="goods-check-skus">
|
已选
|
<span class="goods-check-skus-name">
|
{{ selectName }}
|
<span>,{{ num }}个</span>
|
</span>
|
</view>
|
<view class="goods-check-skus">
|
库存
|
<span class="goods-check-skus-name">
|
<span>{{ goodsDetail.quantity }}</span>
|
</span>
|
</view>
|
</view>
|
</view>
|
<!-- 商品信息 -->
|
<scroll-view class="goods-skus-box" :scroll-y="true">
|
<!-- 规格 -->
|
<view class="goods-skus-view" :key="specIndex" v-for="(spec, specIndex) in formatList">
|
<view class="skus-view-list">
|
<view class="view-class-title">{{ spec.name }}</view>
|
|
<!-- 正常逻辑 循环出sku -->
|
<view
|
v-if="!parentOrder"
|
:class="{ active: spec_val.value == currentSelected[specIndex] }"
|
class="skus-view-item"
|
v-for="(spec_val, spec_index) in spec.values"
|
:key="spec_index"
|
@click="handleClickSpec(spec, specIndex, spec_val)"
|
>
|
{{ spec_val.value }}
|
</view>
|
|
<!-- 拼团购买,仅筛选出当前拼团类型商品 -->
|
<view
|
v-if="parentOrder && spec_val.skuId == goodsDetail.id"
|
:class="{ active: spec_val.value == currentSelected[specIndex] }"
|
class="skus-view-item"
|
v-for="(spec_val, spec_index) in spec.values"
|
:key="spec_index"
|
@click="handleClickSpec(spec, specIndex, spec_val)"
|
>
|
{{ spec_val.value }}
|
</view>
|
</view>
|
</view>
|
<div class="soldout" v-if="goodsDetail.quantity === 0">
|
<u-alert-tips type="warning" title="商品已售罄" description="当前商品库存为0"></u-alert-tips>
|
</div>
|
<!-- 数量 -->
|
<view v-if="goodsDetail.quantity !== 0" class="goods-skus-number flex flex-a-c flex-j-sb">
|
<view class="view-class-title">数量</view>
|
<uni-number-box class="uNumber" :min="1" :max="999" :disabled="goodsDetail.quantity === 0" v-model="num"></uni-number-box>
|
</view>
|
<view class="template">
|
<view class="template-title" v-if="consumizetemplateInfo && consumizetemplateInfo.name">
|
<text class="title-text">{{ consumizetemplateInfo.name }}</text>
|
</view>
|
|
<!-- 图片选择区域 -->
|
<view class="template-images" v-if="consumizetemplateInfo && consumizetemplateInfo.templateImgs && consumizetemplateInfo.templateImgs.length > 0">
|
<view class="images-grid">
|
<view
|
v-for="(img, index) in consumizetemplateInfo.templateImgs"
|
:key="img.id"
|
class="image-item"
|
:class="{ selected: selectedImageObjects.some(selectedImg => selectedImg.id === img.id) }"
|
@click="handleImageClick(img, index)"
|
>
|
<image
|
:src="getFilePreviewUrlSync(img.imgUrl)"
|
class="image-preview"
|
mode="aspectFill"
|
/>
|
<view v-if="selectedImageObjects.some(selectedImg => selectedImg.id === img.id)" class="selected-overlay">
|
<uni-icons type="checkmarkempty" size="30" color="#fff"></uni-icons>
|
</view>
|
</view>
|
</view>
|
|
<!-- 查看选中图片按钮 -->
|
<view class="view-selected-image" v-if="selectedImageObjects.length > 0" @click="viewSelectedImage">
|
查看选中模板图片
|
</view>
|
</view>
|
|
<!-- 动态表单项 -->
|
<view class="template-form" v-if="consumizetemplateInfo && consumizetemplateInfo.templateConstomizeTitles">
|
<view
|
v-for="(item, index) in consumizetemplateInfo.templateConstomizeTitles"
|
:key="item.id"
|
class="form-item"
|
>
|
<text class="form-label">{{ item.templateTitle }}</text>
|
|
<!-- 文本输入框 -->
|
<input
|
v-if="item.contentType === 'TEXT'"
|
class="form-input"
|
:value="getFormValue(item.id)"
|
@input="updateFormValue(item.id, item.templateTitle, $event.target.value)"
|
:placeholder="'请输入' + item.templateTitle"
|
/>
|
|
<!-- 图片上传(不使用u-upload组件) -->
|
<view v-if="item.contentType === 'IMAGE'" class="form-upload">
|
<!-- 上传按钮 -->
|
<view class="upload-btn" @click="chooseImage(item.id)">
|
<uni-icons type="plusempty" size="40" color="#999"></uni-icons>
|
<text class="upload-text">点击上传</text>
|
</view>
|
|
<!-- 图片预览区域 -->
|
<view v-if="imagePreviewUrls[item.id]" class="image-preview-container">
|
<image
|
:src="imagePreviewUrls[item.id]"
|
class="uploaded-image"
|
mode="aspectFill"
|
/>
|
<uni-icons
|
type="closeempty"
|
class="delete-icon"
|
@click="deleteImage(item.id)"
|
size="20"
|
color="#fff"
|
></uni-icons>
|
</view>
|
|
<!-- 上传进度条 -->
|
<progress
|
v-if="uploadProgress[item.id] && uploadProgress[item.id] > 0 && uploadProgress[item.id] < 100"
|
:percent="uploadProgress[item.id]"
|
active-mode="forwards"
|
show-info
|
stroke-width="6"
|
:active="true"
|
active-color="#ff573e"
|
style="margin-top: 10rpx;"
|
/>
|
|
<!-- 上传状态信息 -->
|
<text v-if="uploadStatus[item.id]" class="upload-status">
|
{{ uploadStatus[item.id] }}
|
</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
</scroll-view>
|
<!-- 按钮 -->
|
<view class="btns" v-if="goodsDetail.quantity !== 0">
|
<view class="box-btn card" v-if="buyType != 'PINTUAN' && goodsDetail.goodsType != 'VIRTUAL_GOODS' && goodsDetail.customizeFlag!=='CUSTOMIZE'" @click="addToCartOrBuy('cart')">加入购物车</view>
|
<view class="box-btn buy" @click="addToCartOrBuy('buy')">立即购买</view>
|
</view>
|
</view>
|
</u-popup>
|
</div>
|
</template>
|
<script>
|
import '@/components/uview-components/uview-ui';
|
|
import * as API_trade from '@/api/trade.js';
|
import setup from './popup';
|
// import uniNumberBox from '@/components/uni-number-box'
|
import { getFilePreviewUrl } from "@/api/common.js";
|
import { getSTSToken } from "@/api/common.js";
|
import { getFileKey } from "@/utils/file.js";
|
|
export default {
|
components: {
|
// uniNumberBox
|
},
|
data() {
|
return {
|
setup,
|
num: this.wholesaleList && this.wholesaleList.length > 0 ? this.wholesaleList[0].num : 1,
|
|
selectName: '', //选中商品的昵称
|
selectSkuList: '', //选中商铺sku,
|
selectedSpecImg: '', //选中的图片路径
|
buyType: '', //用于存储促销,拼团等活动类型
|
parentOrder: '', //父级拼团活动的数据 - 如果是团员则有数据
|
formatList: [],
|
currentSelected: [],
|
skuList: '',
|
isClose: false, //是否可以点击遮罩关闭
|
|
// 表单相关数据
|
selectedImages: [], // 选中的模板图片
|
selectedImageObjects: [], // 选中的模板图片对象(包含ID等完整信息)
|
formValues: {
|
templateId: "", // 模板ID
|
templateName: "", // 模板名称
|
chooseImage: "", // 选中的图片
|
chooseImageId: "", // 选中图片的ID
|
templateForm: [] // 表单数组 [{id, templateTitle, value}]
|
}, // 表单值
|
imagePreviewUrls: {}, // 图片预览URL
|
uploadProgress: {}, // 上传进度
|
uploadStatus: {}, // 上传状态信息
|
cosClient: null, // COS客户端
|
bucket: '', // 存储桶
|
region: '', // 地域
|
previewUrls: {} // 文件预览地址缓存
|
};
|
},
|
props: {
|
wholesaleList: {
|
type: null,
|
default: false
|
},
|
buyMask: {
|
type: Boolean,
|
default: false
|
},
|
isGroup: {
|
type: Boolean,
|
default: false
|
},
|
goodsDetail: {
|
default: '',
|
type: null
|
},
|
selectedSku: {
|
default: '',
|
type: null
|
},
|
goodsSpec: {
|
default: '',
|
type: null
|
},
|
addr: {
|
default: '',
|
type: null
|
},
|
pointDetail: {
|
default: '',
|
type: null
|
},
|
consumizetemplateInfo:{
|
default:'',
|
type: null
|
}
|
},
|
computed: {
|
wholesalePrice(key) {
|
return this.wholesaleList.length
|
? this.wholesaleList.map(item => {
|
return item.price;
|
})
|
: [];
|
},
|
wholesaleNum(key) {
|
return this.wholesaleList.length
|
? this.wholesaleList.map(item => {
|
return item.num;
|
})
|
: [];
|
}
|
},
|
watch: {
|
num(val) {
|
|
val == 0 ? this.num = 1 : ''
|
if (val) {
|
|
//超过库存后修改回库存
|
if (val > this.goodsDetail.quantity) {
|
this.$nextTick(function() {
|
this.num = this.goodsDetail.quantity;
|
});
|
}
|
}
|
},
|
buyType: {
|
handler(val) {
|
if (val) {
|
// 使用直接赋值而不是this.$set
|
this.buyType = val;
|
}
|
},
|
immediate: true
|
},
|
selectSkuList: {
|
handler(val, oldval) {
|
// 使用setTimeout延迟触发事件,避免影响弹窗
|
setTimeout(() => {
|
this.$emit('changed', val);
|
}, 100);
|
},
|
deep: true
|
},
|
'goodsDetail.quantity': {
|
handler(val) {
|
if (val == 0) {
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '商品已售罄',
|
duration: 2000,
|
icon: 'none'
|
});
|
}, 100);
|
this.num = 1;
|
}
|
}
|
},
|
// 监听模板信息变化
|
consumizetemplateInfo: {
|
handler(newVal, oldVal) {
|
if (newVal) {
|
// 使用setTimeout延迟执行,避免影响弹窗
|
setTimeout(() => {
|
// 预加载模板图片的预览地址
|
this.preloadTemplateImages();
|
// 初始化模板表单数据
|
this.initTemplateFormData();
|
// 设置默认选中第一张模板图片
|
this.setDefaultSelectedImage();
|
}, 100);
|
}
|
},
|
immediate: true,
|
deep: true
|
}
|
},
|
|
methods: {
|
// 初始化腾讯云COS客户端
|
initCOS() {
|
getSTSToken().then(res => {
|
const COS = require('@/lib/cos-wx-sdk-v5.js');
|
// 使用直接赋值而不是this.$set
|
this.cosClient = new COS({
|
SecretId: res.data.data.tmpSecretId,
|
SecretKey: res.data.data.tmpSecretKey,
|
SecurityToken: res.data.data.sessionToken,
|
StartTime: res.data.data.stsStartTime,
|
ExpiredTime: res.data.data.stsEndTime,
|
SimpleUploadMethod: 'putObject'
|
});
|
this.bucket = res.data.data.bucket;
|
this.region = res.data.data.region;
|
}).catch(err => {
|
console.error('初始化COS失败', err);
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '上传服务初始化失败',
|
icon: 'none'
|
});
|
}, 100);
|
});
|
},
|
|
// 获取文件预览地址
|
async fetchFilePreviewUrl(fileKey) {
|
// 如果是完整的URL,直接返回
|
if (fileKey && (fileKey.startsWith('http://') || fileKey.startsWith('https://'))) {
|
return fileKey;
|
}
|
|
// 如果已经缓存过,直接返回缓存值
|
if (this.previewUrls[fileKey]) {
|
return this.previewUrls[fileKey];
|
}
|
|
// 调用API获取预览地址
|
try {
|
const res = await getFilePreviewUrl(fileKey);
|
const previewUrl = res.data.data;
|
// 使用直接赋值而不是this.$set
|
this.previewUrls[fileKey] = previewUrl;
|
return previewUrl;
|
} catch (error) {
|
console.error('获取文件预览地址失败', error);
|
return fileKey; // 如果获取失败,返回原始值
|
}
|
},
|
|
// 选择模板图片(单选模式)
|
selectImage(imgObject) {
|
// 检查当前选中的图片是否就是点击的图片
|
const selectedIndex = this.selectedImageObjects.findIndex(selectedImg => selectedImg.id === imgObject.id);
|
|
if (selectedIndex > -1) {
|
// 如果点击的是已选中的图片,则取消选择
|
this.selectedImageObjects = [];
|
this.selectedImages = [];
|
} else {
|
// 如果点击的是未选中的图片,则选中该图片(清空之前的选择)
|
this.selectedImageObjects = [imgObject];
|
this.selectedImages = [imgObject.imgUrl];
|
}
|
|
// 更新formValues中的chooseImage和chooseImageId
|
this.formValues.chooseImage = this.selectedImages.join(',');
|
// 将选中图片的ID也存储到formValues中
|
this.formValues.chooseImageId = this.selectedImageObjects.map(img => img.id).join(',');
|
|
// 强制更新视图以确保UI正确反映数据变化
|
this.$forceUpdate();
|
},
|
|
// 选择图片(参考video.vue的实现)
|
chooseImage(fieldId) {
|
if (!this.cosClient) {
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '上传服务未初始化',
|
icon: 'none'
|
});
|
}, 100);
|
// 重新初始化COS
|
this.initCOS();
|
return;
|
}
|
|
uni.chooseImage({
|
count: 1,
|
sizeType: ['compressed'],
|
sourceType: ['album', 'camera'],
|
success: (res) => {
|
const tempFilePath = res.tempFilePaths[0];
|
|
// 获取文件名
|
let fileName = tempFilePath.substring(tempFilePath.lastIndexOf('/') + 1);
|
// 处理安卓可能的URI编码
|
if(fileName.indexOf('%') > -1) {
|
fileName = decodeURIComponent(fileName);
|
}
|
|
// 生成文件key
|
const fileKey = getFileKey(fileName);
|
|
// 显示预览图
|
this.$set(this.imagePreviewUrls, fieldId, tempFilePath);
|
|
// 初始化上传状态
|
this.$set(this.uploadProgress, fieldId, 0);
|
this.$set(this.uploadStatus, fieldId, '上传中...');
|
|
// 强制更新视图
|
this.$forceUpdate();
|
|
// 执行上传
|
this.cosClient.uploadFile({
|
Bucket: this.bucket,
|
Region: this.region,
|
Key: fileKey,
|
FilePath: tempFilePath,
|
SliceSize: 1024 * 1024 * 5, // 5M
|
onProgress: (progressData) => {
|
// 更新上传进度
|
const progress = Math.round(progressData.percent * 100);
|
this.$set(this.uploadProgress, fieldId, progress);
|
|
// 强制更新视图
|
this.$forceUpdate();
|
}
|
}, (err, data) => {
|
// 清除上传进度
|
// 使用Vue.delete或this.$delete可能会触发组件更新,改用直接删除
|
delete this.uploadProgress[fieldId];
|
|
if (err) {
|
console.error('上传失败', err);
|
// 更新上传状态
|
this.$set(this.uploadStatus, fieldId, '上传失败');
|
// 清除预览图
|
this.$delete(this.imagePreviewUrls, fieldId);
|
// 注意:这里不再需要清除formValues中的字段,因为我们已经修改了数据结构
|
|
// 强制更新视图
|
this.$forceUpdate();
|
|
// 使用setTimeout延迟显示错误提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '上传失败',
|
icon: 'none'
|
});
|
}, 100);
|
} else {
|
// 获取当前表单项的信息
|
const currentItem = this.consumizetemplateInfo.templateConstomizeTitles.find(item => item.id === fieldId);
|
const templateTitle = currentItem ? currentItem.templateTitle : '';
|
|
// 上传成功,更新表单值
|
this.updateFormValue(fieldId, templateTitle, fileKey);
|
|
// 更新上传状态
|
this.$set(this.uploadStatus, fieldId, '上传成功');
|
|
// 强制更新视图
|
this.$forceUpdate();
|
|
// 使用setTimeout延迟显示成功提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '上传成功',
|
icon: 'success'
|
});
|
}, 100);
|
}
|
});
|
},
|
fail: (err) => {
|
console.error('选择图片失败', err);
|
// 区分用户取消操作和真正的错误
|
// 如果是用户主动取消,不显示提示
|
if (err.errMsg && err.errMsg.indexOf('cancel') === -1 && err.errMsg.indexOf('fail') !== -1) {
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '未选择图片',
|
icon: 'none'
|
});
|
}, 100);
|
}
|
}
|
});
|
},
|
|
// 删除图片
|
deleteImage(fieldId) {
|
// 清除预览图
|
this.$delete(this.imagePreviewUrls, fieldId);
|
// 清除上传状态
|
this.$delete(this.uploadStatus, fieldId);
|
// 清除上传进度
|
this.$delete(this.uploadProgress, fieldId);
|
|
// 从formValues中移除对应的表单字段
|
const index = this.formValues.templateForm.findIndex(item => item.id === fieldId);
|
if (index > -1) {
|
this.formValues.templateForm.splice(index, 1);
|
}
|
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '删除成功',
|
icon: 'success'
|
});
|
}, 100);
|
|
// 强制更新视图
|
this.$forceUpdate();
|
},
|
|
// 获取表单值
|
getFormValue(fieldId) {
|
const field = this.formValues.templateForm.find(item => item.id === fieldId);
|
return field ? field.value : '';
|
},
|
|
// 更新表单值
|
updateFormValue(fieldId, templateTitle, value) {
|
// 更新模板基本信息
|
this.formValues.templateId = this.consumizetemplateInfo.id;
|
this.formValues.templateName = this.consumizetemplateInfo.name;
|
|
// 查找对应的模板字段以获取contentType
|
const templateItem = this.consumizetemplateInfo.templateConstomizeTitles.find(item => item.id === fieldId);
|
const contentType = templateItem ? templateItem.contentType : '';
|
|
// 查找是否已存在该字段
|
const existingIndex = this.formValues.templateForm.findIndex(item => item.id === fieldId);
|
|
if (existingIndex > -1) {
|
// 更新现有字段
|
this.$set(this.formValues.templateForm, existingIndex, {
|
id: fieldId,
|
templateTitle: templateTitle,
|
contentType: contentType, // 添加contentType
|
value: value
|
});
|
} else {
|
// 添加新字段
|
this.formValues.templateForm.push({
|
id: fieldId,
|
templateTitle: templateTitle,
|
contentType: contentType, // 添加contentType
|
value: value
|
});
|
}
|
},
|
|
// 更新模板表单数据
|
updateTemplateFormData(fieldId, templateTitle, value) {
|
// 这个方法现在可以保持不变,因为我们已经在updateFormValue中处理了formValues的更新
|
// 如果需要其他处理,可以在这里添加
|
},
|
|
// 表单提交处理
|
handleFormSubmit() {
|
// 确保选中的图片对象也被包含在提交数据中
|
const formData = {
|
...this.formValues,
|
selectedImageObjects: this.selectedImageObjects
|
};
|
|
console.log('表单数据:', formData);
|
// 这里可以添加实际的表单提交逻辑
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '表单提交成功',
|
icon: 'success'
|
});
|
}, 100);
|
},
|
|
numCheck(val) {
|
if (this.wholesaleList && this.wholesaleList.length > 0) {
|
if (this.num <= this.wholesaleList[0].num) {
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '批发商品购买数量不能小于起批数量!',
|
duration: 2000,
|
icon: 'none'
|
});
|
}, 100);
|
this.num = this.wholesaleList[0].num;
|
}
|
}
|
},
|
closeMask() {
|
this.$emit('closeBuy', false);
|
},
|
|
// 处理弹窗输入事件,避免直接修改prop
|
handlePopupInput(val) {
|
this.$emit('closeBuy', val);
|
},
|
|
/**点击规格 */
|
handleClickSpec(val, index, specValue) {
|
this.currentSelected[index] = specValue.value;
|
let selectedSkuId = this.goodsSpec.find(i => {
|
let matched = true;
|
let specValues = i.specValues.filter(j => j.specName !== 'images');
|
for (let n = 0; n < specValues.length; n++) {
|
if (specValues[n].specValue !== this.currentSelected[n]) {
|
matched = false;
|
return;
|
}
|
}
|
if (matched) {
|
return i;
|
}
|
});
|
if (selectedSkuId?.skuId) {
|
this.$set(this.currentSelected, index, specValue.value);
|
this.selectSkuList = {
|
spec: {
|
specName: val.name,
|
specValue: specValue.value
|
},
|
data: this.goodsDetail
|
};
|
this.selectName = specValue.value;
|
|
this.$emit('handleClickSku', {
|
skuId: selectedSkuId.skuId,
|
goodsId: this.goodsDetail.goodsId
|
});
|
} else {
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '暂无该商品!',
|
duration: 2000,
|
icon: 'none'
|
});
|
}, 100);
|
}
|
},
|
|
/**
|
* 直接购买
|
*/
|
buy(data) {
|
API_trade.addToCart(data).then(res => {
|
if (res.data.success) {
|
// 使用setTimeout延迟跳转,避免影响弹窗
|
setTimeout(() => {
|
uni.navigateTo({
|
url: `/pages/order/fillorder?way=${data.cartType}&addr=${''}&parentOrder=${encodeURIComponent(JSON.stringify(this.parentOrder))}`
|
});
|
}, 100);
|
}
|
});
|
},
|
|
/**
|
* 添加到购物车或购买
|
*/
|
addToCartOrBuy(val) {
|
// 检查商品是否需要模板并且模板数据是否完整
|
if (this.consumizetemplateInfo && this.consumizetemplateInfo.id) {
|
const isValid = this.validateTemplateData();
|
if (!isValid) {
|
return;
|
}
|
}
|
|
console.log(JSON.stringify(this.formValues))
|
if (!this.selectSkuList) {
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '请选择规格商品',
|
icon: 'none'
|
});
|
}, 100);
|
return;
|
}
|
let data = {
|
skuId: this.goodsDetail.id,
|
num: this.num
|
};
|
|
if (val == 'cart') {
|
API_trade.addToCart(data).then(res => {
|
if (res.data.code == 200) {
|
// 使用setTimeout延迟显示提示,避免影响弹窗
|
setTimeout(() => {
|
uni.showToast({
|
title: '商品已添加到购物车',
|
icon: 'none'
|
});
|
}, 100);
|
|
this.$emit('queryCart');
|
this.closeMask();
|
}
|
});
|
} else {
|
// 判断是否拼团商品
|
if (this.buyType) {
|
data.cartType = 'PINTUAN';
|
} else if (this.goodsDetail.goodsType == 'VIRTUAL_GOODS') {
|
data.cartType = 'VIRTUAL';
|
} else {
|
data.cartType = 'BUY_NOW';
|
}
|
|
// 创建包含图片ID的完整表单数据
|
const templateData = {
|
...this.formValues,
|
selectedImageObjects: this.selectedImageObjects
|
};
|
|
API_trade.addToCart(data).then(res => {
|
if (res.data.code == 200) {
|
// 使用setTimeout延迟跳转,避免影响弹窗
|
setTimeout(() => {
|
uni.navigateTo({
|
url: `/pages/order/fillorder?way=${data.cartType}&addr=${this.addr.id || ''}&template=${encodeURIComponent(JSON.stringify(templateData))}&parentOrder=${encodeURIComponent(JSON.stringify(this.parentOrder))}`
|
});
|
}, 100);
|
}
|
});
|
}
|
},
|
|
/**
|
* 验证模板数据是否完整
|
*/
|
validateTemplateData() {
|
// 检查模板图片是否已选择(如果有模板图片)
|
if (this.consumizetemplateInfo.templateImgs &&
|
this.consumizetemplateInfo.templateImgs.length > 0 &&
|
this.selectedImageObjects.length === 0) {
|
setTimeout(() => {
|
uni.showToast({
|
title: '请选择模板图片',
|
icon: 'none'
|
});
|
}, 100);
|
return false;
|
}
|
|
// 检查动态表单项是否已填写
|
if (this.consumizetemplateInfo.templateConstomizeTitles) {
|
for (const item of this.consumizetemplateInfo.templateConstomizeTitles) {
|
const formItem = this.formValues.templateForm.find(formItem => formItem.id === item.id);
|
|
// 如果表单项不存在或者值为空
|
if (!formItem || !formItem.value || formItem.value.trim() === '') {
|
setTimeout(() => {
|
uni.showToast({
|
title: `请填写${item.templateTitle}`,
|
icon: 'none'
|
});
|
}, 100);
|
return false;
|
}
|
|
// 特别检查图片类型的表单项是否有上传
|
if (item.contentType === 'IMAGE' &&
|
(!this.imagePreviewUrls[item.id] || this.imagePreviewUrls[item.id].trim() === '')) {
|
setTimeout(() => {
|
uni.showToast({
|
title: `请上传${item.templateTitle}`,
|
icon: 'none'
|
});
|
}, 100);
|
return false;
|
}
|
}
|
}
|
|
return true;
|
},
|
formatSku(list) {
|
// 格式化数据
|
let arr = [{}];
|
|
if (!Array.isArray(list)) {
|
return false;
|
}
|
list.forEach((item, index) => {
|
item.specValues.forEach((spec, specIndex) => {
|
let name = spec.specName;
|
let values = {
|
value: spec.specValue,
|
quantity: item.quantity,
|
skuId: item.skuId
|
};
|
if (name === 'images') {
|
return;
|
}
|
|
arr.forEach((arrItem, arrIndex) => {
|
if (
|
arrItem.name == name &&
|
arrItem.values &&
|
!arrItem.values.find(i => {
|
return i.value === values.value;
|
})
|
) {
|
// 使用直接赋值而不是this.$set
|
arrItem.values.push(values);
|
}
|
|
let keys = arr.map(key => {
|
return key.name;
|
});
|
if (!keys.includes(name)) {
|
// 使用直接赋值而不是this.$set
|
arr.push({
|
name: name,
|
values: [values]
|
});
|
}
|
});
|
});
|
});
|
|
arr.shift();
|
// 使用直接赋值而不是this.$set
|
this.formatList = arr;
|
|
list.forEach(item => {
|
// 默认选中
|
if (item.skuId === this.goodsDetail.id) {
|
item.specValues
|
.filter(i => i.specName !== 'images')
|
.forEach((value, _index) => {
|
// 使用直接赋值而不是this.$set
|
this.currentSelected[_index] = value.specValue;
|
|
this.selectName = value.specValue;
|
|
this.selectSkuList = {
|
spec: value,
|
data: this.goodsDetail
|
};
|
});
|
}
|
});
|
|
// 使用直接赋值而不是this.$set
|
this.skuList = list;
|
// console.log(" this.skuList", this.skuList)
|
},
|
|
// 预加载模板图片的预览地址
|
async preloadTemplateImages() {
|
if (this.consumizetemplateInfo && this.consumizetemplateInfo.templateImgs) {
|
for (const img of this.consumizetemplateInfo.templateImgs) {
|
await this.fetchFilePreviewUrl(img.imgUrl);
|
}
|
}
|
},
|
|
// 获取文件预览地址(同步版本)
|
getFilePreviewUrlSync(fileKey) {
|
// 如果是完整的URL,直接返回
|
if (fileKey && (fileKey.startsWith('http://') || fileKey.startsWith('https://'))) {
|
return fileKey;
|
}
|
// 如果有预览地址,返回预览地址
|
if (this.previewUrls && this.previewUrls[fileKey]) {
|
return this.previewUrls[fileKey];
|
}
|
// 否则返回原始值
|
return fileKey;
|
},
|
|
// 初始化模板表单数据
|
initTemplateFormData() {
|
if (this.consumizetemplateInfo && this.consumizetemplateInfo.templateConstomizeTitles) {
|
// 初始化formValues的基本信息
|
this.formValues.templateId = this.consumizetemplateInfo.id;
|
this.formValues.templateName = this.consumizetemplateInfo.name;
|
this.formValues.chooseImage = "";
|
this.formValues.chooseImageId = "";
|
this.formValues.templateForm = [];
|
|
// 初始化选中的图片对象数组和图片URL数组
|
this.selectedImageObjects = [];
|
this.selectedImages = [];
|
}
|
},
|
|
// 预览模板图片
|
previewImage(imgObject, index) {
|
// 只预览当前选中的图片
|
const urls = [this.getFilePreviewUrlSync(imgObject.imgUrl)];
|
|
// 调用uniapp原生API预览图片
|
uni.previewImage({
|
current: 0, // 当前显示图片的索引(只有一张图片,所以是0)
|
urls: urls, // 需要预览的图片链接列表
|
indicator: 'default', // 显示索引指示器
|
loop: false // 只有一张图片,不需要循环预览
|
});
|
},
|
|
// 处理模板图片点击事件(既可以预览又可以选中)
|
handleImageClick(imgObject, index) {
|
// 检查当前图片是否已选中
|
const isSelected = this.selectedImageObjects.some(selectedImg => selectedImg.id === imgObject.id);
|
|
if (isSelected) {
|
// 如果已选中,则预览图片
|
this.previewImage(imgObject, index);
|
} else {
|
// 如果未选中,则选中图片
|
this.selectImage(imgObject);
|
}
|
},
|
|
// 查看选中的图片
|
viewSelectedImage() {
|
// 检查是否有选中的图片
|
if (this.selectedImageObjects.length > 0) {
|
// 获取第一个选中的图片(因为是单选模式)
|
const selectedImage = this.selectedImageObjects[0];
|
// 预览选中的图片
|
this.previewImage(selectedImage, 0);
|
}
|
},
|
|
// 设置默认选中第一张模板图片
|
setDefaultSelectedImage() {
|
// 检查是否有模板图片且当前没有选中任何图片
|
if (this.consumizetemplateInfo &&
|
this.consumizetemplateInfo.templateImgs &&
|
this.consumizetemplateInfo.templateImgs.length > 0 &&
|
this.selectedImageObjects.length === 0) {
|
// 选中第一张图片
|
const firstImage = this.consumizetemplateInfo.templateImgs[0];
|
this.selectImage(firstImage);
|
}
|
}
|
},
|
|
mounted() {
|
// 使用setTimeout延迟执行,避免影响弹窗
|
setTimeout(() => {
|
this.formatSku(this.goodsSpec);
|
this.initCOS(); // 初始化COS客户端
|
|
// 预加载模板图片的预览地址
|
this.preloadTemplateImages();
|
|
// 初始化模板表单数据
|
this.initTemplateFormData();
|
|
// 默认选中第一张模板图片
|
this.setDefaultSelectedImage();
|
|
console.log("goodsDetail",this.goodsDetail)
|
}, 100);
|
}
|
};
|
</script>
|
<style lang="scss" scoped>
|
@import './popup.scss';
|
|
.price-row {
|
text-align: center;
|
}
|
|
.buy {
|
background-image: linear-gradient(135deg, #ffba0d, #ffc30d 69%, #ffcf0d);
|
box-shadow: 0 2px 6px 0 rgba(255, 65, 66, 0.2);
|
}
|
|
.card {
|
background-image: linear-gradient(135deg, #f2140c, #f2270c 70%, #f24d0c);
|
box-shadow: 0 2px 6px 0 rgba(255, 65, 66, 0.2);
|
}
|
|
/deep/.u-icon-plus,
|
.u-icon-minus,
|
.u-icon-disabled {
|
height: 30rpx !important;
|
background: #fff !important;
|
}
|
|
.goods-skus-number {
|
justify-content: space-between;
|
display: flex;
|
|
> .view-class-title {
|
flex: 8;
|
}
|
|
> .view-class-input {
|
flex: 1;
|
}
|
}
|
|
/deep/ .uni-scroll-view {
|
overflow: hidden !important;
|
}
|
|
.active {
|
background: $price-light-color !important;
|
border: 2rpx solid $price-color;
|
font-weight: bold;
|
color: $price-color !important;
|
box-sizing: border-box;
|
}
|
|
.goods-skus-box {
|
overflow-y: auto;
|
height: 610rpx;
|
// #ifdef MP-WEIXIN
|
height: 570rpx;
|
// #endif
|
margin-bottom: 10rpx;
|
}
|
.soldout{
|
margin: 20rpx 0;
|
}
|
|
.goods-skus-view {
|
overflow: hidden;
|
|
.skus-view-list {
|
> .skus-view-item {
|
flex: 1;
|
padding: 0 36rpx;
|
|
overflow: hidden;
|
height: 60rpx;
|
line-height: 60rpx;
|
float: left;
|
text-align: center;
|
margin-left: 24rpx;
|
margin-bottom: 20rpx;
|
font-size: 22rpx;
|
color: #262626;
|
background: #f2f2f2;
|
border-radius: 30rpx;
|
}
|
}
|
}
|
|
.wholesale-item {
|
color: #999 !important;
|
font-size: 24rpx;
|
margin: 0 20rpx;
|
display: flex;
|
justify-content: center;
|
}
|
|
.goods-header {
|
height: 200rpx;
|
display: flex;
|
align-items: center;
|
margin-bottom: 36rpx;
|
}
|
|
.goods-box {
|
padding: 50rpx 36rpx 0 36rpx;
|
}
|
|
.goods-skus {
|
padding: 0 20rpx;
|
}
|
|
.goods-price {
|
color: $price-color;
|
line-height: 80rpx;
|
margin-right: 20rpx;
|
|
> * {
|
color: $price-color;
|
line-height: 80rpx;
|
}
|
}
|
|
.promotion-box {
|
line-height: 1;
|
display: flex;
|
align-items: center;
|
text-decoration: line-through;
|
color: #999;
|
margin-left: 10rpx;
|
|
/deep/ span {
|
font-size: 30rpx;
|
}
|
}
|
|
.promotion {
|
font-size: 30rpx;
|
}
|
|
.goods-price-promotionShow {
|
font-size: 48rpx;
|
}
|
|
.goods-check-skus {
|
font-size: 24rpx;
|
color: #999;
|
|
> .goods-check-skus-name {
|
margin-left: 4rpx;
|
}
|
|
> span {
|
color: #333;
|
}
|
}
|
|
// 模板样式
|
.template {
|
padding: 20rpx;
|
}
|
|
.template-title {
|
margin-bottom: 30rpx;
|
text-align: center;
|
}
|
|
.title-text {
|
font-size: 36rpx;
|
font-weight: bold;
|
}
|
|
// 查看选中图片按钮样式
|
.view-selected-image {
|
margin-top: 20rpx;
|
padding: 30rpx;
|
text-align: center;
|
background-color: #f0f0f0;
|
border-radius: 10rpx;
|
color: #333;
|
font-size: 28rpx;
|
}
|
|
// 图片选择区域
|
.template-images {
|
margin-bottom: 40rpx;
|
}
|
|
.images-grid {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 20rpx;
|
}
|
|
.image-item {
|
width: calc((100% - 40rpx) / 3);
|
height: 200rpx;
|
border: 2rpx solid #e0e0e0;
|
border-radius: 10rpx;
|
overflow: hidden;
|
position: relative;
|
|
&.selected {
|
border-color: #ff6b00;
|
box-shadow: 0 0 10rpx rgba(255, 107, 0, 0.5);
|
}
|
}
|
|
.selected-overlay {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 100%;
|
background-color: rgba(0, 0, 0, 0.3);
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.image-preview {
|
width: 100%;
|
height: 100%;
|
}
|
|
// 表单区域
|
.template-form {
|
|
}
|
|
.form-item {
|
margin-bottom: 30rpx;
|
}
|
|
.form-label {
|
display: block;
|
margin-bottom: 15rpx;
|
font-size: 28rpx;
|
color: #333;
|
}
|
|
.form-input {
|
width: 100%;
|
height: 80rpx;
|
padding: 0 20rpx;
|
border: 2rpx solid #e0e0e0;
|
border-radius: 10rpx;
|
font-size: 28rpx;
|
box-sizing: border-box;
|
}
|
|
.form-upload {
|
margin-top: 15rpx;
|
}
|
|
// 图片上传样式
|
.upload-btn {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
width: 150rpx;
|
height: 150rpx;
|
border: 2rpx dashed #ccc;
|
border-radius: 10rpx;
|
background-color: #f8f8f8;
|
}
|
|
.upload-text {
|
font-size: 24rpx;
|
color: #999;
|
margin-top: 10rpx;
|
}
|
|
.image-preview-container {
|
position: relative;
|
width: 150rpx;
|
height: 150rpx;
|
margin-top: 10rpx;
|
}
|
|
.uploaded-image {
|
width: 100%;
|
height: 100%;
|
border-radius: 10rpx;
|
}
|
|
.delete-icon {
|
position: absolute;
|
top: -10rpx;
|
right: -10rpx;
|
background-color: #ff6b00;
|
border-radius: 50%;
|
width: 30rpx;
|
height: 30rpx;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
</style>
|