Jelajahi Sumber

feat(product-manage): 优化商品列表展示效果

- 重构 productColumns,增加更多列和自定义渲染
- 添加 ProductInfo 组件用于展示商品信息
- 优化 DataTable 组件,移除冗余模板代码
- 调整 EditDrawer 组件布局
- 更新 useTagColor 逻辑,为 ZOSI 设置默认红色标签
WanGxC 7 bulan lalu
induk
melakukan
6066d4860d

+ 1 - 1
src/utils/useTagColor.ts

@@ -2,7 +2,7 @@
   const tagTypes = ['primary', 'success', 'warning', 'danger', 'info'];
   
   export const getTagType = computed(() => (colName: any) => {
-    if (colName === 'ZOSI') return 'danger';
+    if (colName === 'ZOSI') return 'danger';  // 默认zosi为红色
     if (!colName) return 'default';
 
     if (!set.value.has(colName)) {

+ 118 - 15
src/views/product-manage/product-list/ColumnsTsx.tsx

@@ -1,21 +1,27 @@
 import { useCountryInfoStore } from '/@/stores/countryInfo';
+import { getTagType } from '/@/utils/useTagColor';
 
 
 const countryInfoStore = useCountryInfoStore();
 
-export const productColumns: any = [
+export const productColumns = (handleMonitor: Function) => [
   { type: 'checkbox', width: 50, align: 'center', fixed: 'left' },
   { type: 'seq', title: 'No.', width: 60, align: 'center' },
   {
     field: 'is_monitor', title: '监控管理', width: 90, align: 'center',
-    slots: { default: 'is_monitor' }
+    slots: {
+      default({ row }: any) {
+        return <el-switch v-model={ row.is_monitor }
+                          onChange={ (newVal: any) => handleMonitor({ id: row.id, isMonitor: newVal }) } />;
+      }
+    }
   },
   {
     field: 'product_info', title: '商品信息', minWidth: 'auto', align: 'center',
     slots: { default: 'product_info' }
   },
   {
-    field: 'sku', title: 'SKU',  minWidth: 'auto', align: 'center',
+    field: 'sku', title: 'SKU', minWidth: 'auto', align: 'center',
     slots: {
       default({ row }: any) {
         return <span class={ 'font-medium' }>{ row.sku ? row.sku : '--' }</span>;
@@ -23,18 +29,21 @@ export const productColumns: any = [
     }
   },
   {
-    field: 'country_code', title: '国 家',  minWidth: 'auto', align: 'center',
+    field: 'country_code', title: '国 家', minWidth: 'auto', align: 'center',
     slots: {
       default({ row }: any) {
         const country = countryInfoStore.countries.find(c => c.code === row.country_code);
         const color = country ? country.color : '#3875F6';
-        return <el-tag effect="plain" round
-                       style={ { color: color, borderColor: color } }>{ country ? country.name : '--' }</el-tag>;
+        return (
+            <el-tag effect="plain" round
+                    style={ { color: color, borderColor: color } }>{ country ? country.name : '--' }
+            </el-tag>
+        );
       }
     }
   },
   {
-    field: 'platform_number', title: '平台编号',  minWidth: 'auto', align: 'center',
+    field: 'platform_number', title: '平台编号', minWidth: 'auto', align: 'center',
     slots: {
       default({ row }: any) {
         return <span class={ 'font-medium' }>{ row.platform_number ? row.platform_number : '--' }</span>;
@@ -43,29 +52,123 @@ export const productColumns: any = [
   },
   {
     field: 'shop_name', title: '店 铺', minWidth: 'auto', align: 'center',
-    slots: { default: 'shop_name' }
+    slots: {
+      default({ row }: any) {
+        return (
+            <el-tag type={ getTagType.value(row.shop_name) }>
+              { row.shop_name ? row.shop_name : '--' }
+            </el-tag>
+        );
+      }
+    }
   },
   {
     field: 'tag', title: '分 组',  minWidth: 'auto', align: 'center',
-    slots: { default: 'tag' }
+    slots: {
+      default({ row }: any) {
+        return (
+            <el-tag type={ getTagType.value(row.tag) }>
+              { row.tag ? row.tag : '--' }
+            </el-tag>
+        );
+      }
+    }
+  },
+  {
+    field: 'brand', title: '品 牌', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return (
+            <el-tag type={ getTagType.value(row.brand) } effect="plain" round>
+              { row.brand ? row.brand : '--' }
+            </el-tag>
+        );
+      }
+    }
+  },
+  {
+    field: 'price_info', title: '价 格', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return (
+            <div v-if={ row.price > 0 } class={ `font-medium text-left` }>
+              <p>现 价:{ row.currency_code + '‎' + row.price }</p>
+              <p>折 扣:{ row.discount > 0 ? row.discount + '%' : '-' }</p>
+              <p>优惠劵:{ !row || row.coupon <= 0 ? '-' : row.currency_code + '‎' + row.coupon }</p>
+            </div>
+        );
+      }
+    }
+  },
+  {
+    field: 'show_price', title: '展示价格', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <div class={ 'font-medium' }>{ row.show_price ? row.currency_code + row.show_price : '--' }</div>;
+      }
+    }
   },
   {
-    field: 'brand', title: '品 牌',  minWidth: 'auto', align: 'center',
-    slots: { default: 'brand' }
+    field: 'activity_price', title: '平时活动售价', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <div
+            class={ 'font-medium' }>{ row.activity_price ? row.currency_code + row.activity_price : '--' }</div>;
+      }
+    }
+  },
+  {
+    field: 'minimum_price', title: '平时活动售价', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <div class={ 'font-medium' }>{ row.minimum_price ? row.currency_code + row.minimum_price : '--' }</div>;
+      }
+    }
+  },
+  {
+    field: 'launch_date', title: '上架日期', minWidth: 'auto', align: 'center', sortable: true,
+    slots: {
+      default({ row }: any) {
+        return <div class={ 'font-medium' }>{ row.launch_date ? row.launch_date : '--' }</div>;
+      }
+    }
+  },
+  {
+    field: 'category', title: '类 目', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <div class={ 'font-medium' }>{ row.category ? row.category : '--' }</div>;
+      }
+    }
+  },
+  {
+    field: 'status', title: '状 态', minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        const statusText = row.status === 1 ? '在售' : '停售';
+        const statusType = row.status === 1 ? 'success' : 'info';
+
+        return (
+            <el-tag type={ statusType }>
+              { statusText }
+            </el-tag>
+        );
+      }
+    }
   },
   {
-    field: 'price_info', title: '价 格',  minWidth: 'auto', align: 'center',
+    field: 'update_datetime', title: '更新时间', minWidth: 'auto', align: 'center',
     slots: {
       default({ row }: any) {
-        return <span class={ 'font-medium' }>{ row.price_info ? row.price_info : '--' }</span>;
+        return <div class={ 'font-medium' }>{ row.update_datetime ? row.update_datetime : '--' }</div>;
       }
     }
   },
   {
-    field: 'show_price', title: '展示价格',  minWidth: 'auto', align: 'center',
+    field: 'create_datetime', title: '创建时间', minWidth: 'auto', align: 'center',
     slots: {
       default({ row }: any) {
-        return <span class={ 'font-medium' }>{ row.show_price ? row.show_price : '--' }</span>;
+        return <div class={ 'font-medium' }>{ row.create_datetime ? row.create_datetime : '--' }</div>;
       }
     }
   },

