| | |
| | | <template> |
| | | <div class="wrapper"> |
| | | <u-popup class="popup" v-model="buyMask" :height="setup.height" closeable :mode="setup.mode" :border-radius="setup.radius" @close="closeMask()"> |
| | | <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 class="goods-img" v-if="goodsDetail.specList && goodsDetail.specList.length > 0 && goodsDetail.specList[0].specImage.length > 0" @click="previewImageSpec"> |
| | | <u-image width="200rpx" border-radius="20" class="uimage" height="200rpx" :src="selectedSpecImg ? selectedSpecImg : goodsDetail.thumbnail"></u-image> |
| | | </view> |
| | | <view class="goods-skus"> |
| | | <!-- 有活动商品价格 --> |
| | |
| | | <uni-number-box class="uNumber" :min="1" :max="999" :disabled="goodsDetail.quantity === 0" v-model="num"></uni-number-box> |
| | | </view> |
| | | <view class="template"> |
| | | {{JSON.stringify(consumizetemplateInfo)}} |
| | | <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> |
| | | <!-- 按钮 --> |
| | |
| | | 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 |
| | |
| | | formatList: [], |
| | | currentSelected: [], |
| | | skuList: '', |
| | | isClose: false //是否可以点击遮罩关闭 |
| | | 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: '', // 地域 |
| | | endpoint: '', // COS访问endpoint |
| | | previewUrls: {} // 文件预览地址缓存 |
| | | }; |
| | | }, |
| | | props: { |
| | |
| | | buyType: { |
| | | handler(val) { |
| | | if (val) { |
| | | // 使用直接赋值而不是this.$set |
| | | this.buyType = val; |
| | | } |
| | | }, |
| | |
| | | }, |
| | | selectSkuList: { |
| | | handler(val, oldval) { |
| | | this.$emit('changed', val); |
| | | // 使用setTimeout延迟触发事件,避免影响弹窗 |
| | | setTimeout(() => { |
| | | this.$emit('changed', val); |
| | | }, 100); |
| | | }, |
| | | deep: true |
| | | }, |
| | | 'goodsDetail.quantity': { |
| | | handler(val) { |
| | | if (val == 0) { |
| | | uni.showToast({ |
| | | title: '商品已售罄', |
| | | duration: 2000, |
| | | icon: 'none' |
| | | }) |
| | | // 使用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; |
| | | this.endpoint = res.data.data.endpoint; // 获取endpoint |
| | | }).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) { |
| | | uni.showToast({ |
| | | title: '批发商品购买数量不能小于起批数量!', |
| | | duration: 2000, |
| | | icon: 'none' |
| | | }); |
| | | // 使用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); |
| | | }, |
| | | |
| | | /**点击规格 */ |
| | |
| | | goodsId: this.goodsDetail.goodsId |
| | | }); |
| | | } else { |
| | | uni.showToast({ |
| | | title: '暂无该商品!', |
| | | duration: 2000, |
| | | icon: 'none' |
| | | }); |
| | | // 使用setTimeout延迟显示提示,避免影响弹窗 |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: '暂无该商品!', |
| | | duration: 2000, |
| | | icon: 'none' |
| | | }); |
| | | }, 100); |
| | | } |
| | | }, |
| | | |
| | |
| | | buy(data) { |
| | | API_trade.addToCart(data).then(res => { |
| | | if (res.data.success) { |
| | | uni.navigateTo({ |
| | | url: `/pages/order/fillorder?way=${data.cartType}&addr=${''}&parentOrder=${encodeURIComponent(JSON.stringify(this.parentOrder))}` |
| | | }); |
| | | // 使用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) { |
| | | uni.showToast({ |
| | | title: '请选择规格商品', |
| | | icon: 'none' |
| | | }); |
| | | // 使用setTimeout延迟显示提示,避免影响弹窗 |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: '请选择规格商品', |
| | | icon: 'none' |
| | | }); |
| | | }, 100); |
| | | return; |
| | | } |
| | | let data = { |
| | |
| | | if (val == 'cart') { |
| | | API_trade.addToCart(data).then(res => { |
| | | if (res.data.code == 200) { |
| | | uni.showToast({ |
| | | title: '商品已添加到购物车', |
| | | icon: 'none' |
| | | }); |
| | | // 使用setTimeout延迟显示提示,避免影响弹窗 |
| | | setTimeout(() => { |
| | | uni.showToast({ |
| | | title: '商品已添加到购物车', |
| | | icon: 'none' |
| | | }); |
| | | }, 100); |
| | | |
| | | this.$emit('queryCart'); |
| | | this.closeMask(); |
| | |
| | | data.cartType = 'BUY_NOW'; |
| | | } |
| | | |
| | | // 创建包含图片ID的完整表单数据 |
| | | const templateData = { |
| | | ...this.formValues, |
| | | selectedImageObjects: this.selectedImageObjects |
| | | }; |
| | | |
| | | API_trade.addToCart(data).then(res => { |
| | | if (res.data.code == 200) { |
| | | uni.navigateTo({ |
| | | url: `/pages/order/fillorder?way=${data.cartType}&addr=${this.addr.id || ''}&parentOrder=${encodeURIComponent(JSON.stringify(this.parentOrder))}` |
| | | }); |
| | | // 使用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) { |
| | | // 格式化数据 |
| | |
| | | return i.value === values.value; |
| | | }) |
| | | ) { |
| | | // 使用直接赋值而不是this.$set |
| | | arrItem.values.push(values); |
| | | } |
| | | |
| | |
| | | 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 => { |
| | |
| | | item.specValues |
| | | .filter(i => i.specName !== 'images') |
| | | .forEach((value, _index) => { |
| | | // 使用直接赋值而不是this.$set |
| | | this.currentSelected[_index] = value.specValue; |
| | | |
| | | this.selectName = value.specValue; |
| | |
| | | } |
| | | }); |
| | | |
| | | // 使用直接赋值而不是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]; |
| | | } |
| | | // 如果没有http前缀,需要拼接endpoint进行显示 |
| | | if (fileKey && !fileKey.startsWith('http://') && !fileKey.startsWith('https://')) { |
| | | // 使用endpoint拼接完整的URL |
| | | if (this.endpoint) { |
| | | return this.endpoint + '/' + 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 = []; |
| | | } |
| | | }, |
| | | // 预览模板图片 |
| | | previewImageSpec() { |
| | | // 只预览当前选中的图片 |
| | | const url = this.selectedSpecImg ? this.selectedSpecImg : this.goodsDetail.thumbnail |
| | | const urls = [url]; |
| | | |
| | | // 调用uniapp原生API预览图片 |
| | | uni.previewImage({ |
| | | current: 0, // 当前显示图片的索引(只有一张图片,所以是0) |
| | | urls: urls, // 需要预览的图片链接列表 |
| | | indicator: 'default', // 显示索引指示器 |
| | | loop: false // 只有一张图片,不需要循环预览 |
| | | }); |
| | | }, |
| | | // 预览模板图片 |
| | | 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() { |
| | | this.formatSku(this.goodsSpec); |
| | | // 使用setTimeout延迟执行,避免影响弹窗 |
| | | setTimeout(() => { |
| | | this.formatSku(this.goodsSpec); |
| | | this.initCOS(); // 初始化COS客户端 |
| | | |
| | | // 预加载模板图片的预览地址 |
| | | this.preloadTemplateImages(); |
| | | |
| | | console.log("goodsDetail",this.goodsDetail) |
| | | // 初始化模板表单数据 |
| | | this.initTemplateFormData(); |
| | | |
| | | // 默认选中第一张模板图片 |
| | | this.setDefaultSelectedImage(); |
| | | |
| | | console.log("goodsDetail",this.goodsDetail) |
| | | }, 100); |
| | | } |
| | | }; |
| | | </script> |
| | |
| | | color: #333; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | // 模板样式 |
| | | .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> |