瀏覽代碼

style(product-list): 优化标签样式和颜色

- 移除品牌标签的自定义样式,使用预定义的 tagTypes
- 添加 ZOSI 品牌的特定颜色样式
- 统一店铺名称和标签的样式
-优化 getTagType 函数,使其支持不同的列名
WanGxC 7 月之前
父節點
當前提交
d46e383f7a

+ 0 - 10
auto-imports.d.ts

@@ -1,10 +0,0 @@
-/* eslint-disable */
-/* prettier-ignore */
-// @ts-nocheck
-// noinspection JSUnusedGlobalSymbols
-// Generated by unplugin-auto-import
-// biome-ignore lint: disable
-export {}
-declare global {
-
-}

+ 3 - 3
src/auto-imports.d.ts

@@ -72,12 +72,12 @@ declare global {
   const useId: typeof import('vue')['useId']
   const useLink: typeof import('vue-router')['useLink']
   const useModel: typeof import('vue')['useModel']
-  const usePagination: typeof import('../../../../../../@/utils/usePagination')['usePagination']
-  const useResponse: typeof import('../../../../../../@/utils/useResponse')['useResponse']
+  const usePagination: typeof import('../../../../../../../@/utils/usePagination')['usePagination']
+  const useResponse: typeof import('../../../../../../../@/utils/useResponse')['useResponse']
   const useRoute: typeof import('vue-router')['useRoute']
   const useRouter: typeof import('vue-router')['useRouter']
   const useSlots: typeof import('vue')['useSlots']
-  const useTableData: typeof import('../../../../../../@/utils/useTableData')['useTableData']
+  const useTableData: typeof import('../../../../../../../@/utils/useTableData')['useTableData']
   const useTemplateRef: typeof import('vue')['useTemplateRef']
   const watch: typeof import('vue')['watch']
   const watchEffect: typeof import('vue')['watchEffect']

+ 13 - 12
src/stores/countryInfo.ts

@@ -2,28 +2,29 @@
 export const useCountryInfoStore = defineStore('countryInfo', () => {
   const countries = ref([
     { name: '美国', code: 'US', color: '#3C3B6E' }, // 深蓝色
+    { name: '英国', code: 'UK', color: '#00247D' }, // 深蓝色
+    { name: '德国', code: 'DE', color: '#000000' }, // 黑色
+    { name: '法国', code: 'FR', color: '#002395' }, // 深蓝色
+    { name: '西班牙', code: 'ES', color: '#AA151B' }, // 红色
+    { name: '意大利', code: 'IT', color: '#008C45' }, // 绿色
+    { name: '日本', code: 'JP', color: '#BC002D' }, // 红色
+    { name: '加拿大', code: 'CA', color: '#FF0000' }, // 红色
+    { name: '比利时', code: 'BE', color: '#FFD700' }, // 金色
+    { name: '荷兰', code: 'NL', color: '#21468B' }, // 深蓝色
+    { name: '巴西', code: 'BR', color: '#009C3B' },
+    { name: '澳大利亚', code: 'AU', color: '#002868' }, // 深蓝色
     { name: '俄罗斯', code: 'RU', color: '#0033A0' }, // 深蓝色
+    { name: '墨西哥', code: 'MX', color: '#006847' }, // 绿色
+    { name: '沙特阿拉伯', code: 'SA', color: '#006C35' }, // 绿色
     { name: '阿联酋', code: 'AE', color: '#00732F' }, // 绿色
-    { name: '澳大利亚', code: 'AU', color: '#002868' }, // 深蓝色
     { name: '澳洲', code: 'AU', color: '#002868' }, // 深蓝色
-    { name: '比利时', code: 'BE', color: '#FFD700' }, // 金色
     { name: '波兰', code: 'PL', color: '#D22630' }, // 红色
-    { name: '德国', code: 'DE', color: '#000000' }, // 黑色
-    { name: '法国', code: 'FR', color: '#002395' }, // 深蓝色
     { name: '菲律宾', code: 'PH', color: '#0038A8' }, // 蓝色
     { name: '韩国', code: 'KR', color: '#003478' }, // 深蓝色
-    { name: '荷兰', code: 'NL', color: '#21468B' }, // 深蓝色
-    { name: '加拿大', code: 'CA', color: '#FF0000' }, // 红色
     { name: '马来西亚', code: 'MY', color: '#010066' }, // 深蓝色
-    { name: '墨西哥', code: 'MX', color: '#006847' }, // 绿色
     { name: '葡萄牙', code: 'PT', color: '#006600' }, // 绿色
-    { name: '日本', code: 'JP', color: '#BC002D' }, // 红色
-    { name: '沙特阿拉伯', code: 'SA', color: '#006C35' }, // 绿色
     { name: '泰国', code: 'TH', color: '#A51931' }, // 红色
-    { name: '西班牙', code: 'ES', color: '#AA151B' }, // 红色
     { name: '新加坡', code: 'SG', color: '#ED2939' }, // 红色
-    { name: '意大利', code: 'IT', color: '#008C45' }, // 绿色
-    { name: '英国', code: 'GB', color: '#00247D' }, // 深蓝色
   ]);
 
   return { countries };

+ 1 - 1
src/utils/useTableData.ts

@@ -42,7 +42,7 @@ export async function useTableData(
       return {
         total: getReactiveValue(options, 'total') || 0,
         page: getReactiveValue(options, 'page') || 1,
-        limit: getReactiveValue(options, 'limit') || 10
+        limit: getReactiveValue(options, 'limit') || 15
       };
     }
   };

+ 14 - 0
src/utils/useTagColor.ts

@@ -0,0 +1,14 @@
+  const set = ref(new Set());
+  const tagTypes = ['primary', 'success', 'warning', 'danger', 'info'];
+  
+  export const getTagType = computed(() => (colName: any) => {
+    if (colName === 'ZOSI') return 'danger';
+    if (!colName) return 'default';
+
+    if (!set.value.has(colName)) {
+      set.value.add(colName);
+    }
+
+    const index = Array.from(set.value).indexOf(colName);
+    return tagTypes[index % tagTypes.length];
+  });

+ 56 - 4
src/views/product-manage/product-list/ColumnsTsx.tsx

@@ -1,3 +1,8 @@
+import { useCountryInfoStore } from '/@/stores/countryInfo';
+
+
+const countryInfoStore = useCountryInfoStore();
+
 export const productColumns: any = [
   { type: 'checkbox', width: 50, align: 'center', fixed: 'left' },
   { type: 'seq', title: 'No.', width: 60, align: 'center' },
@@ -10,12 +15,59 @@ export const productColumns: any = [
     slots: { default: 'product_info' }
   },
   {
-    field: 'sku', title: 'SKU', minWidth: 'auto', align: 'center',
-    slots: { default: 'sku' }
+    field: 'sku', title: 'SKU',  minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.sku ? row.sku : '--' }</span>;
+      }
+    }
   },
   {
-    field: 'country_code', title: '国 家', minWidth: 'auto', align: 'center',
-    slots: { default: 'country_code' }
+    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>;
+      }
+    }
+  },
+  {
+    field: 'platform_number', title: '平台编号',  minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.platform_number ? row.platform_number : '--' }</span>;
+      }
+    }
+  },
+  {
+    field: 'shop_name', title: '店 铺', minWidth: 'auto', align: 'center',
+    slots: { default: 'shop_name' }
+  },
+  {
+    field: 'tag', title: '分 组',  minWidth: 'auto', align: 'center',
+    slots: { default: 'tag' }
+  },
+  {
+    field: 'brand', title: '品 牌',  minWidth: 'auto', align: 'center',
+    slots: { default: 'brand' }
+  },
+  {
+    field: 'price_info', title: '价 格',  minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.price_info ? row.price_info : '--' }</span>;
+      }
+    }
+  },
+  {
+    field: 'show_price', title: '展示价格',  minWidth: 'auto', align: 'center',
+    slots: {
+      default({ row }: any) {
+        return <span class={ 'font-medium' }>{ row.show_price ? row.show_price : '--' }</span>;
+      }
+    }
   },
   {
     field: 'operate', title: '操 作', width: 100, align: 'center', fixed: 'right',

+ 3 - 3
src/views/product-manage/product-list/api.ts

@@ -1,4 +1,4 @@
-import { request } from '/src/utils/service';
+import { request } from '/@/utils/service';
 
 
 const apiPrefix = '/api/choice/goods/';
@@ -27,9 +27,9 @@ export function getPlatformDetailOverview(query: any) {
   });
 }
 
