فهرست منبع

feat(product): 更新商品基础信息页面功能

- 添加商品简介字段并设为必填项
- 修改商品编号显示逻辑,仅在编辑模式下显示
- 优化品牌、单位、税率下拉选项显示格式,增加编号信息
- 新增供应价格相关字段包括供应价、供应有效时间和包邮状态
- 添加自定义属性功能支持动态增减属性项
- 更新商品属性绑定逻辑,使用productAttributesName替代id
- 强化商品主图、轮播图和详情页的必填验证
- 调整供应商字段映射,统一使用supplierNo标识
- 移除采购价必填校验并调整相关逻辑
- 新增税收编码树形选择组件优化选择体验
- 添加三级分类级联选择组件提升类目选择效率
- 优化图片上传组件界面交互体验
肖路 1 ماه پیش
والد
کامیت
91d4eac7a3

+ 45 - 0
src/api/product/base/types.ts

@@ -578,6 +578,21 @@ export interface BaseForm extends BaseEntity {
    * 商品轮播图URL(逗号分隔)
    */
   imageUrl?: string;
+
+  /**
+   * 供应价
+   */
+  supplyPrice?: number;
+
+  /**
+   * 供应有效时间
+   */
+  supplyValidityPeriod?: string;
+
+  /**
+   * 是否包邮 0 不包邮,1包邮
+   */
+  supplyPostStatus?: number;
 }
 
 export interface BaseQuery extends PageQuery {
@@ -720,6 +735,36 @@ export interface BaseQuery extends PageQuery {
    * 商品ID列表(按指定ID集合查询)
    */
   ids?: Array<string | number>;
+
+  /**
+   * 游标分页 - 最后一条记录ID
+   */
+  lastSeenId?: string | number;
+
+  /**
+   * 游标分页 - 第一条记录ID
+   */
+  firstSeenId?: string | number;
+
+  /**
+   * 翻页方向:0=上一页,1=下一页
+   */
+  way?: number;
+
+  /**
+   * 查询类型
+   */
+  queryType?: string | number;
+
+  /**
+   * 业务类型
+   */
+  type?: number;
+
+  /**
+   * 审核状态
+   */
+  auditStatus?: number;
 }
 /**
  * 状态数量统计视图对象

+ 13 - 0
src/api/system/taxCode/index.ts

@@ -16,6 +16,19 @@ export const listTaxCode = (query?: TaxCodeQuery): AxiosPromise<TaxCodeVO[]> =>
   });
 };
 
+/**
+ * 获取税收编码树
+ * @param query
+ * @returns {*}
+ */
+export const getTaxCodeTree = (query?: TaxCodeQuery): AxiosPromise<any[]> => {
+  return request({
+    url: '/system/taxCode/tree',
+    method: 'get',
+    params: query
+  });
+};
+
 /**
  * 查询税收编码详细
  * @param id

+ 10 - 0
src/api/system/taxCode/types.ts

@@ -49,6 +49,11 @@ export interface TaxCodeVO {
    */
   remark: string;
 
+  /**
+   * 是否有下级 0不存在 1存在
+   */
+  isBottom: string;
+
 }
 
 export interface TaxCodeForm extends BaseEntity {
@@ -146,6 +151,11 @@ export interface TaxCodeQuery extends PageQuery {
    */
   status?: string;
 
+  /**
+   * 是否有下级 0不存在 1存在
+   */
+  isBottom?: string;
+
   /**
    * 平台标识
    */

+ 183 - 0
src/components/CategoryCascadeSelect/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div style="display: flex; gap: 8px; align-items: center; width: 100%">
+    <div style="flex: 1">
+      <el-select
+        :model-value="topCategoryId"
+        placeholder="一级类目"
+        filterable
+        remote
+        clearable
+        :remote-method="handleFirstLevelSearch"
+        :loading="loading"
+        style="width: 100%"
+        @update:model-value="handleFirstLevelChange"
+      >
+        <el-option v-for="item in firstOptions" :key="item.id" :label="item.categoryName" :value="item.id" />
+      </el-select>
+    </div>
+    <div style="flex: 1">
+      <el-select
+        :model-value="mediumCategoryId"
+        placeholder="二级类目"
+        filterable
+        remote
+        clearable
+        :remote-method="handleSecondLevelSearch"
+        :loading="loading"
+        style="width: 100%"
+        :disabled="!topCategoryId"
+        @update:model-value="handleSecondLevelChange"
+      >
+        <el-option v-for="item in secondOptions" :key="item.id" :label="item.categoryName" :value="item.id" />
+      </el-select>
+    </div>
+    <div style="flex: 1">
+      <el-select
+        :model-value="bottomCategoryId"
+        placeholder="三级类目"
+        filterable
+        remote
+        clearable
+        :remote-method="handleThirdLevelSearch"
+        :loading="loading"
+        style="width: 100%"
+        :disabled="!mediumCategoryId"
+        @update:model-value="(val) => emit('update:bottomCategoryId', val)"
+      >
+        <el-option v-for="item in thirdOptions" :key="item.id" :label="item.categoryName" :value="item.id" />
+      </el-select>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { listCategory } from '@/api/product/category';
+import { CategoryVO } from '@/api/product/category/types';
+
+const props = defineProps<{
+  topCategoryId?: string | number;
+  mediumCategoryId?: string | number;
+  bottomCategoryId?: string | number;
+}>();
+
+const emit = defineEmits<{
+  (e: 'update:topCategoryId', val: string | number | undefined): void;
+  (e: 'update:mediumCategoryId', val: string | number | undefined): void;
+  (e: 'update:bottomCategoryId', val: string | number | undefined): void;
+}>();
+
+const loading = ref(false);
+const firstOptions = ref<CategoryVO[]>([]);
+const secondOptions = ref<CategoryVO[]>([]);
+const thirdOptions = ref<CategoryVO[]>([]);
+
+const firstSearchTimer = ref<ReturnType<typeof setTimeout> | null>(null);
+const secondSearchTimer = ref<ReturnType<typeof setTimeout> | null>(null);
+const thirdSearchTimer = ref<ReturnType<typeof setTimeout> | null>(null);
+
+/** 加载一级分类 */
+const loadFirst = async (keyword?: string) => {
+  loading.value = true;
+  try {
+    const res = await listCategory({ classLevel: 1, categoryName: keyword });
+    firstOptions.value = res.data || (res as any).rows || [];
+  } catch (e) {
+    console.error('加载一级分类失败', e);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 加载二级分类 */
+const loadSecond = async (parentId?: string | number, keyword?: string) => {
+  if (!parentId) {
+    secondOptions.value = [];
+    return;
+  }
+  loading.value = true;
+  try {
+    const res = await listCategory({ parentId, classLevel: 2, categoryName: keyword });
+    secondOptions.value = res.data || (res as any).rows || [];
+  } catch (e) {
+    console.error('加载二级分类失败', e);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 加载三级分类 */
+const loadThird = async (parentId?: string | number, keyword?: string) => {
+  if (!parentId) {
+    thirdOptions.value = [];
+    return;
+  }
+  loading.value = true;
+  try {
+    const res = await listCategory({ parentId, classLevel: 3, categoryName: keyword });
+    thirdOptions.value = res.data || (res as any).rows || [];
+  } catch (e) {
+    console.error('加载三级分类失败', e);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 一级分类远程搜索(防抖) */
+const handleFirstLevelSearch = (query: string) => {
+  if (firstSearchTimer.value) clearTimeout(firstSearchTimer.value);
+  firstSearchTimer.value = setTimeout(() => loadFirst(query || undefined), 300);
+};
+
+/** 二级分类远程搜索(防抖) */
+const handleSecondLevelSearch = (query: string) => {
+  if (secondSearchTimer.value) clearTimeout(secondSearchTimer.value);
+  secondSearchTimer.value = setTimeout(() => loadSecond(props.topCategoryId, query || undefined), 300);
+};
+
+/** 三级分类远程搜索(防抖) */
+const handleThirdLevelSearch = (query: string) => {
+  if (thirdSearchTimer.value) clearTimeout(thirdSearchTimer.value);
+  thirdSearchTimer.value = setTimeout(() => loadThird(props.mediumCategoryId, query || undefined), 300);
+};
+
+/** 一级分类改变 */
+const handleFirstLevelChange = (val: string | number | undefined) => {
+  emit('update:topCategoryId', val);
+  emit('update:mediumCategoryId', undefined);
+  emit('update:bottomCategoryId', undefined);
+  secondOptions.value = [];
+  thirdOptions.value = [];
+  if (val) {
+    loadSecond(val);
+  }
+};
+
+/** 二级分类改变 */
+const handleSecondLevelChange = (val: string | number | undefined) => {
+  emit('update:mediumCategoryId', val);
+  emit('update:bottomCategoryId', undefined);
+  thirdOptions.value = [];
+  if (val) {
+    loadThird(val);
+  }
+};
+
+/** 重置组件状态(外部可调用) */
+const reset = () => {
+  secondOptions.value = [];
+  thirdOptions.value = [];
+};
+
+/** 初始化:加载一级分类;若已有值则加载对应下级 */
+onMounted(async () => {
+  await loadFirst();
+  if (props.topCategoryId) {
+    await loadSecond(props.topCategoryId);
+  }
+  if (props.mediumCategoryId) {
+    await loadThird(props.mediumCategoryId);
+  }
+});
+
+defineExpose({ reset });
+</script>

+ 16 - 11
src/components/TaxCodeSelect/index.vue

@@ -12,11 +12,10 @@
         <div class="tax-tree-panel">
           <el-tree
             ref="treeRef"
+            :data="treeData"
             :props="treeProps"
             node-key="id"
             highlight-current
-            lazy
-            :load="loadNode"
             :expand-on-click-node="false"
             @node-click="handleNodeClick"
             @node-dblclick="handleTreeDblClick"
@@ -88,7 +87,7 @@
 </template>
 
 <script setup lang="ts">
-import { listTaxCode, getTaxCode } from '@/api/system/taxCode';
+import { listTaxCode, getTaxCode, getTaxCodeTree } from '@/api/system/taxCode';
 import { TaxCodeVO, TaxCodeQuery } from '@/api/system/taxCode/types';
 import useDialog from '@/hooks/useDialog';
 
@@ -101,8 +100,9 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const dialog = useDialog({ title: '税收编码选择  请双击选择税收编码' });
 
 const treeRef = ref<ElTreeInstance>();
-const treeProps = { label: 'name' };
+const treeProps = { label: 'label', children: 'children' };
 const currentEchoId = ref<string | number | undefined>();
+const treeData = ref<any[]>([]);
 
 const listData = ref<TaxCodeVO[]>([]);
 const loading = ref(false);
@@ -115,17 +115,18 @@ const queryParams = ref<TaxCodeQuery>({
   parentId: undefined,
   name: undefined,
   taxationNo: undefined,
+
   params: {}
 });
 
-/** 懒加载树节点 */
-const loadNode = async (node: any, resolve: (data: any[]) => void) => {
-  const parentId = node.level === 0 ? 0 : node.data.id;
+/** 加载树形数据 */
+const loadTreeData = async () => {
   try {
-    const res = await listTaxCode({ parentId, pageNum: 1, pageSize: 9999, params: {} });
-    resolve(res.rows ?? []);
-  } catch {
-    resolve([]);
+    const res = await getTaxCodeTree();
+    treeData.value = res.data ?? [];
+  } catch (error) {
+    console.error('加载税收编码树失败:', error);
+    treeData.value = [];
   }
 };
 
@@ -170,6 +171,7 @@ const getList = async () => {
   loading.value = true;
   try {
     const res = await listTaxCode(queryParams.value);
+    // 后端已按 isBottom='0' 过滤,仅返回无下级的叶子节点
     listData.value = res.rows ?? [];
     total.value = res.total ?? 0;
   } finally {
@@ -203,6 +205,7 @@ const handleSearch = () => {
 
 /** 双击行选择 */
 const handleRowDblClick = (row: TaxCodeVO) => {
+  if (row.isBottom !== '0') return;
   emit('select', row);
   dialog.closeDialog();
 };
@@ -217,6 +220,7 @@ const open = (id?: string | number) => {
     parentId: undefined,
     name: undefined,
     taxationNo: undefined,
+    isBottom: '0',
     params: {}
   };
   dialog.openDialog();
@@ -226,6 +230,7 @@ watch(
   () => dialog.visible.value,
   async (val) => {
     if (val) {
+      await loadTreeData();
       await getList();
       if (currentEchoId.value) {
         await echoById(currentEchoId.value);

+ 49 - 4
src/components/upload-image/index.vue

@@ -11,9 +11,12 @@
             <div class="w-full h-full flex items-center justify-center">
               <el-image class="w-full h-full" :src="imagesData[0].indexOf('data:image') != -1 ? imagesData[0] : img(imagesData[0])"></el-image>
             </div>
+            <!-- 悬停操作层 -->
             <div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60 operation">
-              <icon name="element ZoomIn" color="#fff" size="18px" class="mr-[10px]" @click="previewImage(imagesData, 0)" />
-              <icon name="element Delete" color="#fff" size="18px" @click.stop="removeImage" />
+              <div class="index-badge">1</div>
+            
+              <span class="op-text mr-[10px]" @click.stop="previewImage(imagesData, 0)">预览</span>
+              <span class="op-text op-delete" @click.stop="removeImage">删除</span>
             </div>
           </div>
           <div class="w-full h-full flex items-center justify-center flex-col content-wrap" v-else @click="openDialog">
@@ -34,10 +37,12 @@
                 <div class="w-full h-full flex items-center justify-center">
                   <el-image :src="img(item)" fit="contain"></el-image>
                 </div>
+                <!-- 悬停操作层 -->
                 <div class="absolute z-[1] flex flex-col items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60 operation">
+                  <div class="index-badge">{{ index + 1 }}</div>
                   <div class="flex items-center justify-center mb-[6px]">
-                    <icon name="element ZoomIn" color="#fff" size="18px" class="mr-[10px]" @click.stop="previewImage(imagesData, index)" />
-                    <icon name="element Delete" color="#fff" size="18px" @click.stop="removeImage(index)" />
+                    <span class="op-text mr-[10px]" @click.stop="previewImage(imagesData, index)">预览</span>
+                    <span class="op-text op-delete" @click.stop="removeImage(index)">删除</span>
                   </div>
                   <div class="flex items-center justify-center gap-[8px]">
                     <el-icon
@@ -972,6 +977,46 @@ const style = computed(() => {
       display: flex;
     }
   }
+
+  /* 序号角标(仅悬停展示,位于左上角) */
+  .index-badge {
+    position: absolute;
+    top: 4px;
+    left: 4px;
+    z-index: 2;
+    min-width: 18px;
+    height: 18px;
+    padding: 0 5px;
+    border-radius: 3px;
+    background: rgba(0, 0, 0, 0.75);
+    color: #fff;
+    font-size: 11px;
+    line-height: 18px;
+    text-align: center;
+    font-weight: 600;
+    pointer-events: none;
+  }
+
+  /* 悬停操作文字按钮 */
+  .op-text {
+    color: #fff;
+    font-size: 12px;
+    line-height: 1;
+    cursor: pointer;
+    user-select: none;
+
+    &:hover {
+      opacity: 0.85;
+    }
+  }
+
+  .op-delete {
+    color: #ff7875;
+
+    &:hover {
+      color: #f56c6c;
+    }
+  }
 }
 .border-color {
   border-color: #e5e7eb;

+ 22 - 4
src/router/index.ts

@@ -152,10 +152,16 @@ export const constantRoutes: RouteRecordRaw[] = [
         meta: { title: '商品池管理', activeMenu: '/product/pool', noCache: true }
       },
       {
-        path: 'base/review',
-        component: () => import('@/views/product/base/review.vue'),
-        name: 'BaseReview',
-        meta: { title: '商品审核', activeMenu: '/product/base' }
+        path: 'baseAudit/add',
+        component: () => import('@/views/product/baseAudit/add.vue'),
+        name: 'BaseAuditAdd',
+        meta: { title: '商品审核', activeMenu: '/product/baseAudit' }
+      },
+      {
+        path: 'baseAudit/edit/:id',
+        component: () => import('@/views/product/baseAudit/add.vue'),
+        name: 'BaseAuditEdit',
+        meta: { title: '商品审核', activeMenu: '/product/baseAudit' }
       },
       {
         path: 'base/detail/:id',
@@ -180,6 +186,18 @@ export const constantRoutes: RouteRecordRaw[] = [
         component: () => import('@/views/product/protocolInfo/review.vue'),
         name: 'ProtocolReview',
         meta: { title: '协议商品审核', activeMenu: '/product/protocolInfo', noCache: true }
+      },
+      {
+        path: 'poolLinkAudit',
+        component: () => import('@/views/product/poolLinkAudit/index.vue'),
+        name: 'PoolLinkAudit',
+        meta: { title: '修改入池单', activeMenu: '/product/poolAudit', noCache: true }
+      },
+      {
+        path: 'poolAuditReview',
+        component: () => import('@/views/product/poolAuditReview/index.vue'),
+        name: 'PoolAuditReview',
+        meta: { title: '入池单审核', activeMenu: '/product/poolAudit', noCache: true }
       }
     ]
   },

+ 102 - 43
src/views/product/base/add.vue

@@ -158,10 +158,10 @@
             </el-form-item>
 
             <!-- 商品编号 -->
-            <el-row :gutter="20">
+            <el-row :gutter="20" v-if="route.params.id">
               <el-col :span="12">
                 <el-form-item label="商品编号:" prop="productNo">
-                  <el-input v-model="productForm.productNo" placeholder="002169745" maxlength="20" show-word-limit disabled />
+                  <el-input v-model="productForm.productNo" maxlength="20" show-word-limit disabled />
                 </el-form-item>
               </el-col>
               <el-col :span="12">
@@ -229,7 +229,7 @@
                     :loading="brandLoading"
                     class="w-full"
                   >
-                    <el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id" />
+                    <el-option v-for="item in brandOptions" :key="item.id" :label="`${item.brandNo},${item.brandName}`" :value="item.id" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -243,7 +243,7 @@
                     class="w-full"
                     :disabled="productForm.productReviewStatus === 1"
                   >
-                    <el-option v-for="option in unitOptions" :key="option.id" :label="option.unitName" :value="option.id" />
+                    <el-option v-for="option in unitOptions" :key="option.id" :label="`${option.unitNo},${option.unitName}`" :value="option.id" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -270,7 +270,7 @@
               <el-col :span="12">
                 <el-form-item label="税率:" required>
                   <el-select v-model="productForm.taxRate" placeholder="请选择税率" clearable class="w-full">
-                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="option.taxrate" :value="option.taxrate" />
+                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="`${option.taxrateNo},${option.taxrateName}`" :value="option.taxrate" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -311,6 +311,11 @@
               <el-input v-model="productForm.packagingSpec" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
             </el-form-item>
 
+            <!-- 商品简介 -->
+            <el-form-item label="商品简介:">
+              <el-input v-model="productForm.productDescription" type="textarea" :rows="3" placeholder="请输入商品简介" maxlength="500" show-word-limit />
+            </el-form-item>
+
             <!-- 重量 和 体积 -->
             <el-row :gutter="20">
               <el-col :span="12">
@@ -384,13 +389,7 @@
             <el-row :gutter="20">
               <el-col :span="8">
                 <el-form-item label="市场价:" prop="marketPrice" required>
-                  <el-input
-                    v-model="productForm.marketPrice"
-                    type="number"
-                    placeholder="请输入市场价"
-                    :min="0"
-                    @blur="formatPrice('marketPrice')"
-                  />
+                  <el-input v-model="productForm.marketPrice" type="number" placeholder="请输入市场价" :min="0" @blur="formatPrice('marketPrice')" />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
@@ -408,7 +407,7 @@
             <el-row :gutter="20">
               <el-col :span="8">
                 <el-form-item label="最低起订量:" prop="minOrderQuantity" required>
-                  <el-input v-model="productForm.minOrderQuantity" type="number" placeholder="请输入最低起订量" />
+                  <el-input v-model="productForm.minOrderQuantity" min="1" type="number" placeholder="请输入最低起订量" />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
@@ -420,31 +419,74 @@
           </el-form>
         </el-card>
 
-        <!-- 采购价格 -->
+        <!-- 供应价格 -->
         <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
           <template #header>
-            <span class="text-lg font-bold">采购价格</span>
+            <span class="text-lg font-bold">供应价格</span>
           </template>
 
-          <el-form ref="purchasePriceFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+          <el-form label-width="120px" class="product-info-form">
             <el-row :gutter="20">
-              <el-col :span="12">
-                <el-form-item label="采购价:" prop="purchasingPrice" required>
+              <el-col :span="8">
+                <el-form-item label="供应价:">
                   <el-input
-                    v-model="productForm.purchasingPrice"
+                    v-model="productForm.supplyPrice"
                     type="number"
-                    placeholder="请输入采购价"
+                    placeholder="请输入供应价"
                     :min="0"
-                    @blur="formatPrice('purchasingPrice')"
+                    @blur="formatPrice('supplyPrice')"
                   />
                 </el-form-item>
               </el-col>
-
+              <el-col :span="8">
+                <el-form-item label="供应有效时间:">
+                  <el-date-picker
+                    v-model="productForm.supplyValidityPeriod"
+                    type="date"
+                    placeholder="请选择供应有效时间"
+                    value-format="YYYY-MM-DD"
+                    class="w-full"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="是否包邮:">
+                  <el-radio-group v-model="productForm.supplyPostStatus">
+                    <el-radio :value="0">不包邮</el-radio>
+                    <el-radio :value="1">包邮</el-radio>
+                  </el-radio-group>
+                </el-form-item>
+              </el-col>
             </el-row>
           </el-form>
         </el-card>
 
+        <!-- 自定义属性 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <div class="flex items-center justify-between">
+              <span class="text-lg font-bold">自定义属性</span>
+              <el-button type="primary" icon="Plus" size="small" @click="addDiyAttribute">添加属性</el-button>
+            </div>
+          </template>
 
+          <el-form label-width="0px" class="product-info-form">
+            <div v-if="diyAttributesList.length === 0" class="text-center text-gray-400 py-4 text-sm">
+              暂无自定义属性,点击右上角"添加属性"按钮添加
+            </div>
+            <el-row v-for="(item, index) in diyAttributesList" :key="index" :gutter="20" class="mb-2">
+              <el-col :span="11">
+                <el-input v-model="item.attributeKey" placeholder="请输入属性名称" clearable />
+              </el-col>
+              <el-col :span="11">
+                <el-input v-model="item.attributeValue" placeholder="请输入属性值" clearable />
+              </el-col>
+              <el-col :span="2" class="flex items-center">
+                <el-button type="danger" icon="Delete" circle size="small" @click="removeDiyAttribute(index)" />
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
 
         <!-- 商品属性 -->
         <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
@@ -464,8 +506,8 @@
                     >
                       <!-- 下拉选择 -->
                       <el-select
-                        v-if="attributesList[rowIndex * 2 + colIndex - 1].entryMethod === '1'"
-                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        v-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '0'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
                         placeholder="请选择"
                         clearable
                         class="w-full"
@@ -479,8 +521,8 @@
                       </el-select>
                       <!-- 多选 -->
                       <el-select
-                        v-else-if="attributesList[rowIndex * 2 + colIndex - 1].entryMethod === '3'"
-                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        v-else-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '2'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
                         placeholder="请选择"
                         multiple
                         clearable
@@ -496,7 +538,7 @@
                       <!-- 文本输入 -->
                       <el-input
                         v-else
-                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
                         placeholder="请输入"
                         clearable
                       />
@@ -516,19 +558,19 @@
 
           <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
             <!-- 商品主图 -->
-            <el-form-item label="商品主图:">
+            <el-form-item label="商品主图:" required>
               <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
               <div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
             </el-form-item>
 
             <!-- 商品轮播图 -->
-            <el-form-item label="商品轮播图:">
+            <el-form-item label="商品轮播图:" required>
               <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
               <div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
             </el-form-item>
 
             <!-- 商品详情 -->
-            <el-form-item label="商品详情:">
+            <el-form-item label="商品详情:" required>
               <el-tabs v-model="activeDetailTab" type="border-card">
                 <el-tab-pane label="电脑端详情" name="pc">
                   <Editor v-model="productForm.pcDetail" :height="400" />
@@ -703,7 +745,6 @@ import { listInfo } from '@/api/customer/supplierInfo';
 import { InfoVO } from '@/api/customer/supplierInfo/types';
 import { listComStaff } from '@/api/system/comStaff';
 import { ComStaffVO } from '@/api/system/comStaff/types';
-import cache from '@/plugins/cache';
 
 const route = useRoute();
 const router = useRouter();
@@ -737,13 +778,13 @@ const handleTaxCodeSelect = async (row: any) => {
   try {
     const taxRes = await getTaxCode(row.id);
     if (taxRes.data) {
-      taxCodeNo.value = taxRes.data.name || row.name || '';
+      taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
     } else {
-      taxCodeNo.value = row.name || '';
+      taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
     }
   } catch (e) {
     console.error('获取税率编码详情失败:', e);
-    taxCodeNo.value = row.name || '';
+    taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
   }
   // 同时将显示值存入 form,方便编辑回显时直接读取
   (productForm as any).taxationNo = taxCodeNo.value;
@@ -909,6 +950,7 @@ const productForm = reactive<BaseForm>({
   invoiceName: undefined,
   invoiceSpecs: undefined,
   packagingSpec: undefined,
+  productDescription: undefined,
   referenceLink: undefined,
   productWeight: undefined,
   weightUnit: 'kg',
@@ -923,6 +965,9 @@ const productForm = reactive<BaseForm>({
   minSellingPrice: undefined,
   purchasingPrice: undefined,
   maxPurchasePrice: undefined,
+  supplyPrice: undefined,
+  supplyValidityPeriod: undefined,
+  supplyPostStatus: undefined,
   productNature: '1',
   purchasingPersonnel: '1',
   pcDetail: undefined,
@@ -938,11 +983,10 @@ const productRules = {
   // productNo: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
   itemName: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
   brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
-  mainLibraryIntro: [{ required: true, message: '主供应商不能为空', trigger: 'change' }],
   marketPrice: [{ required: true, message: '市场价不能为空', trigger: 'blur' }],
   memberPrice: [{ required: true, message: '平台售价不能为空', trigger: 'blur' }],
   minSellingPrice: [{ required: true, message: '最低售价不能为空', trigger: 'blur' }],
-  purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  // purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
   productNature: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
   purchasingPersonnel: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
   taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
@@ -1207,6 +1251,23 @@ const handleSubmit = async () => {
   try {
     submitLoading.value = true;
 
+    // 校验商品主图、轮播图、详情必填
+    if (!productForm.productImage) {
+      ElMessage.warning('请上传商品主图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!carouselImages.value || carouselImages.value.length === 0) {
+      ElMessage.warning('请上传商品轮播图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!productForm.pcDetail) {
+      ElMessage.warning('请填写电脑端商品详情');
+      submitLoading.value = false;
+      return;
+    }
+
     // 校验价格关系:市场价 > 官网价 > 最低售价
     const midRange = parseFloat(String(productForm.marketPrice));
     const standard = parseFloat(String(productForm.memberPrice));
@@ -1227,8 +1288,6 @@ const handleSubmit = async () => {
     // 准备提交数据,包含定制信息(A10产品名称由前端自动拼接,不传后端)
     const submitProductData: any = {
       ...productForm,
-      // 从浏览器缓存中获取supplierId并提交为supplierNo
-      supplierNo: cache.local.get('supplierId') || '',
       // 将服务保障ID数组转换为逗号分隔字符串
       serviceGuarantee: serviceGuarantees.value.map((id) => String(id)).join(','),
       // 轮播图URL逗号分隔
@@ -1246,13 +1305,13 @@ const handleSubmit = async () => {
     delete submitProductData.a10ProductName;
 
     const auditData: BaseAuditForm = {
-      id: productForm.id,
+      productId: productForm.id,
       productData: JSON.stringify(submitProductData),
       type: 0,
       auditStatus: 0
     };
     if (productForm.id) {
-      await updateBase(auditData);
+      await addBaseAudit(auditData);
       ElMessage.success('修改成功');
     } else {
       await addBaseAudit(auditData);
@@ -1482,13 +1541,13 @@ const loadCategoryAttributes = async (categoryId: string | number) => {
           // 下拉选择
           const options = parseAttributesList(attr.attributesList);
           if (options.length > 0) {
-            productAttributesValues.value[attr.id] = options[0];
+            productAttributesValues.value[attr.productAttributesName] = options[0];
           }
         } else if (attr.entryMethod === '3' && attr.attributesList) {
           // 多选
           const options = parseAttributesList(attr.attributesList);
           if (options.length > 0) {
-            productAttributesValues.value[attr.id] = [options[0]];
+            productAttributesValues.value[attr.productAttributesName] = [options[0]];
           }
         }
       });
@@ -1546,7 +1605,7 @@ const loadProductDetail = async () => {
         try {
           const taxRes = await getTaxCode(rawData.taxationId);
           if (taxRes.data) {
-            taxCodeNo.value = taxRes.data.name || '';
+            taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
           }
         } catch (e) {
           console.error('获取税率编码失败:', e);

+ 60 - 42
src/views/product/base/add1.vue

@@ -158,10 +158,10 @@
             </el-form-item>
 
             <!-- 商品编号 -->
-            <el-row :gutter="20">
+            <el-row :gutter="20" v-if="route.params.id">
               <el-col :span="12">
                 <el-form-item label="商品编号:" prop="productNo">
-                  <el-input v-model="productForm.productNo" placeholder="002169745" maxlength="20" show-word-limit disabled />
+                  <el-input v-model="productForm.productNo" maxlength="20" show-word-limit disabled />
                 </el-form-item>
               </el-col>
               <el-col :span="12">
@@ -229,7 +229,7 @@
                     :loading="brandLoading"
                     class="w-full"
                   >
-                    <el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id" />
+                    <el-option v-for="item in brandOptions" :key="item.id" :label="`${item.brandNo},${item.brandName}`" :value="item.id" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -243,7 +243,7 @@
                     class="w-full"
                     :disabled="productForm.productReviewStatus === 1"
                   >
-                    <el-option v-for="option in unitOptions" :key="option.id" :label="option.unitName" :value="option.id" />
+                    <el-option v-for="option in unitOptions" :key="option.id" :label="`${option.unitNo},${option.unitName}`" :value="option.id" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -270,7 +270,7 @@
               <el-col :span="12">
                 <el-form-item label="税率:" required>
                   <el-select v-model="productForm.taxRate" placeholder="请选择税率" clearable class="w-full">
-                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="option.taxrate" :value="option.taxrate" />
+                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="`${option.taxrateNo},${option.taxrateName}`" :value="option.taxrate" />
                   </el-select>
                 </el-form-item>
               </el-col>
@@ -311,6 +311,18 @@
               <el-input v-model="productForm.packagingSpec" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
             </el-form-item>
 
+            <!-- 商品简介 -->
+            <el-form-item label="商品简介:">
+              <el-input
+                v-model="productForm.productDescription"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入商品简介"
+                maxlength="500"
+                show-word-limit
+              />
+            </el-form-item>
+
             <!-- 重量 和 体积 -->
             <el-row :gutter="20">
               <el-col :span="12">
@@ -346,10 +358,9 @@
               <el-input v-model="productForm.referenceLink" type="textarea" :rows="3" placeholder="请输入参考链接" />
             </el-form-item>
 
-
             <!-- 主供应商 -->
-            <el-form-item label="主供应商:" prop="mainLibraryIntro" required>
-              <el-select v-model="productForm.mainLibraryIntro" placeholder="请选择" clearable class="w-full" value-key="id">
+            <el-form-item label="主供应商:" prop="supplierNo" required>
+              <el-select v-model="(productForm as any).supplierNo" placeholder="请选择" clearable class="w-full" value-key="id">
                 <el-option
                   v-for="option in supplierOptions"
                   :key="option.id"
@@ -392,13 +403,7 @@
             <el-row :gutter="20">
               <el-col :span="8">
                 <el-form-item label="市场价:" prop="marketPrice" required>
-                  <el-input
-                    v-model="productForm.marketPrice"
-                    type="number"
-                    placeholder="请输入市场价"
-                    :min="0"
-                    @blur="formatPrice('marketPrice')"
-                  />
+                  <el-input v-model="productForm.marketPrice" type="number" placeholder="请输入市场价" :min="0" @blur="formatPrice('marketPrice')" />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
@@ -427,7 +432,7 @@
             <el-row :gutter="20">
               <el-col :span="8">
                 <el-form-item label="最低起订量:" prop="minOrderQuantity" required>
-                  <el-input v-model="productForm.minOrderQuantity" type="number" placeholder="请输入最低起订量" />
+                  <el-input v-model="productForm.minOrderQuantity" min="1" type="number" placeholder="请输入最低起订量" />
                 </el-form-item>
               </el-col>
               <el-col :span="8">
@@ -553,8 +558,8 @@
                     >
                       <!-- 下拉选择 -->
                       <el-select
-                        v-if="attributesList[rowIndex * 2 + colIndex - 1].entryMethod === '1'"
-                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        v-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '0'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
                         placeholder="请选择"
                         clearable
                         class="w-full"
@@ -568,8 +573,8 @@
                       </el-select>
                       <!-- 多选 -->
                       <el-select
-                        v-else-if="attributesList[rowIndex * 2 + colIndex - 1].entryMethod === '3'"
-                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        v-else-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '2'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
                         placeholder="请选择"
                         multiple
                         clearable
@@ -585,7 +590,7 @@
                       <!-- 文本输入 -->
                       <el-input
                         v-else
-                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].id]"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
                         placeholder="请输入"
                         clearable
                       />
@@ -605,19 +610,19 @@
 
           <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
             <!-- 商品主图 -->
-            <el-form-item label="商品主图:">
+            <el-form-item label="商品主图:" required>
               <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
               <div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
             </el-form-item>
 
             <!-- 商品轮播图 -->
-            <el-form-item label="商品轮播图:">
+            <el-form-item label="商品轮播图:" required>
               <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
               <div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
             </el-form-item>
 
             <!-- 商品详情 -->
-            <el-form-item label="商品详情:">
+            <el-form-item label="商品详情:" required>
               <el-tabs v-model="activeDetailTab" type="border-card">
                 <el-tab-pane label="电脑端详情" name="pc">
                   <Editor v-model="productForm.pcDetail" :height="400" />
@@ -780,11 +785,7 @@ import {
   getUnitList,
   getTaxRateList
 } from '@/api/product/base';
-import {
-  addBaseAudit,
-  updateBaseAudit,
-  getBaseAudit,
-} from '@/api/product/baseAudit';
+import { addBaseAudit, updateBaseAudit, getBaseAudit } from '@/api/product/baseAudit';
 import { BaseAuditVO, BaseAuditQuery, BaseAuditForm } from '@/api/product/baseAudit/types';
 import { getTaxCode } from '@/api/system/taxCode';
 import { listBrand, getBrand } from '@/api/product/brand';
@@ -825,13 +826,13 @@ const handleTaxCodeSelect = async (row: any) => {
   try {
     const taxRes = await getTaxCode(row.id);
     if (taxRes.data) {
-      taxCodeNo.value = taxRes.data.name || row.name || '';
+      taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
     } else {
-      taxCodeNo.value = row.name || '';
+      taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
     }
   } catch (e) {
     console.error('获取税率编码详情失败:', e);
-    taxCodeNo.value = row.name || '';
+    taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
   }
   // 同时将显示值存入 form,方便编辑回显时直接读取
   (productForm as any).taxationNo = taxCodeNo.value;
@@ -1002,7 +1003,7 @@ const productForm = reactive<BaseForm>({
   weightUnit: 'kg',
   productVolume: undefined,
   volumeUnit: 'm3',
-  mainLibraryIntro: undefined,
+  productDescription: undefined,
   afterSalesService: undefined,
   serviceGuarantee: undefined, // 服务保障ID列表,逗号分隔
   freeInstallation: '0',
@@ -1026,7 +1027,7 @@ const productRules = {
   // productNo: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
   itemName: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
   brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
-  mainLibraryIntro: [{ required: true, message: '主供应商不能为空', trigger: 'change' }],
+  supplierNo: [{ required: true, message: '主供应商不能为空', trigger: 'change' }],
   marketPrice: [{ required: true, message: '市场价不能为空', trigger: 'blur' }],
   memberPrice: [{ required: true, message: '平台售价不能为空', trigger: 'blur' }],
   minSellingPrice: [{ required: true, message: '最低售价不能为空', trigger: 'blur' }],
@@ -1295,6 +1296,23 @@ const handleSubmit = async () => {
   try {
     submitLoading.value = true;
 
+    // 校验商品主图、轮播图、详情必填
+    if (!productForm.productImage) {
+      ElMessage.warning('请上传商品主图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!carouselImages.value || carouselImages.value.length === 0) {
+      ElMessage.warning('请上传商品轮播图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!productForm.pcDetail) {
+      ElMessage.warning('请填写电脑端商品详情');
+      submitLoading.value = false;
+      return;
+    }
+
     // 校验价格关系:市场价 > 官网价 > 最低售价
     const midRange = parseFloat(String(productForm.marketPrice));
     const standard = parseFloat(String(productForm.memberPrice));
@@ -1332,13 +1350,13 @@ const handleSubmit = async () => {
     delete submitProductData.a10ProductName;
 
     const auditData: BaseAuditForm = {
-      id: productForm.id,
+      productId: productForm.id,
       productData: JSON.stringify(submitProductData),
       type: 0,
       auditStatus: 0
     };
     if (productForm.id) {
-      await updateBase(auditData);
+      await addBaseAudit(auditData);
       ElMessage.success('修改成功');
     } else {
       await addBaseAudit(auditData);
@@ -1517,8 +1535,8 @@ const getSupplierOptions = async () => {
     supplierOptions.value = dataList;
     console.log('供应商列表:', supplierOptions.value);
     // 如果有选项且当前没有选中值,设置第一个为默认值
-    if (supplierOptions.value.length > 0 && !productForm.mainLibraryIntro) {
-      productForm.mainLibraryIntro = String(supplierOptions.value[0].id);
+    if (supplierOptions.value.length > 0 && !(productForm as any).supplierNo) {
+      (productForm as any).supplierNo = String(supplierOptions.value[0].id);
     }
   } catch (error) {
     console.error('获取主供应商列表失败:', error);
@@ -1568,13 +1586,13 @@ const loadCategoryAttributes = async (categoryId: string | number) => {
           // 下拉选择
           const options = parseAttributesList(attr.attributesList);
           if (options.length > 0) {
-            productAttributesValues.value[attr.id] = options[0];
+            productAttributesValues.value[attr.productAttributesName] = options[0];
           }
         } else if (attr.entryMethod === '3' && attr.attributesList) {
           // 多选
           const options = parseAttributesList(attr.attributesList);
           if (options.length > 0) {
-            productAttributesValues.value[attr.id] = [options[0]];
+            productAttributesValues.value[attr.productAttributesName] = [options[0]];
           }
         }
       });
@@ -1632,7 +1650,7 @@ const loadProductDetail = async () => {
         try {
           const taxRes = await getTaxCode(rawData.taxationId);
           if (taxRes.data) {
-            taxCodeNo.value = taxRes.data.name || '';
+            taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
           }
         } catch (e) {
           console.error('获取税率编码失败:', e);
@@ -1675,7 +1693,7 @@ const loadProductDetail = async () => {
 
       // 回显售后服务 - 确保类型与下拉选项的id一致(数字类型)
       if (res.data.afterSalesService !== undefined && res.data.afterSalesService !== null) {
-        productForm.afterSalesService = String(res.data.afterSalesService);
+        productForm.afterSalesService = Number(res.data.afterSalesService);
       }
 
       // 回显轮播图

+ 24 - 24
src/views/product/base/index.vue

@@ -39,19 +39,6 @@
               </el-col>
             </el-row>
             <el-row :gutter="20">
-              <el-col :span="6">
-                <el-form-item label="商品分类" prop="bottomCategoryId">
-                  <el-tree-select
-                    v-model="queryParams.bottomCategoryId"
-                    :data="categoryOptions"
-                    :props="{ value: 'id', label: 'label', children: 'children' } as any"
-                    value-key="id"
-                    placeholder="请选择商品分类"
-                    clearable
-                    check-strictly
-                  />
-                </el-form-item>
-              </el-col>
               <el-col :span="6">
                 <el-form-item label="是否自营" prop="isSelf">
                   <el-select v-model="queryParams.isSelf" placeholder="请选择" clearable>
@@ -60,6 +47,16 @@
                   </el-select>
                 </el-form-item>
               </el-col>
+              <el-col :span="6">
+                <el-form-item label="上下架状态" prop="productStatus">
+                  <el-select v-model="queryParams.productStatus" placeholder="请选择" clearable>
+                    <el-option label="已上架" :value="1" />
+                    <el-option label="下架" :value="0" />
+                    <el-option label="上架中" :value="2" />
+                    <el-option label="驳回上架" :value="3" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
               <el-col :span="6">
                 <el-form-item label="审核状态" prop="productReviewStatus">
                   <el-select v-model="queryParams.productReviewStatus" placeholder="请选择" clearable>
@@ -70,19 +67,18 @@
                   </el-select>
                 </el-form-item>
               </el-col>
-              <el-col :span="6">
-                <el-form-item label="上下架状态" prop="productStatus">
-                  <el-select v-model="queryParams.productStatus" placeholder="请选择" clearable>
-                    <el-option label="已上架" :value="1" />
-                    <el-option label="下架" :value="0" />
-                    <el-option label="上架中" :value="2" />
-                    <el-option label="驳回上架" :value="3" />
-                  </el-select>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="18">
+                <el-form-item label="商品分类">
+                  <category-cascade-select
+                    v-model:top-category-id="queryParams.topCategoryId"
+                    v-model:medium-category-id="queryParams.mediumCategoryId"
+                    v-model:bottom-category-id="queryParams.bottomCategoryId"
+                  />
                 </el-form-item>
               </el-col>
-            </el-row>
-            <el-row>
-              <el-col :span="24" class="text-left">
+              <el-col :span="6" class="text-left">
                 <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
                 <el-button icon="Refresh" @click="resetQuery">重置</el-button>
               </el-col>
@@ -410,6 +406,7 @@ const data = reactive<PageData<BaseForm, BaseQuery>>({
     productStatus: undefined,
     lastSeenId: undefined, // 游标分页的lastSeenId
     way: undefined,
+    queryType: undefined,
     params: {}
   },
   rules: {
@@ -517,6 +514,9 @@ const initRouteParams = () => {
   if (route.query.productCategory) {
     queryParams.value.productCategory = Number(route.query.productCategory);
   }
+  if (route.query.queryType !== undefined && route.query.queryType !== '') {
+    queryParams.value.queryType = route.query.queryType as string;
+  }
 };
 
 /** 表单重置 */

+ 0 - 562
src/views/product/base/review.vue

@@ -1,562 +0,0 @@
-<template>
-  <div class="app-container">
-    <!-- 搜索区域 -->
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div v-show="showSearch" class="mb-[10px]">
-        <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" label-width="100px">
-            <el-row :gutter="20">
-              <el-col :span="6">
-                <el-form-item label="商品编号" prop="productNo">
-                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品名称" prop="itemName">
-                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品类别" prop="bottomCategoryId">
-                  <el-tree-select
-                    v-model="queryParams.bottomCategoryId"
-                    :data="categoryOptions"
-                    :props="{ value: 'id', label: 'label', children: 'children' } as any"
-                    value-key="id"
-                    placeholder="请选择商品类别"
-                    clearable
-                    check-strictly
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品品牌" prop="brandName">
-                  <el-select-v2
-                    v-model="queryParams.brandName"
-                    :options="brandOptionsFormatted"
-                    placeholder="请选择商品品牌"
-                    clearable
-                    filterable
-                    :loading="brandLoading"
-                    @visible-change="handleBrandVisibleChange"
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="审核状态" prop="productReviewStatus">
-                  <el-select
-                    v-model="queryParams.productReviewStatus"
-                    placeholder="请选择审核状态"
-                    clearable
-                  >
-                    <el-option label="待采购审核" :value="0" />
-                    <el-option label="审核通过" :value="1" />
-                    <el-option label="驳回" :value="2" />
-                    <el-option label="待营销审核" :value="3" />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item>
-                  <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
-                  <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-card>
-      </div>
-    </transition>
-
-    <!-- 审核商品信息列表 -->
-    <el-card shadow="never" class="table-card">
-      <template #header>
-        <div class="flex items-center justify-between">
-          <span class="font-semibold">审核商品信息列表</span>
-          <div class="flex gap-2">
-            <el-button type="primary" icon="Download" @click="handleExport">导出商品</el-button>
-            <el-button type="success" icon="Upload">导入商品</el-button>
-            <el-button circle icon="Refresh" @click="getList"></el-button>
-          </div>
-        </div>
-      </template>
-
-      <el-table v-loading="loading" border :data="baseList" :height="tableHeight" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="商品图片" align="center" prop="productImage" width="100" >
-          <template #default="scope">
-            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品编号" align="center" prop="productNo" width="120" >
-          <template #default="scope">
-            <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productNo }}</el-link>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品信息" align="center" min-width="250" show-overflow-tooltip>
-          <template #default="scope">
-            <div class="text-left">
-              <div>{{ scope.row.itemName }}</div>
-              <div class="text-gray-500" style="font-size: 12px;">品牌: {{ scope.row.brandName || '-' }}</div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="基本情况" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">商品分类:</span>
-                <span>{{ scope.row.categoryName || '-' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">单位:</span>
-                <span>{{ scope.row.unitName || '-' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="SKU价格" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px">
-              <div>
-                <span class="text-gray-500">市场价:</span>
-                <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">官网价:</span>
-                <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">最低价:</span>
-                <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="成本预算" align="center" width="150">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">采购价:</span>
-                <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">暂估毛利率:</span>
-                <span>{{ scope.row.tempGrossMargin || '0.00' }}%</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品来源" align="center" width="100">
-          <template #default="scope">
-            <span>{{ scope.row.dataSource || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品状态" align="center" width="100">
-          <template #default="scope">
-            <el-tag v-if="scope.row.productReviewStatus === 0" type="info">待采购审核</el-tag>
-            <el-tag v-else-if="scope.row.productReviewStatus === 1" type="success">审核通过</el-tag>
-            <el-tag v-else-if="scope.row.productReviewStatus === 2" type="danger">驳回</el-tag>
-            <el-tag v-else-if="scope.row.productReviewStatus === 3" type="warning">待营销审核</el-tag>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="审核意见" align="center" width="180" show-overflow-tooltip>
-          <template #default="scope">
-            <span>{{ scope.row.reviewComments || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center" width="200" fixed="right" class-name="border-left">
-          <template #default="scope">
-            <div class="flex flex-col gap-1">
-              <!-- 根据审核状态显示不同按钮 -->
-              <template v-if="scope.row.productReviewStatus === 0">
-                <!-- 待采购审核 -->
-                <div class="flex gap-1 justify-center">
-                  <el-link type="primary" :underline="false" @click="handleEdit(scope.row)">编辑</el-link>
-                  <el-link type="success" :underline="false" @click="handlePurchaseReview(scope.row)">采购审核</el-link>
-                </div>
-              </template>
-              <template v-else-if="scope.row.productReviewStatus === 1">
-                <!-- 审核通过:不显示任何按钮 -->
-              </template>
-              <template v-else-if="scope.row.productReviewStatus === 2">
-                <!-- 驳回 -->
-                <div class="flex gap-1 justify-center">
-                  <el-link type="primary" :underline="false" @click="handleEdit(scope.row)">编辑</el-link>
-                  <el-link type="success" :underline="false" @click="handleReSubmit(scope.row)">重新提交</el-link>
-                </div>
-              </template>
-              <template v-else-if="scope.row.productReviewStatus === 3">
-                <!-- 待营销审核 -->
-                <div class="flex gap-1 justify-center">
-                  <el-link type="primary" :underline="false" @click="handleEdit(scope.row)">编辑</el-link>
-                  <el-link type="warning" :underline="false" @click="handleMarketingReview(scope.row)">营销审核</el-link>
-                </div>
-              </template>
-            </div>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <!-- 游标分页控制 -->
-      <pagination
-        v-show="baseList.length > 0"
-        v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize"
-        v-model:way="queryParams.way"
-        :cursor-mode="true"
-        :has-more="hasMore"
-        @pagination="getList"
-      />
-    </el-card>
-
-    <!-- 审核对话框 -->
-    <el-dialog v-model="reviewDialog.visible" :title="reviewDialog.title" width="600px" append-to-body>
-      <el-form ref="reviewFormRef" :model="reviewForm" :rules="reviewRules" label-width="100px">
-        <el-form-item label="商品编号">
-          <el-input v-model="reviewForm.productNo" disabled />
-        </el-form-item>
-        <el-form-item label="商品名称">
-          <el-input v-model="reviewForm.itemName" disabled />
-        </el-form-item>
-        <el-form-item label="审核结果" prop="reviewStatus">
-          <el-radio-group v-model="reviewForm.reviewStatus">
-            <el-radio v-if="reviewForm.productReviewStatus === 0" :label="3">采购审核通过</el-radio>
-            <el-radio v-if="reviewForm.productReviewStatus === 3" :label="1">营销审核通过</el-radio>
-            <el-radio :label="2">审核驳回</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="审核意见" prop="reviewComments">
-          <el-input v-model="reviewForm.reviewComments" type="textarea" :rows="4" placeholder="请输入审核意见" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button @click="reviewDialog.visible = false">取消</el-button>
-          <el-button type="primary" @click="submitReview">确定</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="BaseReview" lang="ts">
-import { listBase, getBase, reviewBase, brandList, categoryTree } from '@/api/product/base';
-import { BaseVO, BaseQuery, BaseForm } from '@/api/product/base/types';
-import { BrandVO } from '@/api/product/brand/types';
-import { categoryTreeVO } from '@/api/product/category/types';
-import { useRouter, useRoute } from 'vue-router';
-
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const router = useRouter();
-const route = useRoute();
-
-const baseList = ref<BaseVO[]>([]);
-const loading = ref(true);
-const showSearch = ref(true);
-const ids = ref<Array<string | number>>([]);
-const single = ref(true);
-const multiple = ref(true);
-const total = ref(0);
-const brandOptions = ref<BrandVO[]>([]);
-const brandLoading = ref(false);
-const brandOptionsFormatted = computed(() => {
-  return brandOptions.value.slice(0, 500).map((item) => ({
-    label: item.brandName,
-    value: item.brandName // review.vue使用brandName作为value
-  }));
-});
-const categoryOptions = ref<categoryTreeVO[]>([]);
-const hasMore = ref(true); // 是否还有更多数据
-const pageHistory = ref([]);
-
-// 动态计算表格高度
-const tableHeight = computed(() => {
-  // 基础高度 = 视口高度 - 顶部导航(84) - 容器padding(16) - 搜索区域 - 卡片header(60) - 分页器(60)
-  const baseHeight = window.innerHeight - 84 - 16;
-  const searchHeight = showSearch.value ? 150 : 10; // 搜索区域高度
-  const cardHeaderHeight = 60; // 卡片header高度
-  const paginationHeight = 60; // 分页器高度
-  return baseHeight - searchHeight - cardHeaderHeight - paginationHeight;
-});
-
-const queryFormRef = ref<ElFormInstance>();
-const reviewFormRef = ref<ElFormInstance>();
-
-// 审核对话框
-const reviewDialog = reactive({
-  visible: false,
-  title: '商品审核'
-});
-
-// 审核表单
-const reviewForm = ref<any>({
-  id: undefined,
-  productNo: '',
-  itemName: '',
-  reviewStatus: 2,
-  reviewComments: ''
-});
-
-// 审核表单验证规则
-const reviewRules = ref({
-  reviewStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
-  reviewComments: [{ required: true, message: '请输入审核意见', trigger: 'blur' }]
-});
-
-const queryParams = ref<BaseQuery>({
-  pageNum: 1,
-  pageSize: 10,
-  way: undefined,
-  productNo: undefined,
-  itemName: undefined,
-  brandName: undefined,
-  purchaseNature: undefined,
-  bottomCategoryId: undefined,
-  isSelf: undefined,
-  productReviewStatus: undefined,
-  productStatus: undefined,
-  lastSeenId: undefined // 游标分页的lastSeenId
-});
-
-
-/** 查询商品列表 */
-const getList = async () => {
-  loading.value = true;
-  try {
-    const params = { ...queryParams.value };
-    const currentPageNum = queryParams.value.pageNum;
-
-    // 第一页不需要游标参数
-    if (currentPageNum === 1) {
-      delete params.lastSeenId;
-      delete params.way;
-    } else {
-      // way参数:0=上一页,1=下一页
-      if (queryParams.value.way === 0) {
-        // 上一页:使用目标页(即当前显示页)的firstId
-        const nextPageHistory = pageHistory.value[currentPageNum];
-        if (nextPageHistory) {
-          params.firstSeenId = nextPageHistory.firstId;
-          params.way = 0;
-        }
-      } else {
-        // 下一页:使用前一页的lastId作为lastSeenId
-        const prevPageHistory = pageHistory.value[currentPageNum - 1];
-        if (prevPageHistory) {
-          params.lastSeenId = prevPageHistory.lastId;
-          params.way = 1;
-        }
-      }
-    }
-
-    const res = await listBase(params);
-    baseList.value = res.rows || [];
-
-    // 判断是否还有更多数据
-    hasMore.value = baseList.value.length === queryParams.value.pageSize;
-
-    // 记录当前页的第一个id和最后一个id
-    if (baseList.value.length > 0) {
-      const firstItem = baseList.value[0];
-      const lastItem = baseList.value[baseList.value.length - 1];
-      //如果长度小于currentPageNum则创建
-
-      if (pageHistory.value.length <= currentPageNum) {
-        pageHistory.value[currentPageNum] = {
-          firstId: firstItem.id,
-          lastId: lastItem.id
-        };
-      }
-    }
-    total.value = res.total || 0;
-  } catch (error) {
-    console.error('获取列表失败:', error);
-  } finally {
-    loading.value = false;
-  }
-};
-
-/** 初始化路由参数 */
-const initRouteParams = () => {
-  // 从路由参数中获取筛选条件
-  if (route.query.productReviewStatus) {
-    queryParams.value.productReviewStatus = Number(route.query.productReviewStatus);
-  }
-  if (route.query.brandName) {
-    queryParams.value.brandName = route.query.brandName as string;
-  }
-  if (route.query.bottomCategoryId) {
-    queryParams.value.bottomCategoryId = route.query.bottomCategoryId as string;
-  }
-};
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.value.pageNum = 1;
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  getList();
-};
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  handleQuery();
-};
-
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: BaseVO[]) => {
-  ids.value = selection.map((item) => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
-};
-
-/** 导出按钮操作 */
-const handleExport = () => {
-  proxy?.download(
-    'product/base/export',
-    {
-      ...queryParams.value
-    },
-    `base_review_${new Date().getTime()}.xlsx`
-  );
-};
-
-/** 查看商品详情 */
-const handleView = (row: BaseVO) => {
-  router.push(`/product/base/detail/${row.id}`);
-};
-
-/** 编辑商品 */
-const handleEdit = (row: BaseVO) => {
-  router.push(`/product/base/edit/${row.id}`);
-};
-
-/** 采购审核 */
-const handlePurchaseReview = (row: BaseVO) => {
-  reviewDialog.visible = true;
-  reviewDialog.title = '采购审核';
-  reviewForm.value = {
-    id: row.id,
-    productNo: row.productNo,
-    itemName: row.itemName,
-    reviewStatus: 3,
-    reviewComments: '',
-    productReviewStatus: row.productReviewStatus
-  };
-}
-
-/** 营销审核 */
-const handleMarketingReview = (row: BaseVO) => {
-  reviewDialog.visible = true;
-  reviewDialog.title = '营销审核';
-  reviewForm.value = {
-    id: row.id,
-    productNo: row.productNo,
-    itemName: row.itemName,
-    reviewStatus: 1,
-    reviewComments: '',
-    productReviewStatus: row.productReviewStatus
-  };
-};
-
-/** 重新提交 */
-const handleReSubmit = async (row: BaseVO) => {
-  await proxy?.$modal.confirm('确认重新提交该商品进行审核吗?');
-  const data: BaseForm = {
-    id: row.id,
-    productReviewStatus: 0 // 设置为待审核状态
-  };
-  await reviewBase(data);
-  proxy?.$modal.msgSuccess('重新提交成功');
-  await getList();
-};
-
-/** 提交审核 */
-const submitReview = async () => {
-  await reviewFormRef.value?.validate();
-  const data: BaseForm = {
-    id: reviewForm.value.id,
-    productReviewStatus: Number(reviewForm.value.reviewStatus),
-    reviewComments: reviewForm.value.reviewComments
-  };
-  await reviewBase(data);
-  proxy?.$modal.msgSuccess(reviewForm.value.reviewStatus === 2 ? '审核通过' : '审核驳回');
-  reviewDialog.visible = false;
-  await getList();
-};
-
-/** 查询品牌列表(实时请求,每次只加载500条) */
-const getBrandList = async () => {
-  try {
-    brandLoading.value = true;
-    const res = await brandList({ pageNum: 1, pageSize: 500 });
-    brandOptions.value = res.data || [];
-  } catch (error) {
-    console.error('获取品牌列表失败:', error);
-  } finally {
-    brandLoading.value = false;
-  }
-};
-
-/** 处理品牌下拉框显示/隐藏 */
-const handleBrandVisibleChange = (visible: boolean) => {
-  if (visible && brandOptions.value.length === 0) {
-    getBrandList();
-  }
-};
-
-/** 查询分类树 */
-const getCategoryTree = async () => {
-  const res = await categoryTree();
-  categoryOptions.value = res.data || [];
-};
-
-onMounted(() => {
-  getCategoryTree();
-  initRouteParams();
-  getList();
-});
-</script>
-
-<style scoped lang="scss">
-.app-container {
-  padding: 8px;
-  height: calc(100vh - 84px);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.table-card {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-
-  :deep(.el-card__body) {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-  }
-
-  :deep(.el-table) {
-    flex: 1;
-  }
-
-  // 确保固定列左侧有边框
-  :deep(.el-table__fixed-right) {
-    box-shadow: -1px 0 0 var(--el-table-border-color) !important;
-  }
-
-  // 固定列的单元格左边框
-  :deep(.el-table__fixed-right .el-table__cell) {
-    border-left: 1px solid var(--el-table-border-color) !important;
-  }
-}
-</style>

+ 0 - 453
src/views/product/base/selected.vue

@@ -1,453 +0,0 @@
-<template>
-  <div class="app-container">
-    <!-- 搜索区域 -->
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div v-show="showSearch" class="mb-[10px]">
-        <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" label-width="100px">
-            <el-row :gutter="20">
-              <el-col :span="6">
-                <el-form-item label="商品编号" prop="productNo">
-                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品名称" prop="itemName">
-                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品类别" prop="bottomCategoryId">
-                  <el-tree-select
-                    v-model="queryParams.bottomCategoryId"
-                    :data="categoryOptions"
-                    :props="{ value: 'id', label: 'label', children: 'children' } as any"
-                    value-key="id"
-                    placeholder="请选择商品类别"
-                    clearable
-                    check-strictly
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品品牌" prop="brandName">
-                  <el-select-v2
-                    v-model="queryParams.brandName"
-                    :options="brandOptionsFormatted"
-                    placeholder="请选择商品品牌"
-                    clearable
-                    filterable
-                    :loading="brandLoading"
-                    @visible-change="handleBrandVisibleChange"
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="审核状态" prop="auditStatus">
-                  <el-select
-                    v-model="queryParams.auditStatus"
-                    placeholder="请选择审核状态"
-                    clearable
-                  >
-                    <el-option label="待审核" :value="1" />
-                    <el-option label="审核通过" :value="2" />
-                    <el-option label="审核驳回" :value="3" />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item>
-                  <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
-                  <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-card>
-      </div>
-    </transition>
-
-    <!-- 审核商品信息列表 -->
-    <el-card shadow="never" class="table-card">
-      <template #header>
-        <div class="flex items-center justify-between">
-          <span class="font-semibold">审核商品信息列表</span>
-          <div class="flex gap-2">
-            <el-button circle icon="Refresh" @click="getList"></el-button>
-          </div>
-        </div>
-      </template>
-
-      <el-table v-loading="loading" border :data="baseList" :height="tableHeight" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="商品图片" align="center" prop="productImage" width="100" >
-          <template #default="scope">
-            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品编号" align="center" prop="productNo" width="120" >
-          <template #default="scope">
-            <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productNo }}</el-link>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品信息" align="center" min-width="250" show-overflow-tooltip>
-          <template #default="scope">
-            <div class="text-left">
-              <div>{{ scope.row.itemName }}</div>
-              <div class="text-gray-500" style="font-size: 12px;">品牌: {{ scope.row.brandName || '-' }}</div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="基本情况" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">商品分类:</span>
-                <span>{{ scope.row.categoryName || '-' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">单位:</span>
-                <span>{{ scope.row.unitName || '-' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="SKU价格" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px">
-              <div>
-                <span class="text-gray-500">市场价:</span>
-                <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">官网价:</span>
-                <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">最低价:</span>
-                <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="成本预算" align="center" width="150">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">采购价:</span>
-                <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">暂估毛利率:</span>
-                <span>{{ scope.row.tempGrossMargin || '0.00' }}%</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品来源" align="center" width="100">
-          <template #default="scope">
-            <span>{{ scope.row.dataSource || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品状态" align="center" width="100">
-          <template #default="scope">
-            <el-tag v-if="scope.row.auditStatus === 1" type="warning">待审核</el-tag>
-            <el-tag v-else-if="scope.row.auditStatus === 2" type="success">审核通过</el-tag>
-            <el-tag v-else-if="scope.row.auditStatus === 3" type="danger">审核驳回</el-tag>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="审核意见" align="center" width="180" show-overflow-tooltip>
-          <template #default="scope">
-            <span>{{ scope.row.auditReason || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center" width="150" fixed="right" class-name="border-left">
-          <template #default="scope">
-            <div class="flex gap-1 justify-center">
-              <el-link type="primary" :underline="false" @click="handleView(scope.row)">详情</el-link>
-              <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
-              <el-link v-if="scope.row.auditStatus === 2" type="warning" :underline="false" @click="handleOffShelf(scope.row)">下架</el-link>
-            </div>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <!-- 游标分页控制 -->
-      <pagination
-        v-show="baseList.length > 0"
-        v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize"
-        v-model:way="queryParams.way"
-        :cursor-mode="true"
-        :has-more="hasMore"
-        @pagination="getList"
-      />
-    </el-card>
-
-
-  </div>
-</template>
-
-<script setup name="BaseReview" lang="ts">
-import { getGoodProductPage, brandList, categoryTree } from '@/api/product/base';
-import { BaseVO, BaseQuery } from '@/api/product/base/types';
-import { updateProductExquisite } from '@/api/product/productExquisite';
-import { BrandVO } from '@/api/product/brand/types';
-import { categoryTreeVO } from '@/api/product/category/types';
-import { useRouter, useRoute } from 'vue-router';
-
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const router = useRouter();
-const route = useRoute();
-
-const baseList = ref<BaseVO[]>([]);
-const loading = ref(true);
-const showSearch = ref(true);
-const ids = ref<Array<string | number>>([]);
-const single = ref(true);
-const multiple = ref(true);
-const total = ref(0);
-const brandOptions = ref<BrandVO[]>([]);
-const brandLoading = ref(false);
-const brandOptionsFormatted = computed(() => {
-  return brandOptions.value.slice(0, 500).map((item) => ({
-    label: item.brandName,
-    value: item.brandName // review.vue使用brandName作为value
-  }));
-});
-const categoryOptions = ref<categoryTreeVO[]>([]);
-const hasMore = ref(true); // 是否还有更多数据
-const pageHistory = ref([]);
-
-// 动态计算表格高度
-const tableHeight = computed(() => {
-  // 基础高度 = 视口高度 - 顶部导航(84) - 容器padding(16) - 搜索区域 - 卡片header(60) - 分页器(60)
-  const baseHeight = window.innerHeight - 84 - 16;
-  const searchHeight = showSearch.value ? 150 : 10; // 搜索区域高度
-  const cardHeaderHeight = 60; // 卡片header高度
-  const paginationHeight = 60; // 分页器高度
-  return baseHeight - searchHeight - cardHeaderHeight - paginationHeight;
-});
-
-const queryFormRef = ref<ElFormInstance>();
-
-const queryParams = ref<BaseQuery & { auditStatus?: number }>({
-  pageNum: 1,
-  pageSize: 10,
-  way: undefined,
-  productNo: undefined,
-  itemName: undefined,
-  brandName: undefined,
-  purchaseNature: undefined,
-  bottomCategoryId: undefined,
-  isSelf: undefined,
-  auditStatus: undefined,
-  productStatus: undefined,
-  lastSeenId: undefined // 游标分页的lastSeenId
-});
-
-
-/** 查询商品列表 */
-const getList = async () => {
-  loading.value = true;
-  try {
-    const params = { ...queryParams.value };
-    const currentPageNum = queryParams.value.pageNum;
-
-    // 第一页不需要游标参数
-    if (currentPageNum === 1) {
-      delete params.lastSeenId;
-      delete params.way;
-    } else {
-      // way参数:0=上一页,1=下一页
-      if (queryParams.value.way === 0) {
-        // 上一页:使用目标页(即当前显示页)的firstId
-        const nextPageHistory = pageHistory.value[currentPageNum];
-        if (nextPageHistory) {
-          params.firstSeenId = nextPageHistory.firstId;
-          params.way = 0;
-        }
-      } else {
-        // 下一页:使用前一页的lastId作为lastSeenId
-        const prevPageHistory = pageHistory.value[currentPageNum - 1];
-        if (prevPageHistory) {
-          params.lastSeenId = prevPageHistory.lastId;
-          params.way = 1;
-        }
-      }
-    }
-
-    const res = await getGoodProductPage(params);
-    baseList.value = res.rows || [];
-
-    // 判断是否还有更多数据
-    hasMore.value = baseList.value.length === queryParams.value.pageSize;
-
-    // 记录当前页的第一个id和最后一个id
-    if (baseList.value.length > 0) {
-      const firstItem = baseList.value[0];
-      const lastItem = baseList.value[baseList.value.length - 1];
-      //如果长度小于currentPageNum则创建
-
-      if (pageHistory.value.length <= currentPageNum) {
-        pageHistory.value[currentPageNum] = {
-          firstId: firstItem.id,
-          lastId: lastItem.id
-        };
-      }
-    }
-    total.value = res.total || 0;
-  } catch (error) {
-    console.error('获取列表失败:', error);
-  } finally {
-    loading.value = false;
-  }
-};
-
-/** 初始化路由参数 */
-const initRouteParams = () => {
-  // 从路由参数中获取筛选条件
-  if (route.query.auditStatus) {
-    queryParams.value.auditStatus = Number(route.query.auditStatus);
-  }
-  if (route.query.brandName) {
-    queryParams.value.brandName = route.query.brandName as string;
-  }
-  if (route.query.bottomCategoryId) {
-    queryParams.value.bottomCategoryId = route.query.bottomCategoryId as string;
-  }
-};
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.value.pageNum = 1;
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  getList();
-};
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  handleQuery();
-};
-
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: BaseVO[]) => {
-  ids.value = selection.map((item) => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
-};
-
-/** 导出按钮操作 */
-const handleExport = () => {
-  proxy?.download(
-    'product/base/export',
-    {
-      ...queryParams.value
-    },
-    `base_review_${new Date().getTime()}.xlsx`
-  );
-};
-
-/** 查看商品详情 */
-const handleView = (row: BaseVO) => {
-  const url = `https://item.xiaoluwebsite.xyz/item?id=${row.id}`;
-  window.open(url, '_blank');
-};
-
-/** 编辑商品 */
-const handleUpdate = (row: BaseVO) => {
-  router.push(`/product/base/edit/${row.id}`);
-};
-
-/** 下架操作 */
-const handleOffShelf = async (row: BaseVO) => {
-  await proxy?.$modal.confirm('确认下架该精选商品吗?');
-  try {
-    await updateProductExquisite({
-      id: row.exquisiteId,
-      auditStatus: 3,
-      auditReason: '下架操作'
-    });
-    proxy?.$modal.msgSuccess('下架成功');
-    await getList();
-  } catch (error) {
-    console.error('下架失败:', error);
-    proxy?.$modal.msgError('下架失败');
-  }
-};
-
-/** 查询品牌列表(实时请求,每次只加载500条) */
-const getBrandList = async () => {
-  try {
-    brandLoading.value = true;
-    const res = await brandList({ pageNum: 1, pageSize: 500 });
-    brandOptions.value = res.data || [];
-  } catch (error) {
-    console.error('获取品牌列表失败:', error);
-  } finally {
-    brandLoading.value = false;
-  }
-};
-
-/** 处理品牌下拉框显示/隐藏 */
-const handleBrandVisibleChange = (visible: boolean) => {
-  if (visible && brandOptions.value.length === 0) {
-    getBrandList();
-  }
-};
-
-/** 查询分类树 */
-const getCategoryTree = async () => {
-  const res = await categoryTree();
-  categoryOptions.value = res.data || [];
-};
-
-onMounted(() => {
-  getCategoryTree();
-  initRouteParams();
-  getList();
-});
-</script>
-
-<style scoped lang="scss">
-.app-container {
-  padding: 8px;
-  height: calc(100vh - 84px);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.table-card {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-
-  :deep(.el-card__body) {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-  }
-
-  :deep(.el-table) {
-    flex: 1;
-  }
-
-  // 确保固定列左侧有边框
-  :deep(.el-table__fixed-right) {
-    box-shadow: -1px 0 0 var(--el-table-border-color) !important;
-  }
-
-  // 固定列的单元格左边框
-  :deep(.el-table__fixed-right .el-table__cell) {
-    border-left: 1px solid var(--el-table-border-color) !important;
-  }
-}
-</style>

+ 0 - 583
src/views/product/base/self.vue

@@ -1,583 +0,0 @@
-<template>
-  <div class="app-container">
-    <!-- 搜索区域 -->
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div v-show="showSearch" class="mb-[10px]">
-        <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" label-width="100px">
-            <el-row :gutter="20">
-              <el-col :span="6">
-                <el-form-item label="商品编号" prop="productNo">
-                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品名称" prop="itemName">
-                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品类别" prop="bottomCategoryId">
-                  <el-tree-select
-                    v-model="queryParams.bottomCategoryId"
-                    :data="categoryOptions"
-                    :props="{ value: 'id', label: 'label', children: 'children' } as any"
-                    value-key="id"
-                    placeholder="请选择商品类别"
-                    clearable
-                    check-strictly
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品品牌" prop="brandName">
-                  <el-select-v2
-                    v-model="queryParams.brandName"
-                    :options="brandOptionsFormatted"
-                    placeholder="请选择商品品牌"
-                    clearable
-                    filterable
-                    :loading="brandLoading"
-                    @visible-change="handleBrandVisibleChange"
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="审核状态" prop="auditStatus">
-                  <el-select
-                    v-model="queryParams.auditStatus"
-                    placeholder="请选择审核状态"
-                    clearable
-                  >
-                    <el-option label="待审核" :value="1" />
-                    <el-option label="审核通过" :value="2" />
-                    <el-option label="审核驳回" :value="3" />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item>
-                  <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
-                  <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-card>
-      </div>
-    </transition>
-
-    <!-- 审核商品信息列表 -->
-    <el-card shadow="never" class="table-card">
-      <template #header>
-        <div class="flex items-center justify-between">
-          <span class="font-semibold">审核商品信息列表</span>
-          <div class="flex gap-2">
-
-            <el-button circle icon="Refresh" @click="getList"></el-button>
-          </div>
-        </div>
-      </template>
-
-      <el-table v-loading="loading" border :data="baseList" :height="tableHeight" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="商品图片" align="center" prop="productImage" width="100" >
-          <template #default="scope">
-            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品编号" align="center" prop="productNo" width="120" >
-          <template #default="scope">
-            <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productNo }}</el-link>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品信息" align="center" min-width="250" show-overflow-tooltip>
-          <template #default="scope">
-            <div class="text-left">
-              <div>{{ scope.row.itemName }}</div>
-              <div class="text-gray-500" style="font-size: 12px;">品牌: {{ scope.row.brandName || '-' }}</div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="基本情况" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">商品分类:</span>
-                <span>{{ scope.row.categoryName || '-' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">单位:</span>
-                <span>{{ scope.row.unitName || '-' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="SKU价格" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px">
-              <div>
-                <span class="text-gray-500">市场价:</span>
-                <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">官网价:</span>
-                <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">最低价:</span>
-                <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="成本预算" align="center" width="150">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">采购价:</span>
-                <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">暂估毛利率:</span>
-                <span>{{ scope.row.tempGrossMargin || '0.00' }}%</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品来源" align="center" width="100">
-          <template #default="scope">
-            <span>{{ scope.row.dataSource || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品状态" align="center" width="100">
-          <template #default="scope">
-            <el-tag v-if="scope.row.auditStatus === 1" type="warning">待审核</el-tag>
-            <el-tag v-else-if="scope.row.auditStatus === 2" type="success">审核通过</el-tag>
-            <el-tag v-else-if="scope.row.auditStatus === 3" type="danger">审核驳回</el-tag>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="审核意见" align="center" width="180" show-overflow-tooltip>
-          <template #default="scope">
-            <span>{{ scope.row.auditReason || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center" width="150" fixed="right" class-name="border-left">
-          <template #default="scope">
-            <!-- 待审核状态:只显示编辑 -->
-            <div v-if="scope.row.auditStatus === 1" class="flex gap-1 justify-center">
-              <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
-            </div>
-
-            <!-- 审核通过 -->
-            <div v-else-if="scope.row.auditStatus === 2" class="flex flex-col gap-1">
-              <!-- 下架状态:编辑、上架、停售 -->
-              <div v-if="scope.row.productStatus === 0" class="flex gap-1 justify-center">
-                <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
-                <el-link type="success" :underline="false" @click="handleShelf(scope.row)">上架</el-link>
-                <el-link type="danger" :underline="false" @click="handleDiscontinue(scope.row)">停售</el-link>
-              </div>
-
-              <!-- 上架状态:编辑、下架、停售 -->
-              <div v-else-if="scope.row.productStatus === 1" class="flex gap-1 justify-center">
-                <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
-                <el-link type="warning" :underline="false" @click="handleShelf(scope.row)">下架</el-link>
-                <el-link type="danger" :underline="false" @click="handleDiscontinue(scope.row)">停售</el-link>
-              </div>
-            </div>
-
-            <!-- 审核驳回:显示编辑 -->
-            <div v-else-if="scope.row.auditStatus === 3" class="flex gap-1 justify-center">
-              <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
-            </div>
-
-            <!-- 其他状态:显示编辑 -->
-            <div v-else class="flex gap-1 justify-center">
-              <el-link type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
-            </div>
-            <div class="flex gap-1 justify-center">
-              <el-link type="primary" :underline="false" @click="handleSupply(scope.row)">修改库存</el-link>
-            </div>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <!-- 游标分页控制 -->
-      <pagination
-        v-show="baseList.length > 0"
-        v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize"
-        v-model:way="queryParams.way"
-        :cursor-mode="true"
-        :has-more="hasMore"
-        @pagination="getList"
-      />
-    </el-card>
-
-    <!-- 库存修改弹框 -->
-    <el-dialog v-model="inventoryDialog.visible" title="修改库存" width="500px" :close-on-click-modal="false">
-      <div v-loading="inventoryDialog.loading">
-        <el-form ref="inventoryFormRef" :model="inventoryForm" :rules="inventoryRules" label-width="110px">
-          <el-form-item label="虚拟库存" prop="virtualInventory">
-            <el-input-number
-              v-model="inventoryForm.virtualInventory"
-              :min="0"
-              :precision="0"
-              controls-position="right"
-              style="width: 100%"
-              placeholder="请输入虚拟库存"
-            />
-          </el-form-item>
-        </el-form>
-      </div>
-      <template #footer>
-        <el-button @click="inventoryDialog.visible = false">取消</el-button>
-        <el-button type="primary" :loading="inventoryDialog.submitLoading" @click="submitInventory">确定</el-button>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="BaseReview" lang="ts">
-import { getSelfProductPage, brandList, categoryTree, getBase, updateBase, shelfReview, changeProductType } from '@/api/product/base';
-import { BaseVO, BaseQuery, BaseForm } from '@/api/product/base/types';
-import { BrandVO } from '@/api/product/brand/types';
-import { categoryTreeVO } from '@/api/product/category/types';
-import { useRouter, useRoute } from 'vue-router';
-
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const router = useRouter();
-const route = useRoute();
-
-const baseList = ref<BaseVO[]>([]);
-const loading = ref(true);
-const showSearch = ref(true);
-const ids = ref<Array<string | number>>([]);
-const single = ref(true);
-const multiple = ref(true);
-const total = ref(0);
-const brandOptions = ref<BrandVO[]>([]);
-const brandLoading = ref(false);
-const brandOptionsFormatted = computed(() => {
-  return brandOptions.value.slice(0, 500).map((item) => ({
-    label: item.brandName,
-    value: item.brandName // review.vue使用brandName作为value
-  }));
-});
-const categoryOptions = ref<categoryTreeVO[]>([]);
-const hasMore = ref(true); // 是否还有更多数据
-const pageHistory = ref([]);
-
-// 动态计算表格高度
-const tableHeight = computed(() => {
-  // 基础高度 = 视口高度 - 顶部导航(84) - 容器padding(16) - 搜索区域 - 卡片header(60) - 分页器(60)
-  const baseHeight = window.innerHeight - 84 - 16;
-  const searchHeight = showSearch.value ? 150 : 10; // 搜索区域高度
-  const cardHeaderHeight = 60; // 卡片header高度
-  const paginationHeight = 60; // 分页器高度
-  return baseHeight - searchHeight - cardHeaderHeight - paginationHeight;
-});
-
-const queryFormRef = ref<ElFormInstance>();
-const inventoryFormRef = ref<ElFormInstance>();
-
-// 库存修改弹框
-const inventoryDialog = reactive({
-  visible: false,
-  loading: false,
-  submitLoading: false
-});
-
-const inventoryForm = reactive<any>({
-  id: undefined,
-  totalInventory: undefined,
-  nowInventory: undefined,
-  virtualInventory: undefined
-});
-
-const inventoryRules = {
-  totalInventory: [{ required: true, message: '总库存不能为空', trigger: 'blur' }],
-  nowInventory: [{ required: true, message: '当前可用库存不能为空', trigger: 'blur' }],
-  virtualInventory: [{ required: true, message: '虚拟库存不能为空', trigger: 'blur' }]
-};
-
-const queryParams = ref<BaseQuery & { auditStatus?: number }>({
-  pageNum: 1,
-  pageSize: 10,
-  way: undefined,
-  productNo: undefined,
-  itemName: undefined,
-  brandName: undefined,
-  purchaseNature: undefined,
-  bottomCategoryId: undefined,
-  isSelf: undefined,
-  auditStatus: undefined,
-  productStatus: undefined,
-  lastSeenId: undefined // 游标分页的lastSeenId
-});
-
-
-/** 查询商品列表 */
-const getList = async () => {
-  loading.value = true;
-  try {
-    const params = { ...queryParams.value };
-    const currentPageNum = queryParams.value.pageNum;
-
-    // 第一页不需要游标参数
-    if (currentPageNum === 1) {
-      delete params.lastSeenId;
-      delete params.way;
-    } else {
-      // way参数:0=上一页,1=下一页
-      if (queryParams.value.way === 0) {
-        // 上一页:使用目标页(即当前显示页)的firstId
-        const nextPageHistory = pageHistory.value[currentPageNum];
-        if (nextPageHistory) {
-          params.firstSeenId = nextPageHistory.firstId;
-          params.way = 0;
-        }
-      } else {
-        // 下一页:使用前一页的lastId作为lastSeenId
-        const prevPageHistory = pageHistory.value[currentPageNum - 1];
-        if (prevPageHistory) {
-          params.lastSeenId = prevPageHistory.lastId;
-          params.way = 1;
-        }
-      }
-    }
-
-    const res = await getSelfProductPage(params);
-    baseList.value = res.rows || [];
-
-    // 判断是否还有更多数据
-    hasMore.value = baseList.value.length === queryParams.value.pageSize;
-
-    // 记录当前页的第一个id和最后一个id
-    if (baseList.value.length > 0) {
-      const firstItem = baseList.value[0];
-      const lastItem = baseList.value[baseList.value.length - 1];
-      //如果长度小于currentPageNum则创建
-
-      if (pageHistory.value.length <= currentPageNum) {
-        pageHistory.value[currentPageNum] = {
-          firstId: firstItem.id,
-          lastId: lastItem.id
-        };
-      }
-    }
-    total.value = res.total || 0;
-  } catch (error) {
-    console.error('获取列表失败:', error);
-  } finally {
-    loading.value = false;
-  }
-};
-
-/** 初始化路由参数 */
-const initRouteParams = () => {
-  // 从路由参数中获取筛选条件
-  if (route.query.auditStatus) {
-    queryParams.value.auditStatus = Number(route.query.auditStatus);
-  }
-  if (route.query.brandName) {
-    queryParams.value.brandName = route.query.brandName as string;
-  }
-  if (route.query.bottomCategoryId) {
-    queryParams.value.bottomCategoryId = route.query.bottomCategoryId as string;
-  }
-};
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.value.pageNum = 1;
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  getList();
-};
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  handleQuery();
-};
-
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: BaseVO[]) => {
-  ids.value = selection.map((item) => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
-};
-
-/** 导出按钮操作 */
-const handleExport = () => {
-  proxy?.download(
-    'product/base/export',
-    {
-      ...queryParams.value
-    },
-    `base_review_${new Date().getTime()}.xlsx`
-  );
-};
-
-/** 查看商品详情 */
-const handleView = (row: BaseVO) => {
-  router.push(`/product/base/detail/${row.id}`);
-};
-
-/** 编辑商品 */
-const handleUpdate = (row: BaseVO) => {
-  router.push(`/product/base/edit/${row.id}`);
-};
-
-/** 上下架操作 */
-const handleShelf = async (row: BaseVO) => {
-  const isOnShelf = row.productStatus === 1;
-  const action = isOnShelf ? '下架' : '上架';
-  await proxy?.$modal.confirm(`确认${action}该商品吗?`);
-
-  try {
-    const productStatus = isOnShelf ? 0 : 2;
-    await shelfReview({
-      id: row.id,
-      productStatus: productStatus,
-      shelfComments: `${action}操作`
-    });
-    proxy?.$modal.msgSuccess(`${action}成功`);
-    await getList();
-  } catch (error) {
-    console.error(`${action}失败:`, error);
-    proxy?.$modal.msgError(`${action}失败`);
-  }
-};
-
-/** 停售操作 */
-const handleDiscontinue = async (row: BaseVO) => {
-  await proxy?.$modal.confirm('确认停售该商品吗?停售后商品将无法正常售卖。');
-
-  try {
-    await changeProductType({
-      id: row.id,
-      productCategory: 3
-    });
-    proxy?.$modal.msgSuccess('停售成功');
-    await getList();
-  } catch (error) {
-    console.error('停售失败:', error);
-    proxy?.$modal.msgError('停售失败');
-  }
-};
-
-/** 打开库存修改弹框 */
-const handleSupply = async (row: BaseVO) => {
-  inventoryForm.id = row.id;
-  inventoryForm.totalInventory = undefined;
-  inventoryForm.nowInventory = undefined;
-  inventoryForm.virtualInventory = undefined;
-  inventoryDialog.loading = true;
-  inventoryDialog.visible = true;
-  try {
-    const res = await getBase(row.id);
-    if (res.data) {
-      const data = res.data as any;
-      inventoryForm.totalInventory = data.totalInventory;
-      inventoryForm.nowInventory = data.nowInventory;
-      inventoryForm.virtualInventory = data.virtualInventory;
-    }
-  } catch (error) {
-    console.error('获取库存信息失败:', error);
-  } finally {
-    inventoryDialog.loading = false;
-  }
-};
-
-/** 提交库存修改 */
-const submitInventory = async () => {
-  await inventoryFormRef.value?.validate();
-  inventoryDialog.submitLoading = true;
-  try {
-    await updateBase({ ...inventoryForm });
-    proxy?.$modal.msgSuccess('库存修改成功');
-    inventoryDialog.visible = false;
-    await getList();
-  } catch (error) {
-    console.error('库存修改失败:', error);
-    proxy?.$modal.msgError('库存修改失败');
-  } finally {
-    inventoryDialog.submitLoading = false;
-  }
-};
-
-/** 查询品牌列表(实时请求,每次只加载500条) */
-const getBrandList = async () => {
-  try {
-    brandLoading.value = true;
-    const res = await brandList({ pageNum: 1, pageSize: 500 });
-    brandOptions.value = res.data || [];
-  } catch (error) {
-    console.error('获取品牌列表失败:', error);
-  } finally {
-    brandLoading.value = false;
-  }
-};
-
-/** 处理品牌下拉框显示/隐藏 */
-const handleBrandVisibleChange = (visible: boolean) => {
-  if (visible && brandOptions.value.length === 0) {
-    getBrandList();
-  }
-};
-
-/** 查询分类树 */
-const getCategoryTree = async () => {
-  const res = await categoryTree();
-  categoryOptions.value = res.data || [];
-};
-
-onMounted(() => {
-  getCategoryTree();
-  initRouteParams();
-  getList();
-});
-</script>
-
-<style scoped lang="scss">
-.app-container {
-  padding: 8px;
-  height: calc(100vh - 84px);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.table-card {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-
-  :deep(.el-card__body) {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-  }
-
-  :deep(.el-table) {
-    flex: 1;
-  }
-
-  // 确保固定列左侧有边框
-  :deep(.el-table__fixed-right) {
-    box-shadow: -1px 0 0 var(--el-table-border-color) !important;
-  }
-
-  // 固定列的单元格左边框
-  :deep(.el-table__fixed-right .el-table__cell) {
-    border-left: 1px solid var(--el-table-border-color) !important;
-  }
-}
-</style>

+ 0 - 550
src/views/product/base/shelfReview.vue

@@ -1,550 +0,0 @@
-<template>
-  <div class="app-container">
-    <!-- 搜索区域 -->
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div v-show="showSearch" class="mb-[10px]">
-        <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" label-width="100px">
-            <el-row :gutter="20">
-              <el-col :span="6">
-                <el-form-item label="商品编号" prop="productNo">
-                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品名称" prop="itemName">
-                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品类别" prop="bottomCategoryId">
-                  <el-tree-select
-                    v-model="queryParams.bottomCategoryId"
-                    :data="categoryOptions"
-                    :props="{ value: 'id', label: 'label', children: 'children' } as any"
-                    value-key="id"
-                    placeholder="请选择商品类别"
-                    clearable
-                    check-strictly
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="6">
-                <el-form-item label="商品品牌" prop="brandId">
-                  <el-select
-                    v-model="queryParams.brandId"
-                    placeholder="请输入品牌名称搜索"
-                    filterable
-                    remote
-                    clearable
-                    :remote-method="handleBrandSearch"
-                    :loading="brandLoading"
-                    style="width: 100%"
-                  >
-                    <el-option
-                      v-for="item in brandOptions"
-                      :key="item.id"
-                      :label="item.brandName"
-                      :value="item.id"
-                    />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-
-              <el-col :span="6">
-                <el-form-item label="上下架状态" prop="productStatus">
-                  <el-select v-model="queryParams.productStatus" placeholder="请选择" clearable>
-                    <el-option label="已上架" :value="1" />
-<!--                    <el-option label="下架" :value="0" />-->
-                    <el-option label="上架中" :value="2" />
-                    <el-option label="驳回上架" :value="3" />
-                  </el-select>
-                </el-form-item>
-              </el-col>
-
-              <el-col :span="6">
-                <el-form-item>
-                  <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
-                  <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-                </el-form-item>
-              </el-col>
-            </el-row>
-          </el-form>
-        </el-card>
-      </div>
-    </transition>
-
-    <!-- 上下架审核商品信息列表 -->
-    <el-card shadow="never" class="table-card">
-      <template #header>
-        <div class="flex items-center justify-between">
-          <span class="font-semibold">上下架审核商品信息列表</span>
-          <div class="flex gap-2">
-            <el-button type="primary" icon="Download" @click="handleExport">导出商品</el-button>
-            <el-button type="success" icon="Upload">导入商品</el-button>
-            <el-button circle icon="Refresh" @click="getList"></el-button>
-          </div>
-        </div>
-      </template>
-
-      <el-table v-loading="loading" border :data="baseList" :height="tableHeight" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="商品图片" align="center" prop="productImage" width="100" >
-          <template #default="scope">
-            <image-preview :src="scope.row.productImage" :width="60" :height="60"/>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品编号" align="center" prop="productNo" width="120" >
-          <template #default="scope">
-            <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productNo }}</el-link>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品信息" align="center" min-width="250" show-overflow-tooltip>
-          <template #default="scope">
-            <div class="text-left">
-              <div>{{ scope.row.itemName }}</div>
-              <div class="text-gray-500" style="font-size: 12px;">品牌: {{ scope.row.brandName || '-' }}</div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="基本情况" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">商品分类:</span>
-                <span>{{ scope.row.categoryName || '-' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">单位:</span>
-                <span>{{ scope.row.unitName || '-' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="SKU价格" align="center" width="180">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">市场价:</span>
-                <span class="text-red-500">¥{{ scope.row.marketPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">官网价:</span>
-                <span class="text-red-500">¥{{ scope.row.memberPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">最低价:</span>
-                <span class="text-red-500">¥{{ scope.row.minSellingPrice || '0.00' }}</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="成本预算" align="center" width="150">
-          <template #default="scope">
-            <div class="text-left" style="font-size: 12px;">
-              <div>
-                <span class="text-gray-500">采购价:</span>
-                <span>¥{{ scope.row.purchasingPrice || '0.00' }}</span>
-              </div>
-              <div>
-                <span class="text-gray-500">暂估毛利率:</span>
-                <span>{{ scope.row.tempGrossMargin || '0.00' }}%</span>
-              </div>
-            </div>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品来源" align="center" width="100">
-          <template #default="scope">
-            <span>{{ scope.row.dataSource || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="商品状态" align="center" width="100">
-          <template #default="scope">
-            <el-tag v-if="scope.row.productStatus === '2' || scope.row.productStatus === 2" type="warning">上架中</el-tag>
-            <el-tag v-else-if="scope.row.productStatus === '1' || scope.row.productStatus === 1" type="success">已上架</el-tag>
-            <el-tag v-else-if="scope.row.productStatus === '0' || scope.row.productStatus === 0" type="info">已下架</el-tag>
-            <el-tag v-else-if="scope.row.productStatus === '3' || scope.row.productStatus === 3" type="info">驳回上架</el-tag>
-            <span v-else>-</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="审核意见" align="center" width="180" show-overflow-tooltip>
-          <template #default="scope">
-            <span>{{ scope.row.shelfComments || '-' }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="操作" align="center" width="200" fixed="right" class-name="border-left">
-          <template #default="scope">
-            <div class="flex flex-col gap-1">
-              <!-- 根据商品状态显示不同按钮 -->
-              <template v-if="scope.row.productStatus === '2' || scope.row.productStatus === 2">
-                <!-- 上架中:显示上架审核按钮 -->
-                <div class="flex gap-1 justify-center">
-                  <el-link type="primary" :underline="false" @click="handleEdit(scope.row)">编辑</el-link>
-                  <el-link type="success" :underline="false" @click="handleShelfReview(scope.row)">上架审核</el-link>
-                </div>
-              </template>
-              <template v-else-if="scope.row.productStatus === '1' || scope.row.productStatus === 1">
-                <!-- 已上架:显示下架按钮 -->
-                <div class="flex gap-1 justify-center">
-                  <el-link type="primary" :underline="false" @click="handleEdit(scope.row)">编辑</el-link>
-                  <el-link type="danger" :underline="false" @click="handleOffShelf(scope.row)">下架</el-link>
-                </div>
-              </template>
-            </div>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <!-- 游标分页控制 -->
-      <pagination
-        v-show="baseList.length > 0"
-        v-model:page="queryParams.pageNum"
-        v-model:limit="queryParams.pageSize"
-        v-model:way="queryParams.way"
-        :cursor-mode="true"
-        :has-more="hasMore"
-        @pagination="getList"
-      />
-    </el-card>
-
-    <!-- 上架审核对话框 -->
-    <el-dialog v-model="reviewDialog.visible" :title="reviewDialog.title" width="600px" append-to-body>
-      <el-form ref="reviewFormRef" :model="reviewForm" :rules="reviewRules" label-width="100px">
-        <el-form-item label="商品编号">
-          <el-input v-model="reviewForm.productNo" disabled />
-        </el-form-item>
-        <el-form-item label="商品名称">
-          <el-input v-model="reviewForm.itemName" disabled />
-        </el-form-item>
-        <el-form-item label="审核结果" prop="productStatus">
-          <el-radio-group v-model="reviewForm.productStatus">
-            <el-radio :label="1">通过上架</el-radio>
-            <el-radio :label="3">驳回下架</el-radio>
-          </el-radio-group>
-        </el-form-item>
-        <el-form-item label="审核意见" prop="shelfComments">
-          <el-input v-model="reviewForm.shelfComments" type="textarea" :rows="4" placeholder="请输入审核意见" />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button @click="reviewDialog.visible = false">取消</el-button>
-          <el-button type="primary" @click="submitReview">确定</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
-</template>
-
-<script setup name="ShelfReview" lang="ts">
-import { listBase, getBase, shelfReview, brandList, categoryTree } from '@/api/product/base';
-import { BaseVO, BaseQuery, BaseForm } from '@/api/product/base/types';
-import { BrandVO } from '@/api/product/brand/types';
-import { listBrand } from '@/api/product/brand';
-import { categoryTreeVO } from '@/api/product/category/types';
-import { useRouter, useRoute } from 'vue-router';
-
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const router = useRouter();
-const route = useRoute();
-
-const baseList = ref<BaseVO[]>([]);
-const loading = ref(true);
-const showSearch = ref(true);
-const ids = ref<Array<string | number>>([]);
-const single = ref(true);
-const multiple = ref(true);
-const total = ref(0);
-const brandOptions = ref<BrandVO[]>([]);
-const brandLoading = ref(false);
-let brandSearchTimer: ReturnType<typeof setTimeout> | null = null;
-const categoryOptions = ref<categoryTreeVO[]>([]);
-const hasMore = ref(true); // 是否还有更多数据
-const pageHistory = ref([]);
-
-// 动态计算表格高度
-const tableHeight = computed(() => {
-  // 基础高度 = 视口高度 - 顶部导航(84) - 容器padding(16) - 搜索区域 - 卡片header(60) - 分页器(60)
-  const baseHeight = window.innerHeight - 84 - 16;
-  const searchHeight = showSearch.value ? 150 : 10; // 搜索区域高度
-  const cardHeaderHeight = 60; // 卡片header高度
-  const paginationHeight = 60; // 分页器高度
-  return baseHeight - searchHeight - cardHeaderHeight - paginationHeight;
-});
-
-const queryFormRef = ref<ElFormInstance>();
-const reviewFormRef = ref<ElFormInstance>();
-
-// 审核对话框
-const reviewDialog = reactive({
-  visible: false,
-  title: '上架审核'
-});
-
-// 审核表单
-const reviewForm = ref<any>({
-  id: undefined,
-  productNo: '',
-  itemName: '',
-  productStatus: 1,
-  shelfComments: ''
-});
-
-// 审核表单验证规则
-const reviewRules = ref({
-  productStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
-  shelfComments: [{ required: true, message: '请输入审核意见', trigger: 'blur' }]
-});
-
-const queryParams = ref<BaseQuery>({
-  pageNum: 1,
-  pageSize: 10,
-  way: undefined,
-  productNo: undefined,
-  itemName: undefined,
-  brandId: undefined,
-  purchaseNature: undefined,
-  bottomCategoryId: undefined,
-  isSelf: undefined,
-  productReviewStatus: 1, // 只查询审核通过的数据
-  productStatus: undefined, // 用于筛选商品状态
-  isShelfAudit: 1,
-  lastSeenId: undefined // 游标分页的lastSeenId
-});
-
-
-/** 查询商品列表 */
-const getList = async () => {
-  loading.value = true;
-  try {
-    const params = { ...queryParams.value };
-    const currentPageNum = queryParams.value.pageNum;
-
-    // 强制只查询审核通过的数据
-    params.productReviewStatus = 1;
-
-
-    // 如果没有选择商品状态,默认查询上架中和已上架的数据
-    // 后端需要支持多状态查询,这里通过不传productStatus让后端返回所有状态,前端再过滤
-    // 或者后端支持传入多个状态值
-
-    // 第一页不需要游标参数
-    if (currentPageNum === 1) {
-      delete params.lastSeenId;
-      delete params.way;
-    } else {
-      // way参数:0=上一页,1=下一页
-      if (queryParams.value.way === 0) {
-        // 上一页:使用目标页(即当前显示页)的firstId
-        const nextPageHistory = pageHistory.value[currentPageNum];
-        if (nextPageHistory) {
-          params.firstSeenId = nextPageHistory.firstId;
-          params.way = 0;
-        }
-      } else {
-        // 下一页:使用前一页的lastId作为lastSeenId
-        const prevPageHistory = pageHistory.value[currentPageNum - 1];
-        if (prevPageHistory) {
-          params.lastSeenId = prevPageHistory.lastId;
-          params.way = 1;
-        }
-      }
-    }
-
-    const res = await listBase(params);
-    baseList.value = res.rows || [];
-
-    // 判断是否还有更多数据
-    hasMore.value = baseList.value.length === queryParams.value.pageSize;
-
-    // 记录当前页的第一个id和最后一个id
-    if (baseList.value.length > 0) {
-      const firstItem = baseList.value[0];
-      const lastItem = baseList.value[baseList.value.length - 1];
-      //如果长度小于currentPageNum则创建
-
-      if (pageHistory.value.length <= currentPageNum) {
-        pageHistory.value[currentPageNum] = {
-          firstId: firstItem.id,
-          lastId: lastItem.id
-        };
-      }
-    }
-    total.value = res.total || 0;
-  } catch (error) {
-    console.error('获取列表失败:', error);
-  } finally {
-    loading.value = false;
-  }
-};
-
-/** 初始化路由参数 */
-const initRouteParams = () => {
-  // 从路由参数中获取筛选条件
-  if (route.query.productStatus) {
-    queryParams.value.productStatus = Number(route.query.productStatus);
-  }
-  if (route.query.brandId) {
-    queryParams.value.brandId = route.query.brandId as string;
-  }
-  if (route.query.bottomCategoryId) {
-    queryParams.value.bottomCategoryId = route.query.bottomCategoryId as string;
-  }
-};
-
-/** 搜索按钮操作 */
-const handleQuery = () => {
-  queryParams.value.pageNum = 1;
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  getList();
-};
-
-/** 重置按钮操作 */
-const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  queryParams.value.lastSeenId = undefined;
-  pageHistory.value = [0]; // 重置页面历史
-  handleQuery();
-};
-
-/** 多选框选中数据 */
-const handleSelectionChange = (selection: BaseVO[]) => {
-  ids.value = selection.map((item) => item.id);
-  single.value = selection.length != 1;
-  multiple.value = !selection.length;
-};
-
-/** 导出按钮操作 */
-const handleExport = () => {
-  proxy?.download(
-    'product/base/export',
-    {
-      ...queryParams.value
-    },
-    `base_shelf_review_${new Date().getTime()}.xlsx`
-  );
-};
-
-/** 查看商品详情 */
-const handleView = (row: BaseVO) => {
-  const url = `https://item.xiaoluwebsite.xyz/item?id=${row.id}`;
-  window.open(url, '_blank');
-};
-
-/** 编辑商品 */
-const handleEdit = (row: BaseVO) => {
-  router.push(`/product/base/edit/${row.id}`);
-};
-
-/** 上架审核 */
-const handleShelfReview = (row: BaseVO) => {
-  reviewDialog.visible = true;
-  reviewDialog.title = '上架审核';
-  reviewForm.value = {
-    id: row.id,
-    productNo: row.productNo,
-    itemName: row.itemName,
-    productStatus: 1,
-    shelfComments: ''
-  };
-}
-
-/** 下架操作 */
-const handleOffShelf = async (row: BaseVO) => {
-  await proxy?.$modal.confirm('确认下架该商品吗?');
-  const data: BaseForm = {
-    id: row.id,
-    productStatus: '0' // 设置为下架状态
-  };
-  await shelfReview(data);
-  proxy?.$modal.msgSuccess('下架成功');
-  await getList();
-};
-
-/** 提交审核 */
-const submitReview = async () => {
-  await reviewFormRef.value?.validate();
-  const data: BaseForm = {
-    id: reviewForm.value.id,
-    productStatus: String(reviewForm.value.productStatus),
-    shelfComments: reviewForm.value.shelfComments
-  };
-  await shelfReview(data);
-  proxy?.$modal.msgSuccess(reviewForm.value.productStatus === 1 ? '上架成功' : '驳回成功');
-  reviewDialog.visible = false;
-  await getList();
-};
-
-/** 加载品牌选项(默认100条) */
-const loadBrandOptions = async (keyword?: string) => {
-  brandLoading.value = true;
-  try {
-    const res = await listBrand({ pageNum: 1, pageSize: 100, brandName: keyword });
-    brandOptions.value = res.rows || [];
-  } catch (error) {
-    console.error('加载品牌列表失败:', error);
-  } finally {
-    brandLoading.value = false;
-  }
-};
-
-/** 品牌远程搜索(防抖) */
-const handleBrandSearch = (query: string) => {
-  if (brandSearchTimer) clearTimeout(brandSearchTimer);
-  brandSearchTimer = setTimeout(() => {
-    loadBrandOptions(query || undefined);
-  }, 300);
-};
-
-/** 查询分类树 */
-const getCategoryTree = async () => {
-  const res = await categoryTree();
-  categoryOptions.value = res.data || [];
-};
-
-onMounted(() => {
-  getCategoryTree();
-  initRouteParams();
-  loadBrandOptions();
-  getList();
-});
-</script>
-
-<style scoped lang="scss">
-.app-container {
-  padding: 8px;
-  height: calc(100vh - 84px);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.table-card {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-
-  :deep(.el-card__body) {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-  }
-
-  :deep(.el-table) {
-    flex: 1;
-  }
-
-  // 确保固定列左侧有边框
-  :deep(.el-table__fixed-right) {
-    box-shadow: -1px 0 0 var(--el-table-border-color) !important;
-  }
-
-  // 固定列的单元格左边框
-  :deep(.el-table__fixed-right .el-table__cell) {
-    border-left: 1px solid var(--el-table-border-color) !important;
-  }
-}
-</style>

+ 1924 - 0
src/views/product/baseAudit/add.vue

@@ -0,0 +1,1924 @@
+<template>
+  <div class="app-container">
+    <el-card shadow="never" class="mb-3">
+      <div class="flex items-center justify-between">
+        <div class="flex items-center">
+          <el-button icon="ArrowLeft" @click="handleBack">返回</el-button>
+          <span class="ml-4 text-xl font-bold">{{ pageTitle }}</span>
+        </div>
+      </div>
+    </el-card>
+
+    <div class="product-wizard-page">
+      <!-- 步骤条 -->
+      <el-card shadow="never" class="mb-3">
+        <el-steps :active="currentStep" finish-status="success" align-center>
+          <el-step title="选择分类" description="选择商品分类" />
+          <el-step title="填写商品信息" description="填写商品基本信息" />
+          <el-step title="完成" description="确认提交" />
+        </el-steps>
+      </el-card>
+
+      <!-- 步骤内容 -->
+      <div class="step-content" v-loading="loading">
+        <!-- 步骤1: 选择分类 -->
+        <el-card v-show="currentStep === 0" shadow="never" class="step-card">
+          <template #header>
+            <div class="flex items-center justify-between">
+              <span class="text-lg font-bold">选择分类</span>
+              <span v-if="selectedLevel3Name" class="text-sm ml-4" style="color: #409eff"> 已选:{{ getCategoryPath() }} </span>
+            </div>
+          </template>
+
+          <div class="category-selection">
+            <el-row :gutter="20">
+              <!-- 一级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择一级分类</div>
+                  <div class="category-search">
+                    <el-input v-model="searchLevel1" placeholder="搜索一级分类" clearable prefix-icon="Search" size="small" />
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel1Categories"
+                      :key="item.id"
+                      :class="[
+                        'category-item',
+                        { 'active': categoryForm.topCategoryId === item.id, 'disabled': !item.children || item.children.length === 0 }
+                      ]"
+                      @click="selectLevel1(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.topCategoryId === item.id"><ArrowRight /></el-icon>
+                    </div>
+                    <el-empty v-if="filteredLevel1Categories.length === 0" description="暂无数据" :image-size="60" />
+                  </div>
+                </div>
+              </el-col>
+
+              <!-- 二级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择二级分类</div>
+                  <div class="category-search">
+                    <el-input v-model="searchLevel2" placeholder="搜索二级分类" clearable prefix-icon="Search" size="small" />
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel2Categories"
+                      :key="item.id"
+                      :class="[
+                        'category-item',
+                        { 'active': categoryForm.mediumCategoryId === item.id, 'disabled': !item.children || item.children.length === 0 }
+                      ]"
+                      @click="selectLevel2(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.mediumCategoryId === item.id"><ArrowRight /></el-icon>
+                    </div>
+                    <el-empty
+                      v-if="filteredLevel2Categories.length === 0"
+                      :description="categoryForm.topCategoryId ? '当前分类无子分类,请选择其他一级分类' : '请先选择一级分类'"
+                      :image-size="60"
+                    />
+                  </div>
+                </div>
+              </el-col>
+
+              <!-- 三级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择三级分类</div>
+                  <div class="category-search">
+                    <el-select
+                      v-model="level3SearchValue"
+                      placeholder="搜索全部三级分类"
+                      filterable
+                      remote
+                      clearable
+                      :remote-method="handleLevel3Search"
+                      :loading="level3SearchLoading"
+                      size="small"
+                      class="w-full"
+                      @change="handleLevel3SearchSelect"
+                    >
+                      <el-option v-for="item in level3SearchOptions" :key="item.id" :label="item.categoryName" :value="item.id" />
+                    </el-select>
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel3Categories"
+                      :key="item.id"
+                      :class="['category-item', { 'active': categoryForm.bottomCategoryId === item.id }]"
+                      @click="selectLevel3(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.bottomCategoryId === item.id"><Check /></el-icon>
+                    </div>
+                    <el-empty
+                      v-if="filteredLevel3Categories.length === 0"
+                      :description="categoryForm.mediumCategoryId ? '当前分类无子分类,请选择其他二级分类' : '请先选择二级分类'"
+                      :image-size="60"
+                    />
+                  </div>
+                </div>
+              </el-col>
+            </el-row>
+          </div>
+
+          <!-- 已选分类提示 -->
+          <!-- <div class="mt-4">
+            <el-checkbox v-model="autoCreateCategory" label="如果选择的分类不存在,自动创建分类" />
+          </div>
+          <div class="mt-2">
+            <el-input
+              v-model="manualCategoryInput"
+              placeholder="请输入入口类名称"
+              clearable
+              style="width: 400px;"
+            />
+          </div> -->
+        </el-card>
+
+        <!-- 步骤2: 填写商品信息 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card">
+          <template #header>
+            <span class="text-lg font-bold">基本信息</span>
+          </template>
+
+          <el-form ref="productFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <!-- 商品分类显示 -->
+            <el-form-item label="商品分类:">
+              <div class="category-display">
+                <span class="category-text">{{ getCategoryPath() }}</span>
+                <el-link type="primary" :underline="false" @click="currentStep = 0" class="ml-2">修改</el-link>
+                <el-link type="danger" :underline="false" @click="clearCategory" class="ml-2">删除</el-link>
+              </div>
+            </el-form-item>
+
+            <!-- 商品名称 -->
+            <el-form-item label="商品名称:" prop="itemName" required>
+              <el-input v-model="productForm.itemName" type="textarea" :rows="2" placeholder="请输入商品名称" maxlength="200" show-word-limit />
+            </el-form-item>
+
+            <!-- A10产品名称 -->
+            <el-form-item label="A10产品名称:">
+              <el-input
+                :value="a10ProductNameComputed"
+                type="textarea"
+                :rows="2"
+                disabled
+                placeholder="自动拼接:品牌名 + 规格型号 + 产品分类 + 发票规格"
+              />
+              <div class="form-item-tip">A10产品名称由系统自动拼接:品牌名 + 规格型号 + 产品分类(三级分类)+ 发票规格,无需手动填写</div>
+            </el-form-item>
+
+            <!-- 规格型号 和 UPC(69)条码 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="规格型号:">
+                  <el-input v-model="productForm.specificationsCode" placeholder="请输入规格型号" maxlength="20" show-word-limit />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="UPC(69)条码:">
+                  <el-input v-model="productForm.barCoding" placeholder="请输入UPC(69)条码" maxlength="20" show-word-limit @input="handleUpcInput" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 发票名称 和 发票规格 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="发票名称:">
+                  <el-input v-model="productForm.invoiceName" placeholder="请输入发票名称" maxlength="20" show-word-limit />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="发票规格:">
+                  <el-input v-model="productForm.invoiceSpecs" placeholder="请输入发票规格" maxlength="20" show-word-limit />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <!-- 商品品牌 -->
+              <el-col :span="12">
+                <el-form-item label="商品品牌:" prop="brandId" required>
+                  <el-select
+                    v-model="productForm.brandId"
+                    placeholder="请输入品牌名称搜索"
+                    filterable
+                    remote
+                    clearable
+                    :remote-method="handleBrandSearch"
+                    :loading="brandLoading"
+                    class="w-full"
+                  >
+                    <el-option v-for="item in brandOptions" :key="item.id" :label="`${item.brandNo},${item.brandName}`" :value="item.id" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+
+              <el-col :span="12">
+                <el-form-item label="单位:">
+                  <el-select
+                    v-model="productForm.unitId"
+                    placeholder="请选择"
+                    clearable
+                    class="w-full"
+                    :disabled="productForm.productReviewStatus === 1"
+                  >
+                    <el-option v-for="option in unitOptions" :key="option.id" :label="`${option.unitNo},${option.unitName}`" :value="option.id" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 税率编码 、税率 和 币种 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="税率编码:">
+                  <el-input
+                    v-model="taxCodeNo"
+                    placeholder="点击选择税率编码"
+                    readonly
+                    class="w-full"
+                    style="cursor: pointer"
+                    @click="taxCodeSelectRef?.open()"
+                  >
+                    <template #suffix>
+                      <el-icon style="cursor: pointer" @click.stop="taxCodeSelectRef?.open()"><Search /></el-icon>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="税率:" required>
+                  <el-select v-model="productForm.taxRate" placeholder="请选择税率" clearable class="w-full">
+                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="`${option.taxrateNo},${option.taxrateName}`" :value="option.taxrate" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="币种:">
+                  <el-select v-model="productForm.currency" placeholder="请选择" class="w-full">
+                    <el-option label="人民币(RMB)" value="RMB" />
+                    <el-option label="美元(USD)" value="USD" />
+                    <el-option label="欧元(EUR)" value="EUR" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- TaxCodeSelect 弹窗 -->
+            <TaxCodeSelect ref="taxCodeSelectRef" @select="handleTaxCodeSelect" />
+
+            <!-- 销量人气 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="销量人气:">
+                  <el-input
+                    v-model="productForm.salesVolume"
+                    type="number"
+                    placeholder="请输入销量人气"
+                    :min="0"
+                    step="1"
+                    @input="handleSalesVolumeInput"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 促销标题 -->
+            <el-form-item label="促销标题:">
+              <el-input v-model="productForm.packagingSpec" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
+            </el-form-item>
+
+            <!-- 商品简介 -->
+            <el-form-item label="商品简介:">
+              <el-input v-model="productForm.productDescription" type="textarea" :rows="3" placeholder="请输入商品简介" maxlength="500" show-word-limit />
+            </el-form-item>
+
+            <!-- 重量 和 体积 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="商品重量:">
+                  <el-input v-model="productForm.productWeight" placeholder="0" maxlength="10" show-word-limit>
+                    <template #append>
+                      <el-select v-model="productForm.weightUnit" placeholder="请选择" style="width: 100px">
+                        <el-option label="kg" value="kg" />
+                        <el-option label="g" value="g" />
+                        <el-option label="t" value="t" />
+                      </el-select>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="商品体积:">
+                  <el-input v-model="productForm.productVolume" placeholder="0" maxlength="10" show-word-limit>
+                    <template #append>
+                      <el-select v-model="productForm.volumeUnit" placeholder="请选择" style="width: 80px">
+                        <el-option label="m³" value="m3" />
+                        <el-option label="cm³" value="cm3" />
+                        <el-option label="L" value="L" />
+                      </el-select>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 参考链接 -->
+            <el-form-item label="参考链接">
+              <el-input v-model="productForm.referenceLink" type="textarea" :rows="3" placeholder="请输入参考链接" />
+            </el-form-item>
+
+            <!-- 售后服务 -->
+            <el-form-item label="售后服务:">
+              <el-select v-model="productForm.afterSalesService" placeholder="请选择" clearable class="w-full">
+                <el-option v-for="option in afterSalesOptions" :key="option.id" :label="option.afterSalesItems" :value="option.id" />
+              </el-select>
+            </el-form-item>
+
+            <!-- 服务保障 -->
+            <el-form-item label="服务保障:">
+              <el-checkbox-group v-model="serviceGuarantees">
+                <el-checkbox v-for="option in serviceGuaranteeOptions" :key="option.id" :label="option.ensureName" :value="option.id" />
+              </el-checkbox-group>
+            </el-form-item>
+
+            <!-- 安装服务 -->
+            <el-form-item label="安装服务:">
+              <el-checkbox-group v-model="installationServices">
+                <el-checkbox label="免费安装" value="freeInstallation" />
+              </el-checkbox-group>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <!-- 销售价格 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">销售价格</span>
+          </template>
+
+          <el-form ref="priceFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="市场价:" prop="marketPrice" required>
+                  <el-input v-model="productForm.marketPrice" type="number" placeholder="请输入市场价" :min="0" @blur="formatPrice('marketPrice')" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="官网价:" prop="memberPrice" required>
+                  <el-input
+                    v-model="productForm.memberPrice"
+                    type="number"
+                    placeholder="请输入平台售价"
+                    :min="0"
+                    @blur="formatPrice('memberPrice')"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="最低起订量:" prop="minOrderQuantity" required>
+                  <el-input v-model="productForm.minOrderQuantity" min="1" type="number" placeholder="请输入最低起订量" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="备注:">
+                  <span class="currency-text">市场价>官网价</span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+
+        <!-- 供应价格 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">供应价格</span>
+          </template>
+
+          <el-form label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="供应价:">
+                  <el-input
+                    v-model="productForm.supplyPrice"
+                    type="number"
+                    placeholder="请输入供应价"
+                    :min="0"
+                    @blur="formatPrice('supplyPrice')"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="供应有效时间:">
+                  <el-date-picker
+                    v-model="productForm.supplyValidityPeriod"
+                    type="date"
+                    placeholder="请选择供应有效时间"
+                    value-format="YYYY-MM-DD"
+                    class="w-full"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="是否包邮:">
+                  <el-radio-group v-model="productForm.supplyPostStatus">
+                    <el-radio :value="0">不包邮</el-radio>
+                    <el-radio :value="1">包邮</el-radio>
+                  </el-radio-group>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+        <!-- 自定义属性 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <div class="flex items-center justify-between">
+              <span class="text-lg font-bold">自定义属性</span>
+              <el-button type="primary" icon="Plus" size="small" @click="addDiyAttribute">添加属性</el-button>
+            </div>
+          </template>
+
+          <el-form label-width="0px" class="product-info-form">
+            <div v-if="diyAttributesList.length === 0" class="text-center text-gray-400 py-4 text-sm">
+              暂无自定义属性,点击右上角"添加属性"按钮添加
+            </div>
+            <el-row v-for="(item, index) in diyAttributesList" :key="index" :gutter="20" class="mb-2">
+              <el-col :span="11">
+                <el-input v-model="item.attributeKey" placeholder="请输入属性名称" clearable />
+              </el-col>
+              <el-col :span="11">
+                <el-input v-model="item.attributeValue" placeholder="请输入属性值" clearable />
+              </el-col>
+              <el-col :span="2" class="flex items-center">
+                <el-button type="danger" icon="Delete" circle size="small" @click="removeDiyAttribute(index)" />
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+
+        <!-- 商品属性 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">商品属性</span>
+          </template>
+
+          <el-form ref="attributeFormRef" :model="productForm" label-width="120px" class="product-info-form">
+            <div v-if="attributesList.length === 0" class="text-center text-gray-500 py-8">该分类暂无属性配置</div>
+            <template v-else>
+              <el-row :gutter="20" v-for="(row, rowIndex) in Math.ceil(attributesList.length / 2)" :key="rowIndex">
+                <el-col :span="12" v-for="colIndex in 2" :key="colIndex">
+                  <template v-if="attributesList[rowIndex * 2 + colIndex - 1]">
+                    <el-form-item
+                      :label="attributesList[rowIndex * 2 + colIndex - 1].productAttributesName + ':'"
+                      :required="attributesList[rowIndex * 2 + colIndex - 1].required === '1'"
+                    >
+                      <!-- 下拉选择 -->
+                      <el-select
+                        v-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '0'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
+                        placeholder="请选择"
+                        clearable
+                        class="w-full"
+                      >
+                        <el-option
+                          v-for="option in parseAttributesList(attributesList[rowIndex * 2 + colIndex - 1].attributesList)"
+                          :key="option"
+                          :label="option"
+                          :value="option"
+                        />
+                      </el-select>
+                      <!-- 多选 -->
+                      <el-select
+                        v-else-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '2'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
+                        placeholder="请选择"
+                        multiple
+                        clearable
+                        class="w-full"
+                      >
+                        <el-option
+                          v-for="option in parseAttributesList(attributesList[rowIndex * 2 + colIndex - 1].attributesList)"
+                          :key="option"
+                          :label="option"
+                          :value="option"
+                        />
+                      </el-select>
+                      <!-- 文本输入 -->
+                      <el-input
+                        v-else
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
+                        placeholder="请输入"
+                        clearable
+                      />
+                    </el-form-item>
+                  </template>
+                </el-col>
+              </el-row>
+            </template>
+          </el-form>
+        </el-card>
+
+        <!-- 商品详情 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">商品详情</span>
+          </template>
+
+          <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
+            <!-- 商品主图 -->
+            <el-form-item label="商品主图:" required>
+              <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
+              <div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
+            </el-form-item>
+
+            <!-- 商品轮播图 -->
+            <el-form-item label="商品轮播图:" required>
+              <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
+              <div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
+            </el-form-item>
+
+            <!-- 商品详情 -->
+            <el-form-item label="商品详情:" required>
+              <el-tabs v-model="activeDetailTab" type="border-card">
+                <el-tab-pane label="电脑端详情" name="pc">
+                  <Editor v-model="productForm.pcDetail" :height="400" />
+                </el-tab-pane>
+                <el-tab-pane label="移动端详情" name="mobile">
+                  <Editor v-model="productForm.mobileDetail" :height="400" />
+                </el-tab-pane>
+              </el-tabs>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <!-- 定制说明 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">定制说明</span>
+          </template>
+
+          <el-form ref="customFormRef" :model="customForm" label-width="120px" class="product-info-form">
+            <!-- 可定制开关 -->
+            <el-form-item label="可定制:">
+              <el-switch v-model="customForm.isCustomize" />
+            </el-form-item>
+
+            <!-- 定制内容 -->
+            <template v-if="customForm.isCustomize">
+              <!-- 定制方式 -->
+              <el-form-item label="定制方式:">
+                <div class="custom-options">
+                  <el-button
+                    v-for="option in customMethodOptions"
+                    :key="option.value"
+                    :type="customForm.selectedMethods.includes(option.value) ? 'primary' : 'default'"
+                    @click="toggleMethod(option.value)"
+                  >
+                    {{ option.label }}
+                  </el-button>
+                </div>
+              </el-form-item>
+
+              <!-- 定制工艺 -->
+              <el-form-item label="定制工艺:">
+                <div class="custom-options">
+                  <el-button
+                    v-for="craft in customCraftOptions"
+                    :key="craft.value"
+                    :type="customForm.selectedCrafts.includes(craft.value) ? 'primary' : 'default'"
+                    @click="toggleCraft(craft.value)"
+                  >
+                    {{ craft.label }}
+                  </el-button>
+                </div>
+              </el-form-item>
+
+              <!-- 定制方式表格 -->
+              <el-form-item label="" label-width="120">
+                <el-table :data="customForm.customDetails" border class="custom-table">
+                  <el-table-column label="装饰方法" width="120">
+                    <template #default="{ row }">
+                      <span>{{ row.decorationMethod }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="定制工艺" width="120">
+                    <template #default="{ row }">
+                      <span>{{ row.craft }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="起订数量" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.minOrderQty" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="起订价格" width="150">
+                    <template #default="{ row }">
+                      <el-input
+                        v-model="row.minOrderPrice"
+                        type="number"
+                        :min="0"
+                        placeholder="请输入"
+                        @blur="formatRowPrice(row, 'minOrderPrice')"
+                      />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="打样工期[天]" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.samplePeriod" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="生产周期[天]" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.productionPeriod" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="操作" width="100" fixed="right">
+                    <template #default="{ $index }">
+                      <el-link type="danger" :underline="false" @click="removeCustomDetail($index)"> 删除 </el-link>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-form-item>
+
+              <!-- 定制说明 -->
+              <el-form-item label="定制说明:">
+                <el-input v-model="customForm.customDescription" type="textarea" :rows="5" placeholder="请输入定制说明" />
+              </el-form-item>
+            </template>
+          </el-form>
+        </el-card>
+
+        <!-- 步骤3: 完成 -->
+        <el-card v-show="currentStep === 2" shadow="never" class="step-card completion-card">
+          <div class="completion-content">
+            <div class="success-icon">
+              <el-icon :size="80" color="#67c23a">
+                <CircleCheck />
+              </el-icon>
+            </div>
+            <div class="completion-text">商品编辑完成,请点击返回,继续其他操作</div>
+            <div class="completion-action">
+              <el-button type="primary" @click="handleBackToList">返回</el-button>
+            </div>
+          </div>
+        </el-card>
+      </div>
+
+      <!-- 底部操作按钮 -->
+      <el-card v-if="currentStep < 2" shadow="never" class="mt-3">
+        <div class="flex justify-center gap-4">
+          <el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
+          <el-button v-if="currentStep < 2" type="primary" @click="nextStep">下一步</el-button>
+          <el-button @click="handleBack">取消</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+import { Warning, ArrowRight, Check, Plus, CircleCheck, Search } from '@element-plus/icons-vue';
+import Editor from '@/components/Editor/index.vue';
+import UploadImage from '@/components/upload-image/index.vue';
+import TaxCodeSelect from '@/components/TaxCodeSelect/index.vue';
+import { categoryTreeVO, CategoryVO } from '@/api/product/category/types';
+import { BrandVO } from '@/api/product/brand/types';
+import { BaseForm } from '@/api/product/base/types';
+import { ClassificationDiyForm } from '@/api/product/classificationDiy/types';
+import { AttributesVO } from '@/api/product/attributes/types';
+import {
+  addBase,
+  updateBase,
+  getBase,
+  categoryTree,
+  categoryList,
+  categoryAttributeList,
+  getAfterSaleList,
+  getServiceList,
+  getUnitList,
+  getTaxRateList
+} from '@/api/product/base';
+import {
+  addBaseAudit,
+  updateBaseAudit,
+  getBaseAudit,
+} from '@/api/product/baseAudit';
+import { BaseAuditVO, BaseAuditQuery, BaseAuditForm } from '@/api/product/baseAudit/types';
+import { getTaxCode } from '@/api/system/taxCode';
+import { listBrand, getBrand } from '@/api/product/brand';
+import { listInfo } from '@/api/customer/supplierInfo';
+import { InfoVO } from '@/api/customer/supplierInfo/types';
+import { listComStaff } from '@/api/system/comStaff';
+import { ComStaffVO } from '@/api/system/comStaff/types';
+
+const route = useRoute();
+const router = useRouter();
+
+const currentStep = ref(0);
+const loading = ref(false);
+const submitLoading = ref(false);
+const productFormRef = ref();
+
+// 服务保障和安装服务的多选框
+const serviceGuarantees = ref<(string | number)[]>([]);
+const installationServices = ref<string[]>([]);
+
+// 商品详情选项卡
+const activeDetailTab = ref('pc');
+
+// 轮播图URL数组(UI管理用)
+const carouselImages = ref<string[]>([]);
+
+// 税率选项
+const taxRateOptions = ref<any[]>([]);
+
+// 税率编码选择组件
+const taxCodeSelectRef = ref();
+// 已选的税率编码(显示用)
+const taxCodeNo = ref('');
+
+// 处理税率编码选择
+const handleTaxCodeSelect = async (row: any) => {
+  (productForm as any).taxationId = row.id;
+  try {
+    const taxRes = await getTaxCode(row.id);
+    if (taxRes.data) {
+      taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
+    } else {
+      taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
+    }
+  } catch (e) {
+    console.error('获取税率编码详情失败:', e);
+    taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
+  }
+  // 同时将显示值存入 form,方便编辑回显时直接读取
+  (productForm as any).taxationNo = taxCodeNo.value;
+};
+
+// 定制说明表单
+const customForm = reactive({
+  isCustomize: false,
+  selectedMethods: [] as string[],
+  selectedCrafts: [] as string[],
+  customDetails: [] as Array<{
+    decorationMethod: string;
+    craft: string;
+    minOrderQty: string;
+    minOrderPrice: string;
+    samplePeriod: string;
+    productionPeriod: string;
+  }>,
+  customDescription: ''
+});
+
+// 定制方式选项
+const customMethodOptions = [
+  { label: '包装定制', value: 'package' },
+  { label: '商品定制', value: 'product' },
+  { label: '开模定制', value: 'mold' }
+];
+
+// 定制工艺选项
+const customCraftOptions = [
+  { label: '丝印', value: 'silkScreen' },
+  { label: '热转印', value: 'thermalTransfer' },
+  { label: '激光', value: 'laser' },
+  { label: '烤花', value: 'baking' },
+  { label: '压印', value: 'embossing' }
+];
+
+// 定制方式映射
+const customMethodMap: Record<string, string> = {
+  'package': '包装定制',
+  'product': '商品定制',
+  'mold': '开模定制'
+};
+
+// 定制工艺映射
+const customCraftMap: Record<string, string> = {
+  'silkScreen': '丝印',
+  'thermalTransfer': '热转印',
+  'laser': '激光',
+  'baking': '烤花',
+  'embossing': '压印'
+};
+
+// 服务保障选择不需要watch,在提交时直接转换为逗号分隔字符串
+
+// 监听安装服务复选框变化,同步到表单
+watch(
+  installationServices,
+  (newVal) => {
+    productForm.freeInstallation = newVal.includes('freeInstallation') ? '1' : '0';
+  },
+  { deep: true }
+);
+
+// 监听定制方式和工艺选择变化,更新表格数据
+watch(
+  [() => customForm.selectedMethods, () => customForm.selectedCrafts],
+  ([newMethods, newCrafts]) => {
+    const newDetails: typeof customForm.customDetails = [];
+
+    // 遍历所有选中的定制方式和工艺组合
+    newMethods.forEach((method) => {
+      const decorationMethod = customMethodMap[method];
+
+      newCrafts.forEach((craft) => {
+        const craftName = customCraftMap[craft];
+
+        // 查找是否已存在该组合的数据
+        const existing = customForm.customDetails.find((item) => item.decorationMethod === decorationMethod && item.craft === craftName);
+
+        newDetails.push(
+          existing || {
+            decorationMethod,
+            craft: craftName,
+            minOrderQty: '',
+            minOrderPrice: '',
+            samplePeriod: '',
+            productionPeriod: ''
+          }
+        );
+      });
+    });
+
+    customForm.customDetails = newDetails;
+  },
+  { deep: true }
+);
+
+// 切换定制方式选择
+const toggleMethod = (method: string) => {
+  const index = customForm.selectedMethods.indexOf(method);
+  if (index > -1) {
+    customForm.selectedMethods.splice(index, 1);
+  } else {
+    customForm.selectedMethods.push(method);
+  }
+};
+
+// 切换定制工艺选择
+const toggleCraft = (craft: string) => {
+  const index = customForm.selectedCrafts.indexOf(craft);
+  if (index > -1) {
+    customForm.selectedCrafts.splice(index, 1);
+  } else {
+    customForm.selectedCrafts.push(craft);
+  }
+};
+
+// 删除定制详情行
+const removeCustomDetail = (index: number) => {
+  customForm.customDetails.splice(index, 1);
+};
+
+const pageTitle = computed(() => {
+  return route.params.id ? '编辑商品' : '新增商品';
+});
+
+// 分类选择表单
+const categoryForm = reactive({
+  topCategoryId: undefined as string | number | undefined,
+  mediumCategoryId: undefined as string | number | undefined,
+  bottomCategoryId: undefined as string | number | undefined
+});
+
+const autoCreateCategory = ref(false);
+const manualCategoryInput = ref('');
+
+// 商品信息表单
+const productForm = reactive<BaseForm>({
+  id: undefined,
+  productNo: undefined,
+  itemName: undefined,
+  brandId: undefined,
+  topCategoryId: undefined,
+  mediumCategoryId: undefined,
+  bottomCategoryId: undefined,
+  unitId: undefined,
+  productImage: undefined,
+  imageUrl: undefined,
+  isSelf: 0,
+  productReviewStatus: 0,
+  homeRecommended: 0,
+  categoryRecommendation: 0,
+  cartRecommendation: 0,
+  recommendedProductOrder: 0,
+  isPopular: 0,
+  isNew: 0,
+  productStatus: '0',
+  remark: undefined,
+  a10ProductName: undefined,
+  specificationsCode: undefined,
+  barCoding: undefined,
+  invoiceName: undefined,
+  invoiceSpecs: undefined,
+  packagingSpec: undefined,
+  productDescription: undefined,
+  referenceLink: undefined,
+  productWeight: undefined,
+  weightUnit: 'kg',
+  productVolume: undefined,
+  volumeUnit: 'm3',
+  mainLibraryIntro: undefined,
+  afterSalesService: undefined,
+  serviceGuarantee: undefined, // 服务保障ID列表,逗号分隔
+  freeInstallation: '0',
+  marketPrice: undefined,
+  memberPrice: undefined,
+  minSellingPrice: undefined,
+  purchasingPrice: undefined,
+  maxPurchasePrice: undefined,
+  supplyPrice: undefined,
+  supplyValidityPeriod: undefined,
+  supplyPostStatus: undefined,
+  productNature: '1',
+  purchasingPersonnel: '1',
+  pcDetail: undefined,
+  mobileDetail: undefined,
+  taxRate: undefined,
+  currency: 'RMB',
+  minOrderQuantity: undefined,
+  salesVolume: undefined
+});
+
+// 表单验证规则
+const productRules = {
+  // productNo: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
+  itemName: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
+  brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
+  marketPrice: [{ required: true, message: '市场价不能为空', trigger: 'blur' }],
+  memberPrice: [{ required: true, message: '平台售价不能为空', trigger: 'blur' }],
+  minSellingPrice: [{ required: true, message: '最低售价不能为空', trigger: 'blur' }],
+  // purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  productNature: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
+  purchasingPersonnel: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
+  taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
+  minOrderQuantity: [{ required: true, message: '最低起订量不能为空', trigger: 'blur' }]
+};
+
+// 分类和品牌选项
+const categoryOptions = ref<categoryTreeVO[]>([]);
+const brandOptions = ref<BrandVO[]>([]);
+const brandLoading = ref(false);
+let brandSearchTimer: ReturnType<typeof setTimeout> | null = null;
+
+// 商品属性列表
+const attributesList = ref<AttributesVO[]>([]);
+const productAttributesValues = ref<Record<string | number, any>>({});
+
+// 售后服务和服务保障选项
+const afterSalesOptions = ref<any[]>([]);
+const serviceGuaranteeOptions = ref<any[]>([]);
+
+// 单位选项
+const unitOptions = ref<any[]>([]);
+
+// 主供应商选项
+const supplierOptions = ref<InfoVO[]>([]);
+
+// 自定义属性列表
+const diyAttributesList = ref<ClassificationDiyForm[]>([]);
+
+// 添加自定义属性行
+const addDiyAttribute = () => {
+  diyAttributesList.value.push({ attributeKey: '', attributeValue: '' });
+};
+
+// 删除自定义属性行
+const removeDiyAttribute = (index: number) => {
+  diyAttributesList.value.splice(index, 1);
+};
+
+// 采购人员选项
+const staffOptions = ref<ComStaffVO[]>([]);
+
+// 搜索关键词
+const searchLevel1 = ref('');
+const searchLevel2 = ref('');
+const searchLevel3 = ref('');
+
+// 三级分类下拉搜索
+const level3SearchValue = ref<string | number | null>(null);
+const level3SearchOptions = ref<CategoryVO[]>([]);
+const level3SearchLoading = ref(false);
+
+// 一级分类列表
+const level1Categories = computed(() => {
+  return categoryOptions.value || [];
+});
+
+// 二级分类列表
+const level2Categories = ref<categoryTreeVO[]>([]);
+
+// 三级分类列表
+const level3Categories = ref<categoryTreeVO[]>([]);
+
+// 过滤后的一级分类列表
+const filteredLevel1Categories = computed(() => {
+  if (!searchLevel1.value) {
+    return level1Categories.value;
+  }
+  return level1Categories.value.filter((item) => item.label.toLowerCase().includes(searchLevel1.value.toLowerCase()));
+});
+
+// 过滤后的二级分类列表
+const filteredLevel2Categories = computed(() => {
+  if (!searchLevel2.value) {
+    return level2Categories.value;
+  }
+  return level2Categories.value.filter((item) => item.label.toLowerCase().includes(searchLevel2.value.toLowerCase()));
+});
+
+// 过滤后的三级分类列表
+const filteredLevel3Categories = computed(() => {
+  if (!searchLevel3.value) {
+    return level3Categories.value;
+  }
+  return level3Categories.value.filter((item) => item.label.toLowerCase().includes(searchLevel3.value.toLowerCase()));
+});
+
+// 搜索三级分类(调用接口)
+const handleLevel3Search = async (keyword: string) => {
+  if (!keyword) {
+    level3SearchOptions.value = [];
+    return;
+  }
+  level3SearchLoading.value = true;
+  try {
+    const res = await categoryList({ classLevel: 3, categoryName: keyword, pageNum: 1, pageSize: 50 });
+    level3SearchOptions.value = (res as any).data || (res as any).rows || [];
+  } catch (error) {
+    console.error('搜索三级分类失败:', error);
+  } finally {
+    level3SearchLoading.value = false;
+  }
+};
+
+// 选择三级分类搜索结果后,自动在树中定位
+const handleLevel3SearchSelect = async (categoryId: string | number) => {
+  if (!categoryId) return;
+  const selectedCategory = level3SearchOptions.value.find((item) => String(item.id) === String(categoryId));
+  if (!selectedCategory) return;
+
+  // 在分类树中查找对应的二级节点(三级的父节点)
+  const level2Node = findCategoryById(categoryOptions.value, selectedCategory.parentId);
+  if (!level2Node) return;
+
+  // 在一级列表中查找(二级的父节点)
+  const level1Node = level1Categories.value.find((item) => String(item.id) === String(level2Node.parentId));
+  if (!level1Node) return;
+
+  // 依次选中一级、二级、三级
+  categoryForm.topCategoryId = level1Node.id;
+  selectedLevel1Name.value = level1Node.label;
+  level2Categories.value = level1Node.children || [];
+
+  await nextTick();
+
+  categoryForm.mediumCategoryId = level2Node.id;
+  selectedLevel2Name.value = level2Node.label;
+  level3Categories.value = level2Node.children || [];
+
+  await nextTick();
+
+  // 精确查找三级节点
+  const level3Node = level3Categories.value.find((item) => String(item.id) === String(selectedCategory.id));
+  if (level3Node) {
+    categoryForm.bottomCategoryId = level3Node.id;
+    selectedLevel3Name.value = level3Node.label;
+    await loadCategoryAttributes(level3Node.id);
+  } else {
+    categoryForm.bottomCategoryId = selectedCategory.id;
+    selectedLevel3Name.value = selectedCategory.categoryName;
+    await loadCategoryAttributes(selectedCategory.id);
+  }
+
+  // 清空搜索框
+  level3SearchValue.value = null;
+  level3SearchOptions.value = [];
+};
+
+// 选中的分类名称
+const selectedLevel1Name = ref('');
+const selectedLevel2Name = ref('');
+const selectedLevel3Name = ref('');
+
+// 选择一级分类
+const selectLevel1 = (item: categoryTreeVO) => {
+  if (!item.children || item.children.length === 0) {
+    ElMessage.warning('该分类无子分类,请选择含三级分类的类别');
+    return;
+  }
+  categoryForm.topCategoryId = item.id;
+  categoryForm.mediumCategoryId = undefined;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel1Name.value = item.label;
+  selectedLevel2Name.value = '';
+  selectedLevel3Name.value = '';
+
+  level2Categories.value = item.children || [];
+  level3Categories.value = [];
+};
+
+// 选择二级分类
+const selectLevel2 = (item: categoryTreeVO) => {
+  if (!item.children || item.children.length === 0) {
+    ElMessage.warning('该分类无子分类,请选择含三级分类的类别');
+    return;
+  }
+  categoryForm.mediumCategoryId = item.id;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel2Name.value = item.label;
+  selectedLevel3Name.value = '';
+
+  level3Categories.value = item.children || [];
+};
+
+// 选择三级分类
+const selectLevel3 = async (item: categoryTreeVO) => {
+  categoryForm.bottomCategoryId = item.id;
+  selectedLevel3Name.value = item.label;
+
+  // 加载该分类下的属性列表
+  await loadCategoryAttributes(item.id);
+};
+
+// 获取分类路径
+const getCategoryPath = () => {
+  const parts = [];
+  if (selectedLevel1Name.value) parts.push(selectedLevel1Name.value);
+  if (selectedLevel2Name.value) parts.push(selectedLevel2Name.value);
+  if (selectedLevel3Name.value) parts.push(selectedLevel3Name.value);
+  return parts.join(' > ') || '请选择分类';
+};
+
+// 清除分类
+const clearCategory = () => {
+  categoryForm.topCategoryId = undefined;
+  categoryForm.mediumCategoryId = undefined;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel1Name.value = '';
+  selectedLevel2Name.value = '';
+  selectedLevel3Name.value = '';
+  level2Categories.value = [];
+  level3Categories.value = [];
+  attributesList.value = [];
+  productAttributesValues.value = {};
+};
+
+// 下一步
+const nextStep = async () => {
+  if (currentStep.value === 0) {
+    // 验证分类选择
+    if (!categoryForm.topCategoryId) {
+      ElMessage.warning('请选择一级分类');
+      return;
+    }
+    if (!categoryForm.mediumCategoryId) {
+      ElMessage.warning('请选择二级分类');
+      return;
+    }
+    if (!categoryForm.bottomCategoryId) {
+      ElMessage.warning('请选择三级分类');
+      return;
+    }
+
+    // 将分类信息同步到商品表单
+    productForm.topCategoryId = categoryForm.topCategoryId;
+    productForm.mediumCategoryId = categoryForm.mediumCategoryId;
+    productForm.bottomCategoryId = categoryForm.bottomCategoryId;
+
+    currentStep.value++;
+  } else if (currentStep.value === 1) {
+    // 验证商品信息表单并提交
+    try {
+      await productFormRef.value?.validate();
+      // 调用提交函数
+      await handleSubmit();
+    } catch (error) {
+      ElMessage.warning('请完善商品信息');
+      return;
+    }
+  }
+};
+
+// 上一步
+const prevStep = () => {
+  if (currentStep.value > 0) {
+    currentStep.value--;
+  }
+};
+
+// 提交
+const handleSubmit = async () => {
+  try {
+    submitLoading.value = true;
+
+    // 校验商品主图、轮播图、详情必填
+    if (!productForm.productImage) {
+      ElMessage.warning('请上传商品主图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!carouselImages.value || carouselImages.value.length === 0) {
+      ElMessage.warning('请上传商品轮播图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!productForm.pcDetail) {
+      ElMessage.warning('请填写电脑端商品详情');
+      submitLoading.value = false;
+      return;
+    }
+
+    // 校验价格关系:市场价 > 官网价 > 最低售价
+    const midRange = parseFloat(String(productForm.marketPrice));
+    const standard = parseFloat(String(productForm.memberPrice));
+    const certificate = parseFloat(String(productForm.minSellingPrice));
+    if (!isNaN(midRange) && !isNaN(standard) && !isNaN(certificate)) {
+      if (!(midRange > standard)) {
+        ElMessage.warning('市场价必须大于官网价');
+        submitLoading.value = false;
+        return;
+      }
+      if (!(standard > certificate)) {
+        ElMessage.warning('官网价必须大于最低售价');
+        submitLoading.value = false;
+        return;
+      }
+    }
+
+    // 准备提交数据,包含定制信息(A10产品名称由前端自动拼接,不传后端)
+    const submitProductData: any = {
+      ...productForm,
+      // 将服务保障ID数组转换为逗号分隔字符串
+      serviceGuarantee: serviceGuarantees.value.map((id) => String(id)).join(','),
+      // 轮播图URL逗号分隔
+      imageUrl: carouselImages.value.join(','),
+      // 将商品属性值转换为JSON字符串
+      attributesList: JSON.stringify(productAttributesValues.value),
+      isCustomize: customForm.isCustomize ? 1 : 0,
+      customizedStyle: customForm.selectedMethods.join(','),
+      customizedCraft: customForm.selectedCrafts.join(','),
+      customDescription: customForm.customDescription,
+      customDetailsJson: JSON.stringify(customForm.customDetails),
+      diyAttributesList: diyAttributesList.value.filter((item) => item.attributeKey || item.attributeValue)
+    };
+    // A10产品名称不传后端
+    delete submitProductData.a10ProductName;
+
+    const auditData: BaseAuditForm = {
+      id: route.params.id as string,
+      productData: JSON.stringify(submitProductData),
+      type: 0,
+      auditStatus: 0
+    };
+
+    if (auditData.id) {
+      await updateBaseAudit(auditData);
+      ElMessage.success('修改成功');
+    } else {
+      await addBaseAudit(auditData);
+      ElMessage.success('新增成功');
+    }
+    // 跳转到完成页面(步骤3)
+    currentStep.value = 2;
+  } catch (error) {
+    console.error('提交失败:', error);
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+// 返回
+const handleBack = () => {
+  router.back();
+};
+
+// 返回列表
+const handleBackToList = () => {
+  router.push('/product/base');
+};
+
+// UPC(69)条码只允许输入数字
+const handleUpcInput = () => {
+  if (productForm.barCoding) {
+    productForm.barCoding = productForm.barCoding.replace(/\D/g, '');
+  }
+};
+
+// 销量人气只允许输入整数
+const handleSalesVolumeInput = (val: string) => {
+  if (val !== undefined && val !== null && val !== '') {
+    const intVal = parseInt(String(val).replace(/[^\d]/g, ''), 10);
+    productForm.salesVolume = !isNaN(intVal) ? intVal : undefined;
+  } else {
+    productForm.salesVolume = undefined;
+  }
+};
+
+// A10产品名称自动拼接(品牌名 + 规格型号 + 产品分类 + 发票规格)
+const a10ProductNameComputed = computed(() => {
+  const brand = brandOptions.value.find((b) => Number(b.id) === Number(productForm.brandId));
+  const brandName = brand?.brandName || '';
+  const specificationsCode = productForm.specificationsCode || '';
+  const categoryName = selectedLevel3Name.value || '';
+  const invoiceSpecs = productForm.invoiceSpecs || '';
+  return [brandName, specificationsCode, categoryName, invoiceSpecs].filter((s) => s.trim()).join(' ');
+});
+
+// 格式化价格为两位小数(不允许负数)
+const formatPrice = (field: string) => {
+  const val = (productForm as any)[field];
+  if (val !== undefined && val !== null && val !== '') {
+    let num = parseFloat(String(val));
+    if (!isNaN(num)) {
+      // 不允许负数
+      if (num < 0) num = 0;
+      (productForm as any)[field] = num.toFixed(2);
+    }
+  }
+};
+
+// 格式化表格行中的价格为两位小数(不允许负数)
+const formatRowPrice = (row: any, field: string) => {
+  const val = row[field];
+  if (val !== undefined && val !== null && val !== '') {
+    let num = parseFloat(String(val));
+    if (!isNaN(num)) {
+      // 不允许负数
+      if (num < 0) num = 0;
+      row[field] = num.toFixed(2);
+    }
+  }
+};
+
+// 获取分类树
+const getCategoryTree = async () => {
+  try {
+    const res = await categoryTree();
+    categoryOptions.value = res.data || [];
+  } catch (error) {
+    console.error('获取分类树失败:', error);
+  }
+};
+
+// 加载品牌选项(默认100条)
+const loadBrandOptions = async (keyword?: string) => {
+  brandLoading.value = true;
+  try {
+    const res = await listBrand({ pageNum: 1, pageSize: 100, brandName: keyword });
+    const newList = res.rows || [];
+    // 编辑模式下保留当前选中的品牌,避免被新列表覆盖后 A10产品名称 computed 失效
+    if (productForm.brandId) {
+      const exists = newList.find((item) => Number(item.id) === Number(productForm.brandId));
+      if (!exists) {
+        const currentBrand = brandOptions.value.find((item) => Number(item.id) === Number(productForm.brandId));
+        if (currentBrand) {
+          newList.unshift(currentBrand);
+        }
+      }
+    }
+    brandOptions.value = newList;
+  } catch (error) {
+    console.error('加载品牌列表失败:', error);
+  } finally {
+    brandLoading.value = false;
+  }
+};
+
+// 品牌远程搜索(防抖)
+const handleBrandSearch = (query: string) => {
+  if (brandSearchTimer) clearTimeout(brandSearchTimer);
+  brandSearchTimer = setTimeout(() => {
+    loadBrandOptions(query || undefined);
+  }, 300);
+};
+
+// 处理品牌下拉框显示/隐藏
+const handleBrandVisibleChange = (visible: boolean) => {
+  if (visible && brandOptions.value.length === 0) {
+    loadBrandOptions();
+  }
+};
+
+// 获取售后服务列表
+const getAfterSalesOptions = async () => {
+  try {
+    const res = await getAfterSaleList();
+    afterSalesOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && afterSalesOptions.value.length > 0 && !productForm.afterSalesService) {
+      productForm.afterSalesService = afterSalesOptions.value[0].id;
+    }
+  } catch (error) {
+    console.error('获取售后服务列表失败:', error);
+  }
+};
+
+// 获取服务保障列表
+const getServiceGuaranteeOptions = async () => {
+  try {
+    const res = await getServiceList();
+    serviceGuaranteeOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认选中
+    if (!route.params.id && serviceGuaranteeOptions.value.length > 0 && serviceGuarantees.value.length === 0) {
+      serviceGuarantees.value = [serviceGuaranteeOptions.value[0].id];
+    }
+  } catch (error) {
+    console.error('获取服务保障列表失败:', error);
+  }
+};
+
+// 获取单位列表
+const getUnitOptions = async () => {
+  try {
+    const res = await getUnitList();
+    unitOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && unitOptions.value.length > 0 && !productForm.unitId) {
+      productForm.unitId = unitOptions.value[0].id;
+    }
+  } catch (error) {
+    console.error('获取单位列表失败:', error);
+  }
+};
+
+// 获取主供应商列表
+const getSupplierOptions = async () => {
+  try {
+    const res = await listInfo();
+    console.log('供应商接口返回:', res);
+    // 处理可能的数据结构: res.data 或 res.rows
+    const dataList = res.data || res.rows || [];
+    supplierOptions.value = dataList;
+    console.log('供应商列表:', supplierOptions.value);
+    // 如果有选项且当前没有选中值,设置第一个为默认值
+    if (supplierOptions.value.length > 0 && !productForm.mainLibraryIntro) {
+      productForm.mainLibraryIntro = String(supplierOptions.value[0].id);
+    }
+  } catch (error) {
+    console.error('获取主供应商列表失败:', error);
+  }
+};
+
+// 获取采购人员列表
+const getStaffOptions = async () => {
+  try {
+    const res = await listComStaff();
+    console.log('采购人员接口返回:', res);
+    // 处理可能的数据结构: res.data 或 res.rows
+    const dataList = res.data || res.rows || [];
+    staffOptions.value = dataList;
+    console.log('采购人员列表:', staffOptions.value);
+    // 如果有选项且当前没有选中值,设置第一个为默认值
+    if (staffOptions.value.length > 0 && !productForm.purchasingPersonnel) {
+      productForm.purchasingPersonnel = String(staffOptions.value[0].staffId);
+    }
+  } catch (error) {
+    console.error('获取采购人员列表失败:', error);
+  }
+};
+
+// 获取税率列表
+const getTaxRateOptions = async () => {
+  try {
+    const res = await getTaxRateList();
+    taxRateOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('获取税率列表失败:', error);
+  }
+};
+
+// 加载分类属性列表
+const loadCategoryAttributes = async (categoryId: string | number) => {
+  try {
+    const res = await categoryAttributeList(categoryId);
+    attributesList.value = res.data || [];
+    // 清空之前的属性值
+    productAttributesValues.value = {};
+
+    // 如果是新增模式,为有选项的属性设置默认值
+    if (!route.params.id) {
+      attributesList.value.forEach((attr) => {
+        if (attr.entryMethod === '1' && attr.attributesList) {
+          // 下拉选择
+          const options = parseAttributesList(attr.attributesList);
+          if (options.length > 0) {
+            productAttributesValues.value[attr.productAttributesName] = options[0];
+          }
+        } else if (attr.entryMethod === '3' && attr.attributesList) {
+          // 多选
+          const options = parseAttributesList(attr.attributesList);
+          if (options.length > 0) {
+            productAttributesValues.value[attr.productAttributesName] = [options[0]];
+          }
+        }
+      });
+    }
+  } catch (error) {
+    console.error('加载分类属性失败:', error);
+    attributesList.value = [];
+  }
+};
+
+// 解析属性值列表(JSON数组或逗号分隔字符串)
+const parseAttributesList = (attributesListStr: string): string[] => {
+  if (!attributesListStr) return [];
+
+  try {
+    // 尝试解析为JSON数组
+    const parsed = JSON.parse(attributesListStr);
+    if (Array.isArray(parsed)) {
+      return parsed;
+    }
+  } catch (e) {
+    // 如果不是JSON,按逗号分隔
+    return attributesListStr
+      .split(',')
+      .map((item) => item.trim())
+      .filter((item) => item);
+  }
+
+  return [];
+};
+
+// 加载商品详情(编辑模式)
+const loadProductDetail = async () => {
+  const id = route.params.id;
+  if (id) {
+    try {
+      loading.value = true;
+      const res = await getBaseAudit(id as string);
+      Object.assign(productForm, res.data.productBaseVo);
+
+      // 回显产品经理 - 确保转换为字符串类型以匹配下拉框的value
+      if (res.data.productBaseVo.productNature !== undefined && res.data.productBaseVo.productNature !== null) {
+        productForm.productNature = String(res.data.productBaseVo.productNature);
+      }
+
+      // 回显采购人员 - 确保转换为字符串类型以匹配下拉框的value
+      if (res.data.productBaseVo.purchasingPersonnel !== undefined && res.data.productBaseVo.purchasingPersonnel !== null) {
+        productForm.purchasingPersonnel = String(res.data.productBaseVo.purchasingPersonnel);
+      }
+
+      // 回显税率编码显示值
+      const rawData = res.data.productBaseVo as any;
+      // 通过 taxationId 调接口获取中文名称回显
+      if (rawData.taxationId) {
+        try {
+          const taxRes = await getTaxCode(rawData.taxationId);
+          if (taxRes.data) {
+            taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
+          }
+        } catch (e) {
+          console.error('获取税率编码失败:', e);
+        }
+      }
+
+      // 回显税率 - 在税率选项中查找匹配的值(处理浮点数精度问题)
+      if (res.data.productBaseVo.taxRate !== undefined && res.data.productBaseVo.taxRate !== null) {
+        const apiTaxRate = Number(res.data.productBaseVo.taxRate);
+        // 使用精度容差比较浮点数
+        const matchedOption = taxRateOptions.value.find((opt) => Math.abs(Number(opt.taxrate) - apiTaxRate) < 0.0001);
+        if (matchedOption) {
+          productForm.taxRate = matchedOption.taxrate;
+        } else {
+          productForm.taxRate = apiTaxRate;
+        }
+      }
+
+      // 回显单位 - 确保类型与下拉选项的id一致(数字类型)
+      if (res.data.productBaseVo.unitId !== undefined && res.data.productBaseVo.unitId !== null) {
+        productForm.unitId = Number(res.data.productBaseVo.unitId);
+      }
+
+      // 回显品牌 - 先加载对应的品牌信息到选项列表中
+      if (res.data.productBaseVo.brandId) {
+        productForm.brandId = Number(res.data.productBaseVo.brandId);
+        try {
+          const brandRes = await getBrand(res.data.productBaseVo.brandId);
+          if (brandRes.data) {
+            // 检查品牌是否已在选项列表中
+            const existBrand = brandOptions.value.find((item) => Number(item.id) === Number(res.data.productBaseVo.brandId));
+            if (!existBrand) {
+              brandOptions.value.unshift(brandRes.data);
+            }
+          }
+        } catch (e) {
+          console.error('加载品牌信息失败:', e);
+        }
+      }
+
+      // 回显售后服务 - 确保类型与下拉选项的id一致(数字类型)
+      if (res.data.productBaseVo.afterSalesService !== undefined && res.data.productBaseVo.afterSalesService !== null) {
+        productForm.afterSalesService = String(res.data.productBaseVo.afterSalesService);
+      }
+
+      // 回显轮播图
+      if (res.data.productBaseVo.imageUrl) {
+        carouselImages.value = res.data.productBaseVo.imageUrl.split(',').filter((url: string) => url.trim());
+      } else {
+        carouselImages.value = [];
+      }
+
+      // 回显分类选择
+      categoryForm.topCategoryId = res.data.productBaseVo.topCategoryId;
+      categoryForm.mediumCategoryId = res.data.productBaseVo.mediumCategoryId;
+      categoryForm.bottomCategoryId = res.data.productBaseVo.bottomCategoryId;
+
+      // 回显服务保障复选框 - 将逗号分隔的ID字符串转换为数组
+      if (res.data.productBaseVo.serviceGuarantee) {
+        serviceGuarantees.value = res.data.productBaseVo.serviceGuarantee.split(',').map((id: string) => {
+          // 尝试转换为数字,如果失败则保持字符串
+          const numId = Number(id.trim());
+          return isNaN(numId) ? id.trim() : numId;
+        });
+      } else {
+        serviceGuarantees.value = [];
+      }
+
+      // 回显安装服务复选框
+      const services: string[] = [];
+      if (res.data.productBaseVo.freeInstallation === '1') services.push('freeInstallation');
+      installationServices.value = services;
+
+      // 回显分类名称 - 使用nextTick确保DOM更新后再查找子分类
+      await restoreCategorySelection();
+
+      // 回显商品属性值(必须在restoreCategorySelection之后,避免loadCategoryAttributes清空属性值)
+      if (res.data.productBaseVo.attributesList) {
+        try {
+          const parsedAttributes = JSON.parse(res.data.productBaseVo.attributesList);
+          productAttributesValues.value = parsedAttributes;
+        } catch (e) {
+          console.error('解析商品属性失败:', e);
+          productAttributesValues.value = {};
+        }
+      }
+
+      // 回显自定义属性列表
+      const rawResData = res.data as any;
+      if (Array.isArray(rawResData.productBaseVo.diyAttributesList) && rawResData.productBaseVo.diyAttributesList.length > 0) {
+        diyAttributesList.value = rawResData.productBaseVo.diyAttributesList;
+      } else {
+        diyAttributesList.value = [];
+      }
+    } catch (error) {
+      console.error('加载商品详情失败:', error);
+      ElMessage.error('加载商品详情失败');
+    } finally {
+      loading.value = false;
+    }
+  }
+};
+
+// 递归查找分类节点
+const findCategoryById = (categories: categoryTreeVO[], id: string | number): categoryTreeVO | null => {
+  for (const category of categories) {
+    if (String(category.id) === String(id)) {
+      return category;
+    }
+    if (category.children && category.children.length > 0) {
+      const found = findCategoryById(category.children, id);
+      if (found) return found;
+    }
+  }
+  return null;
+};
+
+// 恢复分类选择状态
+const restoreCategorySelection = async () => {
+  // 先保存原始的分类ID值
+  const originalTopCategoryId = categoryForm.topCategoryId;
+  const originalMediumCategoryId = categoryForm.mediumCategoryId;
+  const originalBottomCategoryId = categoryForm.bottomCategoryId;
+
+  console.log('回显分类 - 原始ID:', {
+    top: originalTopCategoryId,
+    medium: originalMediumCategoryId,
+    bottom: originalBottomCategoryId
+  });
+
+  if (!originalTopCategoryId) return;
+
+  // 查找一级分类
+  const level1 = level1Categories.value.find((item) => String(item.id) === String(originalTopCategoryId));
+  console.log('查找一级分类:', level1);
+  if (!level1) return;
+
+  // 设置一级分类选中状态
+  categoryForm.topCategoryId = level1.id;
+  selectedLevel1Name.value = level1.label;
+  level2Categories.value = level1.children || [];
+
+  await nextTick();
+
+  // 查找二级分类
+  if (originalMediumCategoryId) {
+    // 先在当前一级分类的children中查找
+    let level2 = level2Categories.value.find((item) => String(item.id) === String(originalMediumCategoryId));
+
+    // 如果找不到,尝试在整个分类树中查找(容错处理)
+    if (!level2) {
+      console.log('二级分类在当前一级下未找到,尝试全局查找...');
+      level2 = findCategoryById(categoryOptions.value, originalMediumCategoryId);
+    }
+
+    console.log('查找二级分类:', level2);
+    if (level2) {
+      categoryForm.mediumCategoryId = level2.id;
+      selectedLevel2Name.value = level2.label;
+      level3Categories.value = level2.children || [];
+
+      await nextTick();
+
+      // 查找三级分类
+      if (originalBottomCategoryId) {
+        // 先在当前二级分类的children中查找
+        let level3 = level3Categories.value.find((item) => String(item.id) === String(originalBottomCategoryId));
+
+        // 如果找不到,尝试在整个分类树中查找(容错处理)
+        if (!level3) {
+          console.log('三级分类在当前二级下未找到,尝试全局查找...');
+          level3 = findCategoryById(categoryOptions.value, originalBottomCategoryId);
+        }
+
+        console.log('查找三级分类:', level3, '原始ID:', originalBottomCategoryId);
+        if (level3) {
+          categoryForm.bottomCategoryId = level3.id;
+          selectedLevel3Name.value = level3.label;
+          console.log('设置三级分类名称:', selectedLevel3Name.value);
+          await loadCategoryAttributes(level3.id);
+        }
+      }
+    }
+  }
+};
+
+onMounted(async () => {
+  // 编辑模式下先直接跳到第二步,再加载数据,避免闪烁步骤一
+  if (route.params.id) {
+    currentStep.value = 1;
+  }
+  await getCategoryTree();
+  await getUnitOptions();
+  await getAfterSalesOptions();
+  await getServiceGuaranteeOptions();
+  await getTaxRateOptions();
+  // 先加载商品详情(如果是编辑模式)
+  await loadProductDetail();
+  // 再加载下拉选项,这样如果详情中没有值,会自动设置第一个
+  await getSupplierOptions();
+  await getStaffOptions();
+  loadBrandOptions();
+});
+</script>
+
+<style scoped lang="scss">
+.product-wizard-page {
+  .category-selection {
+    margin-top: 12px;
+  }
+
+  .category-box {
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    overflow: hidden;
+
+    .category-header {
+      background-color: #f5f7fa;
+      padding: 10px 12px;
+      font-weight: 600;
+      border-bottom: 1px solid #e4e7ed;
+      text-align: center;
+      font-size: 14px;
+    }
+
+    .category-search {
+      padding: 10px;
+      border-bottom: 1px solid #e4e7ed;
+      background-color: #fff;
+    }
+
+    .category-list {
+      height: 280px;
+      overflow-y: auto;
+
+      .category-item {
+        padding: 10px 12px;
+        cursor: pointer;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        border-bottom: 1px solid #f0f0f0;
+        transition: all 0.3s;
+
+        &:hover {
+          background-color: #f5f7fa;
+        }
+
+        &.active {
+          background-color: #ecf5ff;
+          color: #409eff;
+          font-weight: 600;
+        }
+
+        &:last-child {
+          border-bottom: none;
+        }
+
+        &.disabled {
+          cursor: not-allowed;
+          opacity: 0.45;
+          pointer-events: none;
+        }
+      }
+    }
+  }
+
+  .confirm-info {
+    margin-top: 12px;
+    text-align: left;
+  }
+
+  .product-info-form {
+    .category-display {
+      display: flex;
+      align-items: center;
+
+      .category-text {
+        color: #606266;
+      }
+    }
+
+    .form-item-tip {
+      font-size: 12px;
+      color: #909399;
+      line-height: 1.5;
+      margin-top: 4px;
+    }
+
+    .currency-text {
+      color: #303133;
+      font-size: 14px;
+    }
+  }
+
+  .custom-options {
+    display: flex;
+    gap: 10px;
+    flex-wrap: wrap;
+  }
+
+  .custom-table {
+    width: 100%;
+    margin-top: 10px;
+  }
+
+  .completion-card {
+    min-height: 400px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .completion-content {
+      text-align: center;
+      padding: 40px 0;
+
+      .success-icon {
+        margin-bottom: 24px;
+      }
+
+      .completion-text {
+        font-size: 16px;
+        color: #606266;
+        margin-bottom: 32px;
+        line-height: 1.6;
+      }
+
+      .completion-action {
+        display: flex;
+        justify-content: center;
+      }
+    }
+  }
+}
+</style>

+ 1995 - 0
src/views/product/baseAudit/add1.vue

@@ -0,0 +1,1995 @@
+<template>
+  <div class="app-container">
+    <el-card shadow="never" class="mb-3">
+      <div class="flex items-center justify-between">
+        <div class="flex items-center">
+          <el-button icon="ArrowLeft" @click="handleBack">返回</el-button>
+          <span class="ml-4 text-xl font-bold">{{ pageTitle }}</span>
+        </div>
+      </div>
+    </el-card>
+
+    <div class="product-wizard-page">
+      <!-- 步骤条 -->
+      <el-card shadow="never" class="mb-3">
+        <el-steps :active="currentStep" finish-status="success" align-center>
+          <el-step title="选择分类" description="选择商品分类" />
+          <el-step title="填写商品信息" description="填写商品基本信息" />
+          <el-step title="完成" description="确认提交" />
+        </el-steps>
+      </el-card>
+
+      <!-- 步骤内容 -->
+      <div class="step-content" v-loading="loading">
+        <!-- 步骤1: 选择分类 -->
+        <el-card v-show="currentStep === 0" shadow="never" class="step-card">
+          <template #header>
+            <div class="flex items-center justify-between">
+              <span class="text-lg font-bold">选择分类</span>
+              <span v-if="selectedLevel3Name" class="text-sm ml-4" style="color: #409eff"> 已选:{{ getCategoryPath() }} </span>
+            </div>
+          </template>
+
+          <div class="category-selection">
+            <el-row :gutter="20">
+              <!-- 一级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择一级分类</div>
+                  <div class="category-search">
+                    <el-input v-model="searchLevel1" placeholder="搜索一级分类" clearable prefix-icon="Search" size="small" />
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel1Categories"
+                      :key="item.id"
+                      :class="[
+                        'category-item',
+                        { 'active': categoryForm.topCategoryId === item.id, 'disabled': !item.children || item.children.length === 0 }
+                      ]"
+                      @click="selectLevel1(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.topCategoryId === item.id"><ArrowRight /></el-icon>
+                    </div>
+                    <el-empty v-if="filteredLevel1Categories.length === 0" description="暂无数据" :image-size="60" />
+                  </div>
+                </div>
+              </el-col>
+
+              <!-- 二级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择二级分类</div>
+                  <div class="category-search">
+                    <el-input v-model="searchLevel2" placeholder="搜索二级分类" clearable prefix-icon="Search" size="small" />
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel2Categories"
+                      :key="item.id"
+                      :class="[
+                        'category-item',
+                        { 'active': categoryForm.mediumCategoryId === item.id, 'disabled': !item.children || item.children.length === 0 }
+                      ]"
+                      @click="selectLevel2(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.mediumCategoryId === item.id"><ArrowRight /></el-icon>
+                    </div>
+                    <el-empty
+                      v-if="filteredLevel2Categories.length === 0"
+                      :description="categoryForm.topCategoryId ? '当前分类无子分类,请选择其他一级分类' : '请先选择一级分类'"
+                      :image-size="60"
+                    />
+                  </div>
+                </div>
+              </el-col>
+
+              <!-- 三级分类 -->
+              <el-col :span="8">
+                <div class="category-box">
+                  <div class="category-header">选择三级分类</div>
+                  <div class="category-search">
+                    <el-select
+                      v-model="level3SearchValue"
+                      placeholder="搜索全部三级分类"
+                      filterable
+                      remote
+                      clearable
+                      :remote-method="handleLevel3Search"
+                      :loading="level3SearchLoading"
+                      size="small"
+                      class="w-full"
+                      @change="handleLevel3SearchSelect"
+                    >
+                      <el-option v-for="item in level3SearchOptions" :key="item.id" :label="item.categoryName" :value="item.id" />
+                    </el-select>
+                  </div>
+                  <div class="category-list">
+                    <div
+                      v-for="item in filteredLevel3Categories"
+                      :key="item.id"
+                      :class="['category-item', { 'active': categoryForm.bottomCategoryId === item.id }]"
+                      @click="selectLevel3(item)"
+                    >
+                      <span>{{ item.label }}</span>
+                      <el-icon v-if="categoryForm.bottomCategoryId === item.id"><Check /></el-icon>
+                    </div>
+                    <el-empty
+                      v-if="filteredLevel3Categories.length === 0"
+                      :description="categoryForm.mediumCategoryId ? '当前分类无子分类,请选择其他二级分类' : '请先选择二级分类'"
+                      :image-size="60"
+                    />
+                  </div>
+                </div>
+              </el-col>
+            </el-row>
+          </div>
+
+          <!-- 已选分类提示 -->
+          <!-- <div class="mt-4">
+            <el-checkbox v-model="autoCreateCategory" label="如果选择的分类不存在,自动创建分类" />
+          </div>
+          <div class="mt-2">
+            <el-input
+              v-model="manualCategoryInput"
+              placeholder="请输入入口类名称"
+              clearable
+              style="width: 400px;"
+            />
+          </div> -->
+        </el-card>
+
+        <!-- 步骤2: 填写商品信息 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card">
+          <template #header>
+            <span class="text-lg font-bold">基本信息</span>
+          </template>
+
+          <el-form ref="productFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <!-- 商品分类显示 -->
+            <el-form-item label="商品分类:">
+              <div class="category-display">
+                <span class="category-text">{{ getCategoryPath() }}</span>
+                <el-link type="primary" :underline="false" @click="currentStep = 0" class="ml-2">修改</el-link>
+                <el-link type="danger" :underline="false" @click="clearCategory" class="ml-2">删除</el-link>
+              </div>
+            </el-form-item>
+
+            <!-- 商品编号 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="商品编号:" prop="productNo">
+                  <el-input v-model="productForm.productNo" placeholder="002169745" maxlength="20" show-word-limit disabled />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="状态:">
+                  <span class="category-text">上架在售</span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 商品名称 -->
+            <el-form-item label="商品名称:" prop="itemName" required>
+              <el-input v-model="productForm.itemName" type="textarea" :rows="2" placeholder="请输入商品名称" maxlength="200" show-word-limit />
+            </el-form-item>
+
+            <!-- A10产品名称 -->
+            <el-form-item label="A10产品名称:">
+              <el-input
+                :value="a10ProductNameComputed"
+                type="textarea"
+                :rows="2"
+                disabled
+                placeholder="自动拼接:品牌名 + 规格型号 + 产品分类 + 发票规格"
+              />
+              <div class="form-item-tip">A10产品名称由系统自动拼接:品牌名 + 规格型号 + 产品分类(三级分类)+ 发票规格,无需手动填写</div>
+            </el-form-item>
+
+            <!-- 规格型号 和 UPC(69)条码 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="规格型号:">
+                  <el-input v-model="productForm.specificationsCode" placeholder="请输入规格型号" maxlength="20" show-word-limit />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="UPC(69)条码:">
+                  <el-input v-model="productForm.barCoding" placeholder="请输入UPC(69)条码" maxlength="20" show-word-limit @input="handleUpcInput" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 发票名称 和 发票规格 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="发票名称:">
+                  <el-input v-model="productForm.invoiceName" placeholder="请输入发票名称" maxlength="20" show-word-limit />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="发票规格:">
+                  <el-input v-model="productForm.invoiceSpecs" placeholder="请输入发票规格" maxlength="20" show-word-limit />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <!-- 商品品牌 -->
+              <el-col :span="12">
+                <el-form-item label="商品品牌:" prop="brandId" required>
+                  <el-select
+                    v-model="productForm.brandId"
+                    placeholder="请输入品牌名称搜索"
+                    filterable
+                    remote
+                    clearable
+                    :remote-method="handleBrandSearch"
+                    :loading="brandLoading"
+                    class="w-full"
+                  >
+                    <el-option v-for="item in brandOptions" :key="item.id" :label="`${item.brandNo},${item.brandName}`" :value="item.id" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+
+              <el-col :span="12">
+                <el-form-item label="单位:">
+                  <el-select
+                    v-model="productForm.unitId"
+                    placeholder="请选择"
+                    clearable
+                    class="w-full"
+                    :disabled="productForm.productReviewStatus === 1"
+                  >
+                    <el-option v-for="option in unitOptions" :key="option.id" :label="`${option.unitNo},${option.unitName}`" :value="option.id" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 税率编码 、税率 和 币种 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="税率编码:">
+                  <el-input
+                    v-model="taxCodeNo"
+                    placeholder="点击选择税率编码"
+                    readonly
+                    class="w-full"
+                    style="cursor: pointer"
+                    @click="taxCodeSelectRef?.open()"
+                  >
+                    <template #suffix>
+                      <el-icon style="cursor: pointer" @click.stop="taxCodeSelectRef?.open()"><Search /></el-icon>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="税率:" required>
+                  <el-select v-model="productForm.taxRate" placeholder="请选择税率" clearable class="w-full">
+                    <el-option v-for="option in taxRateOptions" :key="option.id" :label="`${option.taxrateNo},${option.taxrateName}`" :value="option.taxrate" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="币种:">
+                  <el-select v-model="productForm.currency" placeholder="请选择" class="w-full">
+                    <el-option label="人民币(RMB)" value="RMB" />
+                    <el-option label="美元(USD)" value="USD" />
+                    <el-option label="欧元(EUR)" value="EUR" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- TaxCodeSelect 弹窗 -->
+            <TaxCodeSelect ref="taxCodeSelectRef" @select="handleTaxCodeSelect" />
+
+            <!-- 销量人气 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="销量人气:">
+                  <el-input
+                    v-model="productForm.salesVolume"
+                    type="number"
+                    placeholder="请输入销量人气"
+                    :min="0"
+                    step="1"
+                    @input="handleSalesVolumeInput"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 促销标题 -->
+            <el-form-item label="促销标题:">
+              <el-input v-model="productForm.packagingSpec" type="textarea" :rows="3" placeholder="请输入促销标题" maxlength="300" show-word-limit />
+            </el-form-item>
+
+            <!-- 商品简介 -->
+            <el-form-item label="商品简介:">
+              <el-input v-model="productForm.productDescription" type="textarea" :rows="3" placeholder="请输入商品简介" maxlength="500" show-word-limit />
+            </el-form-item>
+
+
+            <!-- 重量 和 体积 -->
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="商品重量:">
+                  <el-input v-model="productForm.productWeight" placeholder="0" maxlength="10" show-word-limit>
+                    <template #append>
+                      <el-select v-model="productForm.weightUnit" placeholder="请选择" style="width: 100px">
+                        <el-option label="kg" value="kg" />
+                        <el-option label="g" value="g" />
+                        <el-option label="t" value="t" />
+                      </el-select>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="商品体积:">
+                  <el-input v-model="productForm.productVolume" placeholder="0" maxlength="10" show-word-limit>
+                    <template #append>
+                      <el-select v-model="productForm.volumeUnit" placeholder="请选择" style="width: 80px">
+                        <el-option label="m³" value="m3" />
+                        <el-option label="cm³" value="cm3" />
+                        <el-option label="L" value="L" />
+                      </el-select>
+                    </template>
+                  </el-input>
+                </el-form-item>
+              </el-col>
+            </el-row>
+
+            <!-- 参考链接 -->
+            <el-form-item label="参考链接">
+              <el-input v-model="productForm.referenceLink" type="textarea" :rows="3" placeholder="请输入参考链接" />
+            </el-form-item>
+
+
+            <!-- 主供应商 -->
+            <el-form-item label="主供应商:" prop="supplierNo" required>
+              <el-select v-model="(productForm as any).supplierNo" placeholder="请选择" clearable class="w-full" value-key="id">
+                <el-option
+                  v-for="option in supplierOptions"
+                  :key="option.id"
+                  :label="`${option.supplierNo},${option.enterpriseName}`"
+                  :value="String(option.id)"
+                />
+              </el-select>
+            </el-form-item>
+
+            <!-- 售后服务 -->
+            <el-form-item label="售后服务:">
+              <el-select v-model="productForm.afterSalesService" placeholder="请选择" clearable class="w-full">
+                <el-option v-for="option in afterSalesOptions" :key="option.id" :label="option.afterSalesItems" :value="option.id" />
+              </el-select>
+            </el-form-item>
+
+            <!-- 服务保障 -->
+            <el-form-item label="服务保障:">
+              <el-checkbox-group v-model="serviceGuarantees">
+                <el-checkbox v-for="option in serviceGuaranteeOptions" :key="option.id" :label="option.ensureName" :value="option.id" />
+              </el-checkbox-group>
+            </el-form-item>
+
+            <!-- 安装服务 -->
+            <el-form-item label="安装服务:">
+              <el-checkbox-group v-model="installationServices">
+                <el-checkbox label="免费安装" value="freeInstallation" />
+              </el-checkbox-group>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <!-- 销售价格 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">销售价格</span>
+          </template>
+
+          <el-form ref="priceFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="市场价:" prop="marketPrice" required>
+                  <el-input
+                    v-model="productForm.marketPrice"
+                    type="number"
+                    placeholder="请输入市场价"
+                    :min="0"
+                    @blur="formatPrice('marketPrice')"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="官网价:" prop="memberPrice" required>
+                  <el-input
+                    v-model="productForm.memberPrice"
+                    type="number"
+                    placeholder="请输入平台售价"
+                    :min="0"
+                    @blur="formatPrice('memberPrice')"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="最低售价:" prop="minSellingPrice" required>
+                  <el-input
+                    v-model="productForm.minSellingPrice"
+                    type="number"
+                    placeholder="请输入最低售价"
+                    :min="0"
+                    @blur="formatPrice('minSellingPrice')"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="8">
+                <el-form-item label="最低起订量:" prop="minOrderQuantity" required>
+                  <el-input v-model="productForm.minOrderQuantity" type="number" placeholder="请输入最低起订量" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="8">
+                <el-form-item label="备注:">
+                  <span class="currency-text">市场价>官网价>最低售价</span>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+
+        <!-- 采购价格 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">采购价格</span>
+          </template>
+
+          <el-form ref="purchasePriceFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="采购价:" prop="purchasingPrice" required>
+                  <el-input
+                    v-model="productForm.purchasingPrice"
+                    type="number"
+                    placeholder="请输入采购价"
+                    :min="0"
+                    @blur="formatPrice('purchasingPrice')"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="最高采购价:">
+                  <el-input
+                    v-model="productForm.maxPurchasePrice"
+                    type="number"
+                    placeholder="请输入最高采购价"
+                    :min="0"
+                    @blur="formatPrice('maxPurchasePrice')"
+                  />
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+
+        <!-- 采购信息 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">采购信息</span>
+          </template>
+
+          <el-form ref="purchaseInfoFormRef" :model="productForm" :rules="productRules" label-width="120px" class="product-info-form">
+            <el-row :gutter="20">
+              <el-col :span="12">
+                <el-form-item label="产品经理:" prop="productNature" required>
+                  <el-select v-model="productForm.productNature" placeholder="请选择" clearable class="w-full" value-key="staffId">
+                    <el-option
+                      v-for="option in staffOptions"
+                      :key="option.staffId"
+                      :label="`${option.staffCode},${option.staffName}`"
+                      :value="String(option.staffId)"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="12">
+                <el-form-item label="采购人员:" prop="purchasingPersonnel" required>
+                  <el-select v-model="productForm.purchasingPersonnel" placeholder="请选择" clearable class="w-full" value-key="staffId">
+                    <el-option
+                      v-for="option in staffOptions"
+                      :key="option.staffId"
+                      :label="`${option.staffCode},${option.staffName}`"
+                      :value="String(option.staffId)"
+                    />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+        <!-- 自定义属性 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <div class="flex items-center justify-between">
+              <span class="text-lg font-bold">自定义属性</span>
+              <el-button type="primary" icon="Plus" size="small" @click="addDiyAttribute">添加属性</el-button>
+            </div>
+          </template>
+
+          <el-form label-width="0px" class="product-info-form">
+            <div v-if="diyAttributesList.length === 0" class="text-center text-gray-400 py-4 text-sm">
+              暂无自定义属性,点击右上角"添加属性"按钮添加
+            </div>
+            <el-row v-for="(item, index) in diyAttributesList" :key="index" :gutter="20" class="mb-2">
+              <el-col :span="11">
+                <el-input v-model="item.attributeKey" placeholder="请输入属性名称" clearable />
+              </el-col>
+              <el-col :span="11">
+                <el-input v-model="item.attributeValue" placeholder="请输入属性值" clearable />
+              </el-col>
+              <el-col :span="2" class="flex items-center">
+                <el-button type="danger" icon="Delete" circle size="small" @click="removeDiyAttribute(index)" />
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+
+        <!-- 商品属性 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">商品属性</span>
+          </template>
+
+          <el-form ref="attributeFormRef" :model="productForm" label-width="120px" class="product-info-form">
+            <div v-if="attributesList.length === 0" class="text-center text-gray-500 py-8">该分类暂无属性配置</div>
+            <template v-else>
+              <el-row :gutter="20" v-for="(row, rowIndex) in Math.ceil(attributesList.length / 2)" :key="rowIndex">
+                <el-col :span="12" v-for="colIndex in 2" :key="colIndex">
+                  <template v-if="attributesList[rowIndex * 2 + colIndex - 1]">
+                    <el-form-item
+                      :label="attributesList[rowIndex * 2 + colIndex - 1].productAttributesName + ':'"
+                      :required="attributesList[rowIndex * 2 + colIndex - 1].required === '1'"
+                    >
+                      <!-- 下拉选择 -->
+                      <el-select
+                        v-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '0'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
+                        placeholder="请选择"
+                        clearable
+                        class="w-full"
+                      >
+                        <el-option
+                          v-for="option in parseAttributesList(attributesList[rowIndex * 2 + colIndex - 1].attributesList)"
+                          :key="option"
+                          :label="option"
+                          :value="option"
+                        />
+                      </el-select>
+                      <!-- 多选 -->
+                      <el-select
+                        v-else-if="attributesList[rowIndex * 2 + colIndex - 1].isOptional === '2'"
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
+                        placeholder="请选择"
+                        multiple
+                        clearable
+                        class="w-full"
+                      >
+                        <el-option
+                          v-for="option in parseAttributesList(attributesList[rowIndex * 2 + colIndex - 1].attributesList)"
+                          :key="option"
+                          :label="option"
+                          :value="option"
+                        />
+                      </el-select>
+                      <!-- 文本输入 -->
+                      <el-input
+                        v-else
+                        v-model="productAttributesValues[attributesList[rowIndex * 2 + colIndex - 1].productAttributesName]"
+                        placeholder="请输入"
+                        clearable
+                      />
+                    </el-form-item>
+                  </template>
+                </el-col>
+              </el-row>
+            </template>
+          </el-form>
+        </el-card>
+
+        <!-- 商品详情 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">商品详情</span>
+          </template>
+
+          <el-form ref="detailFormRef" :model="productForm" label-width="120px" class="product-info-form">
+            <!-- 商品主图 -->
+            <el-form-item label="商品主图:" required>
+              <upload-image v-model="productForm.productImage" :limit="1" width="178px" height="178px" imageText="选择图片" />
+              <div class="form-item-tip">从图片库选择,建议尺寸300*300px</div>
+            </el-form-item>
+
+            <!-- 商品轮播图 -->
+            <el-form-item label="商品轮播图:" required>
+              <upload-image v-model="carouselImages" :limit="20" width="120px" height="120px" imageText="添加图片" />
+              <div class="form-item-tip">从图片库选择,支持多选,建议尺寸300*300px</div>
+            </el-form-item>
+
+            <!-- 商品详情 -->
+            <el-form-item label="商品详情:" required>
+              <el-tabs v-model="activeDetailTab" type="border-card">
+                <el-tab-pane label="电脑端详情" name="pc">
+                  <Editor v-model="productForm.pcDetail" :height="400" />
+                </el-tab-pane>
+                <el-tab-pane label="移动端详情" name="mobile">
+                  <Editor v-model="productForm.mobileDetail" :height="400" />
+                </el-tab-pane>
+              </el-tabs>
+            </el-form-item>
+          </el-form>
+        </el-card>
+
+        <!-- 定制说明 -->
+        <el-card v-show="currentStep === 1" shadow="never" class="step-card mt-3">
+          <template #header>
+            <span class="text-lg font-bold">定制说明</span>
+          </template>
+
+          <el-form ref="customFormRef" :model="customForm" label-width="120px" class="product-info-form">
+            <!-- 可定制开关 -->
+            <el-form-item label="可定制:">
+              <el-switch v-model="customForm.isCustomize" />
+            </el-form-item>
+
+            <!-- 定制内容 -->
+            <template v-if="customForm.isCustomize">
+              <!-- 定制方式 -->
+              <el-form-item label="定制方式:">
+                <div class="custom-options">
+                  <el-button
+                    v-for="option in customMethodOptions"
+                    :key="option.value"
+                    :type="customForm.selectedMethods.includes(option.value) ? 'primary' : 'default'"
+                    @click="toggleMethod(option.value)"
+                  >
+                    {{ option.label }}
+                  </el-button>
+                </div>
+              </el-form-item>
+
+              <!-- 定制工艺 -->
+              <el-form-item label="定制工艺:">
+                <div class="custom-options">
+                  <el-button
+                    v-for="craft in customCraftOptions"
+                    :key="craft.value"
+                    :type="customForm.selectedCrafts.includes(craft.value) ? 'primary' : 'default'"
+                    @click="toggleCraft(craft.value)"
+                  >
+                    {{ craft.label }}
+                  </el-button>
+                </div>
+              </el-form-item>
+
+              <!-- 定制方式表格 -->
+              <el-form-item label="" label-width="120">
+                <el-table :data="customForm.customDetails" border class="custom-table">
+                  <el-table-column label="装饰方法" width="120">
+                    <template #default="{ row }">
+                      <span>{{ row.decorationMethod }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="定制工艺" width="120">
+                    <template #default="{ row }">
+                      <span>{{ row.craft }}</span>
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="起订数量" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.minOrderQty" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="起订价格" width="150">
+                    <template #default="{ row }">
+                      <el-input
+                        v-model="row.minOrderPrice"
+                        type="number"
+                        :min="0"
+                        placeholder="请输入"
+                        @blur="formatRowPrice(row, 'minOrderPrice')"
+                      />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="打样工期[天]" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.samplePeriod" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="生产周期[天]" width="150">
+                    <template #default="{ row }">
+                      <el-input v-model="row.productionPeriod" placeholder="请输入" />
+                    </template>
+                  </el-table-column>
+                  <el-table-column label="操作" width="100" fixed="right">
+                    <template #default="{ $index }">
+                      <el-link type="danger" :underline="false" @click="removeCustomDetail($index)"> 删除 </el-link>
+                    </template>
+                  </el-table-column>
+                </el-table>
+              </el-form-item>
+
+              <!-- 定制说明 -->
+              <el-form-item label="定制说明:">
+                <el-input v-model="customForm.customDescription" type="textarea" :rows="5" placeholder="请输入定制说明" />
+              </el-form-item>
+            </template>
+          </el-form>
+        </el-card>
+
+        <!-- 步骤3: 完成 -->
+        <el-card v-show="currentStep === 2" shadow="never" class="step-card completion-card">
+          <div class="completion-content">
+            <div class="success-icon">
+              <el-icon :size="80" color="#67c23a">
+                <CircleCheck />
+              </el-icon>
+            </div>
+            <div class="completion-text">商品编辑完成,请点击返回,继续其他操作</div>
+            <div class="completion-action">
+              <el-button type="primary" @click="handleBackToList">返回</el-button>
+            </div>
+          </div>
+        </el-card>
+      </div>
+
+      <!-- 底部操作按钮 -->
+      <el-card v-if="currentStep < 2" shadow="never" class="mt-3">
+        <div class="flex justify-center gap-4">
+          <el-button v-if="currentStep > 0" @click="prevStep">上一步</el-button>
+          <el-button v-if="currentStep < 2" type="primary" @click="nextStep">下一步</el-button>
+          <el-button @click="handleBack">取消</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { ElMessage } from 'element-plus';
+import { Warning, ArrowRight, Check, Plus, CircleCheck, Search } from '@element-plus/icons-vue';
+import Editor from '@/components/Editor/index.vue';
+import UploadImage from '@/components/upload-image/index.vue';
+import TaxCodeSelect from '@/components/TaxCodeSelect/index.vue';
+import { categoryTreeVO, CategoryVO } from '@/api/product/category/types';
+import { BrandVO } from '@/api/product/brand/types';
+import { BaseForm } from '@/api/product/base/types';
+import { ClassificationDiyForm } from '@/api/product/classificationDiy/types';
+import { AttributesVO } from '@/api/product/attributes/types';
+import {
+  addBase,
+  updateBase,
+  getBase,
+  categoryTree,
+  categoryList,
+  categoryAttributeList,
+  getAfterSaleList,
+  getServiceList,
+  getUnitList,
+  getTaxRateList
+} from '@/api/product/base';
+import {
+  addBaseAudit,
+  updateBaseAudit,
+  getBaseAudit,
+} from '@/api/product/baseAudit';
+import { BaseAuditVO, BaseAuditQuery, BaseAuditForm } from '@/api/product/baseAudit/types';
+import { getTaxCode } from '@/api/system/taxCode';
+import { listBrand, getBrand } from '@/api/product/brand';
+import { listInfo } from '@/api/customer/supplierInfo';
+import { InfoVO } from '@/api/customer/supplierInfo/types';
+import { listComStaff } from '@/api/system/comStaff';
+import { ComStaffVO } from '@/api/system/comStaff/types';
+
+const route = useRoute();
+const router = useRouter();
+
+const currentStep = ref(0);
+const loading = ref(false);
+const submitLoading = ref(false);
+const productFormRef = ref();
+
+// 服务保障和安装服务的多选框
+const serviceGuarantees = ref<(string | number)[]>([]);
+const installationServices = ref<string[]>([]);
+
+// 商品详情选项卡
+const activeDetailTab = ref('pc');
+
+// 轮播图URL数组(UI管理用)
+const carouselImages = ref<string[]>([]);
+
+// 税率选项
+const taxRateOptions = ref<any[]>([]);
+
+// 税率编码选择组件
+const taxCodeSelectRef = ref();
+// 已选的税率编码(显示用)
+const taxCodeNo = ref('');
+
+// 处理税率编码选择
+const handleTaxCodeSelect = async (row: any) => {
+  (productForm as any).taxationId = row.id;
+  try {
+    const taxRes = await getTaxCode(row.id);
+    if (taxRes.data) {
+      taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
+    } else {
+      taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
+    }
+  } catch (e) {
+    console.error('获取税率编码详情失败:', e);
+    taxCodeNo.value = row.taxationNo ? `${row.taxationNo},${row.name}` : (row.name || '');
+  }
+  // 同时将显示值存入 form,方便编辑回显时直接读取
+  (productForm as any).taxationNo = taxCodeNo.value;
+};
+
+// 定制说明表单
+const customForm = reactive({
+  isCustomize: false,
+  selectedMethods: [] as string[],
+  selectedCrafts: [] as string[],
+  customDetails: [] as Array<{
+    decorationMethod: string;
+    craft: string;
+    minOrderQty: string;
+    minOrderPrice: string;
+    samplePeriod: string;
+    productionPeriod: string;
+  }>,
+  customDescription: ''
+});
+
+// 定制方式选项
+const customMethodOptions = [
+  { label: '包装定制', value: 'package' },
+  { label: '商品定制', value: 'product' },
+  { label: '开模定制', value: 'mold' }
+];
+
+// 定制工艺选项
+const customCraftOptions = [
+  { label: '丝印', value: 'silkScreen' },
+  { label: '热转印', value: 'thermalTransfer' },
+  { label: '激光', value: 'laser' },
+  { label: '烤花', value: 'baking' },
+  { label: '压印', value: 'embossing' }
+];
+
+// 定制方式映射
+const customMethodMap: Record<string, string> = {
+  'package': '包装定制',
+  'product': '商品定制',
+  'mold': '开模定制'
+};
+
+// 定制工艺映射
+const customCraftMap: Record<string, string> = {
+  'silkScreen': '丝印',
+  'thermalTransfer': '热转印',
+  'laser': '激光',
+  'baking': '烤花',
+  'embossing': '压印'
+};
+
+// 服务保障选择不需要watch,在提交时直接转换为逗号分隔字符串
+
+// 监听安装服务复选框变化,同步到表单
+watch(
+  installationServices,
+  (newVal) => {
+    productForm.freeInstallation = newVal.includes('freeInstallation') ? '1' : '0';
+  },
+  { deep: true }
+);
+
+// 监听定制方式和工艺选择变化,更新表格数据
+watch(
+  [() => customForm.selectedMethods, () => customForm.selectedCrafts],
+  ([newMethods, newCrafts]) => {
+    const newDetails: typeof customForm.customDetails = [];
+
+    // 遍历所有选中的定制方式和工艺组合
+    newMethods.forEach((method) => {
+      const decorationMethod = customMethodMap[method];
+
+      newCrafts.forEach((craft) => {
+        const craftName = customCraftMap[craft];
+
+        // 查找是否已存在该组合的数据
+        const existing = customForm.customDetails.find((item) => item.decorationMethod === decorationMethod && item.craft === craftName);
+
+        newDetails.push(
+          existing || {
+            decorationMethod,
+            craft: craftName,
+            minOrderQty: '',
+            minOrderPrice: '',
+            samplePeriod: '',
+            productionPeriod: ''
+          }
+        );
+      });
+    });
+
+    customForm.customDetails = newDetails;
+  },
+  { deep: true }
+);
+
+// 切换定制方式选择
+const toggleMethod = (method: string) => {
+  const index = customForm.selectedMethods.indexOf(method);
+  if (index > -1) {
+    customForm.selectedMethods.splice(index, 1);
+  } else {
+    customForm.selectedMethods.push(method);
+  }
+};
+
+// 切换定制工艺选择
+const toggleCraft = (craft: string) => {
+  const index = customForm.selectedCrafts.indexOf(craft);
+  if (index > -1) {
+    customForm.selectedCrafts.splice(index, 1);
+  } else {
+    customForm.selectedCrafts.push(craft);
+  }
+};
+
+// 删除定制详情行
+const removeCustomDetail = (index: number) => {
+  customForm.customDetails.splice(index, 1);
+};
+
+const pageTitle = computed(() => {
+  return route.params.id ? '编辑商品' : '新增商品';
+});
+
+// 分类选择表单
+const categoryForm = reactive({
+  topCategoryId: undefined as string | number | undefined,
+  mediumCategoryId: undefined as string | number | undefined,
+  bottomCategoryId: undefined as string | number | undefined
+});
+
+const autoCreateCategory = ref(false);
+const manualCategoryInput = ref('');
+
+// 商品信息表单
+const productForm = reactive<BaseForm>({
+  id: undefined,
+  productNo: undefined,
+  itemName: undefined,
+  brandId: undefined,
+  topCategoryId: undefined,
+  mediumCategoryId: undefined,
+  bottomCategoryId: undefined,
+  unitId: undefined,
+  productImage: undefined,
+  imageUrl: undefined,
+  isSelf: 0,
+  productReviewStatus: 0,
+  homeRecommended: 0,
+  categoryRecommendation: 0,
+  cartRecommendation: 0,
+  recommendedProductOrder: 0,
+  isPopular: 0,
+  isNew: 0,
+  productStatus: '0',
+  remark: undefined,
+  a10ProductName: undefined,
+  specificationsCode: undefined,
+  barCoding: undefined,
+  invoiceName: undefined,
+  invoiceSpecs: undefined,
+  packagingSpec: undefined,
+  referenceLink: undefined,
+  productWeight: undefined,
+  weightUnit: 'kg',
+  productVolume: undefined,
+  volumeUnit: 'm3',
+  productDescription: undefined,
+  mainLibraryIntro: undefined,
+  afterSalesService: undefined,
+  serviceGuarantee: undefined, // 服务保障ID列表,逗号分隔
+  freeInstallation: '0',
+  marketPrice: undefined,
+  memberPrice: undefined,
+  minSellingPrice: undefined,
+  purchasingPrice: undefined,
+  maxPurchasePrice: undefined,
+  productNature: '1',
+  purchasingPersonnel: '1',
+  pcDetail: undefined,
+  mobileDetail: undefined,
+  taxRate: undefined,
+  currency: 'RMB',
+  minOrderQuantity: undefined,
+  salesVolume: undefined
+});
+
+// 表单验证规则
+const productRules = {
+  // productNo: [{ required: true, message: '商品编号不能为空', trigger: 'blur' }],
+  itemName: [{ required: true, message: '商品名称不能为空', trigger: 'blur' }],
+  brandId: [{ required: true, message: '商品品牌不能为空', trigger: 'change' }],
+  // mainLibraryIntro: [{ required: true, message: '主供应商不能为空', trigger: 'change' }],
+  marketPrice: [{ required: true, message: '市场价不能为空', trigger: 'blur' }],
+  memberPrice: [{ required: true, message: '平台售价不能为空', trigger: 'blur' }],
+  minSellingPrice: [{ required: true, message: '最低售价不能为空', trigger: 'blur' }],
+  purchasingPrice: [{ required: true, message: '采购价不能为空', trigger: 'blur' }],
+  productNature: [{ required: true, message: '产品经理不能为空', trigger: 'change' }],
+  purchasingPersonnel: [{ required: true, message: '采购人员不能为空', trigger: 'change' }],
+  taxRate: [{ required: true, message: '税率不能为空', trigger: 'change' }],
+  minOrderQuantity: [{ required: true, message: '最低起订量不能为空', trigger: 'blur' }]
+};
+
+// 分类和品牌选项
+const categoryOptions = ref<categoryTreeVO[]>([]);
+const brandOptions = ref<BrandVO[]>([]);
+const brandLoading = ref(false);
+let brandSearchTimer: ReturnType<typeof setTimeout> | null = null;
+
+// 商品属性列表
+const attributesList = ref<AttributesVO[]>([]);
+const productAttributesValues = ref<Record<string | number, any>>({});
+
+// 售后服务和服务保障选项
+const afterSalesOptions = ref<any[]>([]);
+const serviceGuaranteeOptions = ref<any[]>([]);
+
+// 单位选项
+const unitOptions = ref<any[]>([]);
+
+// 主供应商选项
+const supplierOptions = ref<InfoVO[]>([]);
+
+// 自定义属性列表
+const diyAttributesList = ref<ClassificationDiyForm[]>([]);
+
+// 添加自定义属性行
+const addDiyAttribute = () => {
+  diyAttributesList.value.push({ attributeKey: '', attributeValue: '' });
+};
+
+// 删除自定义属性行
+const removeDiyAttribute = (index: number) => {
+  diyAttributesList.value.splice(index, 1);
+};
+
+// 采购人员选项
+const staffOptions = ref<ComStaffVO[]>([]);
+
+// 搜索关键词
+const searchLevel1 = ref('');
+const searchLevel2 = ref('');
+const searchLevel3 = ref('');
+
+// 三级分类下拉搜索
+const level3SearchValue = ref<string | number | null>(null);
+const level3SearchOptions = ref<CategoryVO[]>([]);
+const level3SearchLoading = ref(false);
+
+// 一级分类列表
+const level1Categories = computed(() => {
+  return categoryOptions.value || [];
+});
+
+// 二级分类列表
+const level2Categories = ref<categoryTreeVO[]>([]);
+
+// 三级分类列表
+const level3Categories = ref<categoryTreeVO[]>([]);
+
+// 过滤后的一级分类列表
+const filteredLevel1Categories = computed(() => {
+  if (!searchLevel1.value) {
+    return level1Categories.value;
+  }
+  return level1Categories.value.filter((item) => item.label.toLowerCase().includes(searchLevel1.value.toLowerCase()));
+});
+
+// 过滤后的二级分类列表
+const filteredLevel2Categories = computed(() => {
+  if (!searchLevel2.value) {
+    return level2Categories.value;
+  }
+  return level2Categories.value.filter((item) => item.label.toLowerCase().includes(searchLevel2.value.toLowerCase()));
+});
+
+// 过滤后的三级分类列表
+const filteredLevel3Categories = computed(() => {
+  if (!searchLevel3.value) {
+    return level3Categories.value;
+  }
+  return level3Categories.value.filter((item) => item.label.toLowerCase().includes(searchLevel3.value.toLowerCase()));
+});
+
+// 搜索三级分类(调用接口)
+const handleLevel3Search = async (keyword: string) => {
+  if (!keyword) {
+    level3SearchOptions.value = [];
+    return;
+  }
+  level3SearchLoading.value = true;
+  try {
+    const res = await categoryList({ classLevel: 3, categoryName: keyword, pageNum: 1, pageSize: 50 });
+    level3SearchOptions.value = (res as any).data || (res as any).rows || [];
+  } catch (error) {
+    console.error('搜索三级分类失败:', error);
+  } finally {
+    level3SearchLoading.value = false;
+  }
+};
+
+// 选择三级分类搜索结果后,自动在树中定位
+const handleLevel3SearchSelect = async (categoryId: string | number) => {
+  if (!categoryId) return;
+  const selectedCategory = level3SearchOptions.value.find((item) => String(item.id) === String(categoryId));
+  if (!selectedCategory) return;
+
+  // 在分类树中查找对应的二级节点(三级的父节点)
+  const level2Node = findCategoryById(categoryOptions.value, selectedCategory.parentId);
+  if (!level2Node) return;
+
+  // 在一级列表中查找(二级的父节点)
+  const level1Node = level1Categories.value.find((item) => String(item.id) === String(level2Node.parentId));
+  if (!level1Node) return;
+
+  // 依次选中一级、二级、三级
+  categoryForm.topCategoryId = level1Node.id;
+  selectedLevel1Name.value = level1Node.label;
+  level2Categories.value = level1Node.children || [];
+
+  await nextTick();
+
+  categoryForm.mediumCategoryId = level2Node.id;
+  selectedLevel2Name.value = level2Node.label;
+  level3Categories.value = level2Node.children || [];
+
+  await nextTick();
+
+  // 精确查找三级节点
+  const level3Node = level3Categories.value.find((item) => String(item.id) === String(selectedCategory.id));
+  if (level3Node) {
+    categoryForm.bottomCategoryId = level3Node.id;
+    selectedLevel3Name.value = level3Node.label;
+    await loadCategoryAttributes(level3Node.id);
+  } else {
+    categoryForm.bottomCategoryId = selectedCategory.id;
+    selectedLevel3Name.value = selectedCategory.categoryName;
+    await loadCategoryAttributes(selectedCategory.id);
+  }
+
+  // 清空搜索框
+  level3SearchValue.value = null;
+  level3SearchOptions.value = [];
+};
+
+// 选中的分类名称
+const selectedLevel1Name = ref('');
+const selectedLevel2Name = ref('');
+const selectedLevel3Name = ref('');
+
+// 选择一级分类
+const selectLevel1 = (item: categoryTreeVO) => {
+  if (!item.children || item.children.length === 0) {
+    ElMessage.warning('该分类无子分类,请选择含三级分类的类别');
+    return;
+  }
+  categoryForm.topCategoryId = item.id;
+  categoryForm.mediumCategoryId = undefined;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel1Name.value = item.label;
+  selectedLevel2Name.value = '';
+  selectedLevel3Name.value = '';
+
+  level2Categories.value = item.children || [];
+  level3Categories.value = [];
+};
+
+// 选择二级分类
+const selectLevel2 = (item: categoryTreeVO) => {
+  if (!item.children || item.children.length === 0) {
+    ElMessage.warning('该分类无子分类,请选择含三级分类的类别');
+    return;
+  }
+  categoryForm.mediumCategoryId = item.id;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel2Name.value = item.label;
+  selectedLevel3Name.value = '';
+
+  level3Categories.value = item.children || [];
+};
+
+// 选择三级分类
+const selectLevel3 = async (item: categoryTreeVO) => {
+  categoryForm.bottomCategoryId = item.id;
+  selectedLevel3Name.value = item.label;
+
+  // 加载该分类下的属性列表
+  await loadCategoryAttributes(item.id);
+};
+
+// 获取分类路径
+const getCategoryPath = () => {
+  const parts = [];
+  if (selectedLevel1Name.value) parts.push(selectedLevel1Name.value);
+  if (selectedLevel2Name.value) parts.push(selectedLevel2Name.value);
+  if (selectedLevel3Name.value) parts.push(selectedLevel3Name.value);
+  return parts.join(' > ') || '请选择分类';
+};
+
+// 清除分类
+const clearCategory = () => {
+  categoryForm.topCategoryId = undefined;
+  categoryForm.mediumCategoryId = undefined;
+  categoryForm.bottomCategoryId = undefined;
+  selectedLevel1Name.value = '';
+  selectedLevel2Name.value = '';
+  selectedLevel3Name.value = '';
+  level2Categories.value = [];
+  level3Categories.value = [];
+  attributesList.value = [];
+  productAttributesValues.value = {};
+};
+
+// 下一步
+const nextStep = async () => {
+  if (currentStep.value === 0) {
+    // 验证分类选择
+    if (!categoryForm.topCategoryId) {
+      ElMessage.warning('请选择一级分类');
+      return;
+    }
+    if (!categoryForm.mediumCategoryId) {
+      ElMessage.warning('请选择二级分类');
+      return;
+    }
+    if (!categoryForm.bottomCategoryId) {
+      ElMessage.warning('请选择三级分类');
+      return;
+    }
+
+    // 将分类信息同步到商品表单
+    productForm.topCategoryId = categoryForm.topCategoryId;
+    productForm.mediumCategoryId = categoryForm.mediumCategoryId;
+    productForm.bottomCategoryId = categoryForm.bottomCategoryId;
+
+    currentStep.value++;
+  } else if (currentStep.value === 1) {
+    // 验证商品信息表单并提交
+    try {
+      await productFormRef.value?.validate();
+      // 调用提交函数
+      await handleSubmit();
+    } catch (error) {
+      ElMessage.warning('请完善商品信息');
+      return;
+    }
+  }
+};
+
+// 上一步
+const prevStep = () => {
+  if (currentStep.value > 0) {
+    currentStep.value--;
+  }
+};
+
+// 提交
+const handleSubmit = async () => {
+  try {
+    submitLoading.value = true;
+
+    // 校验商品主图、轮播图、详情必填
+    if (!productForm.productImage) {
+      ElMessage.warning('请上传商品主图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!carouselImages.value || carouselImages.value.length === 0) {
+      ElMessage.warning('请上传商品轮播图');
+      submitLoading.value = false;
+      return;
+    }
+    if (!productForm.pcDetail) {
+      ElMessage.warning('请填写电脑端商品详情');
+      submitLoading.value = false;
+      return;
+    }
+
+    // 校验价格关系:市场价 > 官网价 > 最低售价
+    const midRange = parseFloat(String(productForm.marketPrice));
+    const standard = parseFloat(String(productForm.memberPrice));
+    const certificate = parseFloat(String(productForm.minSellingPrice));
+    if (!isNaN(midRange) && !isNaN(standard) && !isNaN(certificate)) {
+      if (!(midRange > standard)) {
+        ElMessage.warning('市场价必须大于官网价');
+        submitLoading.value = false;
+        return;
+      }
+      if (!(standard > certificate)) {
+        ElMessage.warning('官网价必须大于最低售价');
+        submitLoading.value = false;
+        return;
+      }
+    }
+
+    // 准备提交数据,包含定制信息(A10产品名称由前端自动拼接,不传后端)
+    const submitProductData: any = {
+      ...productForm,
+      // 将服务保障ID数组转换为逗号分隔字符串
+      serviceGuarantee: serviceGuarantees.value.map((id) => String(id)).join(','),
+      // 轮播图URL逗号分隔
+      imageUrl: carouselImages.value.join(','),
+      // 将商品属性值转换为JSON字符串
+      attributesList: JSON.stringify(productAttributesValues.value),
+      isCustomize: customForm.isCustomize ? 1 : 0,
+      customizedStyle: customForm.selectedMethods.join(','),
+      customizedCraft: customForm.selectedCrafts.join(','),
+      customDescription: customForm.customDescription,
+      customDetailsJson: JSON.stringify(customForm.customDetails),
+      diyAttributesList: diyAttributesList.value.filter((item) => item.attributeKey || item.attributeValue)
+    };
+    // A10产品名称不传后端
+    delete submitProductData.a10ProductName;
+
+    const auditData: BaseAuditForm = {
+      id: route.params.id as string,
+      productData: JSON.stringify(submitProductData),
+      type: 0,
+      auditStatus: 0
+    };
+
+    if (auditData.id) {
+      await updateBaseAudit(auditData);
+      ElMessage.success('修改成功');
+    } else {
+      await addBaseAudit(auditData);
+      ElMessage.success('新增成功');
+    }
+    // 跳转到完成页面(步骤3)
+    currentStep.value = 2;
+  } catch (error) {
+    console.error('提交失败:', error);
+  } finally {
+    submitLoading.value = false;
+  }
+};
+
+// 返回
+const handleBack = () => {
+  router.back();
+};
+
+// 返回列表
+const handleBackToList = () => {
+  router.push('/product/base');
+};
+
+// UPC(69)条码只允许输入数字
+const handleUpcInput = () => {
+  if (productForm.barCoding) {
+    productForm.barCoding = productForm.barCoding.replace(/\D/g, '');
+  }
+};
+
+// 销量人气只允许输入整数
+const handleSalesVolumeInput = (val: string) => {
+  if (val !== undefined && val !== null && val !== '') {
+    const intVal = parseInt(String(val).replace(/[^\d]/g, ''), 10);
+    productForm.salesVolume = !isNaN(intVal) ? intVal : undefined;
+  } else {
+    productForm.salesVolume = undefined;
+  }
+};
+
+// A10产品名称自动拼接(品牌名 + 规格型号 + 产品分类 + 发票规格)
+const a10ProductNameComputed = computed(() => {
+  const brand = brandOptions.value.find((b) => Number(b.id) === Number(productForm.brandId));
+  const brandName = brand?.brandName || '';
+  const specificationsCode = productForm.specificationsCode || '';
+  const categoryName = selectedLevel3Name.value || '';
+  const invoiceSpecs = productForm.invoiceSpecs || '';
+  return [brandName, specificationsCode, categoryName, invoiceSpecs].filter((s) => s.trim()).join(' ');
+});
+
+// 格式化价格为两位小数(不允许负数)
+const formatPrice = (field: string) => {
+  const val = (productForm as any)[field];
+  if (val !== undefined && val !== null && val !== '') {
+    let num = parseFloat(String(val));
+    if (!isNaN(num)) {
+      // 不允许负数
+      if (num < 0) num = 0;
+      (productForm as any)[field] = num.toFixed(2);
+    }
+  }
+};
+
+// 格式化表格行中的价格为两位小数(不允许负数)
+const formatRowPrice = (row: any, field: string) => {
+  const val = row[field];
+  if (val !== undefined && val !== null && val !== '') {
+    let num = parseFloat(String(val));
+    if (!isNaN(num)) {
+      // 不允许负数
+      if (num < 0) num = 0;
+      row[field] = num.toFixed(2);
+    }
+  }
+};
+
+// 获取分类树
+const getCategoryTree = async () => {
+  try {
+    const res = await categoryTree();
+    categoryOptions.value = res.data || [];
+  } catch (error) {
+    console.error('获取分类树失败:', error);
+  }
+};
+
+// 加载品牌选项(默认100条)
+const loadBrandOptions = async (keyword?: string) => {
+  brandLoading.value = true;
+  try {
+    const res = await listBrand({ pageNum: 1, pageSize: 100, brandName: keyword });
+    const newList = res.rows || [];
+    // 编辑模式下保留当前选中的品牌,避免被新列表覆盖后 A10产品名称 computed 失效
+    if (productForm.brandId) {
+      const exists = newList.find((item) => Number(item.id) === Number(productForm.brandId));
+      if (!exists) {
+        const currentBrand = brandOptions.value.find((item) => Number(item.id) === Number(productForm.brandId));
+        if (currentBrand) {
+          newList.unshift(currentBrand);
+        }
+      }
+    }
+    brandOptions.value = newList;
+  } catch (error) {
+    console.error('加载品牌列表失败:', error);
+  } finally {
+    brandLoading.value = false;
+  }
+};
+
+// 品牌远程搜索(防抖)
+const handleBrandSearch = (query: string) => {
+  if (brandSearchTimer) clearTimeout(brandSearchTimer);
+  brandSearchTimer = setTimeout(() => {
+    loadBrandOptions(query || undefined);
+  }, 300);
+};
+
+// 处理品牌下拉框显示/隐藏
+const handleBrandVisibleChange = (visible: boolean) => {
+  if (visible && brandOptions.value.length === 0) {
+    loadBrandOptions();
+  }
+};
+
+// 获取售后服务列表
+const getAfterSalesOptions = async () => {
+  try {
+    const res = await getAfterSaleList();
+    afterSalesOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && afterSalesOptions.value.length > 0 && !productForm.afterSalesService) {
+      productForm.afterSalesService = afterSalesOptions.value[0].id;
+    }
+  } catch (error) {
+    console.error('获取售后服务列表失败:', error);
+  }
+};
+
+// 获取服务保障列表
+const getServiceGuaranteeOptions = async () => {
+  try {
+    const res = await getServiceList();
+    serviceGuaranteeOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认选中
+    if (!route.params.id && serviceGuaranteeOptions.value.length > 0 && serviceGuarantees.value.length === 0) {
+      serviceGuarantees.value = [serviceGuaranteeOptions.value[0].id];
+    }
+  } catch (error) {
+    console.error('获取服务保障列表失败:', error);
+  }
+};
+
+// 获取单位列表
+const getUnitOptions = async () => {
+  try {
+    const res = await getUnitList();
+    unitOptions.value = res.data || [];
+    // 如果是新增模式且有选项,设置第一个为默认值
+    if (!route.params.id && unitOptions.value.length > 0 && !productForm.unitId) {
+      productForm.unitId = unitOptions.value[0].id;
+    }
+  } catch (error) {
+    console.error('获取单位列表失败:', error);
+  }
+};
+
+// 获取主供应商列表
+const getSupplierOptions = async () => {
+  try {
+    const res = await listInfo();
+    console.log('供应商接口返回:', res);
+    // 处理可能的数据结构: res.data 或 res.rows
+    const dataList = res.data || res.rows || [];
+    supplierOptions.value = dataList;
+    console.log('供应商列表:', supplierOptions.value);
+    // 如果有选项且当前没有选中值,设置第一个为默认值
+    if (supplierOptions.value.length > 0 && !productForm.mainLibraryIntro) {
+      productForm.mainLibraryIntro = String(supplierOptions.value[0].id);
+    }
+  } catch (error) {
+    console.error('获取主供应商列表失败:', error);
+  }
+};
+
+// 获取采购人员列表
+const getStaffOptions = async () => {
+  try {
+    const res = await listComStaff();
+    console.log('采购人员接口返回:', res);
+    // 处理可能的数据结构: res.data 或 res.rows
+    const dataList = res.data || res.rows || [];
+    staffOptions.value = dataList;
+    console.log('采购人员列表:', staffOptions.value);
+    // 如果有选项且当前没有选中值,设置第一个为默认值
+    if (staffOptions.value.length > 0 && !productForm.purchasingPersonnel) {
+      productForm.purchasingPersonnel = String(staffOptions.value[0].staffId);
+    }
+  } catch (error) {
+    console.error('获取采购人员列表失败:', error);
+  }
+};
+
+// 获取税率列表
+const getTaxRateOptions = async () => {
+  try {
+    const res = await getTaxRateList();
+    taxRateOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('获取税率列表失败:', error);
+  }
+};
+
+// 加载分类属性列表
+const loadCategoryAttributes = async (categoryId: string | number) => {
+  try {
+    const res = await categoryAttributeList(categoryId);
+    attributesList.value = res.data || [];
+    // 清空之前的属性值
+    productAttributesValues.value = {};
+
+    // 如果是新增模式,为有选项的属性设置默认值
+    if (!route.params.id) {
+      attributesList.value.forEach((attr) => {
+        if (attr.entryMethod === '1' && attr.attributesList) {
+          // 下拉选择
+          const options = parseAttributesList(attr.attributesList);
+          if (options.length > 0) {
+            productAttributesValues.value[attr.id] = options[0];
+          }
+        } else if (attr.entryMethod === '3' && attr.attributesList) {
+          // 多选
+          const options = parseAttributesList(attr.attributesList);
+          if (options.length > 0) {
+            productAttributesValues.value[attr.id] = [options[0]];
+          }
+        }
+      });
+    }
+  } catch (error) {
+    console.error('加载分类属性失败:', error);
+    attributesList.value = [];
+  }
+};
+
+// 解析属性值列表(JSON数组或逗号分隔字符串)
+const parseAttributesList = (attributesListStr: string): string[] => {
+  if (!attributesListStr) return [];
+
+  try {
+    // 尝试解析为JSON数组
+    const parsed = JSON.parse(attributesListStr);
+    if (Array.isArray(parsed)) {
+      return parsed;
+    }
+  } catch (e) {
+    // 如果不是JSON,按逗号分隔
+    return attributesListStr
+      .split(',')
+      .map((item) => item.trim())
+      .filter((item) => item);
+  }
+
+  return [];
+};
+
+// 加载商品详情(编辑模式)
+const loadProductDetail = async () => {
+  const id = route.params.id;
+  if (id) {
+    try {
+      loading.value = true;
+      const res = await getBaseAudit(id as string);
+      Object.assign(productForm, res.data.productBaseVo);
+
+      // 回显产品经理 - 确保转换为字符串类型以匹配下拉框的value
+      if (res.data.productBaseVo.productNature !== undefined && res.data.productBaseVo.productNature !== null) {
+        productForm.productNature = String(res.data.productBaseVo.productNature);
+      }
+
+      // 回显采购人员 - 确保转换为字符串类型以匹配下拉框的value
+      if (res.data.productBaseVo.purchasingPersonnel !== undefined && res.data.productBaseVo.purchasingPersonnel !== null) {
+        productForm.purchasingPersonnel = String(res.data.productBaseVo.purchasingPersonnel);
+      }
+
+      // 回显税率编码显示值
+      const rawData = res.data.productBaseVo as any;
+      // 通过 taxationId 调接口获取中文名称回显
+      if (rawData.taxationId) {
+        try {
+          const taxRes = await getTaxCode(rawData.taxationId);
+          if (taxRes.data) {
+            taxCodeNo.value = `${taxRes.data.taxationNo},${taxRes.data.name}`;
+          }
+        } catch (e) {
+          console.error('获取税率编码失败:', e);
+        }
+      }
+
+      // 回显税率 - 在税率选项中查找匹配的值(处理浮点数精度问题)
+      if (res.data.productBaseVo.taxRate !== undefined && res.data.productBaseVo.taxRate !== null) {
+        const apiTaxRate = Number(res.data.productBaseVo.taxRate);
+        // 使用精度容差比较浮点数
+        const matchedOption = taxRateOptions.value.find((opt) => Math.abs(Number(opt.taxrate) - apiTaxRate) < 0.0001);
+        if (matchedOption) {
+          productForm.taxRate = matchedOption.taxrate;
+        } else {
+          productForm.taxRate = apiTaxRate;
+        }
+      }
+
+      // 回显单位 - 确保类型与下拉选项的id一致(数字类型)
+      if (res.data.productBaseVo.unitId !== undefined && res.data.productBaseVo.unitId !== null) {
+        productForm.unitId = Number(res.data.productBaseVo.unitId);
+      }
+
+      // 回显品牌 - 先加载对应的品牌信息到选项列表中
+      if (res.data.productBaseVo.brandId) {
+        productForm.brandId = Number(res.data.productBaseVo.brandId);
+        try {
+          const brandRes = await getBrand(res.data.productBaseVo.brandId);
+          if (brandRes.data) {
+            // 检查品牌是否已在选项列表中
+            const existBrand = brandOptions.value.find((item) => Number(item.id) === Number(res.data.productBaseVo.brandId));
+            if (!existBrand) {
+              brandOptions.value.unshift(brandRes.data);
+            }
+          }
+        } catch (e) {
+          console.error('加载品牌信息失败:', e);
+        }
+      }
+
+      // 回显售后服务 - 确保类型与下拉选项的id一致(数字类型)
+      if (res.data.productBaseVo.afterSalesService !== undefined && res.data.productBaseVo.afterSalesService !== null) {
+        productForm.afterSalesService = Number(res.data.productBaseVo.afterSalesService);
+      }
+
+      // 回显轮播图
+      if (res.data.productBaseVo.imageUrl) {
+        carouselImages.value = res.data.productBaseVo.imageUrl.split(',').filter((url: string) => url.trim());
+      } else {
+        carouselImages.value = [];
+      }
+
+      // 回显分类选择
+      categoryForm.topCategoryId = res.data.productBaseVo.topCategoryId;
+      categoryForm.mediumCategoryId = res.data.productBaseVo.mediumCategoryId;
+      categoryForm.bottomCategoryId = res.data.productBaseVo.bottomCategoryId;
+
+      // 回显服务保障复选框 - 将逗号分隔的ID字符串转换为数组
+      if (res.data.productBaseVo.serviceGuarantee) {
+        serviceGuarantees.value = res.data.productBaseVo.serviceGuarantee.split(',').map((id: string) => {
+          // 尝试转换为数字,如果失败则保持字符串
+          const numId = Number(id.trim());
+          return isNaN(numId) ? id.trim() : numId;
+        });
+      } else {
+        serviceGuarantees.value = [];
+      }
+
+      // 回显安装服务复选框
+      const services: string[] = [];
+      if (res.data.productBaseVo.freeInstallation === '1') services.push('freeInstallation');
+      installationServices.value = services;
+
+      // 回显分类名称 - 使用nextTick确保DOM更新后再查找子分类
+      await restoreCategorySelection();
+
+      // 回显商品属性值(必须在restoreCategorySelection之后,避免loadCategoryAttributes清空属性值)
+      if (res.data.productBaseVo.attributesList) {
+        try {
+          const parsedAttributes = JSON.parse(res.data.productBaseVo.attributesList);
+          productAttributesValues.value = parsedAttributes;
+        } catch (e) {
+          console.error('解析商品属性失败:', e);
+          productAttributesValues.value = {};
+        }
+      }
+
+      // 回显自定义属性列表
+      const rawResData = res.data as any;
+      if (Array.isArray(rawResData.productBaseVo.diyAttributesList) && rawResData.productBaseVo.diyAttributesList.length > 0) {
+        diyAttributesList.value = rawResData.productBaseVo.diyAttributesList;
+      } else {
+        diyAttributesList.value = [];
+      }
+    } catch (error) {
+      console.error('加载商品详情失败:', error);
+      ElMessage.error('加载商品详情失败');
+    } finally {
+      loading.value = false;
+    }
+  }
+};
+
+// 递归查找分类节点
+const findCategoryById = (categories: categoryTreeVO[], id: string | number): categoryTreeVO | null => {
+  for (const category of categories) {
+    if (String(category.id) === String(id)) {
+      return category;
+    }
+    if (category.children && category.children.length > 0) {
+      const found = findCategoryById(category.children, id);
+      if (found) return found;
+    }
+  }
+  return null;
+};
+
+// 恢复分类选择状态
+const restoreCategorySelection = async () => {
+  // 先保存原始的分类ID值
+  const originalTopCategoryId = categoryForm.topCategoryId;
+  const originalMediumCategoryId = categoryForm.mediumCategoryId;
+  const originalBottomCategoryId = categoryForm.bottomCategoryId;
+
+  console.log('回显分类 - 原始ID:', {
+    top: originalTopCategoryId,
+    medium: originalMediumCategoryId,
+    bottom: originalBottomCategoryId
+  });
+
+  if (!originalTopCategoryId) return;
+
+  // 查找一级分类
+  const level1 = level1Categories.value.find((item) => String(item.id) === String(originalTopCategoryId));
+  console.log('查找一级分类:', level1);
+  if (!level1) return;
+
+  // 设置一级分类选中状态
+  categoryForm.topCategoryId = level1.id;
+  selectedLevel1Name.value = level1.label;
+  level2Categories.value = level1.children || [];
+
+  await nextTick();
+
+  // 查找二级分类
+  if (originalMediumCategoryId) {
+    // 先在当前一级分类的children中查找
+    let level2 = level2Categories.value.find((item) => String(item.id) === String(originalMediumCategoryId));
+
+    // 如果找不到,尝试在整个分类树中查找(容错处理)
+    if (!level2) {
+      console.log('二级分类在当前一级下未找到,尝试全局查找...');
+      level2 = findCategoryById(categoryOptions.value, originalMediumCategoryId);
+    }
+
+    console.log('查找二级分类:', level2);
+    if (level2) {
+      categoryForm.mediumCategoryId = level2.id;
+      selectedLevel2Name.value = level2.label;
+      level3Categories.value = level2.children || [];
+
+      await nextTick();
+
+      // 查找三级分类
+      if (originalBottomCategoryId) {
+        // 先在当前二级分类的children中查找
+        let level3 = level3Categories.value.find((item) => String(item.id) === String(originalBottomCategoryId));
+
+        // 如果找不到,尝试在整个分类树中查找(容错处理)
+        if (!level3) {
+          console.log('三级分类在当前二级下未找到,尝试全局查找...');
+          level3 = findCategoryById(categoryOptions.value, originalBottomCategoryId);
+        }
+
+        console.log('查找三级分类:', level3, '原始ID:', originalBottomCategoryId);
+        if (level3) {
+          categoryForm.bottomCategoryId = level3.id;
+          selectedLevel3Name.value = level3.label;
+          console.log('设置三级分类名称:', selectedLevel3Name.value);
+          await loadCategoryAttributes(level3.id);
+        }
+      }
+    }
+  }
+};
+
+onMounted(async () => {
+  // 编辑模式下先直接跳到第二步,再加载数据,避免闪烁步骤一
+  if (route.params.id) {
+    currentStep.value = 1;
+  }
+  await getCategoryTree();
+  await getUnitOptions();
+  await getAfterSalesOptions();
+  await getServiceGuaranteeOptions();
+  await getTaxRateOptions();
+  // 先加载商品详情(如果是编辑模式)
+  await loadProductDetail();
+  // 再加载下拉选项,这样如果详情中没有值,会自动设置第一个
+  await getSupplierOptions();
+  await getStaffOptions();
+  loadBrandOptions();
+});
+</script>
+
+<style scoped lang="scss">
+.product-wizard-page {
+  .category-selection {
+    margin-top: 12px;
+  }
+
+  .category-box {
+    border: 1px solid #e4e7ed;
+    border-radius: 4px;
+    overflow: hidden;
+
+    .category-header {
+      background-color: #f5f7fa;
+      padding: 10px 12px;
+      font-weight: 600;
+      border-bottom: 1px solid #e4e7ed;
+      text-align: center;
+      font-size: 14px;
+    }
+
+    .category-search {
+      padding: 10px;
+      border-bottom: 1px solid #e4e7ed;
+      background-color: #fff;
+    }
+
+    .category-list {
+      height: 280px;
+      overflow-y: auto;
+
+      .category-item {
+        padding: 10px 12px;
+        cursor: pointer;
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        border-bottom: 1px solid #f0f0f0;
+        transition: all 0.3s;
+
+        &:hover {
+          background-color: #f5f7fa;
+        }
+
+        &.active {
+          background-color: #ecf5ff;
+          color: #409eff;
+          font-weight: 600;
+        }
+
+        &:last-child {
+          border-bottom: none;
+        }
+
+        &.disabled {
+          cursor: not-allowed;
+          opacity: 0.45;
+          pointer-events: none;
+        }
+      }
+    }
+  }
+
+  .confirm-info {
+    margin-top: 12px;
+    text-align: left;
+  }
+
+  .product-info-form {
+    .category-display {
+      display: flex;
+      align-items: center;
+
+      .category-text {
+        color: #606266;
+      }
+    }
+
+    .form-item-tip {
+      font-size: 12px;
+      color: #909399;
+      line-height: 1.5;
+      margin-top: 4px;
+    }
+
+    .currency-text {
+      color: #303133;
+      font-size: 14px;
+    }
+  }
+
+  .custom-options {
+    display: flex;
+    gap: 10px;
+    flex-wrap: wrap;
+  }
+
+  .custom-table {
+    width: 100%;
+    margin-top: 10px;
+  }
+
+  .completion-card {
+    min-height: 400px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+
+    .completion-content {
+      text-align: center;
+      padding: 40px 0;
+
+      .success-icon {
+        margin-bottom: 24px;
+      }
+
+      .completion-text {
+        font-size: 16px;
+        color: #606266;
+        margin-bottom: 32px;
+        line-height: 1.6;
+      }
+
+      .completion-action {
+        display: flex;
+        justify-content: center;
+      }
+    }
+  }
+}
+</style>

+ 833 - 0
src/views/product/baseAudit/index.vue

@@ -0,0 +1,833 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form ref="queryFormRef" :model="queryParams" label-width="90px">
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="商品编号" prop="productNo">
+                  <el-input v-model="queryParams.productNo" placeholder="请输入商品编号" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品名称" prop="itemName">
+                  <el-input v-model="queryParams.itemName" placeholder="请输入商品名称" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品品牌" prop="brandId">
+                  <el-select
+                    v-model="queryParams.brandId"
+                    placeholder="请输入品牌名称搜索"
+                    filterable
+                    remote
+                    clearable
+                    :remote-method="handleBrandSearch"
+                    :loading="brandLoading"
+                    style="width: 100%"
+                    @keyup.enter="handleQuery"
+                  >
+                    <el-option v-for="item in brandOptions" :key="item.id" :label="item.brandName" :value="item.id" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="商品来源" prop="purchaseNature">
+                  <el-input v-model="queryParams.itemName" placeholder="请输入商品来源" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="6">
+                <el-form-item label="是否自营" prop="isSelf">
+                  <el-select v-model="queryParams.isSelf" placeholder="请选择" clearable>
+                    <el-option label="是" :value="1" />
+                    <el-option label="否" :value="0" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="上下架状态" prop="productStatus">
+                  <el-select v-model="queryParams.productStatus" placeholder="请选择" clearable>
+                    <el-option label="已上架" :value="1" />
+                    <el-option label="下架" :value="0" />
+                    <el-option label="上架中" :value="2" />
+                    <el-option label="驳回上架" :value="3" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="审核状态" prop="productReviewStatus">
+                  <el-select v-model="queryParams.productReviewStatus" placeholder="请选择" clearable>
+                    <el-option label="待采购审核" value="0" />
+                    <el-option label="审核通过" value="1" />
+                    <el-option label="驳回" value="2" />
+                    <el-option label="待营销审核" value="3" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </el-row>
+            <el-row :gutter="20">
+              <el-col :span="18">
+                <el-form-item label="商品分类">
+                  <category-cascade-select
+                    ref="categoryCascadeRef"
+                    v-model:top-category-id="queryParams.topCategoryId"
+                    v-model:medium-category-id="queryParams.mediumCategoryId"
+                    v-model:bottom-category-id="queryParams.bottomCategoryId"
+                  />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6" class="text-left">
+                <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
+                <el-button icon="Refresh" @click="resetQuery">重置</el-button>
+              </el-col>
+            </el-row>
+          </el-form>
+        </el-card>
+      </div>
+    </transition>
+
+    <!-- 统计信息区域 -->
+    <el-card shadow="never" class="mb-[10px]">
+      <div class="flex items-center text-sm text-gray-600">
+        <span>商品总数: </span>
+        <span class="text-blue-600 mx-1"
+          >总=<span class="text-red-600">{{ statistics.total || 0 }}</span
+          >条</span
+        >
+        <span class="mx-2">【上架/总数({{ statistics.onSale || 0 }}/{{ statistics.total || 0 }})】</span>
+        <span class="mx-2"
+          >审核状态: 待审核<span class="text-red-600">{{ statistics.waitAudit || 0 }}</span
+          >条,通过<span class="text-green-600">{{ statistics.auditPass || 0 }}</span
+          >条,驳回<span class="text-orange-600">{{ statistics.auditReject || 0 }}</span
+          >条</span
+        >
+        <span class="mx-2"
+          >上下架状态: 已上架<span class="text-green-600">{{ statistics.onSale || 0 }}</span
+          >条,下架<span class="text-gray-600">{{ statistics.offSale || 0 }}</span
+          >条</span
+        >
+        <div class="ml-auto flex gap-2">
+          <el-button v-if="queryParams.auditStatus === 0" type="primary" icon="Plus" @click="handleAdd">商品新增</el-button>
+          <el-button plain icon="Download" @click="handleExport">导出</el-button>
+          <el-button circle icon="Refresh" @click="getList"></el-button>
+        </div>
+      </div>
+    </el-card>
+
+    <el-card shadow="never">
+      <el-table v-loading="loading" border :data="baseList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="商品编号" align="center" prop="productNo" width="120" fixed="left">
+          <template #default="scope">
+            <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.productBaseVo.productNo }}</el-link>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品图片" align="center" prop="productImage" width="100">
+          <template #default="scope">
+            <image-preview :src="scope.row.productBaseVo.productImage" :width="60" :height="60" />
+          </template>
+        </el-table-column>
+        <el-table-column label="商品信息" align="center" min-width="250">
+          <template #default="scope">
+            <div class="text-left">
+              <div style="white-space: normal; word-break: break-all; line-height: 1.4">{{ scope.row.productBaseVo.itemName }}</div>
+              <div class="text-gray-500" style="font-size: 12px">品牌: {{ scope.row.productBaseVo.brandName || '-' }}</div>
+              <div class="text-gray-500" style="font-size: 12px">分类: {{ scope.row.productBaseVo.topCategoryName+'-'+scope.row.productBaseVo.mediumCategoryName+'-'+ scope.row.productBaseVo.bottomCategoryName}}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="单位" align="center" width="100">
+          <template #default="scope">
+            <div class="text-left">
+              <div class="text-gray-500" style="font-size: 12px">单位: {{ scope.row.productBaseVo.unitName || '-' }}</div>
+              <div class="text-gray-500" style="font-size: 12px">起订量: {{ scope.row.productBaseVo.minOrderQuantity || '-' }}</div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="价格信息" align="center" width="150">
+          <template #default="scope">
+            <div class="text-left" style="font-size: 12px">
+              <div>
+                <span class="text-gray-500">市场价:</span>
+                <span class="text-red-500">¥{{ scope.row.productBaseVo.marketPrice || '0.00' }}</span>
+              </div>
+              <div>
+                <span class="text-gray-500">官网价:</span>
+                <span class="text-red-500">¥{{ scope.row.productBaseVo.memberPrice || '0.00' }}</span>
+              </div>
+              <!-- <div>
+                <span class="text-gray-500">最低价:</span>
+                <span class="text-red-500">¥{{ scope.row.productBaseVo.minSellingPrice || '0.00' }}</span>
+              </div> -->
+            </div>
+          </template>
+        </el-table-column>
+
+<!--        <el-table-column label="数据来源" align="center" prop="dataSource" width="80">-->
+<!--          <template #default="scope">-->
+<!--            <span>{{ scope.row.productBaseVo.dataSource || '-' }}</span>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <el-table-column label="审核状态" align="center" prop="productReviewStatus" width="90">
+          <template #default="scope">
+            <span v-if="scope.row.auditStatus === 0">待提交</span>
+            <span v-else-if="scope.row.auditStatus === 1">待审核</span>
+            <span v-else-if="scope.row.auditStatus === 2">审核通过</span>
+            <span v-else-if="scope.row.auditStatus === 3">审核驳回</span>
+            <span v-else-if="scope.row.auditStatus === 4">待审核</span>
+            <span v-else-if="scope.row.auditStatus === 5">待审核</span>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="审核原因" align="center" prop="auditReason" width="150" fixed="right">
+          <template #default="scope">
+            <span>{{ scope.row.auditReason || '-' }}</span>
+          </template>
+        </el-table-column>
+<!--        <el-table-column label="审核人" align="center" prop="auditUserId" width="80">-->
+<!--          <template #default="scope">-->
+<!--            <span>{{ scope.row.auditUserId || '-' }}</span>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+<!--        <el-table-column label="审核时间" align="center" prop="auditTime" width="120">-->
+<!--          <template #default="scope">-->
+<!--            <span>{{ scope.row.auditTime || '-' }}</span>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <el-table-column label="操作" align="center" width="180" fixed="right">
+          <template #default="scope">
+            <div class="flex flex-wrap gap-1 justify-center">
+              <!-- 待提交状态:编辑 + 提交审核 -->
+                <el-link v-if="scope.row.auditStatus === 0" type="primary" :underline="false" @click="handleUpdate(scope.row)">编辑</el-link>
+                <el-link v-if="scope.row.auditStatus === 0" type="success" :underline="false" @click="handleCommitAudit(scope.row)">提交审核</el-link>
+<!--              &lt;!&ndash; 待审核状态:审核 &ndash;&gt;-->
+<!--              <el-link v-if="scope.row.auditStatus === 1 || scope.row.auditStatus ===4 || scope.row.auditStatus ===5" type="warning" :underline="false" @click="handleAudit(scope.row)">审核</el-link>-->
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 游标分页控制 -->
+      <pagination
+        v-show="baseList.length > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        v-model:way="queryParams.way"
+        :cursor-mode="true"
+        :has-more="hasMore"
+        @pagination="getList"
+      />
+    </el-card>
+    <!-- 审核弹框 -->
+    <el-dialog v-model="auditDialog.visible" title="商品审核" width="480px" :close-on-click-modal="false">
+      <el-form ref="auditFormRef" :model="auditForm" :rules="auditRules" label-width="90px">
+        <el-form-item label="审核结果" prop="auditStatus">
+          <el-radio-group v-model="auditForm.auditStatus">
+            <el-radio value="2">审核通过</el-radio>
+            <el-radio value="3">驳回</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="auditForm.auditStatus === '3'" label="驳回原因" prop="auditReason">
+          <el-input
+            v-model="auditForm.auditReason"
+            type="textarea"
+            :rows="3"
+            placeholder="请输入驳回原因"
+            maxlength="200"
+            show-word-limit
+          />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="auditDialog.visible = false">取消</el-button>
+        <el-button type="primary" :loading="auditDialog.submitLoading" @click="submitAudit">确定</el-button>
+      </template>
+    </el-dialog>
+
+    <!-- 库存修改弹框 -->
+    <el-dialog v-model="inventoryDialog.visible" title="修改库存" width="500px" :close-on-click-modal="false">
+      <div v-loading="inventoryDialog.loading">
+        <el-form ref="inventoryFormRef" :model="inventoryForm" :rules="inventoryRules" label-width="110px">
+          <el-form-item label="虚拟库存" prop="virtualInventory">
+            <el-input-number
+              v-model="inventoryForm.virtualInventory"
+              :min="0"
+              :precision="0"
+              controls-position="right"
+              style="width: 100%"
+              placeholder="请输入虚拟库存"
+            />
+          </el-form-item>
+        </el-form>
+      </div>
+      <template #footer>
+        <el-button @click="inventoryDialog.visible = false">取消</el-button>
+        <el-button type="primary" :loading="inventoryDialog.submitLoading" @click="submitInventory">确定</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Base" lang="ts">
+import {
+  listBase,
+  getBase,
+  delBase,
+  brandList,
+  updateBase,
+  shelfReview,
+  changeProductType,
+  getProductStatusCount
+} from '@/api/product/base';
+import { listBaseAudit, auditBaseAudit, commitBaseAudit } from '@/api/product/baseAudit';
+import { ProductAuditForm } from '@/api/product/baseAudit/types';
+import { generatePPT } from '@/utils/pptPlugin';
+import { addProductSelf } from '@/api/product/productSelf';
+import { addProductExquisite } from '@/api/product/productExquisite';
+import { PriceInventoryForm } from '@/api/product/priceInventory/types';
+import { BaseVO, BaseQuery, BaseForm, StatusCountVo } from '@/api/product/base/types';
+import { BaseAuditVO, BaseAuditQuery, BaseAuditForm } from '@/api/product/baseAudit/types';
+import { BrandVO } from '@/api/product/brand/types';
+import { listBrand } from '@/api/product/brand';
+import { useRoute, useRouter } from 'vue-router';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const router = useRouter();
+const route = useRoute();
+
+const baseList = ref<BaseAuditVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const brandOptions = ref<BrandVO[]>([]);
+const brandLoading = ref(false);
+const brandSearchTimer = ref<ReturnType<typeof setTimeout> | null>(null);
+const hasMore = ref(true); // 是否还有更多数据
+// 页面历史记录,存储每页的第一个 id 和最后一个 id,用于支持双向翻页
+const pageHistory = ref([]);
+
+// 三级分类选择组件引用
+const categoryCascadeRef = ref();
+
+// 统计信息
+const statistics = ref<StatusCountVo>({
+  total: 0,
+  onSale: 0,
+  offSale: 0,
+  waitAudit: 0,
+  auditPass: 0,
+  auditReject: 0
+});
+
+const queryFormRef = ref<ElFormInstance>();
+const baseFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: BaseForm = {
+  id: undefined,
+  productNo: undefined,
+  itemName: undefined,
+  brandId: undefined,
+  topCategoryId: undefined,
+  mediumCategoryId: undefined,
+  bottomCategoryId: undefined,
+  unitId: undefined,
+  productImage: undefined,
+  isSelf: undefined,
+  productReviewStatus: undefined,
+  homeRecommended: undefined,
+  categoryRecommendation: undefined,
+  cartRecommendation: undefined,
+  recommendedProductOrder: undefined,
+  isPopular: undefined,
+  isNew: undefined,
+  productStatus: undefined,
+  remark: undefined
+};
+const data = reactive<PageData<BaseForm, BaseQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    productNo: undefined,
+    itemName: undefined,
+    brandId: undefined,
+    productTag: undefined,
+    purchaseNature: undefined,
+    supplierType: undefined,
+    supplierNature: undefined,
+    projectOrg: undefined,
+    topCategoryId: undefined,
+    mediumCategoryId: undefined,
+    bottomCategoryId: undefined,
+    isSelf: undefined,
+    productReviewStatus: undefined,
+    productStatus: undefined,
+    lastSeenId: undefined, // 游标分页的lastSeenId
+    way: undefined,
+    type: undefined,
+    auditStatus: undefined,
+    queryType: undefined,
+    params: {}
+  },
+  rules: {
+    productNo: [{ required: true, message: '产品编号不能为空', trigger: 'blur' }],
+    itemName: [{ required: true, message: '项目名称不能为空', trigger: 'blur' }],
+    brandId: [{ required: true, message: '品牌id不能为空', trigger: 'blur' }],
+    topCategoryId: [{ required: true, message: '顶级分类id不能为空', trigger: 'blur' }],
+    mediumCategoryId: [{ required: true, message: '中级分类id不能为空', trigger: 'blur' }],
+    bottomCategoryId: [{ required: true, message: '底层分类id不能为空', trigger: 'blur' }],
+    unitId: [{ required: true, message: '单位id不能为空', trigger: 'blur' }],
+    productImage: [{ required: true, message: '产品图片URL不能为空', trigger: 'blur' }],
+    productReviewStatus: [{ required: true, message: '产品审核状态 0=待采购审核,1=审核通过,2=驳回,3=待营销审核不能为空', trigger: 'change' }],
+    homeRecommended: [{ required: true, message: '首页推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    categoryRecommendation: [{ required: true, message: '分类推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    cartRecommendation: [{ required: true, message: '购物车推荐:1=推荐,0=不推荐不能为空', trigger: 'blur' }],
+    recommendedProductOrder: [{ required: true, message: '推荐产品顺序不能为空', trigger: 'blur' }],
+    isPopular: [{ required: true, message: '是否热门:1=是,0=否不能为空', trigger: 'blur' }],
+    isNew: [{ required: true, message: '是否新品:1=是,0=否不能为空', trigger: 'blur' }],
+    remark: [{ required: true, message: '备注不能为空', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 查询产品基础信息列表 */
+const getList = async () => {
+  loading.value = true;
+  try {
+    initRouteParams();
+    const params = { ...queryParams.value };
+    const currentPageNum = queryParams.value.pageNum;
+
+    // 第一页不需要游标参数
+    if (currentPageNum === 1) {
+      delete params.lastSeenId;
+      delete params.way;
+    } else {
+      // way参数:0=上一页,1=下一页
+      if (queryParams.value.way === 0) {
+        // 上一页:使用目标页(即当前显示页)的firstId
+        const nextPageHistory = pageHistory.value[currentPageNum];
+        if (nextPageHistory) {
+          params.firstSeenId = nextPageHistory.firstId;
+          params.way = 0;
+        }
+      } else {
+        // 下一页:使用前一页的lastId作为lastSeenId
+        const prevPageHistory = pageHistory.value[currentPageNum - 1];
+        if (prevPageHistory) {
+          params.lastSeenId = prevPageHistory.lastId;
+          params.way = 1;
+        }
+      }
+    }
+
+    const res = await listBaseAudit(params);
+    baseList.value = res.rows || [];
+
+    // 判断是否还有更多数据
+    hasMore.value = baseList.value.length === queryParams.value.pageSize;
+
+    // 记录当前页的第一个id和最后一个id
+    if (baseList.value.length > 0) {
+      const firstItem = baseList.value[0];
+      const lastItem = baseList.value[baseList.value.length - 1];
+      //如果长度小于currentPageNum则创建
+
+      if (pageHistory.value.length <= currentPageNum) {
+        pageHistory.value[currentPageNum] = {
+          firstId: firstItem.id,
+          lastId: lastItem.id
+        };
+      }
+    }
+
+    total.value = res.total || 0;
+  } catch (error) {
+    console.error('获取列表失败:', error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 初始化路由参数 */
+const initRouteParams = () => {
+  // 从路由参数中获取筛选条件
+  if (route.query.productReviewStatus) {
+    queryParams.value.productReviewStatus = Number(route.query.productReviewStatus);
+  }
+  if (route.query.brandName) {
+    queryParams.value.brandName = route.query.brandName as string;
+  }
+  if (route.query.bottomCategoryId) {
+    queryParams.value.bottomCategoryId = route.query.bottomCategoryId as string;
+  }
+  if (route.query.isSelf) {
+    queryParams.value.isSelf = Number(route.query.isSelf);
+  }
+  if (route.query.productCategory) {
+    queryParams.value.productCategory = Number(route.query.productCategory);
+  }
+  if (route.query.type !== undefined && route.query.type !== '') {
+    queryParams.value.type = Number(route.query.type);
+  }
+  if (route.query.auditStatus !== undefined && route.query.auditStatus !== '') {
+    queryParams.value.auditStatus = Number(route.query.auditStatus);
+  }
+  if (route.query.queryType !== undefined && route.query.queryType !== '') {
+    queryParams.value.queryType = route.query.queryType as string;
+  }
+};
+
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  baseFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  // 同步查询参数到游标分页参数
+  queryParams.value = {
+    ...queryParams.value,
+    pageNum: 1,
+    productNo: queryParams.value.productNo,
+    itemName: queryParams.value.itemName,
+    brandName: queryParams.value.brandName,
+    bottomCategoryId: queryParams.value.bottomCategoryId,
+    isSelf: queryParams.value.isSelf,
+    productReviewStatus: queryParams.value.productReviewStatus,
+    productStatus: queryParams.value.productStatus,
+    lastSeenId: undefined
+  };
+  pageHistory.value = []; // 重置页面历史
+  getList();
+};
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  // 手动重置分类字段
+  queryParams.value.topCategoryId = undefined;
+  queryParams.value.mediumCategoryId = undefined;
+  queryParams.value.bottomCategoryId = undefined;
+  categoryCascadeRef.value?.reset();
+  queryParams.value.lastSeenId = undefined;
+  pageHistory.value = []; // 重置页面历史
+  handleQuery();
+};
+
+/** 多选框选中数据 */
+const handleSelectionChange = (selection: BaseVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 新增按钮操作 */
+const handleAdd = () => {
+  router.push('/product/baseAudit/add');
+};
+
+/** 修改按钮操作 */
+const handleUpdate = async (row?: BaseVO) => {
+  const _id = row?.id || ids.value[0];
+  router.push(`/product/baseAudit/edit/${_id}`);
+};
+
+/** 删除按钮操作 */
+const handleDelete = async (row?: BaseVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('是否确认删除产品基础信息编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
+  await delBase(_ids);
+  proxy?.$modal.msgSuccess('删除成功');
+  await getList();
+};
+
+/** 导出按钮操作 */
+const handleExport = async () => {
+  // 检查是否有选中的商品
+  if (ids.value.length === 0) {
+    proxy?.$modal.msgWarning('请先选择要导出的商品');
+    return;
+  }
+
+  // 获取选中商品的完整信息
+  const selectedProducts = baseList.value.filter(item => ids.value.includes(item.id));
+
+  if (selectedProducts.length === 0) {
+    proxy?.$modal.msgWarning('未找到选中的商品信息');
+    return;
+  }
+
+  // 转换为 generatePPT 需要的格式
+  const products = selectedProducts.map(item => ({
+    image: item.productImage || item.productImageUrl || '',
+    name: item.itemName || '',
+    code: item.productNo || '',
+    spec: item.specification || item.packagingSpec || '-',
+    price: item.minSellingPrice || item.memberPrice || 0
+  }));
+
+  // 默认模板配置
+  const template = {
+    name: '商品展示方案',
+    title: '商品展示方案',
+    themeColor: '#C00000',
+    itemsPerPage: 1, // 每页1个商品,展示更详细
+    cover: '',
+    logo: ''
+  };
+
+  try {
+    proxy?.$modal.loading('正在生成PPT...');
+    await generatePPT(template, products);
+    proxy?.$modal.closeLoading();
+    proxy?.$modal.msgSuccess('PPT导出成功');
+  } catch (error) {
+    proxy?.$modal.closeLoading();
+    console.error('PPT导出失败:', error);
+    proxy?.$modal.msgError('PPT导出失败');
+  }
+};
+
+/** 查看商品详情 */
+const handleView = (row: BaseVO) => {
+  const url = `https://item.xiaoluwebsite.xyz/item?id=${row.id}`;
+  window.open(url, '_blank');
+};
+
+/** 上下架操作 */
+const handleShelf = async (row: BaseVO) => {
+  const isOnShelf = row.productStatus === 1;
+  const action = isOnShelf ? '下架' : '上架';
+  await proxy?.$modal.confirm(`确认${action}该商品吗?`);
+
+  try {
+    // 上架:状态改为2(上架中),下架:状态改为0(下架)
+    const productStatus = isOnShelf ? 0 : 2;
+    await shelfReview({
+      id: row.id,
+      productStatus: productStatus,
+      shelfComments: `${action}操作`
+    });
+    proxy?.$modal.msgSuccess(`${action}成功`);
+    await getList();
+  } catch (error) {
+    console.error(`${action}失败:`, error);
+    proxy?.$modal.msgError(`${action}失败`);
+  }
+};
+
+/** 价格设置 */
+const handlePrice = (row: BaseVO) => {
+  console.log('设置价格', row);
+  // TODO: 打开价格设置对话框
+};
+
+/** 库存修改弹框 */
+const inventoryDialog = reactive({
+  visible: false,
+  loading: false,
+  submitLoading: false
+});
+
+const inventoryFormRef = ref<ElFormInstance>();
+
+const inventoryForm = reactive<PriceInventoryForm>({
+  productId: undefined,
+  totalInventory: undefined,
+  nowInventory: undefined,
+  virtualInventory: undefined
+});
+
+const inventoryRules = {
+  virtualInventory: [{ required: true, message: '请输入虚拟库存', trigger: 'blur' }]
+};
+
+
+
+/** 提交库存修改 */
+const submitInventory = async () => {
+  await inventoryFormRef.value?.validate();
+  inventoryDialog.submitLoading = true;
+  try {
+    await updateBase({ ...inventoryForm });
+    proxy?.$modal.msgSuccess('库存修改成功');
+    inventoryDialog.visible = false;
+    await getList();
+  } catch (error) {
+    console.error('库存修改失败:', error);
+    proxy?.$modal.msgError('库存修改失败');
+  } finally {
+    inventoryDialog.submitLoading = false;
+  }
+};
+
+/** 停售操作 */
+const handleDiscontinue = async (row: BaseVO) => {
+  await proxy?.$modal.confirm('确认停售该商品吗?停售后商品将无法正常售卖。');
+
+  try {
+    // 调用停售API,将商品类型改为3(停售商品)
+    await changeProductType({
+      id: row.id,
+      productCategory: 3
+    });
+    proxy?.$modal.msgSuccess('停售成功');
+    await getList();
+  } catch (error) {
+    console.error('停售失败:', error);
+    proxy?.$modal.msgError('停售失败');
+  }
+};
+
+/** 加入自营池操作 */
+const handleAddToSelfPool = async (row: BaseVO) => {
+  await proxy?.$modal.confirm('确认将该商品加入自营池吗?');
+
+  try {
+    await addProductSelf({ productId: row.id, auditStatus: 1 });
+    proxy?.$modal.msgSuccess('加入自营池成功');
+    await getList();
+  } catch (error) {
+    console.error('加入自营池失败:', error);
+    proxy?.$modal.msgError('加入自营池失败');
+  }
+};
+
+/** 加入精品池操作 */
+const handleAddToExquisitePool = async (row: BaseVO) => {
+  await proxy?.$modal.confirm('确认将该商品加入精品池吗?');
+
+  try {
+    await addProductExquisite({ productId: row.id, auditStatus: 1 });
+    proxy?.$modal.msgSuccess('加入精品池成功');
+    await getList();
+  } catch (error) {
+    console.error('加入精品池失败:', error);
+    proxy?.$modal.msgError('加入精品池失败');
+  }
+};
+
+
+/** 审核弹框 */
+const auditDialog = reactive({
+  visible: false,
+  submitLoading: false
+});
+
+const auditFormRef = ref<ElFormInstance>();
+
+const auditForm = reactive<ProductAuditForm>({
+  ids: [],
+  auditStatus: undefined,
+  auditReason: undefined
+});
+
+const auditRules = {
+  auditStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
+  auditReason: [{ required: true, message: '请输入驳回原因', trigger: 'blur' }]
+};
+
+/** 提交审核 */
+const handleCommitAudit = async (row: BaseAuditVO) => {
+  await proxy?.$modal.confirm('确认提交该商品进行审核吗?');
+  try {
+    await commitBaseAudit({ id: row.id, productId: row.productId });
+    proxy?.$modal.msgSuccess('提交审核成功');
+    await getList();
+  } catch (error) {
+    console.error('提交审核失败:', error);
+  }
+};
+
+/** 打开审核弹框 */
+const handleAudit = (row: BaseAuditVO) => {
+  auditForm.ids = [row.id];
+  auditForm.auditStatus = undefined;
+  auditForm.auditReason = undefined;
+  auditFormRef.value?.resetFields();
+  auditDialog.visible = true;
+};
+
+/** 提交审核结果 */
+const submitAudit = async () => {
+  await auditFormRef.value?.validate();
+  auditDialog.submitLoading = true;
+  try {
+    await auditBaseAudit({ ...auditForm });
+    proxy?.$modal.msgSuccess('审核操作成功');
+    auditDialog.visible = false;
+    await getList();
+  } catch (error) {
+    console.error('审核操作失败:', error);
+  } finally {
+    auditDialog.submitLoading = false;
+  }
+};
+
+/** 修改库存(占位,避免TS报错) */
+const handleSupply = (row: BaseAuditVO) => {
+  console.log('修改库存', row);
+};
+
+
+
+/** 加载品牌选项(默认100条) */
+const loadBrandOptions = async (keyword?: string) => {
+  brandLoading.value = true;
+  try {
+    const res = await listBrand({ pageNum: 1, pageSize: 100, brandName: keyword });
+    brandOptions.value = res.rows || [];
+  } catch (error) {
+    console.error('加载品牌列表失败:', error);
+  } finally {
+    brandLoading.value = false;
+  }
+};
+
+/** 品牌远程搜索(防抖) */
+const handleBrandSearch = (query: string) => {
+  if (brandSearchTimer.value) clearTimeout(brandSearchTimer.value);
+  brandSearchTimer.value = setTimeout(() => {
+    loadBrandOptions(query || undefined);
+  }, 300);
+};
+
+/** 获取统计信息 */
+const getStatistics = async () => {
+  try {
+    const res = await getProductStatusCount();
+    if (res.data) {
+      statistics.value = res.data;
+    }
+  } catch (error) {
+    console.error('获取统计信息失败:', error);
+  }
+};
+
+onMounted(() => {
+  getList();
+  getStatistics();
+  loadBrandOptions();
+});
+</script>