Procházet zdrojové kódy

feat(vipSite): 添加样式设置与站点商品管理功能

- 在站点数据结构中新增是否自定义字段 isDiy
- 新增接口支持站点商品分页查询、协议价编辑、导入导出等操作
- 优化ImageUpload组件,支持直接展示URL格式图片
- 路由新增VIP站点样式设置页面入口
- VipSite页面优化调整表格列宽及标签显示
- 实现样式设置页面,支持楼层信息的增删改查及商品、品牌配置
- 商品配置界面新增导入导出功能,支持导入Excel文件和下载模板
- 新增导入商品弹窗及文件选择、确认导入交互
- 添加毛利率计算方法,显示协议价与采购价差异百分比
- 优化商品列表表格显示,修正图片字段及状态标签显示
- 完善页面操作交互及表单校验,提升用户体验
肖路 před 1 měsícem
rodič
revize
5982799c08

+ 10 - 0
src/api/product/site/types.ts

@@ -98,6 +98,11 @@ export interface SiteVO {
    * 状态(0正常 1停用)
    */
   status: string;
+
+  /**
+   * 是否diy(0不diy 1diy)
+   */
+  isDiy: number;
 }
 
 export interface SiteForm extends BaseEntity {
@@ -200,6 +205,11 @@ export interface SiteForm extends BaseEntity {
    * 状态(0正常 1停用)
    */
   status?: string;
+
+  /**
+   * 是否diy(0不diy 1diy)
+   */
+  isDiy?: number;
 }
 
 export interface SiteQuery extends PageQuery {

+ 66 - 1
src/api/product/siteProduct/index.ts

@@ -1,6 +1,6 @@
 import request from '@/utils/request';
 import { AxiosPromise } from 'axios';
-import { SiteProductVO, SiteProductForm, SiteProductQuery } from '@/api/product/siteProduct/types';
+import { SiteProductVO, SiteProductForm, SiteProductQuery, SiteProductPageQuery, PcProductVO, ClientSiteProductExportVO, ClientSiteProductImportVO } from '@/api/product/siteProduct/types';
 
 /**
  * 查询客户站点产品配置列表
@@ -61,3 +61,68 @@ export const delSiteProduct = (id: string | number | Array<string | number>) =>
     method: 'delete'
   });
 };
+/**
+ * 编辑站点产品的协议价
+ * @param data
+ */
+export const updateSiteProductAgreementPrice = (data: SiteProductForm) => {
+  return request({
+    url: '/product/siteProduct/updateAgreementPrice',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 获取站点下的产品数据(分页)
+ * @param query
+ * @returns {*}
+ */
+export const getSiteProductPage = (query?: SiteProductPageQuery & PageQuery) => {
+  return request({
+    url: '/product/siteProduct/getSiteProductPage',
+    method: 'get',
+    params: query
+  }) as AxiosPromise<{ rows: PcProductVO[]; total: number }>;
+};
+
+/**
+ * 导出客户站点产品数据
+ * @param query
+ */
+export const exportSiteProductData = (query?: SiteProductPageQuery) => {
+  return request({
+    url: '/product/siteProduct/exportData',
+    method: 'post',
+    data: query,
+    responseType: 'blob'
+  });
+};
+
+/**
+ * 导入客户站点产品配置数据
+ * @param file 导入文件
+ */
+export const importSiteProductData = (file: File) => {
+  const formData = new FormData();
+  formData.append('file', file);
+  return request({
+    url: '/product/siteProduct/importData',
+    method: 'post',
+    data: formData,
+    headers: {
+      'Content-Type': 'multipart/form-data'
+    }
+  });
+};
+
+/**
+ * 获取导入模板
+ */
+export const getSiteProductImportTemplate = () => {
+  return request({
+    url: '/product/siteProduct/importTemplate',
+    method: 'post',
+    responseType: 'blob'
+  });
+};

+ 195 - 0
src/api/product/siteProduct/types.ts

@@ -213,3 +213,198 @@ export interface SiteProductQuery extends PageQuery {
    */
   params?: any;
 }
+
+/**
+ * 站点产品分页查询参数
+ */
+export interface SiteProductPageQuery {
+  /**
+   * 站点 ID
+   */
+  siteId?: string | number;
+}
+
+/**
+ * 产品基础信息 VO(来自 Elasticsearch)
+ */
+export interface PcProductVO {
+  /**
+   * 产品 ID
+   */
+  id: string | number;
+  
+  /**
+   * 产品编号
+   */
+  productNo?: string;
+  
+  /**
+   * 产品名称
+   */
+  itemName?: string;
+  
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+  
+  /**
+   * 品牌名称
+   */
+  brandName?: string;
+  
+  /**
+   * 单位
+   */
+  unitName?: string;
+  
+  /**
+   * 市场价
+   */
+  marketPrice?: number | string;
+  
+  /**
+   * 会员价
+   */
+  memberPrice?: number | string;
+  
+  /**
+   * 最低售价
+   */
+  minSellingPrice?: number | string;
+  
+  /**
+   * 采购价
+   */
+  purchasingPrice?: number | string;
+}
+
+/**
+ * 客户站点产品导出 VO
+ */
+export interface ClientSiteProductExportVO {
+  /**
+   * 序号
+   */
+  serialNumber?: number;
+  
+  /**
+   * 产品编号
+   */
+  productNo?: string;
+  
+  /**
+   * 产品名称
+   */
+  itemName?: string;
+  
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+  
+  /**
+   * 品牌名称
+   */
+  brandName?: string;
+  
+  /**
+   * 单位
+   */
+  unitName?: string;
+  
+  /**
+   * 市场价
+   */
+  marketPrice?: number | string;
+  
+  /**
+   * 平台价
+   */
+  platformPrice?: number | string;
+  
+  /**
+   * 最低售价
+   */
+  minSellingPrice?: number | string;
+  
+  /**
+   * 采购价
+   */
+  purchasingPrice?: number | string;
+  
+  /**
+   * 协议价
+   */
+  agreementPrice?: number | string;
+}
+
+/**
+ * 客户站点产品导入 VO
+ */
+export interface ClientSiteProductImportVO {
+  /**
+   * 站点 ID
+   */
+  siteId?: string | number;
+  
+  /**
+   * 客户编号
+   */
+  clientNo?: string;
+  
+  /**
+   * 客户 ID
+   */
+  clientId?: string | number;
+  
+  /**
+   * 产品编号
+   */
+  productNo?: string;
+  
+  /**
+   * 产品 ID
+   */
+  productId?: string | number;
+  
+  /**
+   * 是否在中心展示
+   */
+  centerView?: number;
+  
+  /**
+   * 展示方案 1 可见性
+   */
+  zsfa1View?: number;
+  
+  /**
+   * 展示方案 2 可见性
+   */
+  zsfa2View?: number;
+  
+  /**
+   * 展示方案 3 可见性
+   */
+  zsfa3View?: number;
+  
+  /**
+   * 展示方案 4 可见性
+   */
+  zsfa4View?: number;
+  
+  /**
+   * 协议价格
+   */
+  agreementPrice?: number | string;
+  
+  /**
+   * 状态
+   */
+  status?: string;
+  
+  /**
+   * 备注
+   */
+  remark?: string;
+}

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

@@ -99,9 +99,13 @@ watch(
       let list: OssVO[] = [];
       if (Array.isArray(val)) {
         list = val as OssVO[];
+      } else if (typeof val === 'string' && val.startsWith('http')) {
+        // 如果存的是 URL 直接回显,无需调接口
+        fileList.value = [{ name: val, url: val }];
+        return;
       } else {
         const res = await listByIds(val);
-        list = res.data;
+        list = res.data || [];
       }
       // 然后将数组转为对象数组
       fileList.value = list.map((item) => {

+ 6 - 0
src/router/index.ts

@@ -194,6 +194,12 @@ export const constantRoutes: RouteRecordRaw[] = [
         component: () => import('@/views/platform/customerOperation/vipSite/productConfig.vue'),
         name: 'VipProductConfig',
         meta: { title: '商品配置', activeMenu: '/customerOperation/vipSite', noCache: true }
+      },
+      {
+        path: 'vipSite/styleDesign',
+        component: () => import('@/views/platform/customerOperation/vipSite/styleDesign.vue'),
+        name: 'VipStyleDesign',
+        meta: { title: '样式设置', activeMenu: '/customerOperation/vipSite', noCache: true }
       }
     ]
   },

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

@@ -35,7 +35,7 @@
                 </el-form-item>
               </el-col>
               <el-col :span="6">
-                <el-form-item label="平台状态" prop="status">
+                <el-form-item label="状态" prop="status">
                   <el-select v-model="queryParams.status" placeholder="请选择" clearable style="width: 100%">
                     <el-option label="启用" value="0" />
                     <el-option label="禁用" value="1" />
@@ -92,7 +92,7 @@
 
       <el-table v-loading="loading" border :data="siteList">
         <el-table-column label="客户编号" align="center" prop="clientNo" width="100" />
-        <el-table-column label="客户名称" align="center" prop="clientName" min-width="150" />
+        <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">
@@ -116,7 +116,7 @@
             <span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d}') }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="操作" align="center" width="120" fixed="right">
+        <el-table-column label="操作" align="center" width="200" fixed="right">
           <template #default="scope">
             <div class="flex flex-col items-center gap-1">
               <el-link type="primary" :underline="false" @click="handleSiteConfig(scope.row)">站点配置</el-link>
@@ -296,7 +296,10 @@ const handleProductConfig = (row: SiteVO) => {
 
 /** 样式设计 */
 const handleStyleDesign = (row: SiteVO) => {
-  proxy?.$modal.msgWarning('样式设计功能开发中...');
+  router.push({
+    path: '/customerOperation/vipSite/styleDesign',
+    query: { siteId: row.id, siteName: row.siteName }
+  });
 };
 
 /** 状态切换 */

+ 127 - 19
src/views/platform/customerOperation/vipSite/productConfig.vue

@@ -65,7 +65,7 @@
         <el-table-column label="商品编号" align="center" prop="productNo" width="100" />
         <el-table-column label="商品图片" align="center" width="100">
           <template #default="scope">
-            <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
           </template>
         </el-table-column>
         <el-table-column label="商品信息" align="center" min-width="400">
@@ -96,9 +96,9 @@
         </el-table-column>
         <el-table-column label="采购价" align="center" prop="purchasePrice" width="100" />
         <el-table-column label="协议价" align="center" prop="agreementPrice" width="100" />
-        <el-table-column label="平台售价毛利率" align="center" width="130">
+        <el-table-column label="毛利率" align="center" width="130">
           <template #default="scope">
-            {{ scope.row.grossMargin ? `${scope.row.grossMargin}%` : '-' }}
+            {{ calcGrossMargin(scope.row) }}
           </template>
         </el-table-column>
         <el-table-column label="商品状态" align="center" width="100">
@@ -136,6 +136,35 @@
       </template>
     </el-dialog>
 
+    <!-- 导入商品弹窗 -->
+    <el-dialog title="导入商品" v-model="importDialog.visible" width="500px" append-to-body>
+      <el-form label-width="100px">
+        <el-form-item label="导入模板">
+          <el-button type="primary" link icon="Download" @click="handleDownloadTemplate">下载导入模板</el-button>
+        </el-form-item>
+        <el-form-item label="选择文件">
+          <el-upload
+            ref="importUploadRef"
+            action="#"
+            :auto-upload="false"
+            :limit="1"
+            accept=".xlsx,.xls"
+            :on-change="handleImportFileChange"
+            :on-remove="() => (importDialog.file = null)"
+          >
+            <el-button type="primary" icon="Upload">选择文件</el-button>
+            <template #tip>
+              <div class="el-upload__tip">只支持 .xlsx / .xls 格式文件</div>
+            </template>
+          </el-upload>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="importDialog.visible = false">取 消</el-button>
+        <el-button type="primary" :loading="importDialog.loading" @click="handleConfirmImport">确 定</el-button>
+      </template>
+    </el-dialog>
+
     <!-- 添加商品弹窗 -->
     <el-dialog title="添加商品" v-model="addProductDialog.visible" width="1500px" append-to-body top="5vh">
       <div class="add-product-dialog">
@@ -166,7 +195,7 @@
           <el-table-column label="商品编号" align="center" prop="productNo" width="100" />
           <el-table-column label="商品图片" align="center" width="100">
             <template #default="scope">
-              <image-preview :src="scope.row.productImageUrl" :width="60" :height="60"/>
+              <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
             </template>
           </el-table-column>
           <el-table-column label="商品信息" align="center" min-width="400">
@@ -205,7 +234,12 @@
             </template>
           </el-table-column>
           <el-table-column label="采购价" align="center" prop="purchasingPrice" width="80" />
-          <el-table-column label="商品状态" align="center" prop="productStatus" width="80" />
+          <el-table-column label="商品状态" align="center" prop="productStatus" width="80" >
+            <template #default="scope">
+              <el-tag v-if="scope.row.productStatus === '1'" type="success" size="small">上架</el-tag>
+              <el-tag v-else type="info" size="small">下架</el-tag>
+            </template>
+          </el-table-column>
           <el-table-column label="协议价" align="center" width="150">
             <template #default="scope">
               <el-input-number
@@ -244,10 +278,18 @@ import type { FormInstance } from 'element-plus';
 import { useRoute, useRouter } from 'vue-router';
 import { ArrowLeft } from '@element-plus/icons-vue';
 import { listBase } from '@/api/pmsProduct/base';
-import { BaseVO, BaseQuery } from '@/api/pmsProduct/base/types';
 import { listProductCategory } from '@/api/customerOperation/customerBlacklist';
 import { listBrand } from '@/api/product/brand';
-import { addSiteProduct } from '@/api/product/siteProduct';
+import {
+  addSiteProduct,
+  getSiteProductPage,
+  exportSiteProductData,
+  importSiteProductData,
+  getSiteProductImportTemplate,
+  updateSiteProduct,
+  delSiteProduct
+} from '@/api/product/siteProduct/index';
+import FileSaver from 'file-saver';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const route = useRoute();
@@ -307,15 +349,24 @@ const addProductDialog = reactive({
   productList: [] as any[],
   total: 0
 });
-const addProductQuery = ref<BaseQuery>({
+const addProductQuery = ref({
   pageNum: 1,
   pageSize: 10,
-  productNo: undefined,
-  itemName: undefined
+  isSelf: 1,
+  productNo: undefined as string | undefined,
+  itemName: undefined as string | undefined
 });
-const selectedProducts = ref<BaseVO[]>([]);
+const selectedProducts = ref<any[]>([]);
 const addProductTableRef = ref<any>();
 
+// 导入商品弹窗
+const importDialog = reactive({
+  visible: false,
+  loading: false,
+  file: null as File | null
+});
+const importUploadRef = ref<any>();
+
 /** 初始化 */
 const initData = () => {
   siteId.value = route.query.siteId as string;
@@ -328,8 +379,7 @@ const initData = () => {
 const getList = async () => {
   loading.value = true;
   try {
-    // TODO: 调用站点商品列表接口
-    const res = await listBase({
+    const res = await getSiteProductPage({
       ...queryParams.value,
       bottomCategoryId: queryParams.value.bottomCategoryId || queryParams.value.mediumCategoryId || queryParams.value.topCategoryId
     });
@@ -385,6 +435,15 @@ const handleMediumCategoryChange = (val: number | undefined) => {
   }
 };
 
+/** 计算毛利率:(协议价 - 采购价) ÷ 采购价 × 100% */
+const calcGrossMargin = (row: any): string => {
+  const agreement = parseFloat(row.agreementPrice);
+  const purchase = parseFloat(row.purchasePrice || row.purchasingPrice);
+  if (!purchase || purchase === 0 || isNaN(agreement) || isNaN(purchase)) return '-';
+  const margin = ((agreement - purchase) / purchase) * 100;
+  return `${margin.toFixed(2)}%`;
+};
+
 /** 搜索 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
@@ -428,7 +487,10 @@ const handlePriceEdit = (row: any) => {
 /** 提交价格修改 */
 const submitPriceForm = async () => {
   await priceFormRef.value?.validate();
-  // TODO: 调用修改协议价接口
+  await updateSiteProduct({
+    id: priceDialog.form.id,
+    agreementPrice: String(priceDialog.form.agreementPrice)
+  });
   proxy?.$modal.msgSuccess('价格修改成功');
   priceDialog.visible = false;
   await getList();
@@ -437,7 +499,7 @@ const submitPriceForm = async () => {
 /** 删除商品 */
 const handleDelete = async (row: any) => {
   await proxy?.$modal.confirm(`确认要删除商品"${row.itemName}"吗?`);
-  // TODO: 调用删除接口
+  await delSiteProduct(row.id);
   proxy?.$modal.msgSuccess('删除成功');
   await getList();
 };
@@ -448,6 +510,7 @@ const handleAddProduct = () => {
   addProductQuery.value = {
     pageNum: 1,
     pageSize: 10,
+    isSelf: 1,
     productNo: undefined,
     itemName: undefined
   };
@@ -478,7 +541,7 @@ const handleSearchProducts = () => {
 };
 
 /** 选择变化 */
-const handleSelectionChange = (selection: BaseVO[]) => {
+const handleSelectionChange = (selection: any[]) => {
   selectedProducts.value = selection;
 };
 
@@ -537,14 +600,59 @@ const handleAddSingleProduct = async (row: any) => {
   }
 };
 
+/** 下载导入模板 */
+const handleDownloadTemplate = async () => {
+  try {
+    const res = await getSiteProductImportTemplate();
+    const blob = new Blob([res as any], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+    FileSaver.saveAs(blob, '站点商品导入模板.xlsx');
+  } catch (error) {
+    console.error('下载模板失败:', error);
+  }
+};
+
+/** 导入文件选择 */
+const handleImportFileChange = (file: any) => {
+  importDialog.file = file.raw;
+};
+
+/** 确认导入 */
+const handleConfirmImport = async () => {
+  if (!importDialog.file) {
+    proxy?.$modal.msgWarning('请先选择要导入的文件');
+    return;
+  }
+  importDialog.loading = true;
+  try {
+    await importSiteProductData(importDialog.file);
+    proxy?.$modal.msgSuccess('导入成功');
+    importDialog.visible = false;
+    importDialog.file = null;
+    if (importUploadRef.value) importUploadRef.value.clearFiles();
+    await getList();
+  } catch (error) {
+    console.error('导入失败:', error);
+  } finally {
+    importDialog.loading = false;
+  }
+};
+
 /** 导入商品 */
 const handleImportProduct = () => {
-  proxy?.$modal.msgWarning('导入商品功能开发中...');
+  importDialog.visible = true;
+  importDialog.file = null;
+  if (importUploadRef.value) importUploadRef.value.clearFiles();
 };
 
 /** 导出商品 */
-const handleExportProduct = () => {
-  proxy?.$modal.msgWarning('导出商品功能开发中...');
+const handleExportProduct = async () => {
+  try {
+    const res = await exportSiteProductData({ siteId: siteId.value });
+    const blob = new Blob([res as any], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
+    FileSaver.saveAs(blob, `站点商品数据_${new Date().getTime()}.xlsx`);
+  } catch (error) {
+    console.error('导出失败:', error);
+  }
 };
 
 onMounted(() => {

+ 10 - 1
src/views/platform/customerOperation/vipSite/siteConfig.vue

@@ -87,6 +87,14 @@
           />
         </el-form-item>
 
+        <el-form-item label="是否自定义" prop="isDiy">
+          <el-switch
+            v-model="form.isDiy"
+            :active-value="1"
+            :inactive-value="0"
+          />
+        </el-form-item>
+
         <el-form-item label="站点logo" prop="siteLogo">
           <div class="flex items-center gap-2">
             <el-image
@@ -197,7 +205,8 @@ const initForm: SiteForm = {
   startTime: '',
   endTime: '',
   status: '1',
-  isShow: 1
+  isShow: 1,
+  isDiy: 0
 };
 const form = ref<SiteForm>({ ...initForm });
 

+ 693 - 0
src/views/platform/customerOperation/vipSite/styleDesign.vue

@@ -0,0 +1,693 @@
+<template>
+  <div class="p-2">
+    <!-- 返回按钮 + 标题 -->
+    <el-card shadow="never" class="mb-2">
+      <div class="flex items-center">
+        <el-button link @click="handleBack">
+          <el-icon><ArrowLeft /></el-icon>
+          返回
+        </el-button>
+        <el-divider direction="vertical" />
+        <span class="text-base font-medium">样式设置</span>
+        <span v-if="siteName" class="text-gray-500 ml-2 text-sm">- {{ siteName }}</span>
+      </div>
+    </el-card>
+
+    <!-- 搜索 + 操作区域 -->
+    <el-card shadow="never" class="mb-2">
+      <div class="flex items-center gap-3">
+        <span class="text-sm whitespace-nowrap">楼层名称</span>
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入楼层名称"
+          clearable
+          style="width: 220px"
+          @keyup.enter="handleQuery"
+        />
+        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+        <el-button type="primary" icon="Plus" @click="handleAdd">添加楼层</el-button>
+      </div>
+    </el-card>
+
+    <!-- 表格区域 -->
+    <el-card shadow="never">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span class="font-bold text-[#409EFF]">楼层信息列表</span>
+          <el-tooltip content="刷新">
+            <el-button circle icon="Refresh" size="small" @click="getList" />
+          </el-tooltip>
+        </div>
+      </template>
+
+      <el-table v-loading="loading" border :data="floorList">
+        <el-table-column label="楼层名称" align="center" prop="name" min-width="150" />
+        <el-table-column label="是否显示" align="center" width="100">
+          <template #default="scope">
+            <el-link
+              :type="scope.row.isShow === '1' ? 'primary' : 'info'"
+              :underline="false"
+              @click="handleToggleShow(scope.row)"
+            >
+              {{ scope.row.isShow === '1' ? '显示' : '隐藏' }}
+            </el-link>
+          </template>
+        </el-table-column>
+        <el-table-column label="排序" align="center" prop="sort" width="80" />
+        <el-table-column label="更新时间" align="center" prop="updateTime" width="170" />
+        <el-table-column label="操作" align="center" width="300" fixed="right">
+          <template #default="scope">
+            <el-link type="danger" :underline="false" class="mr-2" @click="handleDelete(scope.row)">删除</el-link>
+            <el-link type="primary" :underline="false" class="mr-2" @click="handleEdit(scope.row)">编辑</el-link>
+            <el-link type="primary" :underline="false" class="mr-2" @click="handleConfigProduct(scope.row)">配置商品</el-link>
+            <el-link type="warning" :underline="false" @click="handleConfigBrand(scope.row)">配置品牌</el-link>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="total > 0"
+        :total="total"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        @pagination="getList"
+      />
+    </el-card>
+
+    <!-- 添加/编辑楼层 弹窗 -->
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="620px" :close-on-click-modal="false">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="楼层名称" prop="name">
+              <el-input v-model="form.name" placeholder="请输入楼层名称" maxlength="50" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="链接" prop="link">
+              <el-input v-model="form.link" placeholder="请输入链接" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="16">
+          <el-col :span="12">
+            <el-form-item label="排序" prop="sort">
+              <el-input-number v-model="form.sort" :min="0" :max="9999" controls-position="right" style="width: 150px" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="是否显示" prop="isShow">
+              <el-switch v-model="form.isShow" active-value="1" inactive-value="0" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-form-item label="图片" prop="imageUrl">
+          <image-upload v-model="form.imageUrl" :limit="1" :file-size="5" :file-type="['png', 'jpg', 'jpeg']" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" :loading="submitLoading" @click="handleSubmit">确 认</el-button>
+        <el-button @click="dialogVisible = false">取 消</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 配置商品对话框 -->
+    <el-dialog v-model="productDialog.visible" title="推荐商品" width="900px" append-to-body>
+      <div class="flex items-center gap-3 mb-4">
+        <el-button @click="handleAddProduct">新增商品</el-button>
+        <el-button @click="handleImportProduct">导入商品</el-button>
+      </div>
+      <el-table v-loading="productDialog.loading" :data="linkedProducts" border>
+        <el-table-column label="商品编号" align="center" prop="productNo" width="140" />
+        <el-table-column label="商品图片" align="center" width="100">
+          <template #default="scope">
+            <el-image v-if="scope.row.productImage" :src="scope.row.productImage" fit="cover" style="width: 60px; height: 60px" />
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" align="center" prop="productName" min-width="200" show-overflow-tooltip />
+        <el-table-column label="价格" align="center" prop="price" width="120" />
+        <el-table-column label="操作" align="center" width="80">
+          <template #default="scope">
+            <el-link type="danger" :underline="false" @click="handleRemoveLinkedProduct(scope.row)">删除</el-link>
+          </template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <el-button @click="productDialog.visible = false">关 闭</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 配置品牌对话框 -->
+    <el-dialog v-model="brandDialog.visible" :title="`配置品牌 - ${brandDialog.floorName}`" width="900px" append-to-body>
+      <div class="flex items-center gap-3 mb-4">
+        <el-button type="primary" @click="handleAddBrand">新增品牌</el-button>
+      </div>
+      <el-table v-loading="brandDialog.loading" :data="linkedBrands" border>
+        <el-table-column label="品牌编号" align="center" prop="brandNo" width="120" />
+        <el-table-column label="品牌名称" align="center" prop="brandName" min-width="200" show-overflow-tooltip />
+        <el-table-column label="品牌图片" align="center" width="100">
+          <template #default="scope">
+            <el-image v-if="scope.row.brandLogo" :src="scope.row.brandLogo" fit="contain" style="width: 60px; height: 40px" />
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" width="80">
+          <template #default="scope">
+            <el-link type="danger" :underline="false" @click="handleRemoveLinkedBrand(scope.row)">删除</el-link>
+          </template>
+        </el-table-column>
+      </el-table>
+      <template #footer>
+        <el-button type="primary" @click="brandDialog.visible = false">关 闭</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 选择商品对话框 -->
+    <el-dialog v-model="selectProductDialog.visible" title="选择商品" width="900px" append-to-body>
+      <div class="flex items-center gap-3 mb-4">
+        <el-input v-model="selectProductDialog.productNo" placeholder="请输入商品编号" style="width: 200px" clearable />
+        <el-input v-model="selectProductDialog.productName" placeholder="请输入商品名称" style="width: 200px" clearable />
+        <el-button type="primary" @click="searchProducts">搜 索</el-button>
+      </div>
+      <el-table v-loading="selectProductDialog.loading" :data="productList" border @selection-change="handleProductSelectionChange">
+        <el-table-column type="selection" width="50" align="center" />
+        <el-table-column label="商品编号" align="center" prop="productNo" width="120" />
+        <el-table-column label="商品名称" align="center" prop="itemName" min-width="180" show-overflow-tooltip />
+        <el-table-column label="商品图片" align="center" width="100">
+          <template #default="scope">
+            <el-image v-if="scope.row.productImage" :src="scope.row.productImage" fit="cover" style="width: 60px; height: 60px" />
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="价格" align="center" width="100">
+          <template #default="scope">
+            {{ scope.row.minSellingPrice || scope.row.marketPrice || '-' }}
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="selectProductDialog.total > 0" v-model:page="selectProductDialog.pageNum" v-model:limit="selectProductDialog.pageSize" :total="selectProductDialog.total" @pagination="getProductList" />
+      <template #footer>
+        <el-button type="primary" @click="confirmSelectProducts">确 定</el-button>
+        <el-button @click="selectProductDialog.visible = false">取 消</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 选择品牌对话框 -->
+    <el-dialog v-model="selectBrandDialog.visible" title="选择品牌" width="900px" append-to-body>
+      <div class="flex items-center gap-3 mb-4">
+        <el-input v-model="selectBrandDialog.keyword" placeholder="请输入品牌编号或名称搜索" style="width: 300px" clearable />
+        <el-button type="primary" @click="searchBrands">搜 索</el-button>
+      </div>
+      <el-table v-loading="selectBrandDialog.loading" :data="brandList" border @selection-change="handleBrandSelectionChange">
+        <el-table-column type="selection" width="50" align="center" />
+        <el-table-column label="品牌编号" align="center" prop="brandNo" width="120" />
+        <el-table-column label="品牌名称" align="center" prop="brandName" min-width="180" show-overflow-tooltip />
+        <el-table-column label="品牌图片" align="center" width="100">
+          <template #default="scope">
+            <el-image v-if="scope.row.brandLogo" :src="scope.row.brandLogo" fit="contain" style="width: 60px; height: 40px" />
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="selectBrandDialog.total > 0" v-model:page="selectBrandDialog.pageNum" v-model:limit="selectBrandDialog.pageSize" :total="selectBrandDialog.total" @pagination="getBrandList" />
+      <template #footer>
+        <el-button type="primary" @click="confirmSelectBrands">确 定</el-button>
+        <el-button @click="selectBrandDialog.visible = false">取 消</el-button>
+      </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>
+  </div>
+</template>
+
+<script setup name="StyleDesign" lang="ts">
+import { listSiteFloor, getSiteFloor, addSiteFloor, updateSiteFloor, delSiteFloor } from '@/api/product/siteFloor';
+import { SiteFloorVO, SiteFloorForm, SiteFloorQuery } from '@/api/product/siteFloor/types';
+import { listSiteFloorLink, addSiteFloorLink, delSiteFloorLink } from '@/api/product/siteFloorLink';
+import { listProduct } from '@/api/product/base';
+import { listBrand } from '@/api/product/brand';
+import { useRouter, useRoute } from 'vue-router';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+const route = useRoute();
+
+// 路由参数
+const siteId = computed(() => route.query.siteId as string | undefined);
+const siteName = computed(() => route.query.siteName as string | undefined);
+
+const floorList = ref<SiteFloorVO[]>([]);
+const loading = ref(false);
+const total = ref(0);
+const dialogVisible = ref(false);
+const dialogTitle = ref('添加楼层');
+const submitLoading = ref(false);
+
+const queryParams = ref<SiteFloorQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  siteId: siteId.value
+});
+
+const formRef = ref<ElFormInstance>();
+const form = ref<SiteFloorForm>({
+  name: undefined,
+  link: undefined,
+  imageUrl: undefined,
+  isShow: '1',
+  sort: 0,
+  siteId: siteId.value
+});
+
+const rules = {
+  name: [{ required: true, message: '请输入楼层名称', trigger: 'blur' }],
+  isShow: [{ required: true, message: '请选择是否显示', trigger: 'change' }]
+};
+
+// 配置商品弹框
+const productDialog = reactive({
+  visible: false,
+  loading: false,
+  floorId: null as string | number | null,
+  floorName: ''
+});
+const linkedProducts = ref<any[]>([]);
+
+// 配置品牌弹框
+const brandDialog = reactive({
+  visible: false,
+  loading: false,
+  floorId: null as string | number | null,
+  floorName: ''
+});
+const linkedBrands = ref<any[]>([]);
+
+// 选择商品弹框
+const selectProductDialog = reactive({
+  visible: false,
+  loading: false,
+  productNo: '',
+  productName: '',
+  pageNum: 1,
+  pageSize: 10,
+  total: 0
+});
+const productList = ref<any[]>([]);
+const selectedProducts = ref<any[]>([]);
+
+// 选择品牌弹框
+const selectBrandDialog = reactive({
+  visible: false,
+  loading: false,
+  keyword: '',
+  pageNum: 1,
+  pageSize: 10,
+  total: 0
+});
+const brandList = ref<any[]>([]);
+const selectedBrands = ref<any[]>([]);
+
+// 导入商品弹框
+const importProductDialog = reactive({
+  visible: false,
+  productNos: ''
+});
+
+/** 获取楼层列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    const res = await listSiteFloor({ ...queryParams.value, siteId: siteId.value });
+    floorList.value = res.rows;
+    total.value = res.total;
+  } catch (error) {
+    console.error('获取楼层列表失败:', error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 搜索 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 重置 */
+const resetQuery = () => {
+  queryParams.value = {
+    pageNum: 1,
+    pageSize: 10,
+    name: undefined,
+    siteId: siteId.value
+  };
+  getList();
+};
+
+/** 返回 */
+const handleBack = () => {
+  router.push('/customerOperation/vipSite');
+};
+
+/** 添加楼层 */
+const handleAdd = () => {
+  form.value = { name: undefined, link: undefined, imageUrl: undefined, isShow: '1', sort: 0, siteId: siteId.value };
+  dialogTitle.value = '添加楼层';
+  dialogVisible.value = true;
+  nextTick(() => formRef.value?.clearValidate());
+};
+
+/** 编辑楼层 */
+const handleEdit = async (row: SiteFloorVO) => {
+  try {
+    const res = await getSiteFloor(row.id);
+    console.log('res.data', res.data);
+    form.value = { ...res.data };
+    dialogTitle.value = '编辑楼层';
+    dialogVisible.value = true;
+    nextTick(() => formRef.value?.clearValidate());
+  } catch (error) {
+    proxy?.$modal.msgError('获取楼层信息失败');
+  }
+};
+
+/** 切换显示状态 */
+const handleToggleShow = async (row: SiteFloorVO) => {
+  const newIsShow = row.isShow === '1' ? '0' : '1';
+  const text = newIsShow === '1' ? '显示' : '隐藏';
+  try {
+    await proxy?.$modal.confirm(`确认要将楼层"${row.name}"设置为"${text}"吗?`);
+    await updateSiteFloor({ id: row.id, isShow: newIsShow, siteId: row.siteId });
+    proxy?.$modal.msgSuccess('操作成功');
+    getList();
+  } catch {}
+};
+
+/** 删除楼层 */
+const handleDelete = async (row: SiteFloorVO) => {
+  try {
+    await proxy?.$modal.confirm(`确认要删除楼层"${row.name}"吗?`);
+    await delSiteFloor(row.id);
+    proxy?.$modal.msgSuccess('删除成功');
+    getList();
+  } catch {}
+};
+
+/** 提交表单 */
+const handleSubmit = async () => {
+  await formRef.value?.validate();
+  submitLoading.value = true;
+  try {
+    if (form.value.id) {
+      await updateSiteFloor(form.value as SiteFloorForm);
+      proxy?.$modal.msgSuccess('修改成功');
+    } else {
+      await addSiteFloor(form.value as SiteFloorForm);
+      proxy?.$modal.msgSuccess('添加成功');
+    }
+    dialogVisible.value = false;
+    getList();
+  } catch (error) {
+    console.error('提交失败:', error);
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+/** 配置商品 */
+const handleConfigProduct = (row: SiteFloorVO) => {
+  productDialog.floorId = row.id;
+  productDialog.floorName = row.name;
+  productDialog.visible = true;
+  getLinkedProducts();
+};
+
+/** 获取已关联商品 */
+const getLinkedProducts = async () => {
+  if (!productDialog.floorId) return;
+  productDialog.loading = true;
+  try {
+    const res = await listSiteFloorLink({ floorId: productDialog.floorId, type: 1, pageNum: 1, pageSize: 1000 });
+    const links = res.rows || [];
+    if (links.length === 0) {
+      linkedProducts.value = [];
+      return;
+    }
+    const productIds = links.map((item: any) => item.productId).filter(Boolean);
+    if (productIds.length > 0) {
+      const productRes = await listProduct({ ids: productIds.join(','), pageSize: 1000 });
+      const productMap = new Map<string, any>((productRes.rows || []).map((p: any) => [String(p.id), p]));
+      linkedProducts.value = links.map((item: any) => {
+        const p = productMap.get(String(item.productId)) || {};
+        return { ...item, productName: p.itemName || item.productName || '', productImage: p.productImage || item.productImage || '', price: p.minSellingPrice || p.marketPrice || '' };
+      });
+    } else {
+      linkedProducts.value = links;
+    }
+  } catch (error) {
+    console.error('加载关联商品失败', error);
+  } finally {
+    productDialog.loading = false;
+  }
+};
+
+/** 删除关联商品 */
+const handleRemoveLinkedProduct = (row: any) => {
+  proxy?.$modal.confirm('是否确认删除该商品?').then(() => {
+    delSiteFloorLink(row.id).then(() => {
+      proxy?.$modal.msgSuccess('删除成功');
+      getLinkedProducts();
+    });
+  });
+};
+
+/** 新增商品 */
+const handleAddProduct = () => {
+  selectProductDialog.productNo = '';
+  selectProductDialog.productName = '';
+  selectProductDialog.pageNum = 1;
+  selectProductDialog.visible = true;
+  getProductList();
+};
+
+/** 导入商品 */
+const handleImportProduct = () => {
+  importProductDialog.productNos = '';
+  importProductDialog.visible = true;
+};
+
+/** 确认导入商品 */
+const confirmImportProducts = async () => {
+  const input = importProductDialog.productNos.trim();
+  if (!input) {
+    proxy?.$modal.msgWarning('请输入商品编号');
+    return;
+  }
+  const productNos = input.split(/[,,\s\n]+/).map((s: string) => s.trim()).filter(Boolean);
+  if (productNos.length === 0) {
+    proxy?.$modal.msgWarning('请输入有效的商品编号');
+    return;
+  }
+  const existingProductNos = linkedProducts.value.map((item: any) => item.productNo);
+  const newProductNos = productNos.filter((no: string) => !existingProductNos.includes(no));
+  if (newProductNos.length === 0) {
+    proxy?.$modal.msgWarning('所有商品编号已存在,请勿重复添加');
+    return;
+  }
+  try {
+    const productRes = await listProduct({ productNos: newProductNos.join(','), pageSize: 1000 });
+    const productMap = new Map<string, any>((productRes.rows || []).map((p: any) => [p.productNo, p]));
+    for (const productNo of newProductNos) {
+      const product = productMap.get(productNo);
+      if (!product) {
+        console.warn(`商品编号 ${productNo} 不存在`);
+        continue;
+      }
+      await addSiteFloorLink({ floorId: productDialog.floorId, type: 1, productId: product.id, productNo, sort: 0, status: '0' });
+    }
+    const skipped = productNos.length - newProductNos.length;
+    proxy?.$modal.msgSuccess(skipped > 0 ? `导入成功,${skipped}个商品已存在被跳过` : '导入成功');
+    importProductDialog.visible = false;
+    getLinkedProducts();
+  } catch (error) {
+    proxy?.$modal.msgError('导入失败');
+  }
+};
+
+/** 获取商品列表 */
+const getProductList = async () => {
+  selectProductDialog.loading = true;
+  try {
+    const res = await listProduct({
+      productNo: selectProductDialog.productNo,
+      keyword: selectProductDialog.productName,
+      pageNum: selectProductDialog.pageNum,
+      pageSize: selectProductDialog.pageSize
+    });
+    productList.value = res.rows || [];
+    selectProductDialog.total = res.total || 0;
+  } catch (error) {
+    console.error('加载商品列表失败', error);
+  } finally {
+    selectProductDialog.loading = false;
+  }
+};
+
+/** 搜索商品 */
+const searchProducts = () => {
+  selectProductDialog.pageNum = 1;
+  getProductList();
+};
+
+/** 商品选择变化 */
+const handleProductSelectionChange = (selection: any[]) => {
+  selectedProducts.value = selection;
+};
+
+/** 确认选择商品 */
+const confirmSelectProducts = async () => {
+  if (selectedProducts.value.length === 0) {
+    proxy?.$modal.msgWarning('请选择商品');
+    return;
+  }
+  const existingProductNos = linkedProducts.value.map((item: any) => item.productNo);
+  const duplicates = selectedProducts.value.filter((p: any) => existingProductNos.includes(p.productNo));
+  if (duplicates.length > 0) {
+    const names = duplicates.map((p: any) => p.itemName || p.productNo).join('、');
+    proxy?.$modal.msgWarning(`商品 ${names} 已存在,请勿重复添加`);
+    return;
+  }
+  try {
+    for (const product of selectedProducts.value) {
+      await addSiteFloorLink({ floorId: productDialog.floorId, type: 1, productId: product.id, productNo: product.productNo, sort: 0, status: '0' });
+    }
+    proxy?.$modal.msgSuccess('添加成功');
+    selectProductDialog.visible = false;
+    getLinkedProducts();
+  } catch (error) {
+    proxy?.$modal.msgError('添加失败');
+  }
+};
+
+/** 配置品牌 */
+const handleConfigBrand = (row: SiteFloorVO) => {
+  brandDialog.floorId = row.id;
+  brandDialog.floorName = row.name;
+  brandDialog.visible = true;
+  getLinkedBrands();
+};
+
+/** 获取已关联品牌 */
+const getLinkedBrands = async () => {
+  if (!brandDialog.floorId) return;
+  brandDialog.loading = true;
+  try {
+    const res = await listSiteFloorLink({ floorId: brandDialog.floorId, type: 2, pageNum: 1, pageSize: 1000 });
+    const links = res.rows || [];
+    if (links.length === 0) {
+      linkedBrands.value = [];
+      return;
+    }
+    const brandIds = links.map((item: any) => item.brandId).filter(Boolean);
+    const brandMap = new Map<string, any>();
+    if (brandIds.length > 0) {
+      const brandRes = await listBrand({ ids: brandIds.join(','), pageSize: 200 });
+      (brandRes.rows || []).forEach((b: any) => brandMap.set(String(b.id), b));
+    }
+    linkedBrands.value = links.map((item: any) => {
+      const brand = brandMap.get(String(item.brandId)) || {};
+      return { ...item, brandName: brand.brandName || item.brandName || '', brandLogo: brand.brandLogo || item.brandLogo || '' };
+    });
+  } catch (error) {
+    console.error('加载关联品牌失败', error);
+  } finally {
+    brandDialog.loading = false;
+  }
+};
+
+/** 删除关联品牌 */
+const handleRemoveLinkedBrand = (row: any) => {
+  proxy?.$modal.confirm('是否确认删除该品牌?').then(() => {
+    delSiteFloorLink(row.id).then(() => {
+      proxy?.$modal.msgSuccess('删除成功');
+      getLinkedBrands();
+    });
+  });
+};
+
+/** 新增品牌 */
+const handleAddBrand = () => {
+  selectBrandDialog.keyword = '';
+  selectBrandDialog.pageNum = 1;
+  selectBrandDialog.visible = true;
+  getBrandList();
+};
+
+/** 获取品牌列表 */
+const getBrandList = async () => {
+  selectBrandDialog.loading = true;
+  try {
+    const res = await listBrand({ keyword: selectBrandDialog.keyword, pageNum: selectBrandDialog.pageNum, pageSize: selectBrandDialog.pageSize });
+    brandList.value = res.rows || [];
+    selectBrandDialog.total = res.total || 0;
+  } catch (error) {
+    console.error('加载品牌列表失败', error);
+  } finally {
+    selectBrandDialog.loading = false;
+  }
+};
+
+/** 搜索品牌 */
+const searchBrands = () => {
+  selectBrandDialog.pageNum = 1;
+  getBrandList();
+};
+
+/** 品牌选择变化 */
+const handleBrandSelectionChange = (selection: any[]) => {
+  selectedBrands.value = selection;
+};
+
+/** 确认选择品牌 */
+const confirmSelectBrands = async () => {
+  if (selectedBrands.value.length === 0) {
+    proxy?.$modal.msgWarning('请选择品牌');
+    return;
+  }
+  const existingBrandNos = linkedBrands.value.map((item: any) => item.brandNo);
+  const duplicates = selectedBrands.value.filter((b: any) => existingBrandNos.includes(b.brandNo));
+  if (duplicates.length > 0) {
+    const names = duplicates.map((b: any) => b.brandName || b.brandNo).join('、');
+    proxy?.$modal.msgWarning(`品牌 ${names} 已存在,请勿重复添加`);
+    return;
+  }
+  try {
+    for (const brand of selectedBrands.value) {
+      await addSiteFloorLink({ floorId: brandDialog.floorId, type: 2, brandId: brand.id, brandNo: brand.brandNo, sort: 0, status: '0' });
+    }
+    proxy?.$modal.msgSuccess('添加成功');
+    selectBrandDialog.visible = false;
+    getLinkedBrands();
+  } catch (error) {
+    proxy?.$modal.msgError('添加失败');
+  }
+};
+
+onMounted(() => {
+  getList();
+});
+</script>

+ 8 - 4
src/views/platform/industrial/brandFloor/index.vue

@@ -156,7 +156,8 @@
     <!-- 选择商品对话框 -->
     <el-dialog v-model="selectProductDialog.visible" title="选择商品" width="900px" append-to-body>
       <div class="select-dialog-header">
-        <el-input v-model="selectProductDialog.keyword" placeholder="请输入商品编号或名称搜索" style="width: 300px" />
+        <el-input v-model="selectProductDialog.productNo" placeholder="请输入商品编号" style="width: 200px" clearable />
+        <el-input v-model="selectProductDialog.productName" placeholder="请输入商品名称" style="width: 200px" clearable />
         <el-button type="primary" @click="searchProducts">搜 索</el-button>
       </div>
       <el-table v-loading="selectProductDialog.loading" :data="productList" border @selection-change="handleProductSelectionChange">
@@ -305,7 +306,8 @@ const linkedBrands = ref<any[]>([]);
 const selectProductDialog = reactive({
   visible: false,
   loading: false,
-  keyword: '',
+  productNo: '',
+  productName: '',
   pageNum: 1,
   pageSize: 10,
   total: 0
@@ -493,7 +495,8 @@ const handleRemoveLinked = (row: any) => {
 
 // 新增商品
 const handleAddProduct = () => {
-  selectProductDialog.keyword = '';
+  selectProductDialog.productNo = '';
+  selectProductDialog.productName = '';
   selectProductDialog.pageNum = 1;
   selectProductDialog.visible = true;
   getProductList();
@@ -567,7 +570,8 @@ const getProductList = async () => {
   selectProductDialog.loading = true;
   try {
     const res = await listProduct({
-      keyword: selectProductDialog.keyword,
+      productNo: selectProductDialog.productNo,
+      keyword: selectProductDialog.productName,
       pageNum: selectProductDialog.pageNum,
       pageSize: selectProductDialog.pageSize
     });