Quellcode durchsuchen

feat: 新增商品发布多规格商品功能;

ext.zhangbin71 vor 4 Wochen
Ursprung
Commit
5ac3e4d946

+ 2 - 2
config/app.js

@@ -1,6 +1,6 @@
 // let domain = "https://www.shuibeibyg.com/front-api"; // 正式环境IP
-let domain = "https://test.shuibeibyg.com/front-api"; // 测试环境IP
-// let domain = 'http://192.168.100.199:8081' // 晋守桦IP
+// let domain = "https://test.shuibeibyg.com/front-api"; // 测试环境IP
+let domain = 'http://192.168.100.199:8081' // 晋守桦IP
 // let domain = 'http://192.168.100.246:8081' // 韩朝龙IP
 let share = "https://www.shuibeibyg.com";
 

+ 1 - 1
pages/index/index.vue

@@ -2,7 +2,7 @@
 	<view class="page-container">
 		<up-navbar class="inde-nav-bar" :bgColor="navBgColor">
 			<template #left>
-				<view class="nav-slot" v-if="!merchantNameShow()" @click="toBrowsingHistory()">
+				<view class="nav-slot" v-if="!merchantNameShow() && !!merchantInfo.merchantName" @click="toBrowsingHistory()">
 					<image class="dianpu" src="@/static/images/dianpu.png" mode="widthFix"></image>
 					<view class="storeName line1">{{ merchantInfo.merchantName }}</view>
 					<image class="zhankai" src="@/static/images/zhankai.png" mode="widthFix"></image>

+ 1 - 0
pages/merchantCenters/postInformation.vue