+ 11 - 23
src/views/product-manage/product-list/component/DataTable.vue

@@ -15,6 +15,7 @@ import VerticalDivider from '/src/components/VerticalDivider/index.vue';
 import { productColumns } from '../ColumnsTsx';
 import { downloadFile } from '/@/utils/service';
 import { getTagType } from '/@/utils/useTagColor';
+import ProductInfo from '/@/views/product-manage/product-list/component/ProductInfo.vue';
 
 
 interface Parameter {
@@ -62,7 +63,7 @@ const gridOptions: any = reactive({
     icon: 'vxe-icon-indicator roll',
     text: '正在拼命加载中...'
   },
-  columns: productColumns,
+  columns: productColumns(handleMonitor),
   data: ''
 });
 
@@ -153,6 +154,10 @@ function downloadTemplate() {
   }
 }
 
+function handleMonitor(val: any) {
+  console.log('(DataTable.vue: 157)=> val', val);
+}
+
 defineExpose({ fetchList });
 </script>
 
@@ -168,11 +173,12 @@ defineExpose({ fetchList });
           批量开启
         </PermissionButton>
         <div class="custom-el-input">
-          <el-select v-model="templateType" placeholder="Select" style="width: 190px">
+          <el-select v-model="templateType" style="width: 190px">
             <template #prefix>
               <div class="flex items-center">
