Browse Source

feat: 新增商品添加规格值图片

叶静 3 weeks ago
parent
commit
c9af30b197

+ 70 - 11
src/app/shop/admin/goods/goods/edit.vue

@@ -220,12 +220,27 @@
                   <div class="sku-value sa-flex sa-flex-wrap">
                     <div class="sku-value-title sa-m-r-16 sa-m-b-16 sa-flex"> 规格值 </div>
                     <div v-for="(sc, c) in s.children" :key="c" class="sku-value-box sa-m-b-16">
-                      <el-input
-                        v-model="sc.name"
-                        placeholder="请输入规格值"
-                        class="sku-value-input"
-                        @input="buildSkuPriceTable"
-                      ></el-input>
+                      <div class="sku-value-content">
+                        <el-input
+                          v-model="sc.name"
+                          placeholder="请输入规格值"
+                          class="sku-value-input"
+                          @input="buildSkuPriceTable"
+                        ></el-input>
+                        <div class="sku-value-image sa-m-l-8">
+                          <sa-upload-image
+                            v-model="sc.imageList"
+                            :max-count="1"
+                            :accept="['jpg', 'jpeg', 'png']"
+                            :max-size="5"
+                            :direct-upload="true"
+                            :size="30"
+                            :show-tip="false"
+                            :compact="true"
+                            placeholder=""
+                          />
+                        </div>
+                      </div>
                       <el-icon @click="deleteChildrenSku(k, c)" class="sku-value-icon">
                         <CircleCloseFilled />
                       </el-icon>
@@ -628,6 +643,7 @@
       temp_id: newTempId,
       name: childrenModal[k] || '',
       pid: formData.skus[k].id,
+      imageList: [], // 添加图片列表属性
     });
     childrenModal[k] = '';
 
@@ -841,11 +857,20 @@
       // 处理 attr 数据,构建 skus 结构
       if (attr && attr.length > 0) {
         attr.forEach((attrItem) => {
+          // 处理规格值图片数据
+          const attrImgMap = {};
+          if (attrItem.attrImgValues && Array.isArray(attrItem.attrImgValues)) {
+            attrItem.attrImgValues.forEach((imgItem) => {
+              attrImgMap[imgItem.name] = imgItem.img ? [imgItem.img] : [];
+            });
+          }
+
           const children = attrItem.attrValues.split(',').map((value) => ({
             id: 0, // 子规格值没有单独的ID,保持为0
             temp_id: tempId++,
             name: value.trim(),
             pid: attrItem.id, // 使用父规格的ID
+            imageList: attrImgMap[value.trim()] || [], // 添加图片列表
           }));
 
           skus.push({
@@ -946,6 +971,11 @@
           id: sku.id || 0, // 保留规格ID,新增时为0
           attrName: sku.name,
           attrValues: sku.children.map((child) => child.name).join(','),
+          // 生成 attrImgValues 数据 - 为所有规格值生成结构,没有图片时img为空字符串
+          attrImgValues: sku.children.map((child) => ({
+            name: child.name,
+            img: child.imageList && child.imageList.length > 0 ? child.imageList[0] : '', // 有图片取第一张,没有则为空字符串
+          })),
         }));
 
       // 生成 attrValue 数据
@@ -1397,16 +1427,45 @@
         .sku-value-box {
           position: relative;
           margin-right: 24px;
+          border: 1px solid #e4e7ed;
+          border-radius: 6px;
+          padding: 8px;
+          background: #fafafa;
+
+          .sku-value-content {
+            display: flex;
+            align-items: center;
+            gap: 8px;
+          }
+
           .sku-value-input {
             width: 104px;
           }
+
+          .sku-value-image {
+            flex-shrink: 0;
+          }
+
           .sku-value-icon {
             position: absolute;
-            right: -8px;
-            top: -8px;
-            width: 16px;
-            height: 16px;
-            color: var(--el-color-primary);
+            right: -9px;
+            top: -12px;
+            width: 18px;
+            height: 18px;
+            color: var(--el-color-danger);
+            background: white;
+            border-radius: 50%;
+            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+            cursor: pointer;
+            z-index: 10;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+
+            &:hover {
+              color: var(--el-color-danger);
+              background: var(--el-color-danger-light-9);
+            }
           }
         }
         .sku-value-add {

+ 31 - 4
src/sheep/components/sa-uploader/sa-upload-image.global.vue

@@ -13,7 +13,7 @@
       >
         <template #item="{ element, index }">
           <div
-            class="image-item"
+            class="image-item mr-2px"
             :class="{ 'compact-mode': compact }"
             :style="{ width: size + 'px', height: size + 'px' }"
           >
@@ -197,11 +197,30 @@
     return props.accept.map((type) => `.${type}`).join(',');
   });
 
+  // 添加一个标志来防止循环更新
+  let isUpdatingFromProps = false;
+  let isUpdatingFromInternal = false;
+
+  // 数组比较函数
+  const arraysEqual = (a, b) => {
+    if (a.length !== b.length) return false;
+    return a.every((val, index) => val === b[index]);
+  };
+
   // 监听外部数据变化
   watch(
     () => props.modelValue,
     (newValue) => {
-      imageList.value = [...newValue];
+      if (isUpdatingFromInternal) return;
+
+      const currentValue = imageList.value;
+      if (!arraysEqual(newValue, currentValue)) {
+        isUpdatingFromProps = true;
+        imageList.value = [...newValue];
+        setTimeout(() => {
+          isUpdatingFromProps = false;
+        }, 0);
+      }
     },
     { deep: true },
   );
@@ -210,8 +229,16 @@
   watch(
     imageList,
     (newValue) => {
-      emit('update:modelValue', newValue);
-      emit('change', newValue);
+      if (isUpdatingFromProps) return;
+
+      if (!arraysEqual(newValue, props.modelValue)) {
+        isUpdatingFromInternal = true;
+        emit('update:modelValue', [...newValue]);
+        emit('change', [...newValue]);
+        setTimeout(() => {
+          isUpdatingFromInternal = false;
+        }, 0);
+      }
     },
     { deep: true },
   );