@@ -136,6 +136,7 @@ const handleTemplatePublish = () => {
 .publish-button {
   background: #F8C008;
   border: none;
+  font-weight: bold;
   border-radius: 16rpx;
   color: #333333;
   font-size: 32rpx;

+ 186 - 80
pages/merchantCenters/productCate/productCate.vue

@@ -4,35 +4,36 @@
 			<view class="tit">
 				规格类型
 			</view>
-			<view class="cate-add" @click="addCate">
+			<view class="cate-add" @click="addAttr">
 				新增
 			</view>
 		</view>
 
 		<view class="cate-list">
-			<view class="cate-item" v-for="i in 10">
+			<view class="cate-item" v-for="(item,index) in attr" :key="index">
 				<view class="cate-header">
 					<view class="cate-name">
 						<image class="pen" src="@/static/images/edit-pen.png"></image>
-						<up-input class="cate-name-ipt" v-model="cateDemo.attrName" placeholder="请输入规格类型" inputAlign="left"
+						<up-input class="cate-name-ipt" v-model="item.attrName" placeholder="请输入规格类型" inputAlign="left"
 							border="none" type="text">
 						</up-input>
 					</view>
-					<view class="delete-icon">
+					<view class="delete-icon" @click="deleteAttr(index)">
 						<image class="delete" src="/static/images/delete.png"></image>
 					</view>
 				</view>
-				
+
 				<view class="cate-child">
 					<view class="children">
-						<view class="children-item">
+						<view class="children-item" v-for="(child,idx) in item.attrValue" :key="'c'+idx">
 							<view class="cname">
-								金条
+								{{child}}
 							</view>
-							<image class="close" src="@/static/images/close-icon.png" mode=""></image>
+							<image class="close" @click="deleteSub(index,idx)" src="@/static/images/close-icon.png"
+								mode=""></image>
 						</view>
 					</view>
-					<view class="cate-child-add">
+					<view class="cate-child-add" @click="addSub(index)">
 						<image class="plus" src="@/static/images/plus-icon.png" mode=""></image>
 						<view class="add">
 							添加规格项
@@ -42,20 +43,22 @@
 			</view>
 		</view>
 		<view class="bottom-actions">
-			<view class="action-btn" @click="ensureClickHandle"> 确定 </view>
+			<view class="action-btn" @click="throttle(ensureClickHandle,500)"> 确定 </view>
 		</view>
-		
-		<up-popup :show="showPopUp" :round="20" :duration="150" mode="bottom"  @close="showPopUp=false" @open="showPopUp=true">
+
+		<up-popup :show="showPopUp" :round="20" :duration="150" mode="bottom" @close="showPopUp=false"
+			@open="showPopUp=true">
 			<view class="pop-container">
 				<view class="pop-header">
 					规格类型
-					<image class="pop-close" src="/static/images/close-icon.png" mode="" @click="showPopUp=false"></image>
+					<image class="pop-close" src="/static/images/close-icon.png" mode="" @click="showPopUp=false">
+					</image>
 				</view>
 				<view class="pop-ipt">
-					<textarea class="item-textarea" maxlength="100" v-model="cipt"
-						placeholder="请输入规格类型" />
+					<textarea class="item-textarea" @confirm="ensureAttrHandle" maxlength="100" :focus="iptFocus"
+						v-model="cipt" placeholder="请输入规格类型" />
 				</view>
-				<view class="action-btn" @click="ensureClickHandle"> 确定 </view>
+				<view class="action-btn" @click="ensureAttrHandle"> 确定 </view>
 			</view>
 		</up-popup>
 	</view>
@@ -70,7 +73,8 @@
 	} from 'vue';
 	import {
 		onShow,
-		onLoad
+		onLoad,
+		onBackPress
 	} from "@dcloudio/uni-app";
 	import {
 		productCategory,
@@ -89,18 +93,13 @@
 	import {
 		useToast
 	} from "@/hooks/useToast";
+	import {
+		debounce,
+		throttle
+	} from '../../../uni_modules/uview-plus';
 	const {
 		Toast
 	} = useToast();
-	const {
-		imageList,
-		afterRead,
-		deletePic,
-		uploadLoading
-	} = useImageUpload({
-		pid: 1,
-		model: "product",
-	});
 	const appStore = useAppStore();
 
 	const cateDemo = {
@@ -111,20 +110,120 @@
 		"type": 0,
 		"isDel": false
 	}
-	const cateList = ref([{}])
-	
-	const showPopUp = ref(true)
-	
+	const attr = ref([])
+
+	const showPopUp = ref(false)
+
 	const cipt = ref('')
-	
-	
-	
-	const addCate = ()=>{
+	const cIndex = ref();
+	const cSubIndex = ref()
+	const iptFocus = ref(false)
+
+	// 当前变更类型,0代表规格值,1代表规格子值
+	const cType = ref(0)
+
+	const ensureClickHandle = () => {
+		console.log("ensure")
+
+		let flag = true;
+
+		if (!attr.value || attr.value.length <= 0) {
+			return Toast({
+				title: "规格不能为空"
+			})
+		}
+
+		attr.value.forEach(item => {
+			if (!item.attrName) {
+				flag = false
+				return Toast({
+					title: "规格名称不能为空"
+				})
+			}
+			if (item.attrValue && item.attrValue.length > 0) {
+				item.attrValue.forEach(child => {
+					if (!child) {
+						flag = false
+						return Toast({
+							title: "规格子集名称不能为空"
+						})
+					}
+				})
+			} else {
+				flag = false
+				return Toast({
+					title: "规格子集不能为空"
+				})
+			}
+		})
+		if (!flag) return
+		appStore.SET_CPATTR(attr.value)
+		uni.$emit("updateAttr", attr.value)
+		setTimeout(() => {
+			uni.navigateBack()
+		},300)
+	}
+
+	onLoad(() => {
+		const data = appStore.cProductAttr;
+		attr.value = data.concat();
+	})
+
+	throttle()
+
+	// 添加规格
+	const addAttr = () => {
+		attr.value.push({
+			attrName: '',
+			attrValue: []
+		})
+		cType.value = 0;
+		cIndex.value = attr.value.length - 1
+		cipt.value = '';
 		showPopUp.value = true;
+
+		setTimeout(() => {
+			iptFocus.value = true
+		}, 500)
 	}
-	
-	const ensureClickHandle = ()=>{
-		console.log("ensure")
+
+	// 删除规格
+	const deleteAttr = (index) => {
+		attr.value.splice(index, 1)
+	}
+
+	// 删除规格子项值
+	const deleteSub = (index, idx) => {
+		attr.value[index].attrValue.splice(idx, 1)
+	}
+
+	// 新增规格子项值
+	const addSub = (index) => {
+		cType.value = 1;
+		cIndex.value = index
+		cipt.value = '';
+		showPopUp.value = true;
+		setTimeout(() => {
+			iptFocus.value = true
+		}, 500)
+	}
+
+	const ensureAttrHandle = () => {
+
+		if (!cipt.value || !cipt.value.trim()) {
+			return Toast({
+				title: "请输入规格值"
+			});
+		}
+		if (cType.value == 0) {
+			attr.value[cIndex.value].attrName = cipt.value.trim()
+		} else {
+			attr.value[cIndex.value].attrValue.push(
+				cipt.value.trim()
+			)
+		}
+		iptFocus.value = false
+		console.log(attr.value)
 		showPopUp.value = false;
 	}
 </script>
@@ -163,18 +262,18 @@
 			}
 		}
 	}
-	
-	.cate-list{
-		
-		
-		.cate-item{
+
+	.cate-list {
+
+
+		.cate-item {
 			width: 100%;
 			padding: 20rpx;
 			background-color: #fff;
 			border-radius: 16rpx;
 			margin-bottom: 20rpx;
-			
-			.cate-header{
+
+			.cate-header {
 				width: 100%;
 				height: 64rpx;
 				display: flex;
@@ -182,39 +281,40 @@
 				align-items: center;
 				border-bottom: 1px solid #F1F3F8;
 				padding-bottom: 20rpx;
-				
-				.cate-name{
+
+				.cate-name {
 					display: flex;
 					justify-content: flex-start;
 					align-items: center;
-					
-					.pen{
+
+					.pen {
 						width: 32rpx;
 						height: 32rpx;
 						margin-right: 16rpx;
 					}
-					.cate-name-ipt{
+
+					.cate-name-ipt {
 						height: 44rpx;
 						line-height: 44rpx;
 						font-size: 28rpx;
-						color:#333;
+						color: #333;
 					}
 				}
-				
-				.delete-icon{
+
+				.delete-icon {
 					padding: 10rpx 0 10rpx 10rpx;
 					display: flex;
 					justify-content: center;
 					align-items: center;
-					
-					.delete{
+
+					.delete {
 						width: 32rpx;
 						height: 32rpx;
 					}
 				}
 			}
-			
-			.children{
+
+			.children {
 				width: 100%;
 				display: flex;
 				margin-top: 20rpx;
@@ -222,28 +322,32 @@
 				align-items: center;
 				flex-wrap: wrap;
 			}
-			.children-item{
+
+			.children-item {
 				min-width: 120rpx;
 				padding: 8rpx 16rpx;
 				border-radius: 8rpx;
+				margin-right: 20rpx;
+				margin-bottom: 20rpx;
 				display: flex;
 				justify-content: space-between;
 				align-items: center;
 				background-color: #F9F7F0;
-				
-				.cname{
+
+				.cname {
 					font-size: 28rpx;
-					color:#333;
+					color: #333;
 					margin-right: 16rpx;
 					line-height: 44rpx;
 				}
-				.close{
+
+				.close {
 					width: 32rpx;
 					height: 32rpx;
 				}
 			}
-			
-			.cate-child-add{
+
+			.cate-child-add {
 				padding: 8rpx 16rpx;
 				width: 220rpx;
 				background-color: rgba(248, 192, 8, 0.10);
@@ -251,14 +355,14 @@
 				display: flex;
 				justify-content: flex-start;
 				align-items: center;
-				margin-top: 20rpx;
-				.add{
+
+				.add {
 					font-size: 28rpx;
 					line-height: 44rpx;
 					color: #F8C008;
 				}
-				
-				.plus{
+
+				.plus {
 					width: 32rpx;
 					height: 32rpx;
 					margin-right: 16rpx;
@@ -266,7 +370,7 @@
 			}
 		}
 	}
-	
+
 	.bottom-actions {
 		position: fixed;
 		bottom: 0;
@@ -277,10 +381,10 @@
 		background: #FFFFFF;
 		padding: 22rpx 32rpx calc(22rpx + constant(safe-area-inset-bottom));
 		padding: 22rpx 32rpx calc(22rpx + env(safe-area-inset-bottom));
-	
-		
+
+
 	}
-	
+
 	.action-btn {
 		font-weight: bold;
 		line-height: 88rpx;
@@ -290,22 +394,24 @@
 		font-weight: bold;
 		background: #F8C008;
 	}
-	.pop-container{
+
+	.pop-container {
 		background-color: #fff;
 		border-radius: 40rpx 40rpx 0 0;
 		position: relative;
 		padding: 22rpx 32rpx calc(22rpx + constant(safe-area-inset-bottom));
 		padding: 22rpx 32rpx calc(22rpx + env(safe-area-inset-bottom));
-		
-		.pop-header{
+
+		.pop-header {
 			height: 44rpx;
 			width: 100%;
 			text-align: center;
 			line-height: 48rpx;
 			font-size: 32rpx;
-			color:#333;
+			color: #333;
 			font-weight: bold;
-			.pop-close{
+
+			.pop-close {
 				width: 32rpx;
 				height: 32rpx;
 				position: absolute;
@@ -313,8 +419,8 @@
 				top: 32rpx;
 			}
 		}
-		
-		.pop-ipt{
+
+		.pop-ipt {
 			width: 100%;
 			display: flex;
 			background-color: #F9F7F0;
@@ -322,9 +428,9 @@
 			margin-top: 32rpx;
 			margin-bottom: 32rpx;
 		}
-		
+
 	}
-	
+
 	.item-textarea {
 		width: 100rpx;
 		height: 200rpx;

+ 570 - 154
pages/merchantCenters/releaseProduct.vue

@@ -100,24 +100,33 @@
 
 				<!-- 商品规格 -->
 				<view class="pro-cate-container">
-					<view class="cate-title" v-if="!isProductCenter"><text style="color:red;">*</text>商品规格</view>
+					<view class="cate-title" v-if="!isProductCenter">商品规格批量设置</view>
 
 					<view class="cate-list">
 						<view class="cate-item">
 							<view class="cate-pic-line">
-								<image src="@/static/avator.png" class="cate-pic" mode="widthFix"></image>
-								<view class="cate-name">
-									属性1;属性2
-								</view>
+								<!-- <image src="@/static/avator.png" class="cate-pic" mode="widthFix"></image> -->
+								<up-upload :fileList="oneTemplate.imgs" @afterRead="async (e) => {
+								  await afterRead(e);
+								  getImageProductGg('oneTemplate');
+								}" @delete="onProductImageGgDelete('oneTemplate')" name="productGg" :maxCount="1">
+									<view class="upload-btn">
+										<up-icon name="plus" size="20" color="#666"></up-icon>
+										<text class="upload-tip">上传图片</text>
+									</view>
+								</up-upload>
+								<!-- <view class="cate-name">
+									{{oneTemplate.attrName}}
+								</view> -->
 							</view>
 							<view class="cate-ipt-line">
-								<up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="formData.laborCost" placeholder="请输入工费"
-									inputAlign="left" border="none" type="digit" labelWidth="">
+								<up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="oneTemplate.price"
+									placeholder="请输入工费" inputAlign="left" border="none" type="digit" labelWidth="">
 									<template #suffix>
 										<text class="unit">元/g</text>
 									</template>
 								</up-input>
-								<up-input class="cate-ipt" v-model="formData.stock" placeholder="请输入库存"
+								<up-input class="cate-ipt" v-model="oneTemplate.stock" placeholder="请输入库存"
 									inputAlign="left" border="none" type="number" labelWidth="">
 									<template #suffix>
 										<text class="unit">件</text>
@@ -125,13 +134,72 @@
 								</up-input>
 							</view>
 							<view class="cate-ipt-line">
-								<up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="formData.weight" placeholder="请输入重量"
+								<up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="oneTemplate.weight"
+									placeholder="请输入重量" inputAlign="left" border="none" type="digit" labelWidth="">
+									<template #suffix>
+										<text class="unit">g</text>
+									</template>
+								</up-input>
+								<up-input class="cate-ipt" v-model="oneTemplate.additionalAmount" placeholder="请输入附加金额"
+									inputAlign="left" border="none" type="number" labelWidth="">
+									<template #suffix>
+										<text class="unit">元</text>
+									</template>
+								</up-input>
+							</view>
+							<view class="cate-ipt-line" style="padding: 0;">
+								<up-input class="cate-ipt" v-model="oneTemplate.barCode" placeholder="请输入商品编号"
 									inputAlign="left" border="none" type="digit" labelWidth="">
+								</up-input>
+							</view>
+						</view>
+					</view>
+					<view class="cate-add" @click="setAllAttr">
+						批量设置规格
+					</view>
+				</view>
+				<view class="pro-cate-container">
+					<view class="cate-title" v-if="!isProductCenter"><text style="color:red;">*</text>商品规格</view>
+
+					<view class="cate-list">
+						<view class="cate-item" v-for="(item,index) of attrTable" :key="'attr'+index">
+							<view class="cate-pic-line">
+								<!-- <image src="@/static/avator.png" class="cate-pic" mode="widthFix"></image> -->
+								<up-upload :fileList="item.imgs" @afterRead="async (e) => {
+								  await afterRead(e);
+								  getImageProductGg(index);
+								}" @delete="onProductImageGgDelete(index)" name="productGg" :maxCount="1">
+									<view class="upload-btn">
+										<up-icon name="plus" size="20" color="#666"></up-icon>
+										<text class="upload-tip">上传图片</text>
+									</view>
+								</up-upload>
+								<view class="cate-name">
+									{{item.attrName}}
+								</view>
+							</view>
+							<view class="cate-ipt-line">
+								<up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="item.price"
+									placeholder="请输入工费" inputAlign="left" border="none" type="digit" labelWidth="">
+									<template #suffix>
+										<text class="unit">元/g</text>
+									</template>
+								</up-input>
+								<up-input class="cate-ipt" v-model="item.stock" placeholder="请输入库存" inputAlign="left"
+									border="none" type="number" labelWidth="">
+									<template #suffix>
+										<text class="unit">件</text>
+									</template>
+								</up-input>
+							</view>
+							<view class="cate-ipt-line">
+								<up-input class="cate-ipt" style="margin-right: 16rpx;" v-model="item.weight"
+									placeholder="请输入重量" inputAlign="left" border="none" type="digit" labelWidth="">
 									<template #suffix>
 										<text class="unit">g</text>
 									</template>
 								</up-input>
-								<up-input class="cate-ipt" v-model="formData.additionalFee" placeholder="请输入附加金额"
+								<up-input class="cate-ipt" v-model="item.additionalAmount" placeholder="请输入附加金额"
 									inputAlign="left" border="none" type="number" labelWidth="">
 									<template #suffix>
 										<text class="unit">元</text>
@@ -139,7 +207,7 @@
 								</up-input>
 							</view>
 							<view class="cate-ipt-line" style="padding: 0;">
-								<up-input class="cate-ipt" v-model="formData.barCode" placeholder="请输入商品编号"
+								<up-input class="cate-ipt" v-model="item.barCode" placeholder="请输入商品编号"
 									inputAlign="left" border="none" type="digit" labelWidth="">
 								</up-input>
 							</view>
@@ -149,7 +217,7 @@
 						管理规格
 					</view>
 				</view>
-				<view class="card-title">商品规格</view>
+				<!-- <view class="card-title">商品规格</view>
 				<up-form-item label="工费" prop="laborCost" :borderBottom="false" :required="true">
 					<up-input v-model="formData.laborCost" placeholder="请输入工费" inputAlign="right" border="none"
 						type="digit"></up-input>
@@ -193,7 +261,7 @@
 						</up-upload>
 						<text class="format-tip">支持上传PNG、JPG格式图片,每张不超过5MB,最多可上传1张。</text>
 					</view>
-				</view>
+				</view> -->
 				<up-form-item label="商品排序" prop="sort" :borderBottom="false">
 					<up-input v-model="formData.sort" placeholder="请输入排序号" inputAlign="right" border="none"
 						type="number"></up-input>
@@ -238,7 +306,8 @@
 	} from 'vue';
 	import {
 		onShow,
-		onLoad
+		onLoad,
+		onUnload
 	} from "@dcloudio/uni-app";
 	import {
 		productCategory,
@@ -320,6 +389,25 @@
 	const productId = ref(null);
 	const isProductCenter = ref(null);
 
+	// 规格
+	const attr = ref([]);
+	// 规格列表
+	const attrTable = ref([]);
+	// 规格数据模板
+	const oneTemplate = ref({
+		image: '',
+		imgs: [],
+		price: '',
+		additionalAmount: '',
+		stock: '',
+		barCode: '',
+		weight: '',
+	})
+	// 规格历史数据,防止刷新数据时已填数据丢失
+	let historyAttr = []
+	// 规格表历史数据
+	let historyTable = {}
+
 	// 材质列表
 	const materialList = ref([{
 			name: '黄金',
@@ -379,27 +467,6 @@
 			message: '请选择材质',
 			trigger: ['blur', 'change']
 		},
-		weight: {
-			type: 'string',
-			required: true,
-			pattern: /^\d+(\.\d+)?$/,
-			message: '重量必须是数字,可以是小数',
-			trigger: ['blur', 'change']
-		},
-		laborCost: {
-			type: 'string',
-			required: true,
-			pattern: /^\d+(\.\d+)?$/,
-			message: '工费必须是数字,可以是小数',
-			trigger: ['blur', 'change']
-		},
-		additionalFee: {
-			type: 'string',
-			required: true,
-			pattern: /^\d+(\.\d+)?$/,
-			message: '附加费必须是数字,可以是小数',
-			trigger: ['blur', 'change']
-		},
 		sort: {
 			type: 'string',
 			pattern: /^\d*$/,
@@ -410,36 +477,60 @@
 				return /^\d+$/.test(value);
 			}
 		},
-		stock: {
-			type: 'string',
-			required: true,
-			pattern: /^\d*$/,
-			message: '库存必须是整数',
-			trigger: ['blur', 'change'],
-			validator: (rule, value) => {
-				if (!value || value.trim().length === 0) return false;
-				return /^\d+$/.test(value);
-			}
-		},
-		barCode: {
-			type: 'string',
-			required: true,
-			pattern: /^\d*$/,
-			message: '商品编号必须是整数',
-			trigger: ['blur', 'change'],
-			validator: (rule, value) => {
-				if (!value || value.trim().length === 0) return false;
-				return /^\d+$/.test(value);
-			}
-
-		},
+		// additionalFee: {
+		// 	type: 'string',
+		// 	required: true,
+		// 	pattern: /^\d+(\.\d+)?$/,
+		// 	message: '附加费必须是数字,可以是小数',
+		// 	trigger: ['blur', 'change']
+		// },
+		// weight: {
+		// 	type: 'string',
+		// 	required: true,
+		// 	pattern: /^\d+(\.\d+)?$/,
+		// 	message: '重量必须是数字,可以是小数',
+		// 	trigger: ['blur', 'change']
+		// },
+		// laborCost: {
+		// 	type: 'string',
+		// 	required: true,
+		// 	pattern: /^\d+(\.\d+)?$/,
+		// 	message: '工费必须是数字,可以是小数',
+		// 	trigger: ['blur', 'change']
+		// },
+		// stock: {
+		// 	type: 'string',
+		// 	required: true,
+		// 	pattern: /^\d*$/,
+		// 	message: '库存必须是整数',
+		// 	trigger: ['blur', 'change'],
+		// 	validator: (rule, value) => {
+		// 		if (!value || value.trim().length === 0) return false;
+		// 		return /^\d+$/.test(value);
+		// 	}
+		// },
+		// barCode: {
+		// 	type: 'string',
+		// 	required: true,
+		// 	pattern: /^\d*$/,
+		// 	message: '商品编号必须是整数',
+		// 	trigger: ['blur', 'change'],
+		// 	validator: (rule, value) => {
+		// 		if (!value || value.trim().length === 0) return false;
+		// 		return /^\d+$/.test(value);
+		// 	}
+
+		// },
 	});
 
 	// 页面加载
 	onShow(() => {
 
 	})
+
 	onLoad(async (options) => {
+		appStore.SET_CPATTR([])
+		appStore.SET_CPATTRVALUE([])
 		await getProductCategory();
 		await getTempData();
 		console.log(options)
@@ -450,8 +541,22 @@
 		} else {
 			resetForm();
 		}
+
+		uni.$on("updateAttr", onUpdateAttr)
+	})
+	onUnload(() => {
+		uni.$off("updateAttr")
 	})
 
+	// 规格设置页面点击确认,更新当前页规格表格
+	const onUpdateAttr = (data) => {
+		// console.log("onUpdateAttr", data)
+		const table = attrFormat(data)
+		attr.value = data.concat();
+		attrTable.value = table
+		// console.log("table==>", table)
+	}
+
 	// 获取商品分类
 	async function getProductCategory() {
 		let obj = {
@@ -517,9 +622,12 @@
 			// 方法2:延迟触发字段验证
 			setTimeout(async () => {
 				// 逐个触发必填字段的验证
+				// const requiredFields = ['categoryIds', 'storeName', 'keyword', 'storeInfo', 'unitName',
+				// 	'tempIds', 'metalType', 'weight', 'laborCost', 'additionalFee',
+				// 	'stock', 'barCode'
+				// ];
 				const requiredFields = ['categoryIds', 'storeName', 'keyword', 'storeInfo', 'unitName',
-					'tempIds', 'metalType', 'weight', 'laborCost', 'additionalFee',
-					'stock', 'barCode'
+					'tempIds', 'metalType'
 				];
 
 				for (const field of requiredFields) {
@@ -587,20 +695,42 @@
 				}
 			}
 
+
 			// 商品属性
 			if (data.attrValue && data.attrValue.length > 0) {
-				formData.value.additionalFee = data.attrValue[0].additionalAmount;
-				formData.value.laborCost = data.attrValue[0].price;
-				formData.value.barCode = data.attrValue[0].barCode;
-				formData.value.stock = data.attrValue[0].stock;
-				formData.value.weight = data.attrValue[0].weight;
-
-				// 规格图片
-				productImagesGg.value = data.attrValue[0].image ? [{
-					url: data.attrValue[0].image
-				}] : [];
+				data.attr.forEach(item => {
+					attr.value.push({
+						attrName: item.attrName,
+						attrValue: item.attrValues.split(',')
+					})
+				})
+				data.attrValue.forEach(item => {
+					let attrName = item.suk;
+					attrTable.value.push({
+						...item,
+						attrName: attrName,
+						imgs: item?.image ? [{
+							url: item.image
+						}] : []
+					})
+				})
+				appStore.SET_CPATTR(attr.value)
 			}
 
+			// 商品属性
+			// if (data.attrValue && data.attrValue.length > 0) {
+			// 	formData.value.additionalFee = data.attrValue[0].additionalAmount;
+			// 	formData.value.laborCost = data.attrValue[0].price;
+			// 	formData.value.barCode = data.attrValue[0].barCode;
+			// 	formData.value.stock = data.attrValue[0].stock;
+			// 	formData.value.weight = data.attrValue[0].weight;
+
+			// 	// 规格图片
+			// 	productImagesGg.value = data.attrValue[0].image ? [{
+			// 		url: data.attrValue[0].image
+			// 	}] : [];
+			// }
+
 			// 商品描述
 			descriptionText.value = data.content ? data.content.replace(/<[^>]*>/g, '').substring(0, 500) : '';
 			formData.value.content = data.content || '';
@@ -743,10 +873,19 @@
 		}
 		imageList.value = [];
 	}
-	async function getImageProductGg() {
+	async function getImageProductGg(index) {
 		if (imageList.value.length > 0) {
 			if (imageList.value[0].status == "success") {
-				productImagesGg.value = imageList.value;
+				console.log(imageList.value)
+				if (index == "oneTemplate") {
+					oneTemplate.value.imgs = imageList.value
+					oneTemplate.value.image = imageList.value[0].info.url
+				} else {
+					attrTable.value[index].imgs = imageList.value
+					attrTable.value[index].image = imageList.value[0].info.url
+				}
+
+				// productImagesGg.value = imageList.value;
 				// change();
 			} else {
 				Toast({
@@ -764,6 +903,13 @@
 		productImages.value.splice(e.index, 1);
 	};
 	const onProductImageGgDelete = (e) => {
+		console.log(e)
+		if (e == "oneTemplate") {
+			oneTemplate.value.imgs = []
+		} else {
+			attrTable.value[e].imgs = []
+		}
+
 		productImagesGg.value.splice(e.index, 1);
 	};
 
@@ -830,53 +976,53 @@
 			}
 
 
-			if (productImagesGg.value.length === 0) {
-				uni.showToast({
-					title: '请上传商品规格图片',
-					icon: 'none'
-				});
-				return;
-			}
+			// if (productImagesGg.value.length === 0) {
+			// 	uni.showToast({
+			// 		title: '请上传商品规格图片',
+			// 		icon: 'none'
+			// 	});
+			// 	return;
+			// }
 
 			// 验证数字字段
-			if (!/^\d+(\.\d+)?$/.test(formData.value.weight)) {
-				uni.showToast({
-					title: '重量格式不正确',
-					icon: 'none'
-				});
-				return;
-			}
-
-			if (!/^\d+(\.\d+)?$/.test(formData.value.laborCost)) {
-				uni.showToast({
-					title: '工费格式不正确',
-					icon: 'none'
-				});
-				return;
-			}
-
-			if (!/^\d+(\.\d+)?$/.test(formData.value.additionalFee)) {
-				uni.showToast({
-					title: '附加费格式不正确',
-					icon: 'none'
-				});
-				return;
-			}
-
-			if (!/^\d+$/.test(formData.value.stock)) {
-				uni.showToast({
-					title: '库存必须是整数',
-					icon: 'none'
-				});
-				return;
-			}
-			if (!/^\d+$/.test(formData.value.barCode)) {
-				uni.showToast({
-					title: '商品编号必须是整数',
-					icon: 'none'
-				});
-				return;
-			}
+			// if (!/^\d+(\.\d+)?$/.test(formData.value.weight)) {
+			// 	uni.showToast({
+			// 		title: '重量格式不正确',
+			// 		icon: 'none'
+			// 	});
+			// 	return;
+			// }
+
+			// if (!/^\d+(\.\d+)?$/.test(formData.value.laborCost)) {
+			// 	uni.showToast({
+			// 		title: '工费格式不正确',
+			// 		icon: 'none'
+			// 	});
+			// 	return;
+			// }
+
+			// if (!/^\d+(\.\d+)?$/.test(formData.value.additionalFee)) {
+			// 	uni.showToast({
+			// 		title: '附加费格式不正确',
+			// 		icon: 'none'
+			// 	});
+			// 	return;
+			// }
+
+			// if (!/^\d+$/.test(formData.value.stock)) {
+			// 	uni.showToast({
+			// 		title: '库存必须是整数',
+			// 		icon: 'none'
+			// 	});
+			// 	return;
+			// }
+			// if (!/^\d+$/.test(formData.value.barCode)) {
+			// 	uni.showToast({
+			// 		title: '商品编号必须是整数',
+			// 		icon: 'none'
+			// 	});
+			// 	return;
+			// }
 
 			const valid = formRef.value.validate();
 			return valid;
@@ -885,42 +1031,127 @@
 			return false;
 		}
 	};
+	
+	const validateAttr = (item)=>{
+		if (!item.image) {
+			uni.showToast({
+				title: '请上传商品规格图片',
+				icon: 'none'
+			});
+			return false;
+		}
+		
+		// 验证数字字段
+		if (!/^\d+(\.\d+)?$/.test(item.weight)) {
+			uni.showToast({
+				title: '重量格式不正确',
+				icon: 'none'
+			});
+			return false;
+		}
+		
+		if (!/^\d+(\.\d+)?$/.test(item.price)) {
+			uni.showToast({
+				title: '工费格式不正确',
+				icon: 'none'
+			});
+			return false;
+		}
+		
+		if (!/^\d+(\.\d+)?$/.test(item.additionalAmount)) {
+			uni.showToast({
+				title: '附加费格式不正确',
+				icon: 'none'
+			});
+			return false;
+		}
+		
+		if (!/^\d+$/.test(item.stock)) {
+			uni.showToast({
+				title: '库存必须是整数',
+				icon: 'none'
+			});
+			return false;
+		}
+		if (!/^\d+$/.test(item.barCode)) {
+			uni.showToast({
+				title: '商品编号必须是整数',
+				icon: 'none'
+			});
+			return false;
+		}
+		
+		return true
+	}
 	const submitForm = async () => {
 		const valid = validateForm();
-
 		if (valid) {
 			const submitData = {
 				...formData.value
 			};
 			let urlString = '';
+			console.log(previewImages.value[0])
 			if (isProductCenter.value) {
 				submitData.image = previewImages.value[0].url;
 				urlString = JSON.stringify(productImages.value.map(item => item.url));
 			} else {
-				submitData.image = previewImages.value[0].info.url;
-				urlString = JSON.stringify(productImages.value.map(item => item.info.url));
+				submitData.image = previewImages.value[0]?.info?.url || previewImages.value[0]?.url;
+				urlString = JSON.stringify(productImages.value.map(item => item?.info?.url || item?.url));
 			}
 			submitData.sliderImage = urlString;
 			submitData.cateId = formData.value.categoryIds.join(',');
 
 
 			submitData.merchantId = parseInt(merchantInfo.id);
-			submitData.specType = 0;
+			submitData.specType = true;
 			submitData.isSub = false;
-			submitData.attr = [{
-				"attrName": "规格",
-				"attrValues": "默认",
-				"id": 0
-			}];
-			submitData.attrValue = [{
-				additionalAmount: formData.value.additionalFee,
-				attrValue: "{\"规格\":\"默认\"}",
-				barCode: formData.value.barCode,
-				image: productImagesGg.value[0]?.info?.url || productImagesGg.value[0].url,
-				price: formData.value.laborCost,
-				stock: formData.value.stock,
-				weight: formData.value.weight
-			}];
+			// 规格长度为0
+			console.log(attr.value,attrTable.value)
+			if (!attr.value || attr.value.length <= 0 || !attrTable.value || attrTable.value.length <= 0) {
+				return Toast({
+					title: "请添加商品规格!"
+				});
+			}
+
+			const pass = true;
+			submitData.attr = attr.value.map(item => {
+				return {
+					attrName: item.attrName,
+					attrValues: item.attrValue.join(','),
+				}
+			})
+			submitData.attrValue = attrTable.value.map(item => {
+				
+				if(!validateAttr(item)){
+					pass = false;
+				}
+				
+				console.log(typeof(item.attrValue))
+				return {
+					...item,
+					id: 0,
+					productId: 0,
+					attrValue: typeof(item.attrValue)=='object'?JSON.stringify(item.attrValue):item.attrValue,
+					image: item.image || item.imgs[0]?.info?.url || item.imgs[0]?.url
+				}
+			})
+			console.dir(submitData)
+			if(!pass) return false;
+			// return false;
+			// submitData.attr = [{
+			// 	"attrName": "规格",
+			// 	"attrValues": "默认",
+			// 	"id": 0
+			// }];
+			// submitData.attrValue = [{
+			// 	additionalAmount: formData.value.additionalFee,
+			// 	attrValue: "{\"规格\":\"默认\"}",
+			// 	barCode: formData.value.barCode,
+			// 	image: productImagesGg.value[0]?.info?.url || productImagesGg.value[0].url,
+			// 	price: formData.value.laborCost,
+			// 	stock: formData.value.stock,
+			// 	weight: formData.value.weight
+			// }];
 
 			if (productId.value && !isProductCenter.value) {
 				const {
@@ -939,10 +1170,15 @@
 					icon: 'success'
 				});
 			}
-
-			uni.navigateTo({
-				url: '/pages/merchantCenters/productManagement'
-			})
+			// 编辑时,返回上个页面
+			if(!!productId.value && !isProductCenter.value){
+				uni.navigateBack()
+			}else{
+				uni.redirectTo({
+					url: '/pages/merchantCenters/productManagement'
+				})
+			}
+			
 		}
 	}
 	// 重置表单(用于新建场景)
@@ -976,12 +1212,160 @@
 			formRef.value.clearValidate();
 		}
 	}
-	
-	const editCatePage = ()=>{
+
+	const editCatePage = () => {
+		if (attrTable.value && attrTable.value.length) {
+			historyAttr = []
+			historyTable = {}
+			// 防止刷新数据时已填数据丢失
+			attrTable.value.forEach(item => {
+				historyAttr.push(item.attrName)
+				historyTable[item.attrName] = {
+					...item
+				}
+			})
+		}
+		console.log(historyAttr, historyTable)
+
 		uni.navigateTo({
 			url: "/pages/merchantCenters/productCate/productCate"
 		})
 	}
+
+	const setAllAttr = () => {
+		if(!validateAttr(oneTemplate.value)) return false;
+		uni.showModal({
+			title: '提示',
+			content: '所有商品规格将修改为当前配置',
+			confirmText: '确认',
+			cancelText: "取消",
+			success(res) {
+				if (res.confirm) {
+					console.log(oneTemplate.value)
+					console.log("确认了", attrTable.value);
+					if (attrTable.value && attrTable.value.length > 0) {
+						attrTable.value = attrTable.value.map(item => {
+							return {
+								...item,
+								...oneTemplate.value
+							}
+						})
+					}
+				}
+			}
+		})
+	}
+
+	function attrFormat(arr) {
+		// console.log('arr', arr)
+		let data = [];
+		const res = [];
+		return format(arr);
+
+		function format(arr) {
+			console.log("attrFormat", arr)
+			if (arr.length > 1) {
+				arr.forEach((v, i) => {
+					if (i === 0) data = arr[i]["attrValue"];
+					const tmp = [];
+					if (!data) return;
+					data.forEach(function(vv) {
+						arr[i + 1] &&
+							arr[i + 1]["attrValue"] &&
+							arr[i + 1]["attrValue"].forEach(g => {
+								const rep2 =
+									(i !== 0 ? "" : arr[i]["attrName"] + "_") +
+									vv +
+									"$&" +
+									arr[i + 1]["attrName"] +
+									"_" +
+									g;
+								tmp.push(rep2);
+								if (i === arr.length - 2) {
+									const rep4 = {
+										additionalAmount: "",
+										image: "",
+										price: "",
+										cost: 0,
+										otPrice: 0,
+										stock: "",
+										barCode: "",
+										weight: "",
+										volume: 0,
+										brokerage: 0,
+										brokerage_two: 0,
+										imgs: []
+									};
+									rep2.split("$&").forEach((h, k) => {
+										const rep3 = h.split("_");
+										if (!rep4["attrValue"]) rep4["attrValue"] = {};
+										rep4["attrValue"][rep3[0]] =
+											rep3.length > 1 ? rep3[1] : "";
+									});
+									for (const attrValueKey in rep4.attrValue) {
+										rep4[attrValueKey] = rep4.attrValue[attrValueKey];
+									}
+									res.push(rep4);
+								}
+							});
+					});
+					data = tmp.length ? tmp : [];
+				});
+			} else {
+				const dataArr = [];
+				arr.forEach((v, k) => {
+					// console.log("v['attrValue']", v['attrValue'])
+					v["attrValue"] &&
+						v["attrValue"].forEach((vv, kk) => {
+							dataArr[kk] = v["attrName"] + "_" + vv;
+							res[kk] = {
+								additionalAmount: "",
+								image: "",
+								price: "",
+								cost: 0,
+								otPrice: 0,
+								stock: "",
+								barCode: "",
+								weight: "",
+								volume: 0,
+								brokerage: 0,
+								brokerage_two: 0,
+								attrValue: {
+									[v["attrName"]]: vv
+								},
+								imgs: []
+							};
+							// Object.values(res[kk].attrValue).forEach((v, i) => {
+							//   res[kk]['value' + i] = v
+							// })
+							for (const attrValueKey in res[kk].attrValue) {
+								res[kk][attrValueKey] = res[kk].attrValue[attrValueKey];
+							}
+						});
+				});
+				data.push(dataArr.join("$&"));
+			}
+			// console.log(res)
+			const result = []
+			res.forEach(item => {
+				let attrName = [];
+				for (const name in item.attrValue) {
+					attrName.push(item.attrValue[name]);
+				}
+				item.attrName = attrName.join(',');
+				// 防止刷新数据时已填数据丢失
+				if (historyAttr.includes(item.attrName)) {
+					console.log(historyAttr, item.attrName, historyTable[item.attrName])
+					item = {
+						...historyTable[item.attrName]
+					}
+				}
+				result.push(item)
+				console.log("res-item==>", item)
+			})
+			return result;
+		}
+	}
 </script>
 
 <style scoped lang="scss">
@@ -1033,17 +1417,17 @@
 		flex-direction: column;
 		align-items: center;
 		justify-content: center;
-		width: 200rpx;
-		height: 200rpx;
-		border: 2rpx dashed #ccc;
-		border-radius: 12rpx;
-		background: #fafafa;
+		width: 5rem;
+		height: 5rem;
+		border: 1rpx dashed #DCDFE6;
+		border-radius: 8rpx;
+		background: #fff;
 	}
 
 	.upload-tip {
-		font-size: 24rpx;
-		color: #999;
-		margin-top: 10rpx;
+		font-size: 20rpx;
+		color: #666;
+		margin-top: 6rpx;
 	}
 
 	.format-tip {
@@ -1094,6 +1478,7 @@
 		background: #F8C008;
 		border-radius: 16rpx;
 		font-size: 32rpx;
+		font-weight: bold;
 		color: #333333;
 		border: none;
 		width: 100%;
@@ -1214,8 +1599,8 @@
 			color: #333;
 			margin-bottom: 16rpx;
 		}
-		
-		.cate-add{
+
+		.cate-add {
 			width: 100%;
 			height: 88rpx;
 			line-height: 88rpx;
@@ -1235,6 +1620,19 @@
 				background-color: #F9F7F0;
 				border-radius: 16rpx;
 				padding: 16rpx;
+				margin-bottom: 16rpx;
+				
+				.upload-btn {
+					display: flex;
+					flex-direction: column;
+					align-items: center;
+					justify-content: center;
+					width: 100rpx;
+					height: 100rpx;
+					border: 1rpx dashed #DCDFE6;
+					border-radius: 8rpx;
+					background: #fff;
+				}
 
 				.cate-pic-line {
 					width: 100%;
@@ -1244,6 +1642,24 @@
 					align-items: center;
 					margin-bottom: 16rpx;
 
+					::v-deep .u-upload {
+						flex: 0;
+						margin-right: 16rpx;
+						width: 100rpx;
+						height: 100rpx;
+					}
+
+					::v-deep .u-upload__wrap__preview {
+						margin: 0;
+						width: 100rpx;
+						height: 100rpx;
+					}
+
+					::v-deep .u-upload__wrap__preview__image {
+						width: 100rpx !important;
+						height: 100rpx !important;
+					}
+
 					.cate-pic {
 						width: 100rpx;
 						height: 100rpx;
@@ -1277,9 +1693,9 @@
 						border-radius: 16rpx;
 						padding: 0 16rpx !important;
 						font-size: 28rpx;
-						
-						.unit{
-							color:#333;
+
+						.unit {
+							color: #333;
 						}
 					}
 				}

+ 8 - 0
stores/app.js

@@ -47,6 +47,8 @@ export const useAppStore = defineStore("app", {
 	  // 首页展示刷新标识,当浏览了新店铺主页/扫店铺码时,改为true,首页onshow时判断true刷新列表并改为false
 	  indexRefreshFlag: false,
 	  cartRefreshFlag: false,
+	  cProductAttr: [],
+	  cProductAttrValue: []
     };
   },
   getters: {
@@ -67,6 +69,12 @@ export const useAppStore = defineStore("app", {
     SET_REFRESH(val) {
       this.refreshArticles = val;
     },
+	SET_CPATTR(val){
+		this.cProductAttr = val
+	},
+	SET_CPATTRVALUE(val){
+		this.cProductAttrValue = val
+	},
     SET_CART_REFRESH(val) {
       this.cartRefreshFlag = val;
     },