ソースを参照

fix(components): 修复图片上传组件的无效ID删除问题

- 在ImageUpload组件中添加ossId有效性检查,避免对无效ID执行删除操作
- 修复VIP站点管理页面中站点域名列的显示逻辑
- 调整闪购活动页面中封面图片数据处理,确保存储正确格式
- 优化专题页面产品网格布局的CSS样式
- 为分类商品页面添加商品导入功能和相关搜索条件
肖路 1 日 前
コミット
e1259cf2c3

+ 4 - 1
src/components/ImageUpload/index.vue

@@ -194,7 +194,10 @@ const handleDelete = (file: UploadFile): boolean => {
   const findex = fileList.value.map((f) => f.name).indexOf(file.name);
   if (findex > -1 && uploadList.value.length === number.value) {
     const ossId = fileList.value[findex].ossId;
-    delOss(ossId);
+    // 只有在 ossId 存在且有效时才调用删除接口
+    if (ossId && ossId !== 'undefined' && ossId !== 'null') {
+      // delOss(ossId);
+    }
     fileList.value.splice(findex, 1);
     emit('update:modelValue', listToString(fileList.value));
     return false;

+ 2 - 7
src/views/platform/customerOperation/vipSite/index.vue

@@ -89,11 +89,6 @@
         <el-table-column label="客户编号" align="center" prop="clientNo" width="100" />
         <el-table-column label="客户名称" align="center" prop="clientName" min-width="100" />
         <el-table-column label="站点名称" align="center" prop="siteName" min-width="180" />
-        <el-table-column label="站点域名" align="center" prop="siteDomain" min-width="220">
-          <template #default="scope">
-            <el-link type="primary" :href="scope.row.siteDomain" target="_blank">{{ scope.row.siteDomain }}</el-link>
-          </template>
-        </el-table-column>
         <el-table-column label="状态" align="center" width="80">
           <template #default="scope">
             <el-tag :type="scope.row.status === '0' ? 'success' : 'info'" size="small">
@@ -115,8 +110,8 @@
           <template #default="scope">
             <div class="flex flex-col items-center gap-1">
               <el-link type="primary" :underline="false" @click="handleSiteConfig(scope.row)">站点配置</el-link>
-              <el-link type="primary" :underline="false" @click="handleProductConfig(scope.row)">商品配置</el-link>
-              <el-link type="primary" :underline="false" @click="handleStyleDesign(scope.row)">样式设计</el-link>
+              <!-- <el-link type="primary" :underline="false" @click="handleProductConfig(scope.row)">商品配置</el-link>
+              <el-link type="primary" :underline="false" @click="handleStyleDesign(scope.row)">样式设计</el-link> -->
               <el-link type="primary" :underline="false" @click="handleDiy(scope.row)">diy设计</el-link>
               <el-link :type="scope.row.status === '0' ? 'danger' : 'success'" :underline="false" @click="handleStatusChange(scope.row)">
                 {{ scope.row.status === '0' ? '停 用' : '启 用' }}

+ 8 - 1
src/views/platform/decoration/flashSale/index.vue

@@ -238,10 +238,17 @@ const handleEditBanner = async () => {
       const res = await getFloorAdvertManage(bannerConfig.value.id);
       if (res.data) {
         const data = res.data;
+        // 确保 coverImage 存储的是 ossId 而不是 URL
+        // 如果 data.coverImage 是数字(ossId),直接使用;如果是URL,需要使用 bannerConfig.value.imageOssId
+        let coverImageValue = data.coverImage || '';
+        // 如果 coverImage 是 URL,说明后端返回的不是 ossId,需要用缓存的 imageOssId
+        if (coverImageValue && typeof coverImageValue === 'string' && coverImageValue.startsWith('http')) {
+          coverImageValue = bannerConfig.value.imageOssId || '';
+        }
         Object.assign(bannerForm, { 
           id: data.id, 
           advertTitle: data.advertTitle || '',
-          coverImage: data.coverImage || '', 
+          coverImage: coverImageValue, 
           advertUrl: data.advertUrl || '',
           advertBrief: data.advertBrief || '',
           showText: data.isShow === '1',

+ 18 - 1
src/views/platform/decoration/special/index.vue

@@ -459,7 +459,7 @@ onMounted(() => {
 
 .product-grid {
   display: grid;
-  grid-template-columns: repeat(5, 1fr);
+  grid-template-columns: repeat(5, minmax(0, 1fr));
   gap: 12px;
   margin-bottom: 12px;
 }
@@ -469,6 +469,9 @@ onMounted(() => {
   background: #fff;
   padding: 15px;
   cursor: pointer;
+  min-width: 0;
+  overflow: hidden;
+  box-sizing: border-box;
 
   &:hover { background: #fafafa; }
 
@@ -491,10 +494,24 @@ onMounted(() => {
     width: 100%;
     height: 200px;
     margin-bottom: 10px;
+    overflow: hidden;
 
     .image {
       width: 100%;
       height: 100%;
+      display: block;
+    }
+
+    :deep(.el-image) {
+      width: 100%;
+      height: 100%;
+      display: block;
+    }
+
+    :deep(.el-image__inner) {
+      width: 100%;
+      height: 100%;
+      object-fit: cover;
     }
 
     .image-slot {

+ 100 - 5
src/views/platform/industrial/categoryProduct/index.vue

@@ -53,7 +53,7 @@
     <el-dialog v-model="productDialog.visible" title="推荐商品" width="1000px" append-to-body>
       <div class="product-dialog-header">
         <el-button type="default" @click="handleAddProduct">新增商品</el-button>
-        <el-button type="default">导入商品</el-button>
+        <el-button type="default" @click="handleImportProduct">导入商品</el-button>
       </div>
       <el-table v-loading="productLoading" :data="productList" border max-height="450px">
         <el-table-column label="轮播商品编号" align="center" prop="productNo" width="140" />
@@ -84,7 +84,8 @@
     <!-- 新增商品选择对话框 -->
     <el-dialog v-model="selectProductDialog.visible" title="商品信息" width="800px" append-to-body>
       <div class="select-product-search">
-        <el-input v-model="selectProductQuery.keyword" placeholder="请输入商品编号名称输入搜索" style="width: 350px" />
+        <el-input v-model="selectProductQuery.productNo" placeholder="请输入商品编号" clearable style="width: 220px" />
+        <el-input v-model="selectProductQuery.itemName" placeholder="请输入商品名称" clearable style="width: 220px" />
         <el-button type="primary" @click="handleSearchProduct">搜 索</el-button>
       </div>
       <el-table
@@ -124,6 +125,24 @@
       </template>
     </el-dialog>
 
+    <!-- 导入商品对话框 -->
+    <el-dialog v-model="importProductDialog.visible" title="导入商品" width="500px" append-to-body>
+      <el-form label-width="90px">
+        <el-form-item label="商品编号">
+          <el-input
+            v-model="importProductDialog.productNos"
+            type="textarea"
+            :rows="5"
+            placeholder="请输入商品编号,多个用逗号隔开"
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="confirmImportProducts">确 定</el-button>
+        <el-button @click="importProductDialog.visible = false">取 消</el-button>
+      </template>
+    </el-dialog>
+
     <!-- 添加/编辑对话框 -->
     <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
@@ -319,14 +338,22 @@ const selectProductLoading = ref(false);
 const selectProductTotal = ref(0);
 const selectedProducts = ref<any[]>([]);
 const selectProductQuery = reactive({
-  keyword: '',
+  productNo: '',
+  itemName: '',
   pageNum: 1,
   pageSize: 10
 });
 
+// 导入商品对话框
+const importProductDialog = reactive({
+  visible: false,
+  productNos: ''
+});
+
 // 打开新增商品对话框
 const handleAddProduct = () => {
-  selectProductQuery.keyword = '';
+  selectProductQuery.productNo = '';
+  selectProductQuery.itemName = '';
   selectProductQuery.pageNum = 1;
   selectedProducts.value = [];
   selectProductDialog.visible = true;
@@ -339,7 +366,8 @@ const getSelectProductList = async () => {
   try {
     const res = await request.get('/product/base/list', {
       params: {
-        itemName: selectProductQuery.keyword,
+        productNo: selectProductQuery.productNo,
+        itemName: selectProductQuery.itemName,
         pageNum: selectProductQuery.pageNum,
         pageSize: selectProductQuery.pageSize
       }
@@ -398,6 +426,73 @@ const handleConfirmSelect = async () => {
   }
 };
 
+// 打开导入商品对话框
+const handleImportProduct = () => {
+  importProductDialog.productNos = '';
+  importProductDialog.visible = true;
+};
+
+// 确认导入商品
+const confirmImportProducts = async () => {
+  const input = importProductDialog.productNos.trim();
+  if (!input) {
+    ElMessage.warning('请输入商品编号');
+    return;
+  }
+  const productNos = input
+    .split(/[,,\s\n]+/)
+    .map((s: string) => s.trim())
+    .filter(Boolean);
+  if (productNos.length === 0) {
+    ElMessage.warning('请输入有效的商品编号');
+    return;
+  }
+  const existingProductNos = productList.value.map((item: any) => item.productNo);
+  const newProductNos = productNos.filter((no: string) => !existingProductNos.includes(no));
+  if (newProductNos.length === 0) {
+    ElMessage.warning('所有商品编号已存在,请勿重复添加');
+    return;
+  }
+  try {
+    const productRes: any = await request.get('/product/base/list', {
+      params: { productNos: newProductNos.join(','), pageSize: 1000 }
+    });
+    const productMap = new Map<string, any>((productRes.rows || []).map((p: any) => [p.productNo, p]));
+    let successCount = 0;
+    const notFound: string[] = [];
+    for (const productNo of newProductNos) {
+      const product = productMap.get(productNo);
+      if (!product) {
+        notFound.push(productNo);
+        continue;
+      }
+      await request.post('/product/categoryRecommendedLink', {
+        categoryId: productDialog.categoryId,
+        productId: product.id,
+        productNo: product.productNo,
+        sort: 0
+      });
+      successCount++;
+    }
+    const skipped = productNos.length - newProductNos.length;
+    const tips: string[] = [];
+    if (successCount > 0) tips.push(`成功导入${successCount}个`);
+    if (skipped > 0) tips.push(`${skipped}个已存在被跳过`);
+    if (notFound.length > 0) tips.push(`${notFound.length}个商品编号不存在`);
+    if (successCount > 0) {
+      ElMessage.success(tips.join(','));
+    } else {
+      ElMessage.warning(tips.join(',') || '未导入任何商品');
+    }
+    importProductDialog.visible = false;
+    if (productDialog.categoryId) {
+      getProductList(productDialog.categoryId);
+    }
+  } catch (error) {
+    ElMessage.error('导入失败');
+  }
+};
+
 // 提交表单
 const submitForm = () => {
   formRef.value?.validate(async (valid: boolean) => {