فهرست منبع

feat(product): 添加商品审核和自定义属性功能

- 新增商品审核API接口实现基础CRUD和审核操作
- 添加商品自定义属性管理功能及相关API接口
- 实现产品分类层级结构管理和查询功能
- 创建三级分类联级选择组件支持远程搜索
- 开发产品详情抽屉展示商品审核和入池信息
肖路 1 ماه پیش
والد
کامیت
485eabb85b

+ 87 - 0
src/api/product/baseAudit/index.ts

@@ -0,0 +1,87 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { BaseAuditVO, BaseAuditForm, BaseAuditQuery, ProductAuditForm } from '@/api/product/baseAudit/types';
+
+/**
+ * 查询商品审核列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listBaseAudit = (query?: BaseAuditQuery): AxiosPromise<BaseAuditVO[]> => {
+  return request({
+    url: '/product/baseAudit/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询商品审核详细
+ * @param id
+ */
+export const getBaseAudit = (id: string | number): AxiosPromise<BaseAuditVO> => {
+  return request({
+    url: '/product/baseAudit/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增商品审核
+ * @param data
+ */
+export const addBaseAudit = (data: BaseAuditForm) => {
+  return request({
+    url: '/product/baseAudit',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改商品审核
+ * @param data
+ */
+export const updateBaseAudit = (data: BaseAuditForm) => {
+  return request({
+    url: '/product/baseAudit',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除商品审核
+ * @param id
+ */
+export const delBaseAudit = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/baseAudit/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 审核商品
+ * @param data
+ */
+export const auditBaseAudit = (data: ProductAuditForm) => {
+  return request({
+    url: '/product/baseAudit/audit',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 提交审核
+ * @param data
+ */
+export const commitBaseAudit = (data: BaseAuditForm) => {
+  return request({
+    url: '/product/baseAudit/commit',
+    method: 'post',
+    data: data
+  });
+};

+ 173 - 0
src/api/product/baseAudit/types.ts

@@ -0,0 +1,173 @@
+import { BaseVO } from '@/api/product/base/types';
+
+export interface BaseAuditVO {
+  /**
+   *
+   */
+  id: string | number;
+
+  /**
+   * 商品id
+   */
+  productId: string | number;
+
+  /**
+   * 审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回,4=营销审核,5运营审核
+   */
+  auditStatus: number;
+
+  /**
+   * 审核原因
+   */
+  auditReason: string;
+
+  /**
+   * 审核人
+   */
+  auditUserId: string | number;
+
+  /**
+   * 审核时间
+   */
+  auditTime: string;
+
+  /**
+   * 商品审核数据
+   */
+  productData: string;
+
+  /**
+   * 类型 0=提交审核,1上下架审核
+   */
+  type: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+  * 商品对象
+  * */
+  productBaseVo?: BaseVO;
+
+}
+
+export interface BaseAuditForm extends BaseEntity {
+  /**
+   *
+   */
+  id?: string | number;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回,4=营销审核,5运营审核
+   */
+  auditStatus?: number;
+
+  /**
+   * 审核原因
+   */
+  auditReason?: string;
+
+  /**
+   * 审核人
+   */
+  auditUserId?: string | number;
+
+  /**
+   * 审核时间
+   */
+  auditTime?: string;
+
+  /**
+   * 商品审核数据
+   */
+  productData?: string;
+
+  /**
+   * 类型 0=提交审核,1上下架审核
+   */
+  type?: number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface BaseAuditQuery extends PageQuery {
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 审核状态 0=待提交,1=待审核,2=审核通过,3=审核驳回,4=营销审核,5运营审核
+   */
+  auditStatus?: number;
+
+  /**
+   * 审核原因
+   */
+  auditReason?: string;
+
+  /**
+   * 审核人
+   */
+  auditUserId?: string | number;
+
+  /**
+   * 审核时间
+   */
+  auditTime?: string;
+
+  /**
+   * 商品审核数据
+   */
+  productData?: string;
+
+  /**
+   * 类型 0=提交审核,1上下架审核
+   */
+  type?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+/**
+ * 商品审核BO(用于审核操作)
+ */
+export interface ProductAuditForm {
+  /**
+   * ID列表
+   */
+  ids?: Array<string | number>;
+
+  /**
+   * 审核状态 2=通过,3=驳回
+   */
+  auditStatus?: string;
+
+  /**
+   * 驳回原因
+   */
+  auditReason?: string;
+}
+
+
+

+ 63 - 0
src/api/product/classificationDiy/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ClassificationDiyVO, ClassificationDiyForm, ClassificationDiyQuery } from '@/api/product/classificationDiy/types';
+
+/**
+ * 查询商品自定义属性列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listClassificationDiy = (query?: ClassificationDiyQuery): AxiosPromise<ClassificationDiyVO[]> => {
+  return request({
+    url: '/product/classificationDiy/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询商品自定义属性详细
+ * @param id
+ */
+export const getClassificationDiy = (id: string | number): AxiosPromise<ClassificationDiyVO> => {
+  return request({
+    url: '/product/classificationDiy/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增商品自定义属性
+ * @param data
+ */
+export const addClassificationDiy = (data: ClassificationDiyForm) => {
+  return request({
+    url: '/product/classificationDiy',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改商品自定义属性
+ * @param data
+ */
+export const updateClassificationDiy = (data: ClassificationDiyForm) => {
+  return request({
+    url: '/product/classificationDiy',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除商品自定义属性
+ * @param id
+ */
+export const delClassificationDiy = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/product/classificationDiy/' + id,
+    method: 'delete'
+  });
+};

+ 86 - 0
src/api/product/classificationDiy/types.ts

@@ -0,0 +1,86 @@
+export interface ClassificationDiyVO {
+  /**
+   * 
+   */
+  id: string | number;
+
+  /**
+   * 商品id
+   */
+  productId: string | number;
+
+  /**
+   * 属性key
+   */
+  attributeKey: string;
+
+  /**
+   * 属性value
+   */
+  attributeValue: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface ClassificationDiyForm extends BaseEntity {
+  /**
+   * 
+   */
+  id?: string | number;
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 属性key
+   */
+  attributeKey?: string;
+
+  /**
+   * 属性value
+   */
+  attributeValue?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface ClassificationDiyQuery extends PageQuery {
+
+  /**
+   * 商品id
+   */
+  productId?: string | number;
+
+  /**
+   * 属性key
+   */
+  attributeKey?: string;
+
+  /**
+   * 属性value
+   */
+  attributeValue?: string;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 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>

+ 93 - 0
src/views/external/productCategory/index.ts

@@ -0,0 +1,93 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ProductCategoryVO, ProductCategoryForm, ProductCategoryQuery } from '@/api/external/productCategory/types';
+
+/**
+ * 查询产品分类列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listProductCategory = (query?: Partial<ProductCategoryQuery>): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/external/productCategory/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询产品分类详细
+ * @param id
+ * @param itemId
+ */
+export const getProductCategory = (id: string | number, itemId?: string): AxiosPromise<ProductCategoryVO> => {
+  return request({
+    url: '/external/productCategory/' + id,
+    method: 'get',
+    params: { itemId }
+  });
+};
+
+/**
+ * 新增产品分类
+ * @param data
+ */
+export const addProductCategory = (data: ProductCategoryForm) => {
+  return request({
+    url: '/external/productCategory',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改产品分类
+ * @param data
+ */
+export const updateProductCategory = (data: ProductCategoryForm) => {
+  return request({
+    url: '/external/productCategory',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除产品分类
+ * @param id
+ * @param itemId
+ */
+export const delProductCategory = (id: string | number | Array<string | number>, itemId?: string) => {
+  return request({
+    url: '/external/productCategory/' + id,
+    method: 'delete',
+    params: { itemId }
+  });
+};
+
+/**
+ * 查询产品分类列表(排除指定节点及其子节点)
+ * @param id
+ * @param itemId
+ */
+export const listProductCategoryExcludeChild = (id: string | number, itemId?: string): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/external/productCategory/list/exclude/' + id,
+    method: 'get',
+    params: { itemId }
+  });
+};
+
+/**
+ * 查询产品分类树结构
+ * @param query
+ * @returns {*}
+ */
+export const getProductCategoryTree = (query?: Partial<ProductCategoryQuery>): AxiosPromise<ProductCategoryVO[]> => {
+  return request({
+    url: '/external/productCategory/categoryTree',
+    method: 'get',
+    params: query
+  });
+};

+ 253 - 0
src/views/external/productCategory/types.ts

@@ -0,0 +1,253 @@
+export interface ProductCategoryVO {
+  /**
+   * 第三方系统id
+   */
+  id: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId: string | number;
+
+  /**
+   * 原系统分类id
+   */
+  productCategoryId: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId: string | number;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform: number;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+  /**
+   * 是否有子节点
+   */
+  hasChildren?: boolean;
+
+  /**
+   * 子节点
+   */
+  children?: ProductCategoryVO[];
+
+  /**
+   * 折扣率
+   */
+  discountRate?: number;
+
+}
+
+export interface ProductCategoryForm extends BaseEntity {
+  /**
+   * 第三方系统id
+   */
+  id?: string | number;
+
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 原系统分类id
+   */
+  productCategoryId?: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId?: string | number;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors?: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel?: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode?: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform?: number;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+  /**
+   * 折扣率
+   */
+  discountRate?: number;
+
+}
+
+export interface ProductCategoryDiscountForm {
+  /**
+   * 分类ID
+   */
+  id: string | number;
+
+  /**
+   * 项目ID
+   */
+  itemId?: string | number;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 折扣率
+   */
+  discountRate?: number;
+}
+
+export interface ProductCategoryQuery extends PageQuery {
+  /**
+   * 项目id
+   */
+  itemId?: string | number;
+
+  /**
+   * 原系统分类id
+   */
+  productCategoryId?: string | number;
+
+  /**
+   * 分类编号
+   */
+  categoryNo?: string;
+
+  /**
+   * 分类名称
+   */
+  categoryName?: string;
+
+  /**
+   * 父级分类ID
+   */
+  parentId?: string | number;
+
+  /**
+   * 祖籍列表
+   */
+  ancestors?: string;
+
+  /**
+   * 分类层级(1=一级,2=二级, 3三级)
+   */
+  classLevel?: number;
+
+  /**
+   * 拼音码(用于快速检索)
+   */
+  pyCode?: string;
+
+  /**
+   * 分类描述
+   */
+  classDescription?: string;
+
+  /**
+   * 数据来源
+   */
+  dataSource?: string;
+
+  /**
+   * 所属平台(0=Web, 1=小程序)
+   */
+  platform?: number;
+
+  /**
+   * 平台标识
+   */
+  platformCode?: string;
+
+  /**
+   * 项目key
+   */
+  itemKey?: string;
+
+  /**
+   * 日期范围参数
+   */
+  params?: any;
+}
+
+
+
+

+ 270 - 0
src/views/product/poolAudit/ProductDetailDrawer.vue

@@ -0,0 +1,270 @@
+<template>
+  <el-drawer v-model="visible" title="产品详情" direction="rtl" size="1000px" :before-close="handleClose" destroy-on-close>
+    <div v-loading="loading" class="px-2">
+      <!-- 产品基础信息 -->
+      <div class="section-title">产品基础信息</div>
+
+      <el-descriptions :column="2" border class="mb-4">
+        <el-descriptions-item label="产品编号">{{ baseInfo?.productNo || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="产品名称">{{ baseInfo?.itemName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="商品类型">
+          <el-tag v-if="(baseInfo as any)?.productCategory === 1" type="info">默认类型</el-tag>
+          <el-tag v-else-if="(baseInfo as any)?.productCategory === 2" type="success">精选商品</el-tag>
+          <el-tag v-else-if="(baseInfo as any)?.productCategory === 3" type="danger">停售商品</el-tag>
+          <span v-else>-</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="产品类别">
+          {{ [(baseInfo as any)?.topCategoryName, (baseInfo as any)?.mediumCategoryName, (baseInfo as any)?.bottomCategoryName].filter(Boolean).join(' / ') || '-' }}
+        </el-descriptions-item>
+        <el-descriptions-item label="品牌">{{ (baseInfo as any)?.brandName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="单位">{{ (baseInfo as any)?.unitName || '-' }}</el-descriptions-item>
+      </el-descriptions>
+
+      <!-- 价格信息 -->
+      <div class="sub-title">价格信息</div>
+      <el-descriptions :column="3" border class="mb-4">
+        <el-descriptions-item label="市场价">
+          <span class="text-red-500">¥{{ baseInfo?.marketPrice ?? '-' }}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="官网价">
+          <span class="text-red-500">¥{{ baseInfo?.memberPrice ?? '-' }}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="最低销售价格">
+          <span class="text-red-500">¥{{ baseInfo?.minSellingPrice ?? '-' }}</span>
+        </el-descriptions-item>
+      </el-descriptions>
+
+      <!-- 采购信息 -->
+      <div class="sub-title">采购信息</div>
+      <el-descriptions :column="3" border class="mb-4">
+        <el-descriptions-item label="采购价格">
+          <span>¥{{ baseInfo?.purchasingPrice ?? '-' }}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="暂估毛利率">
+          <span>{{ baseInfo?.tempGrossMargin != null ? baseInfo.tempGrossMargin + '%' : '-' }}</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="最高采购价">
+          <span>¥{{ (baseInfo as any)?.maxPurchasePrice ?? '-' }}</span>
+        </el-descriptions-item>
+      </el-descriptions>
+
+      <!-- 其他基础信息 -->
+      <el-descriptions :column="3" border class="mb-4">
+        <el-descriptions-item label="起订量">{{ baseInfo?.minOrderQuantity ?? '-' }}</el-descriptions-item>
+        <el-descriptions-item label="上下架状态">
+          <el-tag v-if="baseInfo?.productStatus === 1" type="success">已上架</el-tag>
+          <el-tag v-else-if="baseInfo?.productStatus === 0" type="warning">下架</el-tag>
+          <el-tag v-else-if="baseInfo?.productStatus === 2" type="info">上架中</el-tag>
+          <span v-else>-</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="主供应商">{{ (baseInfo as any)?.freightPrice ?? '-' }}</el-descriptions-item>
+      </el-descriptions>
+
+      <el-divider />
+
+      <!-- 入池信息 -->
+      <div class="section-title">入池信息</div>
+      <el-descriptions :column="2" border class="mb-4">
+        <el-descriptions-item label="申请类型" :span="2">
+          <el-tag v-if="String(auditInfo?.type) === '0'" type="success">入池申请</el-tag>
+          <el-tag v-else-if="String(auditInfo?.type) === '1'" type="warning">出池申请</el-tag>
+          <span v-else>-</span>
+        </el-descriptions-item>
+        <el-descriptions-item label="申请单号">{{ auditInfo?.name || auditInfo?.id || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="申请人">{{ auditInfo?.createByName || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="申请时间">{{ (auditInfo as any)?.createTime || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="备注" :span="2">{{ auditInfo?.remark || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="附件" :span="2">
+          <el-tag v-if="(auditInfo as any)?.attachment" type="success">已上传</el-tag>
+          <el-tag v-else type="info">暂无附件</el-tag>
+        </el-descriptions-item>
+      </el-descriptions>
+
+      
+      <el-divider />
+
+      <!-- 客户信息(协议池 type=2) -->
+      <template v-if="auditInfo && String(auditInfo.type) === '2'">
+        <div class="section-title">客户协议信息</div>
+        <el-descriptions :column="2" border class="mb-4" v-loading="extraLoading">
+          <el-descriptions-item label="客户编号">{{ customerInfo?.customerNo || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="客户名称">{{ customerInfo?.customerName || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="协议价格">{{ priceForm.agreementPrice || '-' }}</el-descriptions-item>
+        </el-descriptions>
+      </template>
+
+      <!-- 项目信息(项目池 type=3) -->
+      <template v-if="auditInfo && String(auditInfo.type) === '3'">
+        <div class="section-title">项目信息</div>
+        <el-descriptions :column="2" border class="mb-4" v-loading="extraLoading">
+          <el-descriptions-item label="项目名称">{{ itemInfo?.itemName || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="项目编号">{{ itemInfo?.itemKey || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="第三方价格">{{ priceForm.agreementPrice || '-' }}</el-descriptions-item>
+        </el-descriptions>
+      </template>
+
+      <!-- 营销产品池信息(营销池 type=4) -->
+      <template v-if="auditInfo && String(auditInfo.type) === '4'">
+        <div class="section-title">营销产品池信息</div>
+        <el-descriptions :column="2" border class="mb-4" v-loading="extraLoading">
+          <el-descriptions-item label="池编码">{{ poolInfo?.poolNo || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="池名称">{{ poolInfo?.name || '-' }}</el-descriptions-item>
+          <el-descriptions-item label="协议价格">{{ priceForm.agreementPrice || '-' }}</el-descriptions-item>
+        </el-descriptions>
+      </template>
+    </div>
+
+    <template #footer>
+      <el-button @click="handleClose">关 闭</el-button>
+    </template>
+  </el-drawer>
+</template>
+
+<script setup lang="ts">
+import { getBase } from '@/api/product/base';
+import { getPoolAudit } from '@/api/product/poolAudit';
+import { getPoolLinkAudit } from '@/api/product/poolLinkAudit';
+import { getCustomerInfo } from '@/api/customer/customerInfo';
+import { getItem } from '@/api/external/item';
+import { getPool } from '@/api/product/pool';
+import { BaseVO } from '@/api/product/base/types';
+import { PoolAuditVO } from '@/api/product/poolAudit/types';
+import { PoolLinkAuditVO } from '@/api/product/poolLinkAudit/types';
+import { CustomerInfoVO } from '@/api/customer/customerInfo/types';
+import { ItemVO } from '@/api/external/item/types';
+import { PoolVO } from '@/api/product/pool/types';
+
+interface Props {
+  modelValue: boolean;
+  id?: string | number;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  modelValue: false
+});
+
+const emit = defineEmits<{
+  (e: 'update:modelValue', val: boolean): void;
+}>();
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (val) => emit('update:modelValue', val)
+});
+
+const loading = ref(false);
+const extraLoading = ref(false);
+const linkInfo = ref<PoolLinkAuditVO | null>(null);
+const baseInfo = ref<BaseVO | null>(null);
+const auditInfo = ref<PoolAuditVO | null>(null);
+const customerInfo = ref<CustomerInfoVO | null>(null);
+const itemInfo = ref<ItemVO | null>(null);
+const poolInfo = ref<PoolVO | null>(null);
+
+const priceForm = reactive({
+  discountRate: undefined as number | undefined,
+  agreementPrice: undefined as number | undefined
+});
+
+/** 根据池类型加载对应详情 */
+const loadExtraInfo = async (type: string, auditData: PoolAuditVO) => {
+  extraLoading.value = true;
+  try {
+    if (type === '2' && auditData.customerId) {
+      // 协议池 → 查询客户信息
+      const res = await getCustomerInfo(auditData.customerId);
+      customerInfo.value = res.data || null;
+    } else if (type === '3' && auditData.itemId) {
+      // 项目池 → 查询项目信息
+      const res = await getItem(auditData.itemId);
+      itemInfo.value = res.data || null;
+    } else if (type === '4' && auditData.poolId) {
+      // 营销池 → 查询产品池信息
+      const res = await getPool(auditData.poolId);
+      poolInfo.value = res.data || null;
+    }
+  } catch (error) {
+    console.error('加载附加信息失败:', error);
+  } finally {
+    extraLoading.value = false;
+  }
+};
+
+/** 加载数据:先查关联审核,再并行查商品基础信息和入池单信息 */
+const loadData = async () => {
+  if (!props.id) return;
+  loading.value = true;
+  try {
+    // Step 1: 查询关联审核记录,获取 productId 和 poolAuditId
+    const linkRes = await getPoolLinkAudit(props.id);
+    linkInfo.value = linkRes.data || null;
+
+    if (!linkInfo.value) return;
+
+    const { productId, poolAuditId, negotiatedPrice } = linkInfo.value;
+
+    // Step 2: 并行查商品基础信息和入池单信息
+    const [baseRes, auditRes] = await Promise.all([
+      getBase(productId),
+      poolAuditId ? getPoolAudit(poolAuditId) : Promise.resolve({ data: null })
+    ]);
+    baseInfo.value = baseRes.data || null;
+    auditInfo.value = (auditRes as any)?.data || null;
+
+    // 初始化协议价格
+    priceForm.agreementPrice = negotiatedPrice ?? undefined;
+    if (priceForm.agreementPrice && baseInfo.value?.memberPrice) {
+      priceForm.discountRate = Number(((priceForm.agreementPrice / baseInfo.value.memberPrice) * 100).toFixed(2));
+    }
+
+    // Step 3: 根据池类型加载对应的附加信息
+    if (auditInfo.value?.type) {
+      await loadExtraInfo(String(auditInfo.value.type), auditInfo.value);
+    }
+  } catch (error) {
+    console.error('加载产品详情失败:', error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleClose = () => {
+  visible.value = false;
+};
+
+watch(
+  () => props.modelValue,
+  (val) => {
+    if (val) {
+      linkInfo.value = null;
+      baseInfo.value = null;
+      auditInfo.value = null;
+      customerInfo.value = null;
+      itemInfo.value = null;
+      poolInfo.value = null;
+      priceForm.discountRate = undefined;
+      priceForm.agreementPrice = undefined;
+      loadData();
+    }
+  }
+);
+</script>
+
+<style scoped lang="scss">
+.section-title {
+  font-size: 15px;
+  font-weight: 600;
+  color: #303133;
+  margin-bottom: 12px;
+  padding-left: 8px;
+  border-left: 3px solid var(--el-color-primary);
+}
+
+.sub-title {
+  font-size: 13px;
+  font-weight: 500;
+  color: #606266;
+  margin-bottom: 8px;
+  padding-left: 6px;
+}
+</style>