瀏覽代碼

Merge branch 'development' of http://8.152.195.41:3000/alien/group_web_merchant into development

sunshibo 3 天之前
父節點
當前提交
5051f95b59
共有 29 個文件被更改,包括 327 次插入129 次删除
  1. 二進制
      src/assets/images/headImg.png
  2. 2 2
      src/components/Upload/Img.vue
  3. 11 6
      src/components/Upload/Imgs.vue
  4. 1 1
      src/views/businessInfo/manageInfo.vue
  5. 4 4
      src/views/businessInfo/subjectInfo.vue
  6. 5 5
      src/views/businessInfo/zfbIndex.vue
  7. 3 3
      src/views/dynamicManagement/index.vue
  8. 3 3
      src/views/dynamicManagement/myDynamic.vue
  9. 2 2
      src/views/dynamicManagement/publishDynamic.vue
  10. 3 3
      src/views/dynamicManagement/userDynamic.vue
  11. 16 0
      src/views/groupPackageManagement/newGroup.vue
  12. 45 11
      src/views/licenseManagement/businessLicense.vue
  13. 94 34
      src/views/licenseManagement/contractManagement.vue
  14. 34 7
      src/views/licenseManagement/entertainmentLicense.vue
  15. 45 11
      src/views/licenseManagement/foodBusinessLicense.vue
  16. 6 6
      src/views/operationManagement/newActivity.vue
  17. 2 2
      src/views/performance/components/PerformanceFormDialog.vue
  18. 2 2
      src/views/performance/edit.vue
  19. 2 2
      src/views/priceList/edit.vue
  20. 3 3
      src/views/storeDecoration/add.vue
  21. 13 0
      src/views/storeDecoration/decorationChat.vue
  22. 3 3
      src/views/storeDecoration/facilitiesAndServices/components/FacilityManagement.vue
  23. 1 1
      src/views/storeDecoration/facilitiesAndServices/components/ServiceManagement.vue
  24. 1 1
      src/views/storeDecoration/menuManagement/index.vue
  25. 2 2
      src/views/storeDecoration/officialPhotoAlbum/index.vue
  26. 6 3
      src/views/storeDecoration/personnelConfig/index.vue
  27. 16 10
      src/views/storeDecoration/storeCoverMap/index.vue
  28. 1 1
      src/views/storeDecoration/storeEntranceMap/index.vue
  29. 1 1
      src/views/storeDecoration/wineMenuManagement/index.vue

二進制
src/assets/images/headImg.png


+ 2 - 2
src/components/Upload/Img.vue

