소스 검색

Merge remote-tracking branch 'origin/bjcz_his_scrm' into bjcz_his_scrm

yjwang 2 일 전
부모
커밋
1851cec97b

+ 236 - 1
src/views/components/course/userCourseCatalogDetails.vue

@@ -124,6 +124,14 @@
             <img v-if="form.thumbnail" :src="form.thumbnail" class="avatar" width="300px">
             <i v-else class="el-icon-plus avatar-uploader-icon"></i>
           </el-upload>
+          <div class="image-size-tip">
+            <i class="el-icon-info"></i>
+            <span>建议尺寸:90×60px(比例3:2)</span>
+          </div>
+          <div v-if="thumbnailValidation.show" :class="['validation-message', thumbnailValidation.type]">
+            <i :class="thumbnailValidation.type === 'warning' ? 'el-icon-warning' : 'el-icon-success'"></i>
+            <span>{{ thumbnailValidation.message }}</span>
+          </div>
         </el-form-item>
         <video-upload :type="1" :isPrivate="isPrivate" :fileKey.sync="form.fileKey" :fileSize.sync="form.fileSize"
                       :videoUrl.sync="videoUrl" :fileName.sync="form.fileName" :line_1.sync="form.lineOne"
@@ -338,6 +346,18 @@ export default {
   components: { VideoUpload, QuestionBank, CourseWatchComment, CourseProduct,},
   data() {
     return {
+      // 缩略图验证相关
+      thumbnailValidation: {
+        show: false,
+        type: 'success', // success 或 warning
+        message: ''
+      },
+      // 尺寸配置
+      thumbnailSizeConfig: {
+        width: 90,
+        height: 60,
+        tolerance: 0.15 // 15%容差
+      },
       duration: null,
       packageList: [],
       //课题
@@ -613,6 +633,8 @@ export default {
     handleAvatarSuccess(res, file) {
       if (res.code == 200) {
         this.form.thumbnail = res.url;
+        // 验证缩略图尺寸
+        this.validateThumbnailImage(res.url);
         this.$forceUpdate()
       }
       else {
@@ -623,9 +645,128 @@ export default {
       const isLt1M = file.size / 1024 / 1024 < 5;
       if (!isLt1M) {
         this.$message.error('上传图片大小不能超过 5MB!');
+        return false;
+      }
+      const isPic = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/jpg';
+      if (!isPic) {
+        this.$message.error('只能上传JPG、PNG、GIF格式的图片!');
+        return false;
       }
-      return isLt1M;
+      return true;
     },
+    
+    // 验证缩略图尺寸
+    validateThumbnailImage(imageUrl) {
+      if (!imageUrl) {
+        this.thumbnailValidation.show = false;
+        return;
+      }
+      
+      console.log('开始验证缩略图尺寸:', imageUrl);
+      
+      const img = new Image();
+      const config = this.thumbnailSizeConfig;
+      
+      // 设置超时处理
+      const timeoutId = setTimeout(() => {
+        this.thumbnailValidation = {
+          show: true,
+          type: 'warning',
+          message: '图片加载超时,无法验证尺寸,请检查网络连接'
+        };
+        console.log('图片加载超时');
+      }, 10000);
+      
+      img.onload = () => {
+        clearTimeout(timeoutId);
+        const { width: actualWidth, height: actualHeight } = img;
+        const { width: expectedWidth, height: expectedHeight, tolerance } = config;
+        
+        console.log(`实际尺寸: ${actualWidth}x${actualHeight}px, 期望尺寸: ${expectedWidth}x${expectedHeight}px`);
+        
+        // 计算比例差异
+        const expectedRatio = expectedWidth / expectedHeight;
+        const actualRatio = actualWidth / actualHeight;
+        const ratioDiff = Math.abs(actualRatio - expectedRatio) / expectedRatio;
+        
+        console.log(`期望比例: ${expectedRatio.toFixed(3)}, 实际比例: ${actualRatio.toFixed(3)}, 差异: ${(ratioDiff * 100).toFixed(2)}%`);
+        
+        this.thumbnailValidation.show = true;
+        
+        if (ratioDiff <= tolerance) {
+          this.thumbnailValidation.type = 'success';
+          this.thumbnailValidation.message = `✓ 尺寸符合要求 (实际: ${actualWidth}x${actualHeight}px)`;
+          console.log('尺寸验证通过');
+        } else {
+          this.thumbnailValidation.type = 'warning';
+          this.thumbnailValidation.message = `⚠ 尺寸不符合要求!实际: ${actualWidth}x${actualHeight}px,建议: ${expectedWidth}x${expectedHeight}px (比例${expectedRatio.toFixed(1)}:1)`;
+          console.log('尺寸验证失败');
+        }
+      };
+      
+      img.onerror = (error) => {
+        clearTimeout(timeoutId);
+        console.error('图片加载失败:', error);
+        
+        // 检查URL格式和可访问性
+        this.checkImageUrl(imageUrl).then(isValid => {
+          if (!isValid) {
+            this.thumbnailValidation = {
+              show: true,
+              type: 'warning',
+              message: '图片URL无效或无法访问,请检查图片地址是否正确'
+            };
+          } else {
+            this.thumbnailValidation = {
+              show: true,
+              type: 'warning',
+              message: '图片加载失败,可能是跨域问题或格式不支持,请重新上传'
+            };
+          }
+        });
+      };
+      
+      // 处理完整的图片URL
+      const fullImageUrl = this.getFullImageUrl(imageUrl);
+      img.crossOrigin = 'anonymous'; // 尝试处理跨域
+      img.src = fullImageUrl;
+    },
+    
+    // 获取完整的图片URL
+    getFullImageUrl(imageUrl) {
+      if (!imageUrl) return '';
+      
+      // 如果已经是完整URL(http/https开头)
+      if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {
+        return imageUrl;
+      }
+      
+      // 如果是base64
+      if (imageUrl.startsWith('data:image/')) {
+        return imageUrl;
+      }
+      
+      // 如果是相对路径,拼接基础URL
+      const baseUrl = process.env.VUE_APP_BASE_API || '';
+      if (imageUrl.startsWith('/')) {
+        return baseUrl + imageUrl;
+      } else {
+        return baseUrl + '/' + imageUrl;
+      }
+    },
+    
+    // 检查图片URL有效性
+    async checkImageUrl(imageUrl) {
+      try {
+        const fullUrl = this.getFullImageUrl(imageUrl);
+        const response = await fetch(fullUrl, { method: 'HEAD' });
+        return response.ok;
+      } catch (error) {
+        console.error('URL检查失败:', error);
+        return false;
+      }
+    },
+    
     getDetails(courseId, courseName, isPrivate) {
       this.isPrivate = isPrivate
       this.courseName = courseName
@@ -690,6 +831,8 @@ export default {
         listingStartTime: null,
         listingEndTime: null,
       };
+      // 重置缩略图验证状态
+      this.thumbnailValidation = { show: false, type: 'success', message: '' };
       this.videoURL = '';
       this.progress = 0;
       this.resetForm("form");
@@ -749,6 +892,14 @@ export default {
         setTimeout(() => {
           this.$refs.videoUpload.resetUpload();
         }, 500);
+        
+        // 如果有缩略图,验证其尺寸
+        if (this.form.thumbnail) {
+          setTimeout(() => {
+            this.validateThumbnailImage(this.form.thumbnail);
+          }, 1000);
+        }
+        
         this.open = true;
         this.title = "修改课堂视频";
       });
@@ -808,12 +959,36 @@ export default {
               this.msgSuccess("修改成功");
               this.open = false;
               this.getList();
+            }).catch(error => {
+              // 处理后端返回的图片校验错误
+              if (error.response && error.response.data && error.response.data.msg) {
+                const msg = error.response.data.msg;
+                if (msg.includes("图片") && (msg.includes("尺寸") || msg.includes("比例"))) {
+                  this.$message.error(msg);
+                  return;
+                }
+                this.$message.error(msg);
+                return;
+              }
+              this.$message.error("修改失败:" + (error.message || error));
             });
           } else {
             addUserCourseVideo(this.form).then(response => {
               this.msgSuccess("新增成功");
               this.open = false;
               this.getList();
+            }).catch(error => {
+              // 处理后端返回的图片校验错误
+              if (error.response && error.response.data && error.response.data.msg) {
+                const msg = error.response.data.msg;
+                if (msg.includes("图片") && (msg.includes("尺寸") || msg.includes("比例"))) {
+                  this.$message.error(msg);
+                  return;
+                }
+                this.$message.error(msg);
+                return;
+              }
+              this.$message.error("新增失败:" + (error.message || error));
             });
           }
         }
@@ -920,6 +1095,66 @@ export default {
   }
 }
 </script>
+<style scoped>
+.image-size-tip {
+  margin-top: 5px;
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+}
+
+.image-size-tip i {
+  margin-right: 5px;
+  color: #409EFF;
+}
+
+.validation-message {
+  margin-top: 8px;
+  padding: 6px 10px;
+  border-radius: 4px;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  animation: fadeIn 0.3s ease-in;
+}
+
+.validation-message i {
+  margin-right: 6px;
+  font-size: 14px;
+}
+
+.validation-message.success {
+  background-color: #f0f9ff;
+  border: 1px solid #b3d8ff;
+  color: #67C23A;
+}
+
+.validation-message.success i {
+  color: #67C23A;
+}
+
+.validation-message.warning {
+  background-color: #fdf6ec;
+  border: 1px solid #f5dab1;
+  color: #E6A23C;
+}
+
+.validation-message.warning i {
+  color: #E6A23C;
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-5px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+</style>
 <style>
 .avatar-uploader .el-upload {
   border: 1px dashed #d9d9d9;

+ 227 - 19
src/views/hisStore/adv/index.vue

@@ -147,6 +147,14 @@
               <img v-if="form.imageUrl" :src="form.imageUrl" class="avatar">
               <i v-else class="el-icon-plus avatar-uploader-icon"></i>
             </el-upload>
+            <div class="image-size-tip">
+              <i class="el-icon-info"></i>
+              <span>建议尺寸:280×90px(比例28:9)</span>
+            </div>
+            <div v-if="imageValidation.show" :class="['validation-message', imageValidation.type]">
+              <i :class="imageValidation.type === 'warning' ? 'el-icon-warning' : 'el-icon-success'"></i>
+              <span>{{ imageValidation.message }}</span>
+            </div>
         </el-form-item>
         <el-form-item label="广告视频" prop="video" v-if="form.urlType==2">
           <div>
@@ -211,6 +219,18 @@ export default {
   },
   data() {
     return {
+      // 广告图片验证相关
+      imageValidation: {
+        show: false,
+        type: 'success', // success 或 warning
+        message: ''
+      },
+      // 广告图片尺寸配置
+      imageSizeConfig: {
+        width: 280,
+        height: 90,
+        tolerance: 0.15 // 15%容差
+      },
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       baseUrl: process.env.VUE_APP_BASE_API,
       videoAccept:"video/*",
@@ -315,6 +335,8 @@ export default {
     handleAvatarSuccess(res, file) {
         if(res.code==200){
           this.form.imageUrl=res.url;
+          // 验证广告图片尺寸
+          this.validateAdvertImage(res.url);
         }
         else{
           this.msgError(res.msg);
@@ -325,9 +347,128 @@ export default {
       const isLt1M = file.size / 1024 / 1024 < 200;
       if (!isLt1M) {
         this.$message.error('上传图片大小不能超过 200MB!');
+        return false;
       }
-      return   isLt1M;
+      const isPic = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif' || file.type === 'image/jpg';
+      if (!isPic) {
+        this.$message.error('只能上传JPG、PNG、GIF格式的图片!');
+        return false;
+      }
+      return true;
     },
+    
+    // 验证广告图片尺寸
+    validateAdvertImage(imageUrl) {
+      if (!imageUrl) {
+        this.imageValidation.show = false;
+        return;
+      }
+      
+      console.log('开始验证广告图片尺寸:', imageUrl);
+      
+      const img = new Image();
+      const config = this.imageSizeConfig;
+      
+      // 设置超时处理
+      const timeoutId = setTimeout(() => {
+        this.imageValidation = {
+          show: true,
+          type: 'warning',
+          message: '图片加载超时,无法验证尺寸,请检查网络连接'
+        };
+        console.log('图片加载超时');
+      }, 10000);
+      
+      img.onload = () => {
+        clearTimeout(timeoutId);
+        const { width: actualWidth, height: actualHeight } = img;
+        const { width: expectedWidth, height: expectedHeight, tolerance } = config;
+        
+        console.log(`实际尺寸: ${actualWidth}x${actualHeight}px, 期望尺寸: ${expectedWidth}x${expectedHeight}px`);
+        
+        // 计算比例差异
+        const expectedRatio = expectedWidth / expectedHeight;
+        const actualRatio = actualWidth / actualHeight;
+        const ratioDiff = Math.abs(actualRatio - expectedRatio) / expectedRatio;
+        
+        console.log(`期望比例: ${expectedRatio.toFixed(3)}, 实际比例: ${actualRatio.toFixed(3)}, 差异: ${(ratioDiff * 100).toFixed(2)}%`);
+        
+        this.imageValidation.show = true;
+        
+        if (ratioDiff <= tolerance) {
+          this.imageValidation.type = 'success';
+          this.imageValidation.message = `✓ 尺寸符合要求 (实际: ${actualWidth}x${actualHeight}px)`;
+          console.log('尺寸验证通过');
+        } else {
+          this.imageValidation.type = 'warning';
+          this.imageValidation.message = `⚠ 尺寸不符合要求!实际: ${actualWidth}x${actualHeight}px,建议: ${expectedWidth}x${expectedHeight}px (比例${expectedRatio.toFixed(1)}:1)`;
+          console.log('尺寸验证失败');
+        }
+      };
+      
+      img.onerror = (error) => {
+        clearTimeout(timeoutId);
+        console.error('图片加载失败:', error);
+        
+        // 检查URL格式和可访问性
+        this.checkImageUrl(imageUrl).then(isValid => {
+          if (!isValid) {
+            this.imageValidation = {
+              show: true,
+              type: 'warning',
+              message: '图片URL无效或无法访问,请检查图片地址是否正确'
+            };
+          } else {
+            this.imageValidation = {
+              show: true,
+              type: 'warning',
+              message: '图片加载失败,可能是跨域问题或格式不支持,请重新上传'
+            };
+          }
+        });
+      };
+      
+      // 处理完整的图片URL
+      const fullImageUrl = this.getFullImageUrl(imageUrl);
+      img.crossOrigin = 'anonymous'; // 尝试处理跨域
+      img.src = fullImageUrl;
+    },
+    
+    // 获取完整的图片URL
+    getFullImageUrl(imageUrl) {
+      if (!imageUrl) return '';
+      
+      // 如果已经是完整URL(http/https开头)
+      if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {
+        return imageUrl;
+      }
+      
+      // 如果是base64
+      if (imageUrl.startsWith('data:image/')) {
+        return imageUrl;
+      }
+      
+      // 如果是相对路径,拼接基础URL
+      const baseUrl = process.env.VUE_APP_BASE_API || '';
+      if (imageUrl.startsWith('/')) {
+        return baseUrl + imageUrl;
+      } else {
+        return baseUrl + '/' + imageUrl;
+      }
+    },
+    
+    // 检查图片URL有效性
+    async checkImageUrl(imageUrl) {
+      try {
+        const fullUrl = this.getFullImageUrl(imageUrl);
+        const response = await fetch(fullUrl, { method: 'HEAD' });
+        return response.ok;
+      } catch (error) {
+        console.error('URL检查失败:', error);
+        return false;
+      }
+    },
+    
     /** 查询广告列表 */
     getList() {
       this.loading = true;
@@ -358,6 +499,8 @@ export default {
         advType: null,
         showType: null
       };
+      // 重置广告图片验证状态
+      this.imageValidation = { show: false, type: 'success', message: '' };
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -392,6 +535,14 @@ export default {
         this.form.status = response.data.status.toString();
         this.form.advType = response.data.advType.toString();
         this.form.showType = response.data.showType ? response.data.showType.toString() : "";
+        
+        // 如果有广告图片且是图片类型,验证其尺寸
+        if (this.form.imageUrl && this.form.urlType === 1) {
+          setTimeout(() => {
+            this.validateAdvertImage(this.form.imageUrl);
+          }, 1000);
+        }
+        
         this.open = true;
         this.title = "修改广告";
         setTimeout(() => {
@@ -459,6 +610,81 @@ export default {
   }
 };
 </script>
+<style scoped>
+.image-size-tip {
+  margin-top: 5px;
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+}
+
+.image-size-tip i {
+  margin-right: 5px;
+  color: #409EFF;
+}
+
+.validation-message {
+  margin-top: 8px;
+  padding: 6px 10px;
+  border-radius: 4px;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  animation: fadeIn 0.3s ease-in;
+}
+
+.validation-message i {
+  margin-right: 6px;
+  font-size: 14px;
+}
+
+.validation-message.success {
+  background-color: #f0f9ff;
+  border: 1px solid #b3d8ff;
+  color: #67C23A;
+}
+
+.validation-message.success i {
+  color: #67C23A;
+}
+
+.validation-message.warning {
+  background-color: #fdf6ec;
+  border: 1px solid #f5dab1;
+  color: #E6A23C;
+}
+
+.validation-message.warning i {
+  color: #E6A23C;
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-5px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
+.avatar-uploader-icon {
+  font-size: 28px;
+  color: #8c939d;
+  width: 300px;
+  height: 150px;
+  line-height: 150px;
+  text-align: center;
+}
+
+.avatar {
+  width: 300px;
+  height: 150px;
+  display: block;
+}
+</style>
 <style >
  .avatar-uploader .el-upload {
     border: 1px dashed #d9d9d9;
@@ -471,22 +697,4 @@ export default {
     border-color: #409EFF;
   }
 </style>
-<style scoped>
-
-  .avatar-uploader-icon {
-    font-size: 28px;
-    color: #8c939d;
-    width: 300px;
-    height: 150px;
-    line-height: 150px;
-    text-align: center;
-  }
-  .avatar {
-    width: 300px;
-    height: 150px;
-    display: block;
-  }
-
-
-</style>
 

+ 172 - 18
src/views/hisStore/storeActivity/index.vue

@@ -116,9 +116,25 @@
         </el-form-item> -->
         <el-form-item label="弹框广告图" prop="logoUrl">
           <Material v-model="imageArr" type="image" :num="1" :width="150" :height="150" />
+          <div class="image-size-tip">
+            <i class="el-icon-info"></i>
+            <span>建议尺寸:750×400px(比例15:8)</span>
+          </div>
+          <div v-if="logoValidation.show" :class="['validation-message', logoValidation.type]">
+            <i :class="logoValidation.type === 'warning' ? 'el-icon-warning' : 'el-icon-success'"></i>
+            <span>{{ logoValidation.message }}</span>
+          </div>
         </el-form-item>
         <el-form-item label="BANNER图" prop="images">
           <Material v-model="photoArr" type="image" :num="10" :width="150" :height="150" />
+          <div class="image-size-tip">
+            <i class="el-icon-info"></i>
+            <span>建议尺寸:1200×600px(比例2:1)</span>
+          </div>
+          <div v-if="bannerValidation.show" :class="['validation-message', bannerValidation.type]">
+            <i :class="bannerValidation.type === 'warning' ? 'el-icon-warning' : 'el-icon-success'"></i>
+            <span>{{ bannerValidation.message }}</span>
+          </div>
         </el-form-item>
         <el-form-item label="活动商品" prop="productIds"  >
               <el-row  >
@@ -198,10 +214,22 @@ export default {
   },
   watch: {
     imageArr: function(val) {
-      this.form.logoUrl = val.join(',')
+      this.form.logoUrl = val.join(',');
+      // 验证弹框广告图
+      if (val.length > 0) {
+        this.validateImage(val[val.length - 1], 'logo');
+      } else {
+        this.logoValidation.show = false;
+      }
     },
     photoArr: function(val) {
-      this.form.images = val.join(',')
+      this.form.images = val.join(',');
+      // 验证BANNER图
+      if (val.length > 0) {
+        this.validateImage(val[val.length - 1], 'banner');
+      } else {
+        this.bannerValidation.show = false;
+      }
     },
   },
   data() {
@@ -215,6 +243,22 @@ export default {
       statusOptions:[],
       imageArr:[],
       activeName:"1",
+      // 图片验证相关
+      logoValidation: {
+        show: false,
+        type: 'success', // success 或 warning
+        message: ''
+      },
+      bannerValidation: {
+        show: false,
+        type: 'success',
+        message: ''
+      },
+      // 尺寸配置
+      sizeConfig: {
+        logo: { width: 750, height: 400, tolerance: 0.15 }, // 15%容差
+        banner: { width: 1200, height: 600, tolerance: 0.15 }
+      },
       // 遮罩层
       loading: true,
       // 选中数组
@@ -277,6 +321,42 @@ export default {
     updateText(text){
       this.form.content=text
     },
+    // 验证图片尺寸
+    validateImage(imageUrl, type) {
+      if (!imageUrl) return;
+      
+      const img = new Image();
+      const config = this.sizeConfig[type];
+      const validation = type === 'logo' ? this.logoValidation : this.bannerValidation;
+      
+      img.onload = () => {
+        const { width: actualWidth, height: actualHeight } = img;
+        const { width: expectedWidth, height: expectedHeight, tolerance } = config;
+        
+        // 计算比例差异
+        const expectedRatio = expectedWidth / expectedHeight;
+        const actualRatio = actualWidth / actualHeight;
+        const ratioDiff = Math.abs(actualRatio - expectedRatio) / expectedRatio;
+        
+        validation.show = true;
+        
+        if (ratioDiff <= tolerance) {
+          validation.type = 'success';
+          validation.message = `✓ 尺寸符合要求 (实际: ${actualWidth}x${actualHeight}px)`;
+        } else {
+          validation.type = 'warning';
+          validation.message = `⚠ 尺寸不符合要求!实际: ${actualWidth}x${actualHeight}px,建议: ${expectedWidth}x${expectedHeight}px`;
+        }
+      };
+      
+      img.onerror = () => {
+        validation.show = true;
+        validation.type = 'warning';
+        validation.message = '图片加载失败,无法验证尺寸';
+      };
+      
+      img.src = imageUrl;
+    },
     handleClick(tab, event) {
       this.queryParams.status=tab.name;
       this.getList();
@@ -312,6 +392,9 @@ export default {
       this.photoArr=[];
       this.products=[];
       this.products=[];
+      // 重置验证状态
+      this.logoValidation = { show: false, type: 'success', message: '' };
+      this.bannerValidation = { show: false, type: 'success', message: '' };
       this.resetForm("form");
     },
     /** 搜索按钮操作 */
@@ -374,23 +457,33 @@ export default {
               productIds.push(element.productId);
           });
           this.form.productIds=productIds.toString();
-          if (this.form.activityId != null) {
-            updateStoreActivity(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("修改成功");
-                this.open = false;
-                this.getList();
+          
+          const savePromise = this.form.activityId != null 
+            ? updateStoreActivity(this.form) 
+            : addStoreActivity(this.form);
+          
+          savePromise.then(response => {
+            if (response.code === 200) {
+              this.msgSuccess(this.form.activityId != null ? "修改成功" : "新增成功");
+              this.open = false;
+              this.getList();
+            }
+          }).catch(error => {
+            // 处理后端返回的图片校验错误
+            if (error.response && error.response.data && error.response.data.msg) {
+              const msg = error.response.data.msg;
+              // 检查是否为图片尺寸验证错误
+              if (msg.includes("图片") && (msg.includes("尺寸") || msg.includes("比例"))) {
+                this.$message.error(msg);
+                return;
               }
-            });
-          } else {
-            addStoreActivity(this.form).then(response => {
-              if (response.code === 200) {
-                this.msgSuccess("新增成功");
-                this.open = false;
-                this.getList();
-              }
-            });
-          }
+              // 其他业务错误
+              this.$message.error(msg);
+              return;
+            }
+            // 网络或其他未知错误
+            this.$message.error("操作失败:" + (error.message || error));
+          });
         }
       });
     },
@@ -424,3 +517,64 @@ export default {
   }
 };
 </script>
+
+<style scoped>
+.image-size-tip {
+  margin-top: 5px;
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+}
+
+.image-size-tip i {
+  margin-right: 5px;
+  color: #409EFF;
+}
+
+.validation-message {
+  margin-top: 8px;
+  padding: 6px 10px;
+  border-radius: 4px;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  animation: fadeIn 0.3s ease-in;
+}
+
+.validation-message i {
+  margin-right: 6px;
+  font-size: 14px;
+}
+
+.validation-message.success {
+  background-color: #f0f9ff;
+  border: 1px solid #b3d8ff;
+  color: #67C23A;
+}
+
+.validation-message.success i {
+  color: #67C23A;
+}
+
+.validation-message.warning {
+  background-color: #fdf6ec;
+  border: 1px solid #f5dab1;
+  color: #E6A23C;
+}
+
+.validation-message.warning i {
+  color: #E6A23C;
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-5px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+</style>

+ 260 - 46
src/views/hisStore/storeProduct/index.vue

@@ -270,6 +270,14 @@
         </el-row>
         <el-form-item label="商品图片" prop="image">
           <Material v-model="imageArr" type="image" :num="1" :width="150" :height="150" />
+          <div class="image-size-tip">
+            <i class="el-icon-info"></i>
+            <span>建议尺寸:800×640px</span>
+          </div>
+          <div v-if="productImageValidation.show" :class="['validation-message', productImageValidation.type]">
+            <i :class="productImageValidation.type === 'warning' ? 'el-icon-warning' : 'el-icon-success'"></i>
+            <span>{{ productImageValidation.message }}</span>
+          </div>
         </el-form-item>
         <!--        <el-form-item label="商品视频" prop="video">
                   <div>
@@ -289,6 +297,14 @@
                 </el-form-item>-->
         <el-form-item label="轮播图" prop="sliderImage">
           <Material v-model="photoArr" type="image" :num="10" :width="150" :height="150" />
+          <div class="image-size-tip">
+            <i class="el-icon-info"></i>
+            <span>建议尺寸:300×300px</span>
+          </div>
+          <div v-if="carouselImageValidation.show" :class="['validation-message', carouselImageValidation.type]">
+            <i :class="carouselImageValidation.type === 'warning' ? 'el-icon-warning' : 'el-icon-success'"></i>
+            <span>{{ carouselImageValidation.message }}</span>
+          </div>
         </el-form-item>
         <el-row>
           <el-col :span="24">
@@ -682,14 +698,52 @@ export default {
   },
   watch: {
     imageArr: function(val) {
-      this.form.image = val.join(',')
+      this.form.image = val.join(',');
+      // 验证商品图片
+      if (val.length > 0) {
+        const latestImage = val[val.length - 1];
+        if (latestImage && latestImage.trim()) {
+          this.validateImage(latestImage, 'product');
+        } else {
+          this.productImageValidation.show = false;
+        }
+      } else {
+        this.productImageValidation = { show: false, type: 'success', message: '' };
+      }
     },
     photoArr: function(val) {
-      this.form.sliderImage = val.join(',')
+      this.form.sliderImage = val.join(',');
+      // 验证轮播图片
+      if (val.length > 0) {
+        const latestImage = val[val.length - 1];
+        if (latestImage && latestImage.trim()) {
+          this.validateImage(latestImage, 'carousel');
+        } else {
+          this.carouselImageValidation.show = false;
+        }
+      } else {
+        this.carouselImageValidation = { show: false, type: 'success', message: '' };
+      }
     }
   },
   data() {
     return {
+      // 图片验证相关
+      productImageValidation: {
+        show: false,
+        type: 'success', // success 或 warning
+        message: ''
+      },
+      carouselImageValidation: {
+        show: false,
+        type: 'success',
+        message: ''
+      },
+      // 尺寸配置
+      sizeConfig: {
+        product: { width: 800, height: 640, tolerance: 0.15 }, // 15%容差
+        carousel: { width: 300, height: 300, tolerance: 0.15 }
+      },
       companyId: null,
       uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
       //videoAccept:"video/*",
@@ -1053,6 +1107,65 @@ export default {
     updateText(text){
       this.form.description=text
     },
+    // 验证图片尺寸
+    validateImage(imageUrl, type) {
+      if (!imageUrl || !imageUrl.trim()) {
+        const validation = type === 'product' ? this.productImageValidation : this.carouselImageValidation;
+        validation.show = false;
+        return;
+      }
+      
+      const img = new Image();
+      const config = this.sizeConfig[type];
+      const validation = type === 'product' ? this.productImageValidation : this.carouselImageValidation;
+      
+      // 设置跨域属性,避免CORS问题
+      img.crossOrigin = 'anonymous';
+      
+      img.onload = () => {
+        const { width: actualWidth, height: actualHeight } = img;
+        const { width: expectedWidth, height: expectedHeight, tolerance } = config;
+        
+        // 计算比例差异
+        const expectedRatio = expectedWidth / expectedHeight;
+        const actualRatio = actualWidth / actualHeight;
+        const ratioDiff = Math.abs(actualRatio - expectedRatio) / expectedRatio;
+        
+        validation.show = true;
+        
+        if (ratioDiff <= tolerance) {
+          validation.type = 'success';
+          validation.message = `✓ 尺寸符合要求 (实际: ${actualWidth}x${actualHeight}px)`;
+          // 3秒后隐藏成功消息
+        } else {
+          validation.type = 'warning';
+          const typeName = type === 'product' ? '商品图片' : '轮播图片';
+          const expectedRatioText = expectedWidth / expectedHeight === 1.25 ? '5:4' : '1:1';
+          validation.message = `⚠ ${typeName}尺寸不符合要求!实际: ${actualWidth}x${actualHeight}px (${(actualRatio).toFixed(2)}:1),建议: ${expectedWidth}x${expectedHeight}px (${expectedRatioText})`;
+        }
+      };
+      
+      img.onerror = () => {
+        validation.show = true;
+        validation.type = 'warning';
+        validation.message = '图片加载失败,无法验证尺寸,请检查图片URL是否正确';
+      };
+      
+      // 处理相对路径和绝对路径
+      const fullImageUrl = this.getFullImageUrl(imageUrl);
+      img.src = fullImageUrl;
+    },
+    // 获取完整的图片URL
+    getFullImageUrl(imageUrl) {
+      if (!imageUrl) return '';
+      // 如果是完整的URL,直接返回
+      if (imageUrl.startsWith('http://') || imageUrl.startsWith('https://')) {
+        return imageUrl;
+      }
+      // 如果是相对路径,拼接基础URL
+      const baseUrl = process.env.VUE_APP_BASE_API || '';
+      return baseUrl + (imageUrl.startsWith('/') ? imageUrl : '/' + imageUrl);
+    },
     handleClick(tab, event) {
       this.queryParams.isShow=tab.name;
       this.getList();
@@ -1160,6 +1273,9 @@ export default {
       this.attrs=[];
       this.photoArr=[];
       this.imageArr=[];
+      // 重置验证状态
+      this.productImageValidation = { show: false, type: 'success', message: '' };
+      this.carouselImageValidation = { show: false, type: 'success', message: '' };
     },
     /** 搜索按钮操作 */
     handleQuery() {
@@ -1259,11 +1375,21 @@ export default {
             this.$refs.myeditor.setText(this.form.description);
           }
         }, 200);
-        if(this.form.image!=null){
+        if(this.form.image!=null && this.form.image.trim()){
           this.imageArr=this.form.image.split(",");
+          // 验证现有的商品图片
+          const productImages = this.imageArr.filter(img => img && img.trim());
+          if (productImages.length > 0) {
+            this.validateImage(productImages[productImages.length - 1], 'product');
+          }
         }
-        if(this.form.sliderImage!=null){
+        if(this.form.sliderImage!=null && this.form.sliderImage.trim()){
           this.photoArr=this.form.sliderImage.split(",");
+          // 验证现有的轮播图片
+          const carouselImages = this.photoArr.filter(img => img && img.trim());
+          if (carouselImages.length > 0) {
+            this.validateImage(carouselImages[carouselImages.length - 1], 'carousel');
+          }
         }
         console.log(this.oneFormValidate)
         this.open = true;
@@ -1274,53 +1400,80 @@ export default {
     submitForm() {
       this.$refs["form"].validate(valid => {
         if (valid) {
-          if(this.form.specType ===0 ){
-            this.form.items = [];
-            this.form.values = this.oneFormValidate;
-          }else{
-            this.form.items = this.attrs;
-            this.form.values = this.manyFormValidate;
+          // 检查是否有图片验证警告
+          if (this.productImageValidation.show && this.productImageValidation.type === 'warning') {
+            this.$confirm('商品图片尺寸不符合建议要求,是否继续提交?', '提示', {
+              confirmButtonText: '继续提交',
+              cancelButtonText: '取消',
+              type: 'warning'
+            }).then(() => {
+              this.doSubmit();
+            }).catch(() => {});
+            return;
           }
-
-          const hasError = this.form.values.some(v => {
-            if (v.hasDepositFeatureType === 1) {
-              let  depositAmount =Number(v.depositAmount);
-              let onBehalfPaymentAmount=Number(v.onBehalfPaymentAmount);
-              if (depositAmount < 1) {
-                this.$message.warning('数据提交失败,当前商品已开启定金,定金金额不能小于1!');
-                return true;
-              } else if (onBehalfPaymentAmount < 1) {
-                this.$message.warning('数据提交失败,当前商品已开启定金,代付金额不能小于1!');
-                return true;
-              } else if ((depositAmount + onBehalfPaymentAmount) !== Number(v.price)) {
-                this.$message.warning('数据提交失败,当前商品已开启定金,商品定金和代付总和要与售价相等!');
-                return true;
-              }
-            }
-            return false;
-          });
-
-          if (hasError) {
+          
+          if (this.carouselImageValidation.show && this.carouselImageValidation.type === 'warning') {
+            this.$confirm('轮播图片尺寸不符合建议要求,是否继续提交?', '提示', {
+              confirmButtonText: '继续提交',
+              cancelButtonText: '取消',
+              type: 'warning'
+            }).then(() => {
+              this.doSubmit();
+            }).catch(() => {});
             return;
           }
+          
+          this.doSubmit();
+        }
+      });
+    },
+    // 执行提交操作
+    doSubmit() {
+      if(this.form.specType ===0 ){
+        this.form.items = [];
+        this.form.values = this.oneFormValidate;
+      }else{
+        this.form.items = this.attrs;
+        this.form.values = this.manyFormValidate;
+      }
 
-          if(this.form.specType === 1 && this.manyFormValidate.length===0){
-            return this.$message.warning('请点击生成规格!');
-          }
-          // 组装companyIds
-          if (this.form.companyIds != null && this.form.companyIds != undefined) {
-            this.form.companyIds = this.form.companyIds.join(',');
+      const hasError = this.form.values.some(v => {
+        if (v.hasDepositFeatureType === 1) {
+          let  depositAmount =Number(v.depositAmount);
+          let onBehalfPaymentAmount=Number(v.onBehalfPaymentAmount);
+          if (depositAmount < 1) {
+            this.$message.warning('数据提交失败,当前商品已开启定金,定金金额不能小于1!');
+            return true;
+          } else if (onBehalfPaymentAmount < 1) {
+            this.$message.warning('数据提交失败,当前商品已开启定金,代付金额不能小于1!');
+            return true;
+          } else if ((depositAmount + onBehalfPaymentAmount) !== Number(v.price)) {
+            this.$message.warning('数据提交失败,当前商品已开启定金,商品定金和代付总和要与售价相等!');
+            return true;
           }
-          if(this.form.productId !== null && this.form.productId !== undefined && this.form.productId === 0) {
-            this.form.productId=null;
-          }
-          addOrEdit(this.form).then(response => {
-            if (response.code === 200) {
-              this.msgSuccess("操作成功");
-              this.open = false;
-              this.getList();
-            }
-          });
+        }
+        return false;
+      });
+
+      if (hasError) {
+        return;
+      }
+
+      if(this.form.specType === 1 && this.manyFormValidate.length===0){
+        return this.$message.warning('请点击生成规格!');
+      }
+      // 组装companyIds
+      if (this.form.companyIds != null && this.form.companyIds != undefined) {
+        this.form.companyIds = this.form.companyIds.join(',');
+      }
+      if(this.form.productId !== null && this.form.productId !== undefined && this.form.productId === 0) {
+        this.form.productId=null;
+      }
+      addOrEdit(this.form).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess("操作成功");
+          this.open = false;
+          this.getList();
         }
       });
     },
@@ -1362,3 +1515,64 @@ export default {
   }
 };
 </script>
+
+<style scoped>
+.image-size-tip {
+  margin-top: 5px;
+  font-size: 12px;
+  color: #909399;
+  display: flex;
+  align-items: center;
+}
+
+.image-size-tip i {
+  margin-right: 5px;
+  color: #409EFF;
+}
+
+.validation-message {
+  margin-top: 8px;
+  padding: 6px 10px;
+  border-radius: 4px;
+  font-size: 12px;
+  display: flex;
+  align-items: center;
+  animation: fadeIn 0.3s ease-in;
+}
+
+.validation-message i {
+  margin-right: 6px;
+  font-size: 14px;
+}
+
+.validation-message.success {
+  background-color: #f0f9ff;
+  border: 1px solid #b3d8ff;
+  color: #67C23A;
+}
+
+.validation-message.success i {
+  color: #67C23A;
+}
+
+.validation-message.warning {
+  background-color: #fdf6ec;
+  border: 1px solid #f5dab1;
+  color: #E6A23C;
+}
+
+.validation-message.warning i {
+  color: #E6A23C;
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: translateY(-5px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+</style>