-                <el-button bg size="small" style="margin-left: -7px; font-size: 14px; border-radius: 29px;" text
-                           type="success"
+                <el-button size="small"
+                           type="success" bg text
+                           style="margin-left: -7px; font-size: 14px; border-radius: 29px;"
                            @click.stop="downloadTemplate">下载
                 </el-button>
                 <VerticalDivider style="margin-left: 7px" />
@@ -220,26 +226,8 @@ defineExpose({ fetchList });
       />
     </template>
     <!-- 自定义列插槽 -->
-    <template #is_monitor="{ row }">
-      <el-switch v-model="row.is_monitor"></el-switch>
-    </template>
     <template #product_info="{ row }">
-      {{ row.product_info }}
-    </template>
-    <template #shop_name="{ row }">
-      <el-tag :type="getTagType(row.shop_name)">
-        {{ row.shop_name || '--' }}
-      </el-tag>
-    </template>
-    <template #tag="{ row }">
-      <el-tag :type="getTagType(row.tag)">
-        {{ row.tag || '--' }}
-      </el-tag>
-    </template>
-    <template #brand="{ row }">
-      <el-tag :type="getTagType(row.brand)" round effect="plain">
-        {{ row.brand ? row.brand : '--' }}
-      </el-tag>
+      <ProductInfo :item="row" :img-width="50"></ProductInfo>
     </template>
     <template #operate="{ row }">
       <div class="flex justify-center gap-2">

+ 1 - 2
src/views/product-manage/product-list/component/EditDrawer.vue

@@ -6,8 +6,6 @@
  */
 
 import { ElMessage, FormInstance, FormRules } from 'element-plus';
-import { useResponse } from '/src/utils/useResponse';
-import * as api from '/src/views/shop-information/api';
 
 
 const loading = ref(false);
@@ -99,6 +97,7 @@ const resetForm = (formEl: FormInstance | undefined) => {
         <!--</el-select>-->
       </el-form-item>
       <el-form-item>
+        <el-divider />
         <div class="flex flex-1 justify-center">
           <el-button :loading="loading" type="primary" @click="submitForm(ruleFormRef)">确 定</el-button>
           <el-button @click="resetForm(ruleFormRef)">重 置</el-button>

+ 90 - 0
src/views/product-manage/product-list/component/ProductInfo.vue

@@ -0,0 +1,90 @@
+<script lang="ts" setup>
+/**
+ * @Name: ProductInfo.vue
+ * @Description: 商品列表-商品信息插槽
+ * @Author: Cheney
+ */
+
+const props = defineProps({
+  item: {
+    type: Object,
+    required: true
+  },
+  imgWidth: {
+    type: Number,
+    default: 30
+  },
+  showTitleTooltip: {
+    type: Boolean,
+    default: false
+  },
+  displaySKU: {
+    type: Boolean,
+    default: false
+  }
+});
+
+</script>
+
+<template>
+  <div class="flex justify-start items-center font-medium">
+    <div v-if="item.img">
+      <el-tooltip effect="light" placement="right-start">
+        <el-image
+            :src="`https://d1ge0kk1l5kms0.cloudfront.net/images/I/${item.img}.jpg`"
+            :style="`width: ${imgWidth}px; margin-right: 5px;`"
+            fit="fill"
+        />
+        <template #content>
+          <el-image
+              :src="`https://d1ge0kk1l5kms0.cloudfront.net/images/I/${item.img}.jpg`"
+              style="width: 250px"
+          />
+        </template>
+      </el-tooltip>
+    </div>
+    <el-image v-else :style="`width: ${imgWidth}px; margin-right: 5px;`">
+      <div slot="error" class="image-slot">
+        <i class="el-icon-picture-outline"></i>
+      </div>
+    </el-image>
+    <div class="text-left">
+      <el-link type="primary" :href=item.url :underline="false" target="_blank">
+        <el-tooltip :content="item.title" :disabled="showTitleTooltip" :offset="-50" effect="dark" placement="top-start">
+          <span class="en-text">{{ item.title ?? '暂无标题' }}</span>
+        </el-tooltip>
+      </el-link>
+      <div class="asin-sku-text">
+        <span>
+          {{ item.asin }}
+        </span>
+        <p v-show="displaySKU" class="clean-text">
+          {{ item.sku }}
+        </p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style scoped>
+span.en-text {
+  word-break: break-word;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: normal;
+  font-size: 12px;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+
+.clean-text {
+  margin: 0;
+  padding: 0;
+}
+
+.asin-sku-text {
+  display: flex;
+  justify-content: space-between;
+}
+</style>