@@ -59,7 +59,7 @@ interface UploadFileProps {
   api?: (params: any) => Promise<any>; // 上传图片的 api 方法,一般项目上传都是同一个 api 方法,在组件里直接引入即可 ==> 非必传
   drag?: boolean; // 是否支持拖拽上传 ==> 非必传(默认为 true)
   disabled?: boolean; // 是否禁用上传组件 ==> 非必传(默认为 false)
-  fileSize?: number; // 图片大小限制 ==> 非必传(默认为 5M)
+  fileSize?: number; // 图片大小限制 ==> 非必传(默认为 20M)
   fileType?: File.ImageMimeType[]; // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
   height?: string; // 组件高度 ==> 非必传(默认为 150px)
   width?: string; // 组件宽度 ==> 非必传(默认为 150px)
@@ -73,7 +73,7 @@ const props = withDefaults(defineProps<UploadFileProps>(), {
   imageUrl: "",
   drag: true,
   disabled: false,
-  fileSize: 5,
+  fileSize: 20,
   fileType: () => ["image/jpeg", "image/png", "image/gif"],
   height: "150px",
   width: "150px",

+ 11 - 6
src/components/Upload/Imgs.vue

@@ -78,7 +78,9 @@ interface UploadFileProps {
   drag?: boolean; // 是否支持拖拽上传 ==> 非必传(默认为 true)
   disabled?: boolean; // 是否禁用上传组件 ==> 非必传(默认为 false)
   limit?: number; // 最大图片上传数 ==> 非必传(默认为 5张)
-  fileSize?: number; // 图片大小限制 ==> 非必传(默认为 5M)
+  fileSize?: number; // 图片大小限制(MB)==> 非必传(默认 20M)
+  /** 视频大小限制(MB);含 video/* 的 fileType 时生效;默认 200;仅发布动态/门店封面页在业务里单独放宽 */
+  videoFileSize?: number;
   fileType?: string[]; // 接受的 MIME(图片 + 可选视频)==> 非必传
   height?: string; // 组件高度 ==> 非必传(默认为 150px)
   width?: string; // 组件宽度 ==> 非必传(默认为 150px)
@@ -94,7 +96,8 @@ const props = withDefaults(defineProps<UploadFileProps>(), {
   drag: true,
   disabled: false,
   limit: 5,
-  fileSize: 5,
+  fileSize: 20,
+  videoFileSize: 200,
   fileType: () => ["image/jpeg", "image/png", "image/gif", "video/mp4", "video/webm", "video/quicktime", "video/ogg"],
   height: "150px",
   width: "150px",
@@ -197,7 +200,6 @@ const isVideoFile = (file: UploadFile) => {
  * @param rawFile 选择的文件
  * */
 const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
-  const imgSize = rawFile.size / 1024 / 1024 < props.fileSize;
   const acceptVideo = props.fileType.some(t => String(t).startsWith("video/"));
   const byNameVideo = /\.(mp4|m4v|webm|ogg|mov)(\?.*)?$/i.test(rawFile.name || "");
   const mimeVideo = typeof rawFile.type === "string" && rawFile.type.startsWith("video/");
@@ -205,21 +207,24 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
     acceptVideo && byNameVideo && (!rawFile.type || rawFile.type === "application/octet-stream" || mimeVideo);
   const typeListed = props.fileType.includes(rawFile.type);
   const okType = typeListed || looseVideoMime;
+  const isVideo = mimeVideo || looseVideoMime;
+  const limitMb = isVideo && acceptVideo && props.videoFileSize > 0 ? props.videoFileSize : props.fileSize;
+  const okSize = rawFile.size / 1024 / 1024 < limitMb;
   if (!okType)
     ElNotification({
       title: "温馨提示",
       message: "上传文件不符合所需的格式!",
       type: "warning"
     });
-  if (!imgSize)
+  if (!okSize)
     setTimeout(() => {
       ElNotification({
         title: "温馨提示",
-        message: `上传文件大小不能超过 ${props.fileSize}M!`,
+        message: `上传文件大小不能超过 ${limitMb}M!`,
         type: "warning"
       });
     }, 0);
-  return okType && imgSize;
+  return okType && okSize;
 };
 
 /**

+ 1 - 1
src/views/businessInfo/manageInfo.vue

@@ -757,7 +757,7 @@ const rules: FormRules = {
   ]
 };
 
-const MAX_IMG_MB = 5;
+const MAX_IMG_MB = 20;
 
 function beforeImageUpload(file: File) {
   const name = file.name?.toLowerCase() || "";

+ 4 - 4
src/views/businessInfo/subjectInfo.vue

@@ -62,7 +62,7 @@
               1.
               请上传彩色照片或彩色扫描件或加盖公章鲜章的复印件,要求正面拍摄,露出证件四角且清晰、完整,所有字符清晰可识别,不得反光或遮挡。不得翻拍、截图、镜像、PS。
             </p>
-            <p>2. 图片只支持 JPG、BMP、PNG 格式,文件大小不能超过 5M。</p>
+            <p>2. 图片只支持 JPG、BMP、PNG 格式,文件大小不能超过 20M。</p>
           </div>
         </el-form-item>
         <div v-if="hasBusinessLicenseOcr" class="license-ocr-result">
@@ -135,7 +135,7 @@
                 1. 请上传彩色照片 or 彩色扫描件 or
                 加盖公章鲜章的复印件,要求正面拍摄,露出证件四角且清晰、完整,所有字符清晰可识别,不得反光或遮挡。不得翻拍、截图、镜像、PS。
               </p>
-              <p>2. 图片只支持 JPG、BMP、PNG 格式,文件大小不能超过 5M。</p>
+              <p>2. 图片只支持 JPG、BMP、PNG 格式,文件大小不能超过 20M。</p>
             </div>
           </el-form-item>
 
@@ -172,7 +172,7 @@
                 1. 请上传彩色照片 or 彩色扫描件 or
                 加盖公章鲜章的复印件,要求正面拍摄,露出证件四角且清晰、完整,所有字符清晰可识别,不得反光或遮挡。不得翻拍、截图、镜像、PS。
               </p>
-              <p>2. 图片只支持 JPG、BMP、PNG 格式,文件大小不能超过 5M。</p>
+              <p>2. 图片只支持 JPG、BMP、PNG 格式,文件大小不能超过 20M。</p>
             </div>
 
             <div v-if="showIdPortraitOcrBlock" class="ocr-result-container">
@@ -622,7 +622,7 @@ watch(
   }
 );
 
-const MAX_LICENSE_MB = 5;
+const MAX_LICENSE_MB = 20;
 
 function beforeLicenseUpload(file: File) {
   const name = file.name?.toLowerCase() || "";

+ 5 - 5
src/views/businessInfo/zfbIndex.vue

@@ -1097,8 +1097,8 @@ function onSubjectTypeChange(val: string | number | boolean | undefined) {
   nextTick(() => formRef.value?.clearValidate());
 }
 
-/** 支付宝图片上传接口:原始二进制最大 10MB(与接口文档一致) */
-const MAX_ALIPAY_IMAGE_BYTES = 10 * 1024 * 1024;
+/** 商户端图片上传统一上限 20MB(若支付宝接口仍限 10MB,超大文件可能在服务端被拒) */
+const MAX_ALIPAY_IMAGE_BYTES = 20 * 1024 * 1024;
 
 /** 事业单位材料:仅 jpg / png(与页面说明一致) */
 function beforeImageUploadInstJpgPng(file: File) {
@@ -1110,7 +1110,7 @@ function beforeImageUploadInstJpgPng(file: File) {
     return false;
   }
   if (file.size > MAX_ALIPAY_IMAGE_BYTES) {
-    ElMessage.error("文件大小不能超过 10M");
+    ElMessage.error("文件大小不能超过 20M");
     return false;
   }
   return true;
@@ -1125,7 +1125,7 @@ function beforeImageUpload(file: File) {
     return false;
   }
   if (file.size > MAX_ALIPAY_IMAGE_BYTES) {
-    ElMessage.error("文件大小不能超过 10M");
+    ElMessage.error("文件大小不能超过 20M");
     return false;
   }
   return true;
@@ -1554,7 +1554,7 @@ async function handleSingleUpload(options: UploadRequestOptions, kind: UploadKin
   try {
     if (file.size > MAX_ALIPAY_IMAGE_BYTES) {
       uploadFileItem.status = "fail";
-      ElMessage.error("文件大小不能超过 10M");
+      ElMessage.error("文件大小不能超过 20M");
       return;
     }
     const imageType = resolveAlipayImageType(file);

+ 3 - 3
src/views/dynamicManagement/index.vue

@@ -1421,14 +1421,14 @@ const handleReportRemove = (uploadFile: any, uploadFiles: any[]) => {
 // 举报图片上传前验证
 const beforeReportUpload = (file: File) => {
   const isImage = file.type.startsWith("image/");
-  const isLt5M = file.size / 1024 / 1024 < 5;
+  const isLt20M = file.size / 1024 / 1024 < 20;
 
   if (!isImage) {
     ElMessage.error("只能上传图片文件!");
     return false;
   }
-  if (!isLt5M) {
-    ElMessage.error("图片大小不能超过 5MB!");
+  if (!isLt20M) {
+    ElMessage.error("图片大小不能超过 20MB!");
     return false;
   }
   return true;

+ 3 - 3
src/views/dynamicManagement/myDynamic.vue

@@ -1582,14 +1582,14 @@ const handleReportRemove = (uploadFile: any, uploadFiles: any[]) => {
 // 举报图片上传前验证
 const beforeReportUpload = (file: File) => {
   const isImage = file.type.startsWith("image/");
-  const isLt5M = file.size / 1024 / 1024 < 5;
+  const isLt20M = file.size / 1024 / 1024 < 20;
 
   if (!isImage) {
     ElMessage.error("只能上传图片文件!");
     return false;
   }
-  if (!isLt5M) {
-    ElMessage.error("图片大小不能超过 5MB!");
+  if (!isLt20M) {
+    ElMessage.error("图片大小不能超过 20MB!");
     return false;
   }
   return true;

+ 2 - 2
src/views/dynamicManagement/publishDynamic.vue

@@ -346,7 +346,7 @@ const handleFileChange = (uploadFile: UploadFile, uploadFiles: UploadFile[]) =>
     }
 
     // 根据文件类型设置不同的大小限制
-    const maxSize = isVideo ? 100 : 20;
+    const maxSize = isVideo ? 500 : 20;
     const isLtMaxSize = uploadFile.raw.size / 1024 / 1024 < maxSize;
     if (!isLtMaxSize) {
       const index = uploadFiles.findIndex(f => f.uid === uploadFile.uid);
@@ -480,7 +480,7 @@ const beforeImageUpload = (file: File) => {
   }
 
   // 图片和视频使用不同的大小限制
-  const maxSize = isVideo ? 100 : 20; // 视频100MB,图片20MB
+  const maxSize = isVideo ? 500 : 20; // 视频500MB,图片20MB
   const isLtMaxSize = file.size / 1024 / 1024 < maxSize;
 
   if (!isLtMaxSize) {

+ 3 - 3
src/views/dynamicManagement/userDynamic.vue

@@ -1499,14 +1499,14 @@ const handleReportRemove = (uploadFile: any, uploadFiles: any[]) => {
 // 举报图片上传前验证
 const beforeReportUpload = (file: File) => {
   const isImage = file.type.startsWith("image/");
-  const isLt5M = file.size / 1024 / 1024 < 5;
+  const isLt20M = file.size / 1024 / 1024 < 20;
 
   if (!isImage) {
     ElMessage.error("只能上传图片文件!");
     return false;
   }
-  if (!isLt5M) {
-    ElMessage.error("图片大小不能超过 5MB!");
+  if (!isLt20M) {
+    ElMessage.error("图片大小不能超过 20MB!");
     return false;
   }
   return true;

+ 16 - 0
src/views/groupPackageManagement/newGroup.vue

@@ -1288,6 +1288,22 @@ const handleUploadChange: UploadProps["onChange"] = async (uploadFile, uploadFil
       ElMessage.warning("只支持上传 JPG 和 PNG 格式的图片");
       return;
     }
+    const maxBytes = 20 * 1024 * 1024;
+    if (uploadFile.raw.size > maxBytes) {
+      const index = storeInfoModel.value.imageValueStr.findIndex((f: any) => f.uid === uploadFile.uid);
+      if (index > -1) {
+        storeInfoModel.value.imageValueStr.splice(index, 1);
+      }
+      const uploadIndex = uploadFiles.findIndex((f: any) => f.uid === uploadFile.uid);
+      if (uploadIndex > -1) {
+        uploadFiles.splice(uploadIndex, 1);
+      }
+      if (uploadFile.url && uploadFile.url.startsWith("blob:")) {
+        URL.revokeObjectURL(uploadFile.url);
+      }
+      ElMessage.warning("上传图片不得超过20M");
+      return;
+    }
   }
 
   // 同步文件列表到表单数据(只添加通过验证的文件)

+ 45 - 11
src/views/licenseManagement/businessLicense.vue

@@ -9,7 +9,12 @@
     </div>
     <div class="license-container" v-if="licenseImage">
       <div class="license-display">
-        <el-image :src="licenseImage" fit="contain" class="license-image" :preview-src-list="[licenseImage]">
+        <el-image
+          :src="licenseImage"
+          fit="contain"
+          class="license-image"
+          :preview-src-list="licensePreviewList.length ? licensePreviewList : [licenseImage]"
+        >
           <template #error>
             <div class="empty-image-box">
               <el-icon class="empty-icon">
@@ -82,16 +87,20 @@
               {{ item.createdDateFormat }}
             </div>
             <div class="record-items">
-              <div class="record-item">
+              <div
+                v-for="(url, uIdx) in parseCommaSeparatedImgUrls(item.imgUrl)"
+                :key="`${item.id ?? index}-${uIdx}`"
+                class="record-item"
+              >
                 <div class="record-status-badge" :class="getStatusClass(item.licenseExecuteStatus)">
-                  {{ item.licenseExecuteName }}
+                  {{ getLicenseExecuteRecordText(item) }}
                 </div>
                 <el-image
-                  :src="item.imgUrl"
+                  :src="url"
                   fit="cover"
                   class="record-image"
-                  :preview-src-list="changeRecordList.map(record => record.imgUrl)"
-                  :initial-index="index"
+                  :preview-src-list="parseCommaSeparatedImgUrls(item.imgUrl)"
+                  :initial-index="uIdx"
                 >
                   <template #error>
                     <div class="image-slot">
@@ -138,12 +147,14 @@ const userInfo = localGet("geeker-user")?.userInfo || {};
 const statusMap: Record<number, { name: string; class: string }> = {
   1: { name: "审核通过", class: "status-success" },
   2: { name: "审核中", class: "status-pending" },
-  3: { name: "审核拒绝", class: "status-failed" }
+  3: { name: "审核失败", class: "status-failed" }
 };
 
 const id = localGet("createdId");
 
 const licenseImage = ref<string>("");
+/** 接口 imgUrl 可能为多图逗号拼接 */
+const licensePreviewList = ref<string[]>([]);
 const expirationTime = ref<string>("");
 const replaceDialogVisible = ref(false);
 const changeRecordDialogVisible = ref(false);
@@ -184,13 +195,29 @@ onMounted(async () => {
   await initData();
 });
 
+/** 资质/变更记录:imgUrl 多为英文逗号分隔的多张图地址 */
+function parseCommaSeparatedImgUrls(raw: unknown): string[] {
+  if (raw == null || raw === "") return [];
+  if (Array.isArray(raw)) {
+    return raw.map(u => String(u).trim()).filter(Boolean);
+  }
+  const s = String(raw).trim();
+  if (!s) return [];
+  return s
+    .split(",")
+    .map(part => part.trim())
+    .filter(Boolean);
+}
+
 const initData = async () => {
   const params = {
     id: id
   };
   const res: any = await getBusinessLicense(params);
   if (res.code == 200) {
-    licenseImage.value = res.data[0]?.imgUrl;
+    const urls = parseCommaSeparatedImgUrls(res.data[0]?.imgUrl);
+    licensePreviewList.value = urls;
+    licenseImage.value = urls[0] || "";
     expirationTime.value = res.data[0]?.expirationTime;
   }
 };
@@ -625,16 +652,23 @@ const handleSubmitReplace = async () => {
   }
 };
 
-const getStatusClass = (status: string) => {
-  const statusInfo = statusMap[status];
+const getStatusClass = (status: string | number) => {
+  const statusInfo = statusMap[status as number];
   return statusInfo ? statusInfo.class : "";
 };
 
+/** 变更记录每条图都展示同一执行状态文案(与 licenseExecuteStatus 对齐) */
+const getLicenseExecuteRecordText = (item: { licenseExecuteStatus?: number; licenseExecuteName?: string }) => {
+  const s = item.licenseExecuteStatus;
+  if (s == null) return item.licenseExecuteName ?? "未知";
+  return statusMap[s]?.name ?? item.licenseExecuteName ?? "未知";
+};
+
 const getStatusText = (status: string) => {
   const map: Record<string, string> = {
     pending: "审核中",
     success: "审核通过",
-    failed: "审核拒绝"
+    failed: "审核失败"
   };
   return map[status] || "未知";
 };

+ 94 - 34
src/views/licenseManagement/contractManagement.vue

@@ -10,23 +10,33 @@
     </div>
     <div class="contract-container" v-if="contractList && contractList.length > 0">
       <el-row :gutter="20">
-        <el-col v-for="(item, index) in contractList" :key="index" :xs="12" :sm="8" :md="6" :lg="4" :xl="4">
-          <div class="contract-item">
-            <el-image
-              :src="item.imgUrl"
-              fit=""
-              class="contract-image"
-              :preview-src-list="contractList.map(img => img.imgUrl)"
-              :initial-index="index"
-            >
-              <template #error>
-                <div class="image-slot">
-                  <el-icon><Picture /></el-icon>
-                </div>
-              </template>
-            </el-image>
-          </div>
-        </el-col>
+        <template v-for="(item, index) in contractList" :key="index">
+          <el-col
+            v-for="(url, uIdx) in parseCommaSeparatedImgUrls(item.imgUrl)"
+            :key="`${index}-${uIdx}`"
+            :xs="12"
+            :sm="8"
+            :md="6"
+            :lg="4"
+            :xl="4"
+          >
+            <div class="contract-item">
+              <el-image
+                :src="url"
+                fit=""
+                class="contract-image"
+                :preview-src-list="contractPreviewFlatUrls"
+                :initial-index="contractFlatInitialIndex(index, uIdx)"
+              >
+                <template #error>
+                  <div class="image-slot">
+                    <el-icon><Picture /></el-icon>
+                  </div>
+                </template>
+              </el-image>
+            </div>
+          </el-col>
+        </template>
       </el-row>
     </div>
     <div v-else class="empty-contract">
@@ -95,24 +105,30 @@
               {{ item.createdDateFormat }}
             </div>
             <div class="record-items">
-              <div class="record-item" v-for="(citem, cindex) in item.licenseList" :key="cindex">
-                <el-image
-                  :src="citem.imgUrl"
-                  fit="cover"
-                  class="record-image"
-                  :preview-src-list="item.licenseList.map(v => v.imgUrl)"
-                  :initial-index="cindex"
+              <template v-for="(citem, cindex) in item.licenseList || []" :key="cindex">
+                <div
+                  v-for="(url, uIdx) in parseCommaSeparatedImgUrls(citem.imgUrl)"
+                  :key="`${cindex}-${uIdx}`"
+                  class="record-item"
                 >
-                  <template #error>
-                    <div class="image-slot">
-                      <el-icon><Picture /></el-icon>
-                    </div>
-                  </template>
-                </el-image>
-                <div class="record-status-badge" :class="getStatusClass(item.licenseExecuteStatus)">
-                  {{ item.licenseExecuteName }}
+                  <el-image
+                    :src="url"
+                    fit="cover"
+                    class="record-image"
+                    :preview-src-list="getRecordGroupPreviewUrls(item)"
+                    :initial-index="getRecordGroupFlatIndex(item, cindex, uIdx)"
+                  >
+                    <template #error>
+                      <div class="image-slot">
+                        <el-icon><Picture /></el-icon>
+                      </div>
+                    </template>
+                  </el-image>
+                  <div class="record-status-badge" :class="getStatusClass(item.licenseExecuteStatus)">
+                    {{ getStatusName(item.licenseExecuteStatus) }}
+                  </div>
                 </div>
-              </div>
+              </template>
             </div>
             <div v-if="item.reasonRefusal" class="rejection-reason">拒绝原因: {{ item.reasonRefusal }}</div>
           </div>
@@ -149,7 +165,7 @@ import {
 const statusMap: Record<number, { name: string; class: string }> = {
   1: { name: "审核通过", class: "status-success" },
   2: { name: "审核中", class: "status-pending" },
-  3: { name: "审核拒绝", class: "status-failed" }
+  3: { name: "审核失败", class: "status-failed" }
 };
 
 const contractList = ref<any>([]);
@@ -196,6 +212,50 @@ onMounted(async () => {
   await initData();
 });
 
+/** 合同/变更记录:imgUrl 多为英文逗号分隔的多张图地址 */
+function parseCommaSeparatedImgUrls(raw: unknown): string[] {
+  if (raw == null || raw === "") return [];
+  if (Array.isArray(raw)) {
+    return raw.map(u => String(u).trim()).filter(Boolean);
+  }
+  const s = String(raw).trim();
+  if (!s) return [];
+  return s
+    .split(",")
+    .map(part => part.trim())
+    .filter(Boolean);
+}
+
+const contractPreviewFlatUrls = computed(() => {
+  const list = contractList.value || [];
+  return list.flatMap((row: any) => parseCommaSeparatedImgUrls(row?.imgUrl));
+});
+
+function contractFlatInitialIndex(itemIndex: number, urlIndexInItem: number): number {
+  const list = contractList.value || [];
+  let offset = 0;
+  for (let i = 0; i < itemIndex; i++) {
+    offset += parseCommaSeparatedImgUrls(list[i]?.imgUrl).length;
+  }
+  return offset + urlIndexInItem;
+}
+
+function getRecordGroupPreviewUrls(recordItem: any): string[] {
+  const list = recordItem?.licenseList;
+  if (!Array.isArray(list)) return [];
+  return list.flatMap((v: any) => parseCommaSeparatedImgUrls(v?.imgUrl));
+}
+
+function getRecordGroupFlatIndex(recordItem: any, cindex: number, uIdx: number): number {
+  const list = recordItem?.licenseList;
+  if (!Array.isArray(list)) return 0;
+  let offset = 0;
+  for (let i = 0; i < cindex; i++) {
+    offset += parseCommaSeparatedImgUrls(list[i]?.imgUrl).length;
+  }
+  return offset + uIdx;
+}
+
 const initData = async () => {
   const params = {
     id: id

+ 34 - 7
src/views/licenseManagement/entertainmentLicense.vue

@@ -9,7 +9,12 @@
     </div>
     <div class="license-container" v-if="licenseImage">
       <div class="license-display">
-        <el-image :src="licenseImage" fit="contain" class="license-image" :preview-src-list="[licenseImage]">
+        <el-image
+          :src="licenseImage"
+          fit="contain"
+          class="license-image"
+          :preview-src-list="licensePreviewList.length ? licensePreviewList : [licenseImage]"
+        >
           <template #error>
             <div class="empty-image-box">
               <el-icon class="empty-icon">
@@ -82,16 +87,20 @@
               {{ item.createdTime || item.updatedTime || "--" }}
             </div>
             <div class="record-items">
-              <div class="record-item">
+              <div
+                v-for="(url, uIdx) in parseCommaSeparatedImgUrls(item.imgUrl)"
+                :key="`${item.id ?? index}-${uIdx}`"
+                class="record-item"
+              >
                 <div class="record-status-badge" :class="getChangeRecordStatusClass(item)">
                   {{ getChangeRecordStatusText(item) }}
                 </div>
                 <el-image
-                  :src="item.imgUrl"
+                  :src="url"
                   fit="cover"
                   class="record-image"
-                  :preview-src-list="changeRecordList.map(record => record.imgUrl).filter(Boolean)"
-                  :initial-index="Number(index)"
+                  :preview-src-list="parseCommaSeparatedImgUrls(item.imgUrl)"
+                  :initial-index="uIdx"
                 >
                   <template #error>
                     <div class="image-slot">
@@ -136,13 +145,15 @@ import PcImagePreviewViewer from "@/components/pcMediaPreview/PcImagePreviewView
 const statusMap: Record<number, { name: string; class: string }> = {
   1: { name: "审核通过", class: "status-success" },
   2: { name: "审核中", class: "status-pending" },
-  3: { name: "审核拒绝", class: "status-failed" }
+  3: { name: "审核失败", class: "status-failed" }
 };
 
 const id = localGet("createdId");
 const userInfo = localGet("geeker-user")?.userInfo || {};
 const expirationTime = ref<string>("");
 const licenseImage = ref<string>("");
+/** 接口 imgUrl 可能为多图逗号拼接,预览需完整列表 */
+const licensePreviewList = ref<string[]>([]);
 const replaceDialogVisible = ref(false);
 const changeRecordDialogVisible = ref(false);
 const fileList = ref<UploadFile[]>([]);
@@ -182,13 +193,29 @@ onMounted(async () => {
   await initData();
 });
 
+/** 资质/变更记录:imgUrl 多为英文逗号分隔的多张图地址 */
+function parseCommaSeparatedImgUrls(raw: unknown): string[] {
+  if (raw == null || raw === "") return [];
+  if (Array.isArray(raw)) {
+    return raw.map(u => String(u).trim()).filter(Boolean);
+  }
+  const s = String(raw).trim();
+  if (!s) return [];
+  return s
+    .split(",")
+    .map(part => part.trim())
+    .filter(Boolean);
+}
+
 const initData = async () => {
   const params = {
     id: id
   };
   const res: any = await getEntertainmentBusinessLicense(params);
   if (res.code == 200) {
-    licenseImage.value = res.data[0]?.imgUrl;
+    const urls = parseCommaSeparatedImgUrls(res.data[0]?.imgUrl);
+    licensePreviewList.value = urls;
+    licenseImage.value = urls[0] || "";
     expirationTime.value = res.data[0]?.expirationTime;
   }
 };

+ 45 - 11
src/views/licenseManagement/foodBusinessLicense.vue

@@ -9,7 +9,12 @@
     </div>
     <div class="license-container" v-if="licenseImage">
       <div class="license-display">
-        <el-image :src="licenseImage" fit="contain" class="license-image" :preview-src-list="[licenseImage]">
+        <el-image
+          :src="licenseImage"
+          fit="contain"
+          class="license-image"
+          :preview-src-list="licensePreviewList.length ? licensePreviewList : [licenseImage]"
+        >
           <template #error>
             <div class="empty-image-box">
               <el-icon class="empty-icon">
@@ -82,16 +87,20 @@
               {{ item.createdDateFormat }}
             </div>
             <div class="record-items">
-              <div class="record-item">
+              <div
+                v-for="(url, uIdx) in parseCommaSeparatedImgUrls(item.imgUrl)"
+                :key="`${item.id ?? index}-${uIdx}`"
+                class="record-item"
+              >
                 <div class="record-status-badge" :class="getStatusClass(item.licenseExecuteStatus)">
-                  {{ item.licenseExecuteName }}
+                  {{ getLicenseExecuteRecordText(item) }}
                 </div>
                 <el-image
-                  :src="item.imgUrl"
+                  :src="url"
                   fit="cover"
                   class="record-image"
-                  :preview-src-list="changeRecordList.map(record => record.imgUrl)"
-                  :initial-index="index"
+                  :preview-src-list="parseCommaSeparatedImgUrls(item.imgUrl)"
+                  :initial-index="uIdx"
                 >
                   <template #error>
                     <div class="image-slot">
@@ -138,12 +147,14 @@ const userInfo = localGet("geeker-user")?.userInfo || {};
 const statusMap: Record<number, { name: string; class: string }> = {
   1: { name: "审核通过", class: "status-success" },
   2: { name: "审核中", class: "status-pending" },
-  3: { name: "审核拒绝", class: "status-failed" }
+  3: { name: "审核失败", class: "status-failed" }
 };
 
 const id = localGet("createdId");
 
 const licenseImage = ref<string>("");
+/** 接口 imgUrl 可能为多图逗号拼接 */
+const licensePreviewList = ref<string[]>([]);
 const expirationTime = ref<string>("");
 const replaceDialogVisible = ref(false);
 const changeRecordDialogVisible = ref(false);
@@ -184,13 +195,29 @@ onMounted(async () => {
   await initData();
 });
 
+/** 资质/变更记录:imgUrl 多为英文逗号分隔的多张图地址 */
+function parseCommaSeparatedImgUrls(raw: unknown): string[] {
+  if (raw == null || raw === "") return [];
+  if (Array.isArray(raw)) {
+    return raw.map(u => String(u).trim()).filter(Boolean);
+  }
+  const s = String(raw).trim();
+  if (!s) return [];
+  return s
+    .split(",")
+    .map(part => part.trim())
+    .filter(Boolean);
+}
+
 const initData = async () => {
   const params = {
     id: id
   };
   const res: any = await getFoodBusinessLicense(params);
   if (res.code == 200) {
-    licenseImage.value = res.data[0]?.imgUrl;
+    const urls = parseCommaSeparatedImgUrls(res.data[0]?.imgUrl);
+    licensePreviewList.value = urls;
+    licenseImage.value = urls[0] || "";
     expirationTime.value = res.data[0]?.expirationTime;
   }
 };
@@ -628,16 +655,23 @@ const handleSubmitReplace = async () => {
   }
 };
 
-const getStatusClass = (status: string) => {
-  const statusInfo = statusMap[status];
+const getStatusClass = (status: string | number) => {
+  const statusInfo = statusMap[status as number];
   return statusInfo ? statusInfo.class : "";
 };
 
+/** 变更记录每条图都展示同一执行状态文案(与 licenseExecuteStatus 对齐,不用接口里的「审核拒绝」等旧文案) */
+const getLicenseExecuteRecordText = (item: { licenseExecuteStatus?: number; licenseExecuteName?: string }) => {
+  const s = item.licenseExecuteStatus;
+  if (s == null) return item.licenseExecuteName ?? "未知";
+  return statusMap[s]?.name ?? item.licenseExecuteName ?? "未知";
+};
+
 const getStatusText = (status: string) => {
   const map: Record<string, string> = {
     pending: "审核中",
     success: "审核通过",
-    failed: "审核拒绝"
+    failed: "审核失败"
   };
   return map[status] || "未知";
 };

+ 6 - 6
src/views/operationManagement/newActivity.vue

@@ -899,8 +899,8 @@ const handleTitleUploadChange: UploadProps["onChange"] = async (uploadFile, uplo
       return;
     }
 
-    // 检查文件大小,不得超过5M
-    const maxSize = 5 * 1024 * 1024; // 5MB
+    // 检查文件大小,不得超过20M
+    const maxSize = 20 * 1024 * 1024; // 20MB
     if (uploadFile.raw.size > maxSize) {
       // 从文件列表中移除超过大小的文件
       const index = titleFileList.value.findIndex((f: any) => f.uid === uploadFile.uid);
@@ -916,7 +916,7 @@ const handleTitleUploadChange: UploadProps["onChange"] = async (uploadFile, uplo
       if (uploadFile.url && uploadFile.url.startsWith("blob:")) {
         URL.revokeObjectURL(uploadFile.url);
       }
-      ElMessage.warning("上传图片不得超过5M");
+      ElMessage.warning("上传图片不得超过20M");
       return;
     }
   }
@@ -968,8 +968,8 @@ const handleDetailUploadChange: UploadProps["onChange"] = async (uploadFile, upl
       return;
     }
 
-    // 检查文件大小,不得超过5M
-    const maxSize = 5 * 1024 * 1024; // 5MB
+    // 检查文件大小,不得超过20M
+    const maxSize = 20 * 1024 * 1024; // 20MB
     if (uploadFile.raw.size > maxSize) {
       // 从文件列表中移除超过大小的文件
       const index = detailFileList.value.findIndex((f: any) => f.uid === uploadFile.uid);
@@ -985,7 +985,7 @@ const handleDetailUploadChange: UploadProps["onChange"] = async (uploadFile, upl
       if (uploadFile.url && uploadFile.url.startsWith("blob:")) {
         URL.revokeObjectURL(uploadFile.url);
       }
-      ElMessage.warning("上传图片不得超过5M");
+      ElMessage.warning("上传图片不得超过20M");
       return;
     }
   }

+ 2 - 2
src/views/performance/components/PerformanceFormDialog.vue

@@ -19,7 +19,7 @@
                 v-model:file-list="posterFileList"
                 :api="uploadImgStore"
                 :limit="1"
-                :file-size="5"
+                :file-size="20"
                 class="poster-upload"
                 @update:file-list="onPosterChange"
               />
@@ -180,7 +180,7 @@
                 v-model:file-list="detailImageFileList"
                 :api="uploadImgStore"
                 :limit="9"
-                :file-size="5"
+                :file-size="20"
                 class="detail-upload"
                 @update:file-list="onDetailImagesChange"
               />

+ 2 - 2
src/views/performance/edit.vue

@@ -46,7 +46,7 @@
                   v-model:file-list="posterFileList"
                   :api="uploadImgStore"
                   :limit="1"
-                  :file-size="5"
+                  :file-size="20"
                   :disabled="viewMode"
                   class="poster-upload"
                   @update:file-list="onPosterChange"
@@ -196,7 +196,7 @@
                   v-model:file-list="detailImageFileList"
                   :api="uploadImgStore"
                   :limit="9"
-                  :file-size="5"
+                  :file-size="20"
                   :disabled="viewMode"
                   class="detail-upload"
                   @update:file-list="onDetailImagesChange"

+ 2 - 2
src/views/priceList/edit.vue

@@ -45,7 +45,7 @@
                 v-model:file-list="imageFileList"
                 :api="handleCustomImageUpload"
                 :limit="9"
-                :file-size="5"
+                :file-size="20"
                 :width="'100px'"
                 :height="'100px'"
                 :disabled="viewMode"
@@ -242,7 +242,7 @@
                 v-model:file-list="detailImageFileList"
                 :api="handleCustomImageUpload"
                 :limit="9"
-                :file-size="5"
+                :file-size="20"
                 :width="'100px'"
                 :height="'100px'"
                 :disabled="viewMode"

+ 3 - 3
src/views/storeDecoration/add.vue

@@ -797,14 +797,14 @@ const handleCityClear = () => {
 // 图片上传前验证
 const beforeUpload: UploadProps["beforeUpload"] = (rawFile: File) => {
   const isImage = rawFile.type.startsWith("image/");
-  const isLt10M = rawFile.size / 1024 / 1024 < 10;
+  const isLt20M = rawFile.size / 1024 / 1024 < 20;
 
   if (!isImage) {
     ElMessage.error("只能上传图片文件!");
     return false;
   }
-  if (!isLt10M) {
-    ElMessage.error("图片大小不能超过 10MB!");
+  if (!isLt20M) {
+    ElMessage.error("图片大小不能超过 20MB!");
     return false;
   }
   return true;

+ 13 - 0
src/views/storeDecoration/decorationChat.vue

@@ -150,6 +150,9 @@ const videoInputRef = ref<HTMLInputElement | null>(null);
 let cleanMessageFn: (() => void) | null = null;
 let statusTimer: ReturnType<typeof setInterval> | null = null;
 
+const MAX_CHAT_IMAGE_MB = 20;
+const MAX_CHAT_VIDEO_MB = 200;
+
 const userInfo = computed(() => localGet("geeker-user")?.userInfo || {});
 
 /** 与 myDynamic.vue「cachedHeadImg」一致:geeker-user 的 headImg → avatar;无有效地址则空串走图标占位 */
@@ -236,6 +239,11 @@ const handleImageSelect = async (e: Event) => {
   const file = target.files?.[0];
   target.value = "";
   if (!file || !receiverId.value) return;
+  const sizeMb = file.size / (1024 * 1024);
+  if (sizeMb > MAX_CHAT_IMAGE_MB) {
+    ElMessage.warning(`图片大小不能超过 ${MAX_CHAT_IMAGE_MB}MB`);
+    return;
+  }
   if (!isWsReady()) {
     ElMessage.warning("连接已断开,请稍后重试");
     return;
@@ -286,6 +294,11 @@ const handleVideoSelect = async (e: Event) => {
   const file = target.files?.[0];
   target.value = "";
   if (!file || !receiverId.value) return;
+  const sizeMb = file.size / (1024 * 1024);
+  if (sizeMb > MAX_CHAT_VIDEO_MB) {
+    ElMessage.warning(`视频大小不能超过 ${MAX_CHAT_VIDEO_MB}MB`);
+    return;
+  }
   if (!isWsReady()) {
     ElMessage.warning("连接已断开,请稍后重试");
     return;

+ 3 - 3
src/views/storeDecoration/facilitiesAndServices/components/FacilityManagement.vue

@@ -496,13 +496,13 @@ const openImageUpload = () => {
 
 const beforePanoramaUpload = (file: File) => {
   const isImage = file.type.startsWith("image/");
-  const isLt10M = file.size / 1024 / 1024 < 10;
+  const isLt20M = file.size / 1024 / 1024 < 20;
   if (!isImage) {
     ElMessage.error("只能上传图片文件");
     return false;
   }
-  if (!isLt10M) {
-    ElMessage.error("图片大小不能超过10MB");
+  if (!isLt20M) {
+    ElMessage.error("图片大小不能超过20MB");
     return false;
   }
   return true;

+ 1 - 1
src/views/storeDecoration/facilitiesAndServices/components/ServiceManagement.vue

@@ -118,7 +118,7 @@
             v-model:image-url="formData.imageUrl"
             :width="'150px'"
             :height="'150px'"
-            :file-size="9999"
+            :file-size="20"
             :api="formData => uploadFormDataToOss(formData, 'image')"
             :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
             :border-radius="'8px'"

+ 1 - 1
src/views/storeDecoration/menuManagement/index.vue

@@ -100,7 +100,7 @@
             v-model:image-url="formData.imgUrl"
             :width="'200px'"
             :height="'200px'"
-            :file-size="9999"
+            :file-size="20"
             :api="uploadImg"
             :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
             :border-radius="'8px'"

+ 2 - 2
src/views/storeDecoration/officialPhotoAlbum/index.vue

@@ -25,7 +25,7 @@
     <div class="content-section">
       <div v-if="isAlbumTab && currentAlbumRef" class="upload-tips">
         <span>照片宽高不小于500像素</span>
-        <span>大小不超过10M</span>
+        <span>大小不超过20M</span>
         <span>可拖拽排序</span>
         <span>暂不支持上传动图</span>
         <span>不可上传手机截屏、含清晰人脸/电话/二维码/第三方水印/LOGO等图片。</span>
@@ -37,7 +37,7 @@
           <UploadImgs
             v-model:file-list="currentAlbumRef.images"
             :limit="50"
-            :file-size="10"
+            :file-size="20"
             :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
             :width="'100%'"
             :height="'200px'"

+ 6 - 3
src/views/storeDecoration/personnelConfig/index.vue

@@ -256,7 +256,7 @@
             v-model:image-url="formData.avatar"
             :width="'150px'"
             :height="'150px'"
-            :file-size="9999"
+            :file-size="20"
             :api="personnelOssUploadAvatar"
             :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
             :border-radius="'8px'"
@@ -277,7 +277,8 @@
               ref="backgroundImagesUploadRef"
               v-model:file-list="formData.backgroundImages"
               :limit="9"
-              :file-size="100"
+              :file-size="20"
+              :video-file-size="200"
               :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4']"
               :width="'150px'"
               :height="'150px'"
@@ -289,7 +290,9 @@
               :on-video-preview="handleVideoPreviewInForm"
             >
               <template #tip>
-                <div class="upload-tip">上传图片或视频 ({{ formData.backgroundImages.length }}/9),单个文件不超过 100M</div>
+                <div class="upload-tip">
+                  上传图片或视频 ({{ formData.backgroundImages.length }}/9),图片单文件不超过 20M,视频单文件不超过 200M
+                </div>
               </template>
             </UploadImgs>
           </div>

+ 16 - 10
src/views/storeDecoration/storeCoverMap/index.vue

@@ -7,12 +7,11 @@
           <div class="preview-card preview-card--blue">
             <div class="preview-media">
               <img :src="previewDemoImg" alt="效果预览示例" class="preview-img preview-img--single" />
-              <div class="preview-album-pill" aria-hidden="true">
+              <img src="@/assets/images/headImg.png" alt="效果预览示例" class="preview-img-head" />
+              <!--<div class="preview-album-pill" aria-hidden="true">
                 <span>相册</span>
-                <el-icon>
-                  <ArrowRight />
-                </el-icon>
-              </div>
+                <el-icon><ArrowRight /></el-icon>
+              </div> -->
             </div>
             <div class="preview-meta">
               <div>
@@ -55,7 +54,7 @@
                 <Plus />
               </el-icon>
               <div class="upload-slot__primary">上传图片/视频 {{ slotCountTip }}</div>
-              <div class="upload-slot__hint">建议尺寸 1242*640px,大小不超过 {{ maxMbDisplay }}MB</div>
+              <div class="upload-slot__hint">建议尺寸 1242*640px;图片不超过 20MB,视频不超过 500MB</div>
             </template>
             <template v-else-if="draftCover.isVideo">
               <div class="upload-slot__preview upload-slot__preview--video">
@@ -141,10 +140,8 @@ import PcVideoPreviewDialog from "@/components/pcMediaPreview/PcVideoPreviewDial
 /** 与商户端封面 imgType 一致 */
 const IMG_TYPE_COVER = 38;
 
-const IMAGE_MAX_MB = 10;
-const VIDEO_MAX_MB = 80;
-/** 与设计稿文案「不超 10M」一致展示 */
-const maxMbDisplay = IMAGE_MAX_MB;
+const IMAGE_MAX_MB = 20;
+const VIDEO_MAX_MB = 500;
 
 /** 对齐官方相册 OSS 快照规则(节选) */
 function tryOssVideoSnapshotCoverUrl(videoUrl: string): string {
@@ -503,10 +500,19 @@ onMounted(async () => {
   }
 }
 .preview-img {
+  position: relative;
   width: 100%;
   height: 100%;
   object-fit: cover;
 }
+.preview-img-head {
+  position: absolute;
+  right: 5px;
+  bottom: 5px;
+  width: 86px;
+  height: 24px;
+  object-fit: cover;
+}
 .preview-img--single {
   display: block;
   width: 100%;

+ 1 - 1
src/views/storeDecoration/storeEntranceMap/index.vue

@@ -10,7 +10,7 @@
           <ol class="instruction-list">
             <li>入口图建议上传能够代表店铺的图片,如环境范围、菜品样式、本店特色等。</li>
             <li>建议上传1:1尺寸的图片,避免图片出现显示不全问题。</li>
-            <li>建议尺寸900*900像素以内,不超过10MB。</li>
+            <li>建议尺寸900*900像素以内,不超过20MB。</li>
           </ol>
         </div>
 

+ 1 - 1
src/views/storeDecoration/wineMenuManagement/index.vue

@@ -142,7 +142,7 @@
               v-model:image-url="formData.imgUrl"
               :width="'200px'"
               :height="'200px'"
-              :file-size="9999"
+              :file-size="20"
               :api="uploadImg"
               :file-type="['image/jpeg', 'image/png', 'image/gif', 'image/webp']"
               :border-radius="'8px'"