-export function getImportTemplateNotice() {
+export function getGroupOptions() {
   return request({
-    url: apiPrefix + 'alarm/import_data/',
+    url: apiPrefix + 'tags/',
     method: 'GET',
   });
 }

+ 62 - 12
src/views/product-manage/product-list/component/DataTable.vue

@@ -6,28 +6,39 @@
  */
 
 import { Download, Message, Money, Open, Operation, Refresh } from '@element-plus/icons-vue';
-import * as api from '/src/views/product-manage/product-list/api';
+import * as api from '../api';
 import PermissionButton from '/src/components/PermissionButton/index.vue';
 import EditDrawer from '/src/views/product-manage/product-list/component/EditDrawer.vue';
 import NoticeDialog from '/src/views/product-manage/product-list/component/NoticeDialog.vue';
 import ImportButton from '/src/components/ImportButton/index.vue';
 import VerticalDivider from '/src/components/VerticalDivider/index.vue';
-import { productColumns } from '/src/views/product-manage/product-list/ColumnsTsx';
-import DataTableSlot from './DataTableSlot.vue';
-import { getImportTemplateNotice, getImportTemplatePrice, getImportTemplateProduct } from '/src/views/product-manage/product-list/api';
-import { useResponse } from '/src/utils/useResponse';
-import { downloadFile } from '/src/utils/service';
+import { productColumns } from '../ColumnsTsx';
+import { downloadFile } from '/@/utils/service';
+import { getTagType } from '/@/utils/useTagColor';
 
 
+interface Parameter {
+  country: string,
+  brand: string,
+  group: string,
+  status: string,
+  asin: string,
+  sku: string,
+  shop: string,
+}
+
+const queryParameter: Parameter | undefined = inject('query-parameter');
 const { tableOptions, handlePageChange } = usePagination(fetchList);
 
 const gridRef = ref();
 const gridOptions: any = reactive({
+  id: 'tableID',
   border: false,
   round: true,
   stripe: true,
   currentRowHighLight: true,
   height: '100%',
+  width: '100%',
   toolbarConfig: {
     custom: true,
     slots: {
@@ -70,8 +81,13 @@ onBeforeMount(() => {
 
 async function fetchList() {
   const query = {
-    page: gridOptions.pagerConfig.page,
-    limit: gridOptions.pagerConfig.limit
+    country_code: queryParameter?.country,
+    brand: queryParameter?.brand,
+    tag: queryParameter?.group,
+    status: queryParameter?.status,
+    asin: queryParameter?.asin,
+    sku: queryParameter?.sku,
+    shop_id: queryParameter?.shop
   };
   await useTableData(api.getTableData, query, gridOptions);
 }
@@ -137,10 +153,12 @@ function downloadTemplate() {
   }
 }
 
+defineExpose({ fetchList });
 </script>
 
 <template>
-  <vxe-grid ref="gridRef" v-bind="gridOptions"
+  <vxe-grid :key="gridOptions.id" ref="gridRef"
+            v-bind="gridOptions"
             @checkbox-change="selectChangeEvent"
             @checkbox-all="selectAllChangeEvent">
     <!-- 工具栏左侧 -->
@@ -166,7 +184,7 @@ function downloadTemplate() {
           </el-select>
         </div>
         <VerticalDivider class="px-1" style="margin-left: 7px;" />
-        
+
         <ImportButton :icon="Message" bg text>变更通知导入</ImportButton>
         <ImportButton bg text>
           <i class="bi bi-box-seam mr-3"></i>
@@ -202,8 +220,40 @@ function downloadTemplate() {
       />
     </template>
     <!-- 自定义列插槽 -->
-    <template v-for="col in productColumns" :key="col.field" #[`${col.field}`]="{ row }">
-      <DataTableSlot :field="col.field" :row="row" @edit-row="handleEdit" @handle-notice="handleNotice" />
+    <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>
+    </template>
+    <template #operate="{ row }">
+      <div class="flex justify-center gap-2">
+        <PermissionButton circle plain type="warning" @click="handleEdit(row)">
+          <el-icon>
+            <Operation />
+          </el-icon>
+        </PermissionButton>
+        <PermissionButton circle plain type="info" @click="handleNotice(row)">
+          <el-icon>
+            <Message />
+          </el-icon>
+        </PermissionButton>
+      </div>
     </template>
   </vxe-grid>
   <EditDrawer v-if="editOpen" v-model="editOpen" :row-data="rowData" />

+ 16 - 2
src/views/product-manage/product-list/component/DataTableSlot.vue

@@ -5,9 +5,10 @@
  * @Author: Cheney
  */
 
-import PermissionButton from '/src/components/PermissionButton/index.vue';
-import { useCountryInfoStore } from '/src/stores/countryInfo';
+
+import { useCountryInfoStore } from '/@/stores/countryInfo';
 import { Operation, Delete, Message } from '@element-plus/icons-vue';
+import PermissionButton from '/@/components/PermissionButton/index.vue'
 
 
 const props = defineProps<{
@@ -44,6 +45,19 @@ function handleNotice(row: any) {
     <div v-else-if="field === 'product_info'">
         {{ row.goods_info ? row.goods_info : '--' }}
     </div>
+    <div v-else-if="field === 'platform_number'">
+        {{ row.platform_number ? row.platform_number : '--' }}
+    </div>
+    <div v-else-if="field === 'shop_name'">
+      <el-tag type="primary" effect="plain">{{ row.shop_name ? 
+          row.shop_name : 
+          '--' }}</el-tag>
+    </div>
+    <div v-else-if="field === 'brand'">
+      <el-tag :style="{ color: '#F88B8A', borderColor: '#FBC4C4'}"  effect="plain">{{ row.brand ? 
+          row.brand : 
+          '--' }}</el-tag>
+    </div>
     <div v-else-if="field === 'operate'">
       <div class="flex justify-center gap-2">
         <PermissionButton circle plain type="warning" @click="handleEdit(row)">

+ 51 - 17
src/views/product-manage/product-list/index.vue

@@ -7,27 +7,52 @@
 
 import VerticalDivider from '/src/components/VerticalDivider/index.vue';
 import { RefreshRight, Search } from '@element-plus/icons-vue';
-import { useTableHeight } from '/src/utils/useTableHeight';
+import { useTableHeight } from '/@/utils/useTableHeight';
 import DataTable from './component/DataTable.vue';
+import { DictionaryStore } from '/@/stores/dictionary';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from './api';
+import { useTemplateRef } from 'vue';
 
 
+const { data: staticData } = DictionaryStore()
+
 const titleContainer: Ref<HTMLElement | null> = useTemplateRef('titleContainer');
 const queryContainer: Ref<HTMLElement | null> = useTemplateRef('queryContainer');
 const { tableHeight } = useTableHeight(titleContainer, queryContainer);
 
-const loading = ref(false);
+const tableRef: Ref<any> = useTemplateRef('table')
+
+const btnLoading = ref(false);
 
 const formInline = reactive({
-  user: '',
-  region: '',
-  date: ''
+  country: '',
+  brand: '',
+  group: '',
+  status: '',
+  asin: '',
+  sku: '',
+  shop: '',
 });
+provide('query-parameter', formInline)
+
+const groupOptions:any = ref([])
 
-function onClick() {
-  loading.value = true;
-  
+onBeforeMount(() => {
+  fetchGroupOptions()
+})
+
+async function fetchGroupOptions() {
+  const res = await useResponse(api.getGroupOptions)
+  groupOptions.value = res.data
 }
 
+async function handleQuery() {
+  console.log('tableRef=> ', tableRef.value);
+  btnLoading.value = true;
+  await tableRef.value?.fetchList();
+  btnLoading.value = false;
+}
 </script>
 
 <template>
@@ -42,25 +67,34 @@ function onClick() {
               <el-col :span="6">
                 <div class="flex items-center">
                   <span class="mr-2">国 家</span>
-                  <el-select v-model="formInline.date" clearable placeholder="请选择国家" />
+                  <el-select v-model="formInline.country" clearable placeholder="请选择国家">
+                    <el-option v-for="item in staticData.country_code" :key="item.value" :label="item.label" 
+                               :value="item.value" />
+                  </el-select>
                 </div>
               </el-col>
               <el-col :span="6">
                 <div class="flex items-center">
                   <span class="mr-2">品 牌</span>
-                  <el-select v-model="formInline.date" clearable placeholder="请选择品牌" />
+                  <el-select v-model="formInline.brand" clearable placeholder="请选择品牌" />
                 </div>
               </el-col>
               <el-col :span="6">
                 <div class="flex items-center">
                   <span class="mr-2">分 组</span>
-                  <el-select v-model="formInline.date" clearable placeholder="请选择分组" />
+                  <el-select v-model="formInline.group" clearable placeholder="请选择分组">
+                    <el-option v-for="item in groupOptions" :label="item.tag" 
+                               :value="item.tag" />
+                  </el-select>
                 </div>
               </el-col>
               <el-col :span="6">
                 <div class="flex items-center">
                   <span class="mr-2">状 态</span>
-                  <el-select v-model="formInline.date" clearable placeholder="请选择状态" />
+                  <el-select v-model="formInline.status" clearable placeholder="请选择状态">
+                    <el-option v-for="item in staticData.goods_status" :key="item.value" :label="item.label" 
+                               :value="item.value" />
+                  </el-select>
                 </div>
               </el-col>
             </el-row>
@@ -68,19 +102,19 @@ function onClick() {
               <el-col :span="6">
                 <div class="flex items-center">
                   <span class="mr-2">ASIN</span>
-                  <el-input v-model="formInline.region" clearable placeholder="请输入ASIN"></el-input>
+                  <el-input v-model="formInline.asin" clearable placeholder="请输入ASIN"></el-input>
                 </div>
               </el-col>
               <el-col :span="6">
                 <div class="flex items-center">
                   <span class="mr-2">SKU</span>
-                  <el-input v-model="formInline.region" clearable placeholder="请输入SKU"></el-input>
+                  <el-input v-model="formInline.sku" clearable placeholder="请输入SKU"></el-input>
                 </div>
               </el-col>
               <el-col :span="6" class="flex">
                 <div class="flex items-center">
                   <span class="mr-2">店 铺</span>
-                  <el-input v-model="formInline.region" clearable placeholder="请输入店铺"></el-input>
+                  <el-input v-model="formInline.shop" clearable placeholder="请输入店铺"></el-input>
                 </div>
               </el-col>
             </el-row>
@@ -88,7 +122,7 @@ function onClick() {
         </div>
         <VerticalDivider />
         <div class="flex flex-col gap-1.5 items-end">
-          <el-button :icon="Search" :loading="loading" class="mb-4" type="primary" @click="onClick">
+          <el-button :icon="Search" :loading="btnLoading" class="mb-4" type="primary" @click="handleQuery">
             查 询
           </el-button>
           <el-button :icon="RefreshRight" color="#ECECF1C9" style="width: 88px; color: #3c3c3c;">
@@ -98,7 +132,7 @@ function onClick() {
       </div>
       <el-divider ref="dividerContainer" style="margin: 20px 0 12px 0;" />
       <div :style="{ height: tableHeight + 'px' }">
-        <DataTable />
+        <DataTable ref="table" />
       </div>
     </el-card>
   </div>